在本文中,我将展示一个简化的实现无冷却、无UI、无多个技能的冰冻系统。

首先,让我们看看主 Freeze 脚本:

public class Freeze : MonoBehaviour
{
    [SerializeField] private float _duration;

    public event Action FreezeActivate;
    public event Action FreezeDisactivate;

    public static Freeze Instance;

    private void Awake()
    {
        if (Instance != null && Instance != this)
            Destroy(gameObject);

        Instance = this;
    }

    public void UseAbility()
    {
        StartCoroutine(ActivateFreeze());
    }

    private IEnumerator ActivateFreeze()
    {
        FreezeActivate?.Invoke();

        yield return new WaitForSeconds(_duration);

        FreezeDisactivate?.Invoke();
    }
}

这个脚本只触发事件的正确顺序。

_duration 变量控制冰冻的持续时间。

接下来,有两个事件用于冻结/解冻,单例用于快速访问脚本。

要激活技能,public UseAbility() 方法被调用,它开始一个协程。

在协程内部:

  • FreezeActivate 被触发
  • 游戏等待 _duration
  • FreezeDisactivate 被触发

现在,我们需要在需要的对象中订阅这些事件。让我们从障碍物脚本开始,该脚本应该被冻结。

首先,创建几个变量:

// 冻结时的颜色
[SerializeField] private Color _freezeColor;

private Rigidbody _rigidbody;

// 存储对象的速度前冻结
private Vector3 _savedVelocity;

// 存储角速度
private Vector3 _savedAngularVelocity;

// 用于改变颜色的
private MeshRenderer _meshRenderer;

接下来,创建两个方法用于冻结和解冻。

ActivateFreeze() 内部:

// 改变物体颜色
_meshRenderer.material.color = _freezeColor;

// 保存速度和角速度
_savedVelocity = _rigidbody.linearVelocity;
_savedAngularVelocity = _rigidbody.angularVelocity;

// 将速度值设置为零
_rigidbody.linearVelocity = Vector3.zero;
_rigidbody.angularVelocity = Vector3.zero;

// 开启刚体模式
_rigidbody.isKinematic = true;

DisactivateFreeze() 内部,我们做相反的事情:

// 关闭刚体模式
_rigidbody.isKinematic = false;

// 恢复保存的速度
_rigidbody.linearVelocity = _savedVelocity;
_rigidbody.angularVelocity = _savedAngularVelocity;

// 恢复原来的颜色
// (如果物体原本有其他颜色,
// 最好也保存和恢复它)
_meshRenderer.material.color = Color.white;

现在,我们只需订阅事件:

private void OnEnable()
{
    Freeze.Instance.FreezeActivate += ActivateFreeze;
    Freeze.Instance.FreezeDisactivate += DisactivateFreeze;
}

private void OnDisable()
{
    Freeze.Instance.FreezeActivate -= ActivateFreeze;
    Freeze.Instance.FreezeDisactivate -= DisactivateFreeze;
}

我也有一段独立的障碍物生成脚本。

在冻结期间,不应该生成新的障碍物,所以也在那里使用事件:

private void OnEnable()
{
    Freeze.Instance.FreezeActivate += StopSpawn;
    Freeze.Instance.FreezeDisactivate += StartSpawn;
}

private void OnDisable()
{
    Freeze.Instance.FreezeActivate -= StopSpawn;
    Freeze.Instance.FreezeDisactivate -= StartSpawn;
}

private void StopSpawn()
{
    _isSpawn = false;
}

private void StartSpawn()
{
    _isSpawn = true;
}

然后,在生成障碍物之前,只需检查 _isSpawn 变量。

结果是,一个简单的物理对象冻结系统,它通过事件扩展得很好,游戏中看起来也很满意。