【行为型之解释器模式】游戏开发实战——Unity动态公式解析与脚本系统的架构奥秘

📜 解释器模式(Interpreter Pattern)深度解析

——以Unity实现动态技能公式自定义条件系统为核心案例


一、模式本质与核心价值

核心目标
定义语言的文法,并构建解释器执行表达式
灵活扩展语法规则,支持自定义DSL(领域特定语言)
解耦语法解析与执行,提升系统可维护性

关键术语

  • AbstractExpression(抽象表达式):定义解释操作的接口
  • TerminalExpression(终结符表达式):语法中的基础元素
  • NonterminalExpression(非终结符表达式):组合其他表达式的复合元素
  • Context(上下文):存储解释器全局信息

数学表达
对于文法G,解释器I满足:
I(E) = Value
其中E为表达式,Value为执行结果


二、经典UML结构
«interface»
IExpression
+Interpret(context: GameContext) : int
NumberExpression
-value: int
+Interpret()
AddExpression
-left: IExpression
-right: IExpression
+Interpret()
SkillDamageExpression
+Interpret()

三、Unity实战代码(技能伤害计算系统)
1. 定义表达式接口与上下文
public interface ISkillExpression {
    float Interpret(SkillContext context);
}

public class SkillContext {
    public GameObject Caster;
    public GameObject Target;
    public int SkillLevel;
    public Dictionary<string, float> Variables = new();
}
2. 实现基础表达式
// 终结符:常数值
public class ConstantExpr : ISkillExpression {
    private float _value;
    
    public ConstantExpr(float value) => _value = value;
    
    public float Interpret(SkillContext context) => _value;
}

// 终结符:获取角色属性
public class AttributeExpr : ISkillExpression {
    private string _attrName;
    
    public AttributeExpr(string attrName) => _attrName = attrName;
    
    public float Interpret(SkillContext context) {
        return context.Caster.GetComponent<CharacterStats>()
                      .GetAttribute(_attrName);
    }
}

// 非终结符:加法运算
public class AddExpr : ISkillExpression {
    private ISkillExpression _left;
    private ISkillExpression _right;
    
    public AddExpr(ISkillExpression left, ISkillExpression right) {
        _left = left;
        _right = right;
    }
    
    public float Interpret(SkillContext context) {
        return _left.Interpret(context) + _right.Interpret(context);
    }
}
3. 构建语法解析器
public class SkillParser {
    public ISkillExpression Parse(string formula) {
        // 示例:解析 "ATK * 2 + SKILL_LV * 5"
        var tokens = Tokenize(formula);
        return ParseAdditive(tokens);
    }
    
    private Queue<string> Tokenize(string input) {
        // 实现分词逻辑(此处简化)
        return new Queue<string>(input.Split(' '));
    }
    
    private ISkillExpression ParseAdditive(Queue<string> tokens) {
        ISkillExpression left = ParseMultiplicative(tokens);
        while(tokens.Count > 0 && (tokens.Peek() == "+" || tokens.Peek() == "-")) {
            string op = tokens.Dequeue();
            ISkillExpression right = ParseMultiplicative(tokens);
            left = op == "+" ? new AddExpr(left, right) : new SubtractExpr(left, right);
        }
        return left;
    }
    
    private ISkillExpression ParseMultiplicative(Queue<string> tokens) {
        // 类似实现乘法/除法解析
    }
}
4. 客户端使用
public class SkillSystem : MonoBehaviour {
    private Dictionary<string, ISkillExpression> _skillFormulas = new();
    
    void Start() {
        // 预编译技能公式
        var parser = new SkillParser();
        _skillFormulas["Fireball"] = parser.Parse("ATK * 1.5 + INT * 2");
        _skillFormulas["Heal"] = parser.Parse("(WIS * 3) + (SKILL_LV * 10)");
    }
    
    public float CalculateDamage(string skillName, SkillContext context) {
        return _skillFormulas[skillName].Interpret(context);
    }
}

四、模式进阶技巧
1. 语法树优化
public class CachedExpression : ISkillExpression {
    private float? _cache;
    private ISkillExpression _expr;
    
    public float Interpret(SkillContext context) {
        return _cache ??= _expr.Interpret(context);
    }
}
2. 条件表达式
public class ConditionExpr : ISkillExpression {
    private ISkillExpression _condition;
    private ISkillExpression _trueExpr;
    private ISkillExpression _falseExpr;
    
