using System;
using System.Collections.Generic;
using Unity.Properties;
using Uigenx.Binding;
using UnityEngine;
using UnityEngine.UIElements;
namespace Game.UI.Samples
{
///
/// A hand written data source. `[UgxDataSource]` puts it in the studio's schema, `[CreateProperty]`
/// makes a property bindable by Unity's own runtime binding, and calling Notify from the setters
/// is what tells the bindings to refresh.
///
///
/// The paths the generated binding table uses are exactly the member names reported by
/// `UgxSchemaWalker`: `hp`, `maxHp`, `hpNormalised`, `warningOpacity`. Prefixing them in the
/// studio without a matching nested member here would make every binding fail to resolve.
///
[UgxDataSource("Player")]
public sealed class HudDataSource : UgxDataSource
{
int _hp = 100;
int _maxHp = 100;
[CreateProperty]
public int hp
{
get => _hp;
set
{
if (_hp == value) return;
_hp = value;
Notify();
Notify(nameof(hpNormalised));
Notify(nameof(lowHealth));
Notify(nameof(warningOpacity));
}
}
[CreateProperty]
public int maxHp
{
get => _maxHp;
set
{
if (_maxHp == value) return;
_maxHp = value;
Notify();
Notify(nameof(hpNormalised));
Notify(nameof(lowHealth));
Notify(nameof(warningOpacity));
}
}
[CreateProperty(ReadOnly = true)]
public float hpNormalised => _maxHp <= 0 ? 0f : Mathf.Clamp01((float)_hp / _maxHp);
[CreateProperty(ReadOnly = true)]
public bool lowHealth => hpNormalised < 0.25f;
///
/// Style bindings need a value the style parser understands, so the boolean is exposed as
/// the number the `opacity` binding actually wants.
///
[CreateProperty(ReadOnly = true)]
public float warningOpacity => lowHealth ? 1f : 0f;
}
///
/// Drives the player's health so the sample HUD visibly reacts. It forwards the whole data
/// source contract, change notification included: a component that implemented only
/// IUgxDataSource would render once and then freeze.
///
[AddComponentMenu("UI Creator/Samples/Minimal HUD Driver")]
public sealed class MinimalHudDriver : MonoBehaviour, IUgxDataSource, INotifyBindablePropertyChanged,
IDataSourceViewHashProvider
{
[SerializeField] float damagePerSecond = 8f;
public HudDataSource Data { get; } = new HudDataSource();
float _accumulated;
public event EventHandler propertyChanged
{
add => Data.propertyChanged += value;
remove => Data.propertyChanged -= value;
}
public long GetViewHashCode() => Data.GetViewHashCode();
public bool TryGet(string path, out object value) => Data.TryGet(path, out value);
public bool TrySet(string path, object value) => Data.TrySet(path, value);
public IEnumerable Paths => Data.Paths;
void Update()
{
_accumulated += damagePerSecond * Time.deltaTime;
if (_accumulated < 1f) return;
int damage = Mathf.FloorToInt(_accumulated);
_accumulated -= damage;
Data.hp = Mathf.Max(0, Data.hp - damage);
}
}
}