自学内容网 自学内容网

Unity自我实现响应式属性

        其实只是写着玩,响应式编程建议使用UniRx插件(一套成熟的响应式编程解决方案),我写的主要是借鉴一下这个思想,实现的也不够优雅,不过逻辑也算严密可以正常使用.你可以查看我写的理解响应式属性的思想.

        借鉴UniRx的ReactiveProperty类,且UniRx不仅有响应式属性.

using System;
using System.Linq;

/// <summary>
/// 当值发生变化时触发回调(例如当玩家血量变化时更新GUI)。
/// </summary>
public class ReactiveValue<T>
{
    /// <summary>
    /// 定义过滤器委托,允许在设置新值时对值进行过滤或修正。
    /// </summary>
    private delegate T Filter(T originalValue, T newValue);

    /// <summary>
    /// 值变化时的回调委托。
    /// </summary>
    private Action<T> m_Set;

    /// <summary>
    /// 用于处理值变化前的过滤器。
    /// </summary>
    private Filter m_Filter;

    /// <summary>
    /// 当前的值。
    /// </summary>
    private T m_CurrentValue;

    /// <summary>
    /// 上一次的值,仅供内部使用。
    /// </summary>
    private T m_LastValue;

    /// <summary>
    /// 初始化值。
    /// </summary>
    /// <param name="initialValue">初始值。</param>
    public ReactiveValue(T initialValue)
    {
        m_CurrentValue = initialValue;
        m_LastValue = m_CurrentValue;
    }
    /// <summary>
    /// 使用T的默认值初始化值
    /// </summary>
    public ReactiveValue() : this(default(T)) { }

    /// <summary>
    /// 判断当前值是否等于指定值。
    /// </summary>
    /// <param name="value">要比较的值。</param>
    /// <returns>如果当前值等于指定值,返回 true;否则返回 false。</returns>
    public bool IsEquals(T value)
    {
        return m_CurrentValue != null && m_CurrentValue.Equals(value);
    }

    /// <summary>
    /// 添加一个回调
    /// </summary>
    /// <param name="callback">回调</param>
    /// <param name="immediateCall">添加了回调是否立即调用</param>
    public void AddChangeListener(Action<T> callback, bool immediateCall = false)
    {

        if (m_Set == null || !m_Set.GetInvocationList().Contains(callback))
        {
            m_Set += callback;
            if (immediateCall)
            {
                callback?.Invoke(m_CurrentValue);
            }
        }
    }

    /// <summary>
    /// 移除指定的监听器。
    /// </summary>
    /// <param name="callback">要移除的回调函数。</param>
    public void RemoveChangeListener(Action<T> callback)
    {
        if (m_Set != null && m_Set.GetInvocationList().Contains(callback))
        {
            m_Set -= callback;
        }
    }

    /// <summary>
    /// 移除所有的监听器,谨慎使用,因为别人也进行了订阅
    /// </summary>
    public void RemoveAllChangeListeners()
    {
        m_Set = null;
    }

    /// <summary>
    /// 设置过滤器,过滤器将在回调前被调用,适合用于值限制(如玩家血量不能超过最大值等)。
    /// </summary>
    /// <param name="filter">过滤器委托:<原值,新值,返回值>。</param>
    public void SetFilter(Func<T, T, T> filter)
    {
        m_Filter = new Filter(filter);
    }

    /// <summary>
    /// 获取当前值。
    /// </summary>
    /// <returns>返回当前值。</returns>
    public T Get()
    {
        return m_CurrentValue;
    }

    /// <summary>
    /// 设置新值,只有当新值与旧值不相同时才会触发回调。
    /// </summary>
    /// <param name="value">要设置的新值。</param>
    public void Set(T value)
    {
        m_LastValue = m_CurrentValue;
        m_CurrentValue = value;

        if (m_Filter != null)
            m_CurrentValue = m_Filter(m_LastValue, m_CurrentValue);

        // 当新值和旧值不相等时触发回调
        if (m_LastValue == null || !m_LastValue.Equals(m_CurrentValue))
            m_Set?.Invoke(m_CurrentValue);
    }

    /// <summary>
    /// 强制更新值并触发回调,即使新值与旧值相同。
    /// </summary>
    /// <param name="value">要设置的新值。</param>
    public void SetValueForceCallBack(T value)
    {
        m_LastValue = m_CurrentValue;
        m_CurrentValue = value;

        if (m_Filter != null)
            m_CurrentValue = m_Filter(m_LastValue, m_CurrentValue);

        // 强制触发回调
        m_Set?.Invoke(m_CurrentValue);
    }

    /// <summary>
    /// 设置新值但不触发回调。
    /// </summary>
    /// <param name="value">要设置的新值。</param>
    public void SetValueDontCallBack(T value)
    {
        m_LastValue = m_CurrentValue;
        m_CurrentValue = value;

        if (m_Filter != null)
            m_CurrentValue = m_Filter(m_LastValue, m_CurrentValue);
    }
}




示例用法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Text textComponent;
    //一般会声明在数据层,可以优雅地实现MVC等框架,这里为了方便
    ReactiveValue<string> t = new ReactiveValue<string>("张三");
    void Start()
    {
        t.AddChangeListener(Handle, true);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            if (t.IsEquals("张三"))
            {
                t.Set("李四");
            }
            else
            {
                t.Set("张三");
            }
        }
    }

    void Handle(string t)
    {
        textComponent.text = t;
    }
    private void OnDestroy()
    {
        t.RemoveChangeListener(Handle);
    }
}

UniRx实现

如果使用UniRx实现的话非常优雅.

AddTo的意思就是绑定了这个gameObject对象,当这个对象被销毁那么自动移除这个对象绑定的回调,就不需要傻傻的在OnDestroy中移除了,并且也可以使用匿名函数.

using System.Collections;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Text textComponent;
    ReactiveProperty<string> t = new ReactiveProperty<string>("张三");
    void Start()
    {
        t.Subscribe(t => textComponent.text = t).AddTo(gameObject);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            if (t.Value == "张三")
            {
                t.Value = "李四";
            }
            else
            {
                t.Value = "张三";
            }
        }
    }


}

实际是动态为gameObject对象动态添加了一个


原文地址:https://blog.csdn.net/R3333355726856/article/details/142374816

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!