网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 其他技术区 > 游戏开发 >

在Unity中玩转表达式树:解锁游戏逻辑的动态魔法

时间:2025-02-22 17:42

人气:

作者:admin

标签:

导读:在Unity中玩转表达式树:解锁游戏逻辑的动态魔法 在Unity 2021 LTS版本中,结合Burst Compiler可以将表达式树编译后的委托性能提升至接近原生C代码水平,特别适合高频调用的游戏系统(如物...

在Unity 2021 LTS版本中,结合Burst Compiler可以将表达式树编译后的委托性能提升至接近原生C++代码水平,特别适合高频调用的游戏系统(如物理伤害计算、AI决策等)

参考:git-amend

Expression

一、为什么要学习表达式树?

传统Unity开发面临三大痛点:

  1. 逻辑固化 - 编译后无法修改行为逻辑
  2. 组件强耦合 - GameObject之间依赖关系复杂
  3. 动态性不足 - 难以实现运行时逻辑热替换

表达式树(Expression Trees)技术通过将代码转换为可操作的数据结构,完美解决了这些问题。它允许我们:

  • 运行时动态构建逻辑运行时动态构建逻辑
  • 实现组件间的弱耦合通信实现组件间的弱耦合通信
  • 支持可视化配置游戏行为支持可视化配置游戏行为

二、核心应用场景

  1. 动态技能系统

  2. 数据驱动AI

    • 通过JSON配置行为树
    • 运行时解析并生成表达式
    • 实现无需重新编译的AI逻辑更新
  3. MOD支持系统

    • 玩家自定义逻辑脚本
    • 安全沙箱运行表达式
    • 实时加载玩家创作内容

三、实战演示

一、 属性获取器

传统模式缺陷:

     
xxxxxxxxxx
       
public int GetPlayerStat(Player p, string statName)
{
    switch(statName)
    {
        case "Health": return p.Health;
        case "Mana": return p.Mana;
        // 每新增一个属性需要修改此处
    }
}
   

表达式树:

     
xxxxxxxxxx
       
using System;
using System.Linq.Expressions;
using UnityEngine;

public class ExpressionTreeDemo : MonoBehaviour {
    void Start() {
        Player player = new () { Health = 100 };
        Func<Player, int> healthProperty = CreatePropertyGetter<Player, int>("Health");
        Debug.Log($"Player Health: {healthProperty(player)}");
    }

    public int GetPlayerStat(Player player, string statName) {
        Func<Player, int> propertyGetter = CreatePropertyGetter<Player, int>(statName);
        return propertyGetter(player);
    }

    public Func<T, TProperty> CreatePropertyGetter<T, TProperty>(string propertyName) {
        ParameterExpression param = Expression.Parameter(typeof(T), "x");
        MemberExpression property = Expression.Property(param, propertyName);
        Expression<Func<T, TProperty>> lambda = Expression.Lambda<Func<T, TProperty>>(property, param);
        return lambda.Compile();
    }
}
   

应用场景:获取对象属性 技术要点

  1. 属性访问表达式属性访问表达式:Expression.Property

二、条件触发系统

     
xxxxxxxxxx
       
public class ConditionTrigger : MonoBehaviour 
{
    public string ConditionExpression = "Player.Health.CurrentHP < 0.3";
    public GameObject ContextObject;

    private Func<GameObject, bool> _compiledCondition;
    private static Dictionary<string, Func<GameObject, bool>> _cache = new();

    void Start()
    {
        if (!_cache.TryGetValue(ConditionExpression, out _compiledCondition))
        {
            var elements = ConditionExpression.Split('.');
            var rootObj = Expression.Parameter(typeof(GameObject), "context");
            Expression accessChain = rootObj;
            foreach (var element in elements.Skip(1))
            {
                accessChain = Expression.PropertyOrField(accessChain, element);
            }

            var conditionExpr = BuildComparison(accessChain, "<", Expression.Constant(0.3f));
            _compiledCondition = Expression.Lambda<Func<GameObject, bool>>(conditionExpr, rootObj).Compile();
            _cache[ConditionExpression] = _compiledCondition;
        }
    }

    void Update()
    {
        if (_compiledCondition(ContextObject))
        {
            Debug.Log("触发条件!");
        }
    }

    private Expression BuildComparison(Expression left, string operatorStr, Expression right)
    {
        return operatorStr switch
        {
            "<" => Expression.LessThan(left, right),
            ">" => Expression.GreaterThan(left, right),
            "==" => Expression.Equal(left, right),
            "!=" => Expression.NotEqual(left, right),
            "<=" => Expression.LessThanOrEqual(left, right),
            ">=" => Expression.GreaterThanOrEqual(left, right),
            _ => throw new NotSupportedException($"不支持的运算符: {operatorStr}")
        };
    }
}
   

应用场景:动态游戏事件触发 优势对比

传统方式表达式树方案
硬编码条件判断 支持运行时修改条件逻辑
需要预定义所有情况 可通过配置文件动态加载

三、行为链组合

     
xxxxxxxxxx
       
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using UnityEngine;

// 使用示例
public class ComboExample : MonoBehaviour
{
    private ComboSystem _comboSystem;

    void Start()
    {
        _comboSystem = new ComboSystem();

        // 添加连招动作
        _comboSystem.ActionExpressions.Add(GetComboExpression(typeof(AttackAnimation), nameof(AttackAnimation.Play), Expression.Constant("SwordSwing")));
        _comboSystem.ActionExpressions.Add(GetComboExpression(typeof(EffectsManager), nameof(EffectsManager.Spawn), Expression.Constant("SwordHit")));
        _comboSystem.ActionExpressions.Add(GetComboExpression(typeof(AttackAnimation), nameof(AttackAnimation.Play), Expression.Constant("SwordSwing2")));
        _comboSystem.ActionExpressions.Add(GetComboExpression(typeof(DamageCalculator), nameof(DamageCalculator.Apply), Expression.Constant(new Vector3(0, 1, 0)), Expression.Constant(100f)));

        // 执行连招
        _comboSystem.ExecuteCombo();
    }

    Expression<Action> GetComboExpression(Type type, string methodName, params Expression[] args)
    {
        return Expression.Lambda<Action>(Expression.Call(type, methodName, null, args));
    }
}

public class ComboSystem
{
    // 存储动作表达式的列表
    public List<Expression<Action>> ActionExpressions = new();

    // 执行连招
    public void ExecuteCombo()
    {
        // 构建复合表达式
        var comboBlock = Expression.Block(
            ActionExpressions.Select(exp => exp.Body)
        );

        // 创建最终的表达式
        //Compile() 方法将 Lambda 表达式编译为可执行的委托。
        //Invoke() 方法调用该委托,从而执行块中的所有表达式。
        var finalExpr = Expression.Lambda<Action>(comboBlock);
        finalExpr.Compile().Invoke(); // 执行连招
    }
}

// 示例动作类
public class AttackAnimation
{
    public static void Play(string animationName)
    {
        Debug.Log($"播放动画: {animationName}");
    }
}

public class EffectsManager
{
    public static void Spawn(string effectName)
    {
        Debug.Log($"生成特效: {effectName}");
    }
}

public class DamageCalculator
{
    public static void Apply(Vector3 position, float damage)
    {
        Debug.Log($"应用伤害: {damage} 到位置: {position}");
    }
}

//生成特效: SwordHit
//播放动画: SwordSwing2
//应用伤害: 100 到位置: (0.00, 1.00, 0.00)
   

Expression.Block:

Expression.Block 是一个非常强大的功能,它允许我们将多个表达式组合成一个块(block),并在执行时按顺序执行这些表达式。

 

四、运行时状态机

     
xxxxxxxxxx
       
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;

public class EnemyStateMachine : MonoBehaviour {
    
    //定义状态机委托,以Enemy和Hero为参数,返回一个以Enemy和Hero为参数的空方法
    //它会根据英雄的状态(如生命值和距离)返回一个相应的行为函数。
    Func<Enemy, Hero, Action<Enemy, Hero>> stateEvaluator;
    //存储当前行为函数
    Action<Enemy, Hero> behavior;
    Enemy enemy;
    Hero hero;

    void Start() {
        enemy = FindObjectOfType<Enemy>();
        hero = FindObjectOfType<Hero>();
        stateEvaluator = CreateDynamicStateMachine();
    }

    void Update() {
        //获取当前行为
        behavior = stateEvaluator(enemy, hero);
        //执行当前行为
        behavior(enemy, hero);
        
        Debug.Log("Enemy Aggression Level:", enemy.AggressionLevel);
    }

    public Func<Enemy, Hero, Action<Enemy, Hero>> CreateDynamicStateMachine() {
        //定义参数表达式
        ParameterExpression enemyParam = Expression.Parameter(typeof(Enemy), "enemy");
        ParameterExpression heroParam = Expression.Parameter(typeof(Hero), "hero");
        
        //定义一个二元表达式
        BinaryExpression heroLowHealth = Expression.LessThan(
            Expression.Property(heroParam, "Health"),
            Expression.Constant(30)
        );
        BinaryExpression heroNear = Expression.LessThan(
            Expression.Property(heroParam, "Distance"),
            Expression.Constant(10f)
        );
        
        Debug.Log($"HeroLowHealth{heroLowHealth}");
        Debug.Log($"HeroNear{heroNear}");
        
        var attack = CreateActionExpression("Attack").Compile();
        var taunt = CreateActionExpression("Taunt").Compile();
        var patrol = CreateActionExpression("Patrol").Compile();
        
        //条件表达式,如果heroNear为真则执行taunt,否则执行patrol
        ConditionalExpression tauntOrPatrol = Expression.Condition(heroNear, Expression.Constant(taunt), Expression.Constant(patrol));
        ConditionalExpression finalCondition = Expression.Condition(heroLowHealth, Expression.Constant(attack), tauntOrPatrol);
        
        //
        var lambda = Expression.Lambda<Func<Enemy, Hero, Action<Enemy, Hero>>>(finalCondition, enemyParam, heroParam);
        return lambda.Compile();
    }
    
    Expression<Action<Enemy, Hero>> CreateActionExpression(string methodName) {
        ParameterExpression enemyParam = Expression.Parameter(typeof(Enemy), "enemy");
        ParameterExpression heroParam = Expression.Parameter(typeof(Hero), "hero");
        
        MethodInfo method = typeof(Enemy).GetMethod(methodName, new[] { typeof(Hero) });
        
        MethodCallExpression call = Expression.Call(enemyParam, method, heroParam);
        return Expression.Lambda<Action<Enemy, Hero>>(call, enemyParam, heroParam);
    }
}
   

CreateDynamicStateMachine

  • 参数定义:定义了两个参数 enemyParamheroParam,用于表示敌人和英雄。

  • 条件表达式:

  • heroLowHealth: 检查英雄的生命值是否低于 30。

    • heroNear: 检查英雄与敌人的距离是否小于 10。
  • 行为选择

  • 使用 CreateActionExpression 方法创建 AttackTauntPatrol 行为的表达式。

    • 使用 Expression.Condition 创建条件表达式,根据条件选择行为。
  • 返回 Lambda 表达式:最终返回一个 Lambda 表达式,接受 EnemyHero 作为参数,并返回相应的行为函数。

CreateActionExpression

  • 参数定义:定义 enemyParamheroParam
  • 获取方法信息:使用反射获取 Enemy 类中与 methodName 相对应的方法。
  • 创建方法调用表达式:使用 Expression.Call 创建方法调用表达式,并返回一个 Lambda 表达式。
Unity最受欢迎的插件,可以让您的游戏如虎添翼,为您节省大量时间可以投入在游戏的创意和细节上 点赞鼓励下,(づ ̄3 ̄)づ╭❤~

作者:世纪末的魔术师
       出处:https://www.cnblogs.com/Firepad-magic/
       Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
  • 便捷打包Instant Asset脚本

    便捷打包Instant Asset脚本

    使用说明 将脚本放在Assets/Editor目录下 在需要打包同一个table的资源要放在同一...
  • Tuanjie InstantAsset

    Tuanjie InstantAsset

    InstantAsset InstantAsset 是一套全新的资产解决方案,旨在提升开发者的开发和运行...
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信