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

Unity如何在Inspector中序列化可配置的生物解锁判断条件

Unity原生序列化系统不支持序列化Func<bool>这类委托、Lambda表达式,你直接加[SerializeField]的委托字段不会在Inspector正常显示,就算通过第三方插件强行序列化,Lambda捕获的外部引用也很容易在编辑器重启、程序集重载、打包后丢失,稳定性很差,不建议使用。

下面是生产环境验证过的可行方案,按稳定性和维护成本从高到低排序:

方案1:原生可序列化条件配置(零依赖、最稳定)

不要直接存储委托,把所有可能用到的判断维度、比较规则、阈值拆成可序列化的字段,运行时组装判断逻辑,完全适配Unity默认Inspector,不会出现任何序列化丢失问题。

  1. 首先定义基础枚举,约束可配置的判断维度和比较规则:
// 可用于解锁判断的玩家属性类型
public enum PlayerStatType
{
    MaxHealth,
    MoveSpeed,
    AttackPower,
    CoinCount
    // 后续新增判断维度直接在这里加枚举值即可
}

// 数值比较规则
public enum CompareType
{
    GreaterThan,
    LessThan,
    EqualTo,
    GreaterOrEqual,
    LessOrEqual
}

// 多条件组合规则
public enum ConditionCombineType
{
    And, // 所有子条件全部满足才解锁
    Or   // 任意一个子条件满足即可解锁
}
  1. 编写可序列化的解锁条件类,封装判断逻辑:
[System.Serializable]
public class UnlockCondition
{
    public ConditionCombineType combineType = ConditionCombineType.And;
    public List<UnlockCondition> subConditions = new List<UnlockCondition>();
    
    // 单条件配置字段
    public PlayerStatType targetStat;
    public CompareType compareRule;
    public float threshold;

    public bool Check(PlayerData player)
    {
        // 存在子条件时优先按组合规则判断子条件
        if (subConditions.Count > 0)
        {
            bool result = combineType == ConditionCombineType.And;
            foreach (var sub in subConditions)
            {
                bool subCheck = sub.Check(player);
                if (combineType == ConditionCombineType.And && !subCheck)
                {
                    result = false;
                    break;
                }
                if (combineType == ConditionCombineType.Or && subCheck)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }

        // 单条件判断逻辑
        float currentValue = targetStat switch
        {
            PlayerStatType.MaxHealth => player.maxHealth,
            PlayerStatType.MoveSpeed => player.moveSpeed,
            PlayerStatType.AttackPower => player.attack,
            PlayerStatType.CoinCount => player.coins,
            _ => 0
        };

        return compareRule switch
        {
            CompareType.GreaterThan => currentValue > threshold,
            CompareType.LessThan => currentValue < threshold,
            CompareType.EqualTo => Mathf.Approximately(currentValue, threshold),
            CompareType.GreaterOrEqual => currentValue >= threshold,
            CompareType.LessOrEqual => currentValue <= threshold,
            _ => false
        };
    }
}
  1. 修改Creature和Manager类,替换原来的Func字段:
public class Creature : MonoBehaviour
{
    // 该字段会直接显示在Inspector中,可直接下拉选择配置
    public UnlockCondition unlockCondition;

    public void Unlock()
    {
        // 原有解锁逻辑
        gameObject.SetActive(true);
    }
}

public class Manager : MonoBehaviour
{
    public Creature[] creatures;
    public PlayerData playerData; // 场景中挂载的玩家数据引用

    void Start()
    {
        foreach (var creature in creatures)
        {
            if (creature.unlockCondition.Check(playerData))
            {
                creature.Unlock();
            }
        }
    }
}

这个方案的优缺点:

  • 优点:零第三方依赖,序列化100%稳定,编辑器重启、打包都不会丢配置,非程序人员也能直接在Inspector配置规则。对应你提到的需求,配置Ogre时选MaxHealth+GreaterThan+阈值120,配置Bat时选MoveSpeed+GreaterThan+阈值80即可,不需要写额外代码。后续要加新判断维度只要修改枚举和对应的switch分支,维护成本极低。
  • 缺点:对于非数值类的特殊解锁条件(比如“通关第三章”“击败特定BOSS”),需要额外加对应的枚举和判断分支。
方案2:自定义可序列化UnityEvent(适配复杂特殊条件)

如果有大量非数值类的解锁判断,可以用自定义UnityEvent实现Inspector可视化绑定方法,不需要把所有判断逻辑都堆在条件类的switch里:

// 自定义带返回值的可序列化事件
[System.Serializable]
public class UnlockCheckEvent : UnityEvent<UnlockCheckContext> {}

// 事件传递的上下文,用来回写判断结果
public class UnlockCheckContext
{
    public bool isPassed;
}

public class Creature : MonoBehaviour
{
    public UnlockCheckEvent onCheckUnlock;
    private UnlockCheckContext _checkContext = new UnlockCheckContext();

    public bool CheckUnlock()
    {
        _checkContext.isPassed = false;
        onCheckUnlock?.Invoke(_checkContext);
        return _checkContext.isPassed;
    }

    public void Unlock()
    {
        gameObject.SetActive(true);
    }
}

使用时直接在Inspector里把对应判断逻辑的方法绑定到事件上,方法内拿到UnlockCheckContext对象后根据逻辑设置isPassed的值即可。

  • 优点:灵活度高,任意组件的任意判断逻辑都可以直接绑定,不需要修改核心条件类代码。
  • 缺点:配置成本比方案1高,绑定的方法如果改名、删除会出现引用丢失,需要定期检查配置。
方案3:借助第三方插件实现

如果项目已经集成了Odin Inspector、Unity Visual Scripting这类插件,可以直接用插件提供的可序列化委托、可视化节点功能配置条件,省掉自己写序列化类的步骤。

注意:这类方案依赖第三方插件的序列化实现,版本升级、打包时容易出现引用丢失问题,小型项目不建议为了这个功能额外引入插件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 22:16:19