时间:2025-02-22 17:42
人气:
作者:admin
在Unity 2021 LTS版本中,结合Burst Compiler可以将表达式树编译后的委托性能提升至接近原生C++代码水平,特别适合高频调用的游戏系统(如物理伤害计算、AI决策等)
参考:git-amend
传统Unity开发面临三大痛点:
表达式树(Expression Trees)技术通过将代码转换为可操作的数据结构,完美解决了这些问题。它允许我们:
动态技能系统
数据驱动AI
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();
}
}
应用场景:获取对象属性 技术要点:
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);
}
}
参数定义:定义了两个参数 enemyParam 和 heroParam,用于表示敌人和英雄。
条件表达式:
heroLowHealth: 检查英雄的生命值是否低于 30。
heroNear: 检查英雄与敌人的距离是否小于 10。行为选择:
使用 CreateActionExpression 方法创建 Attack、Taunt 和 Patrol 行为的表达式。
Expression.Condition 创建条件表达式,根据条件选择行为。返回 Lambda 表达式:最终返回一个 Lambda 表达式,接受 Enemy 和 Hero 作为参数,并返回相应的行为函数。
enemyParam 和 heroParam。Enemy 类中与 methodName 相对应的方法。Expression.Call 创建方法调用表达式,并返回一个 Lambda 表达式。
作者:世纪末的魔术师
出处:https://www.cnblogs.com/Firepad-magic/
Unity最受欢迎插件推荐:点击查看
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。