Location>code7788 >text

Playing the Expression Tree in Unity: Dynamic Magic of Unlocking Game Logic

Popularity:450 ℃/2025-02-22 17:44:10
 
 
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;

public class EnemyStateMachine : MonoBehaviour {
    
    //Define the state machine delegate, take Enemy and Hero as parameters, and return an empty method with Enemy and Hero as parameters
    //It will return a corresponding behavior function based on the hero's state (such as health and distance).
    Func<Enemy, Hero, Action<Enemy, Hero>> stateEvaluator;
    //Storing the current behavior function
    Action<Enemy, Hero> behavior;
    Enemy enemy;
    Hero hero;

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

    void Update() {
        //Get the current behavior
        behavior = stateEvaluator(enemy, hero);
        //Execute the current behavior
        behavior(enemy, hero);
        
        Debug.Log("Enemy Aggression Level:", enemy.AggressionLevel);
    }

    public Func<Enemy, Hero, Action<Enemy, Hero>> CreateDynamicStateMachine() {
        //Define parameter expression
        ParameterExpression enemyParam = Expression.Parameter(typeof(Enemy), "enemy");
        ParameterExpression heroParam = Expression.Parameter(typeof(Hero), "hero");
        
        //Define a binary expression
        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();
        
        // Conditional expression, if heroNear is true, execute taunt, otherwise execute 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);
    }
}