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

时间:2025-02-22 17:46:25
 
 
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);
    }
}