You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scriptable Object架构下玩家生命值的数据安全问题咨询

关于Scriptable Object生命值权限控制的解决方案

一、确保只有PlayerHealth类能修改生命值的实现方式

1. 封装修改逻辑,用访问修饰符限制调用

最稳妥的方案是在HealthSO中封装所有修改生命值的逻辑,仅对外暴露只读访问接口,将修改方法设为internal(或配合程序集可见性):

using UnityEngine;

[CreateAssetMenu(fileName = "HealthSO", menuName = "Scriptable Objects/Health")]
public class HealthSO : ScriptableObject
{
    [SerializeField] private int _currentHealth;
    [SerializeField] private int _maxHealth;

    // 对外只读属性,供UI、成就等系统读取数据
    public int CurrentHealth => _currentHealth;
    public int MaxHealth => _maxHealth;

    // 仅允许同程序集内的PlayerHealth调用的修改方法
    internal void UpdateHealth(int newValue)
    {
        _currentHealth = Mathf.Clamp(newValue, 0, _maxHealth);
        // 触发事件,通知依赖系统更新状态
        HealthChanged?.Invoke(_currentHealth, _maxHealth);
    }

    internal void ApplyDamage(int damage)
    {
        UpdateHealth(_currentHealth - damage);
    }

    internal void ApplyHeal(int amount)
    {
        UpdateHealth(_currentHealth + amount);
    }

    // 供外部系统监听生命值变化的事件
    public event System.Action<int, int> HealthChanged;
}

在PlayerHealth类中直接调用这些internal方法即可:

public class PlayerHealth : MonoBehaviour
{
    [SerializeField] private HealthSO _playerHealthSO;

    public void TakeDamage(int damage)
    {
        _playerHealthSO.ApplyDamage(damage);
    }

    public void Heal(int amount)
    {
        _playerHealthSO.ApplyHeal(amount);
    }
}

如果HealthSO和PlayerHealth不在同一个程序集,可在HealthSO所在程序集的AssemblyInfo.cs中添加:

[assembly: InternalsVisibleTo("PlayerAssemblyName")]

指定程序集内的代码就能访问internal方法。

2. (不推荐)通过反射检查调用者

若要严格限制同程序集内其他类误调用,可通过栈追踪检查调用者类型,但这种方式存在性能开销,仅适合调试场景:

internal void UpdateHealth(int newValue)
{
    var stackTrace = new System.Diagnostics.StackTrace();
    var callingMethod = stackTrace.GetFrame(1)?.GetMethod();
    if (callingMethod?.DeclaringType != typeof(PlayerHealth))
    {
        Debug.LogError("仅PlayerHealth类允许修改生命值!");
        return;
    }
    _currentHealth = Mathf.Clamp(newValue, 0, _maxHealth);
    HealthChanged?.Invoke(_currentHealth, _maxHealth);
}

二、大型项目中是否需要重视这个问题?

必须重视,原因如下:

  • 大型项目参与人员多、模块复杂,无权限控制的Scriptable Object极易被其他模块误修改,导致难以复现和排查的bug(比如生命值莫名变动却找不到修改源)。
  • 从架构设计角度,遵循单一职责原则:只有负责玩家生命管理的PlayerHealth类应拥有修改权限,其他系统(如UI、成就、音效)仅需读取数据或监听变化,能有效降低模块耦合,提升代码可维护性。
  • Scriptable Object是全局共享资源,缺乏权限控制会破坏数据一致性,引发连锁问题(比如UI显示异常、成就触发错误等)。

内容的提问来源于stack exchange,提问作者t_Gmek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 10:15:47