    public float Interpret(SkillContext context) {
        bool result = _condition.Interpret(context) > 0;
        return result ? _trueExpr.Interpret(context) 
                     : _falseExpr.Interpret(context);
    }
}
3. 自定义函数支持
public class FunctionExpr : ISkillExpression {
    private Func<SkillContext, float> _func;
    
    public float Interpret(SkillContext context) => _func(context);
}

五、游戏开发典型应用场景
  1. 技能伤害公式系统

    // 公式示例:"BASE_DAMAGE + (STR * 0.8) + (WEAPON_ATK * 1.2)"
    public class DamageCalculator {
        private ISkillExpression _damageFormula;
        
        public void SetFormula(string expr) {
            _damageFormula = new SkillParser().Parse(expr);
        }
    }
    
  2. 对话条件系统

    public class DialogueCondition {
        private ISkillExpression _condition;
        
        public bool IsMet(GameState state) {
            return _condition.Interpret(state) > 0;
        }
    }
    
  3. AI行为决策树

    public class BehaviorTree {
        private ISkillExpression _decisionExpr;
        
        public AIAction DecideAction(AIContext context) {
            float value = _decisionExpr.Interpret(context);
            return value > 0.5f ? new AttackAction() : new DefendAction();
        }
    }
    
  4. 成就解锁条件

    public class AchievementCondition {
        // 示例条件:"PLAYER_LEVEL > 10 && COMPLETED_QUESTS >= 5"
        public bool Check(PlayerStats stats) {
            return _conditionExpr.Interpret(stats.ToContext()) > 0;
        }
    }
    

六、性能优化策略
策略实现方式适用场景
预编译表达式提前构建语法树频繁使用的公式
字节码转换转换为中间指令复杂表达式
记忆化缓存存储计算结果重复参数调用
JIT编译动态生成C#代码超高性能需求

七、模式对比与选择
维度解释器模式策略模式
关注点语法解析算法选择
扩展性通过新增表达式类扩展通过新增策略类扩展
复杂度较高(需处理语法树)较低
典型场景DSL解析运行时算法切换

八、最佳实践原则
  1. 限制语法复杂度:避免实现完整编程语言
  2. 防御式解析
    private ISkillExpression ParseTerm(Queue<string> tokens) {
        if(!tokens.Any()) 
            throw new SyntaxException("Unexpected end of input");
        // 继续解析...
    }
    
  3. 上下文隔离:每个解释器实例使用独立上下文
  4. 安全类型转换
    if(expr is AddExpr addExpr) {
        // 安全处理加法表达式
    }
    

九、常见问题解决方案

Q1:如何处理语法错误?
→ 实现详细错误报告

public class SyntaxException : Exception {
    public int Position { get; }
    public string ReceivedToken { get; }
    
    public SyntaxException(string message, int pos, string token)
        : base($"{message} at position {pos} (token: '{token}')") {
        Position = pos;
        ReceivedToken = token;
    }
}

Q2:如何优化深层递归性能?
→ 改用迭代解释器模式

public class IterativeInterpreter {
    public float Evaluate(ISkillExpression expr, SkillContext context) {
        var stack = new Stack<float>();
        var evalStack = new Stack<ISkillExpression>();
        evalStack.Push(expr);
        
        while(evalStack.Count > 0) {
            var current = evalStack.Pop();
            if(current is TerminalExpr term) {
                stack.Push(term.Interpret(context));
            } else if(current is BinaryExpr bin) {
                evalStack.Push(bin.Right);
                evalStack.Push(bin.Left);
                // 后续处理操作符...
            }
        }
        return stack.Pop();
    }
}

Q3:如何实现变量赋值?
→ 扩展上下文支持

public class AssignExpr : ISkillExpression {
    private string _varName;
    private ISkillExpression _valueExpr;
    
    public float Interpret(SkillContext context) {
        float value = _valueExpr.Interpret(context);
        context.Variables[_varName] = value;
        return value;
    }
}

上一篇 【行为型之命令模式】游戏开发实战——Unity可撤销系统与高级输入管理的架构秘钥
下一篇 【行为型之迭代器模式】游戏开发实战——Unity高效集合遍历与场景管理的架构精髓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值