Skip to content

Instantly share code, notes, and snippets.

@adammyhre
Created November 17, 2024 08:57
Show Gist options
  • Save adammyhre/13c3685f48016e5b197322113082e418 to your computer and use it in GitHub Desktop.
Save adammyhre/13c3685f48016e5b197322113082e418 to your computer and use it in GitHub Desktop.
Expression Trees in Unity Examples
using System;
using System.Linq.Expressions;
using UnityEngine;
public class DamageCalculator : MonoBehaviour {
void Start() {
Func<int, int, int> damageExpression = CreateDamageEvaluator();
int damage = damageExpression(10, 2);
Debug.Log($"Calculated Damage: {damage}");
}
public Func<int, int, int> CreateDamageEvaluator() {
ParameterExpression baseDamage = Expression.Parameter(typeof(int), "baseDamage");
ParameterExpression multiplier = Expression.Parameter(typeof(int), "multiplier");
BinaryExpression body = Expression.Multiply(baseDamage, multiplier);
return Expression.Lambda<Func<int, int, int>>(body, baseDamage, multiplier).Compile();
}
}
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;
public class EnemyStateMachine : MonoBehaviour {
Func<Enemy, Hero, Action<Enemy, Hero>> stateEvaluator;
Action<Enemy, Hero> behavior;
Enemy enemy;
Hero hero;
void Start() {
enemy = FindFirstObjectByType<Enemy>();
hero = FindFirstObjectByType<Hero>();
stateEvaluator = CreateDynamicStateMachine();
}
void Update() {
behavior = stateEvaluator(enemy, hero);
behavior(enemy, hero);
Logwin.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)
);
Logwin.Log("HeroLowHealth", heroLowHealth);
Logwin.Log("HeroNear", heroNear);
var attack = CreateActionExpression("Attack").Compile();
var taunt = CreateActionExpression("Taunt").Compile();
var patrol = CreateActionExpression("Patrol").Compile();
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);
}
}
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();
}
}
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;
public class MethodInvocationDemo : MonoBehaviour {
void Start() {
Action<Player, object> action = CreateMethodInvoker<Player>("TakeDamage");
Player player = new Player();
action(player, 25);
}
public Action<T, object> CreateMethodInvoker<T>(string methodName) {
ParameterExpression instance = Expression.Parameter(typeof(T), "instance");
ParameterExpression argument = Expression.Parameter(typeof(object), "arg");
MethodInfo method = typeof(T).GetMethod(methodName)
?? throw new ArgumentException($"Method {methodName} not found on type {typeof(T).Name}");
MethodCallExpression call = Expression.Call(instance, method, Expression.Convert(argument, method.GetParameters()[0].ParameterType));
return Expression.Lambda<Action<T, object>>(call, instance, argument).Compile();
}
}
using System;
using System.Linq.Expressions;
using UnityEngine;
public class NPCBehaviorDemo : MonoBehaviour {
void Start() {
Func<int, string> aggressionEvaluator = CreateBehaviorEvaluator();
int playerHealth = 50;
string aggressionLevel = aggressionEvaluator(playerHealth);
Debug.Log($"Aggression Level: {aggressionLevel}");
}
public Func<int, string> CreateBehaviorEvaluator() {
ParameterExpression health = Expression.Parameter(typeof(int), "health");
BinaryExpression condition = Expression.LessThan(health, Expression.Constant(30));
ConstantExpression ifTrue = Expression.Constant("High");
ConstantExpression ifFalse = Expression.Constant("Low");
ConditionalExpression body = Expression.Condition(condition, ifTrue, ifFalse);
return Expression.Lambda<Func<int, string>>(body, health).Compile();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment