【行为型之解释器模式】游戏开发实战——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. 定义表达式接口与上下文
csharp 复制代码
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. 实现基础表达式
csharp 复制代码
// 终结符:常数值
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. 构建语法解析器
csharp 复制代码
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. 客户端使用
csharp 复制代码
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. 语法树优化
csharp 复制代码
public class CachedExpression : ISkillExpression {
    private float? _cache;
    private ISkillExpression _expr;
    
    public float Interpret(SkillContext context) {
        return _cache ??= _expr.Interpret(context);
    }
}
2. 条件表达式
csharp 复制代码
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. 自定义函数支持
csharp 复制代码
public class FunctionExpr : ISkillExpression {
    private Func<SkillContext, float> _func;
    
    public float Interpret(SkillContext context) => _func(context);
}

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

    csharp 复制代码
    // 公式示例:"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. 对话条件系统

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

    csharp 复制代码
    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. 成就解锁条件

    csharp 复制代码
    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. 防御式解析

    csharp 复制代码
    private ISkillExpression ParseTerm(Queue<string> tokens) {
        if(!tokens.Any()) 
            throw new SyntaxException("Unexpected end of input");
        // 继续解析...
    }
  3. 上下文隔离:每个解释器实例使用独立上下文

  4. 安全类型转换

    csharp 复制代码
    if(expr is AddExpr addExpr) {
        // 安全处理加法表达式
    }

九、常见问题解决方案

Q1:如何处理语法错误?

→ 实现详细错误报告

csharp 复制代码
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:如何优化深层递归性能?

→ 改用迭代解释器模式

csharp 复制代码
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:如何实现变量赋值?

→ 扩展上下文支持

csharp 复制代码
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高效集合遍历与场景管理的架构精髓

相关推荐
君鼎1 小时前
C++设计模式——单例模式
c++·单例模式·设计模式
敲代码的 蜡笔小新3 小时前
【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
unity·设计模式·c#·中介者模式
令狐前生3 小时前
设计模式学习整理
学习·设计模式
JANYI20186 小时前
嵌入式设计模式基础--C语言的继承封装与多态
java·c语言·设计模式
Magnum Lehar8 小时前
3d游戏引擎的Utilities模块实现
c++·算法·游戏引擎
敲代码的 蜡笔小新10 小时前
【行为型之观察者模式】游戏开发实战——Unity事件驱动架构的核心实现策略
观察者模式·unity·设计模式·c#
向宇it10 小时前
【unity游戏开发——编辑器扩展】使用EditorGUI的EditorGUILayout绘制工具类在自定义编辑器窗口绘制各种UI控件
开发语言·ui·unity·c#·编辑器·游戏引擎
Cuit小唐10 小时前
C++ 解释器模式详解
解释器模式
琢磨先生David13 小时前
构建优雅对象的艺术:Java 建造者模式的架构解析与工程实践
java·设计模式·建造者模式