C#和Unity中的解釋器模式使用方式
概述
解釋器模式(Interpreter Pattern)是一種行為型設(shè)計(jì)模式,它定義了一種語言的文法表示,并提供一個(gè)解釋器來解釋該語言中的句子。
這種模式主要用于處理特定類型的問題,特別是那些可以被表示為語言句子的領(lǐng)域。
一、解釋器模式的核心概念
1. 解釋器模式的主要角色
AbstractExpression(抽象表達(dá)式) :
- 聲明一個(gè)抽象的解釋操作接口
TerminalExpression(終結(jié)符表達(dá)式) :
- 實(shí)現(xiàn)與文法中的終結(jié)符相關(guān)聯(lián)的解釋操作
- 句子中的每個(gè)終結(jié)符都需要一個(gè)實(shí)例
NonterminalExpression(非終結(jié)符表達(dá)式) :
- 文法中的每條規(guī)則都需要一個(gè)非終結(jié)符表達(dá)式類
- 包含對(duì)其他表達(dá)式的引用(可能是終結(jié)符或非終結(jié)符)
Context(上下文) :
- 包含解釋器之外的全局信息
Client(客戶端) :
- 構(gòu)建(或被給定)表示該語言中特定句子的抽象語法樹
- 調(diào)用解釋操作
2. 解釋器模式的 UML 類圖

二、解釋器模式的實(shí)現(xiàn)方式
1. 基礎(chǔ)實(shí)現(xiàn)(布爾表達(dá)式解釋器)
// 上下文 - 存儲(chǔ)變量值
public class Context
{
private readonly Dictionary<string, bool> _variables = new();
public bool GetVariable(string name) => _variables.TryGetValue(name, out var value) ? value : false;
public void SetVariable(string name, bool value) => _variables[name] = value;
}
// 抽象表達(dá)式
public interface IExpression
{
bool Interpret(Context context);
}
// 終結(jié)符表達(dá)式 - 變量
public class VariableExpression : IExpression
{
private readonly string _name;
public VariableExpression(string name) => _name = name;
public bool Interpret(Context context) => context.GetVariable(_name);
}
// 非終結(jié)符表達(dá)式 - AND
public class AndExpression : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public AndExpression(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public bool Interpret(Context context) => _left.Interpret(context) && _right.Interpret(context);
}
// 非終結(jié)符表達(dá)式 - OR
public class OrExpression : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public OrExpression(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public bool Interpret(Context context) => _left.Interpret(context) || _right.Interpret(context);
}
// 非終結(jié)符表達(dá)式 - NOT
public class NotExpression : IExpression
{
private readonly IExpression _expression;
public NotExpression(IExpression expression) => _expression = expression;
public bool Interpret(Context context) => !_expression.Interpret(context);
}
// 客戶端代碼
class Program
{
static void Main()
{
// 創(chuàng)建上下文并設(shè)置變量
var context = new Context();
context.SetVariable("A", true);
context.SetVariable("B", false);
context.SetVariable("C", true);
// 構(gòu)建表達(dá)式: (A AND B) OR (NOT C)
var expression = new OrExpression(
new AndExpression(
new VariableExpression("A"),
new VariableExpression("B")),
new NotExpression(
new VariableExpression("C")));
// 解釋執(zhí)行
bool result = expression.Interpret(context);
Console.WriteLine($"表達(dá)式結(jié)果為: {result}"); // 輸出 False
}
}
2. 數(shù)學(xué)表達(dá)式解釋器(四則運(yùn)算)
// 上下文 - 存儲(chǔ)變量值
public class MathContext
{
private readonly Dictionary<string, int> _variables = new();
public int GetVariable(string name) => _variables.TryGetValue(name, out var value) ? value : 0;
public void SetVariable(string name, int value) => _variables[name] = value;
}
// 抽象表達(dá)式
public interface IMathExpression
{
int Interpret(MathContext context);
}
// 終結(jié)符表達(dá)式 - 數(shù)字
public class NumberExpression : IMathExpression
{
private readonly int _number;
public NumberExpression(int number) => _number = number;
public int Interpret(MathContext context) => _number;
}
// 終結(jié)符表達(dá)式 - 變量
public class VariableMathExpression : IMathExpression
{
private readonly string _name;
public VariableMathExpression(string name) => _name = name;
public int Interpret(MathContext context) => context.GetVariable(_name);
}
// 非終結(jié)符表達(dá)式 - 加法
public class AddExpression : IMathExpression
{
private readonly IMathExpression _left;
private readonly IMathExpression _right;
public AddExpression(IMathExpression left, IMathExpression right)
{
_left = left;
_right = right;
}
public int Interpret(MathContext context) => _left.Interpret(context) + _right.Interpret(context);
}
// 非終結(jié)符表達(dá)式 - 減法
public class SubtractExpression : IMathExpression
{
private readonly IMathExpression _left;
private readonly IMathExpression _right;
public SubtractExpression(IMathExpression left, IMathExpression right)
{
_left = left;
_right = right;
}
public int Interpret(MathContext context) => _left.Interpret(context) - _right.Interpret(context);
}
// 使用示例
var context = new MathContext();
context.SetVariable("x", 10);
context.SetVariable("y", 5);
// 構(gòu)建表達(dá)式: (x + 5) - (y - 2)
var expression = new SubtractExpression(
new AddExpression(
new VariableMathExpression("x"),
new NumberExpression(5)),
new SubtractExpression(
new VariableMathExpression("y"),
new NumberExpression(2)));
int result = expression.Interpret(context); // 結(jié)果為 12
三、解釋器模式的特點(diǎn)
1. 優(yōu)點(diǎn)
- 易于擴(kuò)展語法:新增表達(dá)式類即可擴(kuò)展語言
- 易于實(shí)現(xiàn)簡(jiǎn)單語言:對(duì)于簡(jiǎn)單文法實(shí)現(xiàn)直觀
- 分離語法分析:將語法分析與表達(dá)式執(zhí)行分離
- 靈活性強(qiáng):可以動(dòng)態(tài)改變解釋方式
2. 缺點(diǎn)
- 復(fù)雜度高:對(duì)于復(fù)雜文法,類數(shù)量會(huì)急劇增加
- 效率較低:解釋器模式通常比編譯器效率低
- 難以維護(hù)復(fù)雜文法:文法規(guī)則過多時(shí)代碼難以維護(hù)
- 應(yīng)用場(chǎng)景有限:僅適用于特定領(lǐng)域問題
四、解釋器模式的使用場(chǎng)景
1. 典型應(yīng)用場(chǎng)景
領(lǐng)域特定語言(DSL) :
- 正則表達(dá)式解釋器
- SQL條件解釋器
- 業(yè)務(wù)規(guī)則引擎
數(shù)學(xué)表達(dá)式處理:
- 科學(xué)計(jì)算器
- 公式編輯器
- 財(cái)務(wù)計(jì)算系統(tǒng)
配置文件解析:
- 自定義配置語法
- 過濾條件解析
編譯器/解釋器:
- 簡(jiǎn)單編程語言解釋器
- 模板引擎
游戲開發(fā):
- 游戲AI行為腳本
- 技能效果描述語言
2. 具體案例
案例1:正則表達(dá)式解釋器(簡(jiǎn)化版)
// 抽象表達(dá)式
public interface IRegexExpression
{
bool Interpret(string input);
}
// 終結(jié)符表達(dá)式 - 字符匹配
public class CharExpression : IRegexExpression
{
private readonly char _char;
public CharExpression(char c) => _char = c;
public bool Interpret(string input) => input.Length > 0 && input[0] == _char;
}
// 非終結(jié)符表達(dá)式 - 序列
public class SequenceExpression : IRegexExpression
{
private readonly List<IRegexExpression> _expressions;
public SequenceExpression(params IRegexExpression[] expressions)
=> _expressions = new List<IRegexExpression>(expressions);
public bool Interpret(string input)
{
string remaining = input;
foreach (var expr in _expressions)
{
if (!expr.Interpret(remaining)) return false;
remaining = remaining.Substring(1);
}
return true;
}
}
// 非終結(jié)符表達(dá)式 - 或
public class OrExpression : IRegexExpression
{
private readonly IRegexExpression _left;
private readonly IRegexExpression _right;
public OrExpression(IRegexExpression left, IRegexExpression right)
{
_left = left;
_right = right;
}
public bool Interpret(string input) => _left.Interpret(input) || _right.Interpret(input);
}
// 使用
var regex = new SequenceExpression(
new CharExpression('a'),
new OrExpression(
new CharExpression('b'),
new CharExpression('c')));
bool match1 = regex.Interpret("ab"); // true
bool match2 = regex.Interpret("ac"); // true
bool match2 = regex.Interpret("ad"); // false
案例2:業(yè)務(wù)規(guī)則引擎
// 業(yè)務(wù)規(guī)則上下文
public class BusinessContext
{
public Dictionary<string, object> Data { get; } = new();
}
// 條件表達(dá)式
public class ConditionExpression
{
private readonly string _field;
private readonly object _value;
private readonly string _operator;
public ConditionExpression(string field, string op, object value)
{
_field = field;
_operator = op;
_value = value;
}
public bool Interpret(BusinessContext context)
{
if (!context.Data.TryGetValue(_field, out var fieldValue)) return false;
return _operator switch
{
"==" => Equals(fieldValue, _value),
">" => Comparer.Default.Compare(fieldValue, _value) > 0,
"<" => Comparer.Default.Compare(fieldValue, _value) < 0,
_ => false
};
}
}
// 規(guī)則集
public class RuleSet
{
private readonly List<ConditionExpression> _conditions = new();
public void AddCondition(ConditionExpression condition) => _conditions.Add(condition);
public bool Evaluate(BusinessContext context)
{
return _conditions.All(c => c.Interpret(context));
}
}
// 使用
var context = new BusinessContext();
context.Data["Age"] = 25;
context.Data["Salary"] = 50000;
context.Data["IsEmployed"] = true;
var rule = new RuleSet();
rule.AddCondition(new ConditionExpression("Age", ">", 18));
rule.AddCondition(new ConditionExpression("Salary", ">", 40000));
rule.AddCondition(new ConditionExpression("IsEmployed", "==", true));
bool eligible = rule.Evaluate(context); // true
五、解釋器模式的進(jìn)階話題
1. 語法樹構(gòu)建
通常需要配合解析器將輸入文本轉(zhuǎn)換為抽象語法樹(AST):
public class Parser
{
public IExpression Parse(string input)
{
// 簡(jiǎn)單實(shí)現(xiàn) - 實(shí)際需要更復(fù)雜的詞法/語法分析
if (input.Contains("AND"))
{
var parts = input.Split(new[] {" AND "}, StringSplitOptions.None);
return new AndExpression(Parse(parts[0]), Parse(parts[1]));
}
else if (input.Contains("OR"))
{
var parts = input.Split(new[] {" OR "}, StringSplitOptions.None);
return new OrExpression(Parse(parts[0]), Parse(parts[1]));
}
else
{
return new VariableExpression(input.Trim());
}
}
}
// 使用
var parser = new Parser();
var expression = parser.Parse("A AND B OR C");
2. 使用訪問者模式遍歷語法樹
public interface IExpressionVisitor
{
void Visit(VariableExpression exp);
void Visit(AndExpression exp);
void Visit(OrExpression exp);
}
public class PrintVisitor : IExpressionVisitor
{
public void Visit(VariableExpression exp) => Console.Write(exp.Name);
public void Visit(AndExpression exp)
{
Console.Write("(");
exp.Left.Accept(this);
Console.Write(" AND ");
exp.Right.Accept(this);
Console.Write(")");
}
public void Visit(OrExpression exp)
{
Console.Write("(");
exp.Left.Accept(this);
Console.Write(" OR ");
exp.Right.Accept(this);
Console.Write(")");
}
}
// 在表達(dá)式接口中添加Accept方法
public interface IExpression
{
bool Interpret(Context context);
void Accept(IExpressionVisitor visitor);
}
3. 解釋器模式與編譯器技術(shù)的區(qū)別
| 特性 | 解釋器模式 | 編譯器 |
|---|---|---|
| 執(zhí)行方式 | 直接執(zhí)行語法樹 | 生成中間代碼/機(jī)器碼 |
| 效率 | 較低(每次解釋) | 較高(預(yù)先編譯) |
| 靈活性 | 高(可動(dòng)態(tài)修改) | 低(編譯后固定) |
| 實(shí)現(xiàn)復(fù)雜度 | 相對(duì)簡(jiǎn)單 | 復(fù)雜 |
| 適用場(chǎng)景 | 簡(jiǎn)單DSL、動(dòng)態(tài)需求 | 通用編程語言 |
六、解釋器模式的最佳實(shí)踐
控制文法復(fù)雜度:
- 解釋器模式適合相對(duì)簡(jiǎn)單的文法(BNF范式不超過一頁)
- 復(fù)雜文法考慮使用解析器生成器(如ANTLR)
共享終結(jié)符:
- 終結(jié)符表達(dá)式通常是無狀態(tài)的,可以共享實(shí)例
分離解析與解釋:
- 使用單獨(dú)解析器構(gòu)建語法樹
- 保持解釋器專注于執(zhí)行
考慮性能優(yōu)化:
- 緩存解釋結(jié)果
- 預(yù)編譯常用表達(dá)式
合理使用組合:
- 與訪問者模式結(jié)合遍歷語法樹
- 與享元模式共享終結(jié)符
七、解釋器模式與其他模式的關(guān)系
與組合模式:
- 抽象語法樹就是組合模式的應(yīng)用
- 非終結(jié)符表達(dá)式是組合節(jié)點(diǎn),終結(jié)符表達(dá)式是葉節(jié)點(diǎn)
與訪問者模式:
- 訪問者模式可用于在語法樹上執(zhí)行多種操作
- 分離解釋邏輯與語法樹結(jié)構(gòu)
與享元模式:
- 共享終結(jié)符表達(dá)式實(shí)例
- 減少內(nèi)存使用
與策略模式:
- 解釋器模式可以看作是在語法樹上應(yīng)用的策略模式
八、現(xiàn)代替代方案
對(duì)于復(fù)雜語言處理,現(xiàn)代開發(fā)中更常用:
解析器生成器:
- ANTLR
- Yacc/Lex
表達(dá)式樹:
- C#的
Expression<T> - 動(dòng)態(tài)構(gòu)建和執(zhí)行表達(dá)式
腳本引擎:
- Roslyn腳本API
- Lua、Python等嵌入式腳本
總結(jié)一下:
解釋器模式在C#中適用于:
- 特定領(lǐng)域語言:需要為特定領(lǐng)域定義簡(jiǎn)單語言
- 靈活規(guī)則系統(tǒng):業(yè)務(wù)規(guī)則需要?jiǎng)討B(tài)配置
- 數(shù)學(xué)表達(dá)式:需要解釋執(zhí)行公式
關(guān)鍵優(yōu)勢(shì):
? 易于實(shí)現(xiàn)簡(jiǎn)單語言的解釋執(zhí)行
? 靈活擴(kuò)展語法規(guī)則
? 分離語法定義與執(zhí)行
適用限制:
? 不適合復(fù)雜文法(類爆炸問題)
? 性能不如編譯執(zhí)行
? 維護(hù)成本隨文法復(fù)雜度增加
在實(shí)際開發(fā)中,應(yīng)權(quán)衡需求復(fù)雜度,對(duì)于簡(jiǎn)單DSL可以使用解釋器模式快速實(shí)現(xiàn),對(duì)于復(fù)雜語言處理建議使用專業(yè)解析工具。
九、在Uniry中的應(yīng)用
示例1:簡(jiǎn)單數(shù)學(xué)表達(dá)式解釋器
using UnityEngine;
using System.Collections.Generic;
// 抽象表達(dá)式
public abstract class Expression
{
public abstract int Interpret(Dictionary<string, int> context);
}
// 終結(jié)符表達(dá)式 - 變量
public class VariableExpression : Expression
{
private string name;
public VariableExpression(string name)
{
this.name = name;
}
public override int Interpret(Dictionary<string, int> context)
{
// 從上下文中獲取變量值
if (context.ContainsKey(name))
{
return context[name];
}
throw new System.Exception($"變量 {name} 未定義");
}
}
// 終結(jié)符表達(dá)式 - 常量
public class ConstantExpression : Expression
{
private int value;
public ConstantExpression(int value)
{
this.value = value;
}
public override int Interpret(Dictionary<string, int> context)
{
return value;
}
}
// 非終結(jié)符表達(dá)式 - 加法
public class AddExpression : Expression
{
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right)
{
this.left = left;
this.right = right;
}
public override int Interpret(Dictionary<string, int> context)
{
return left.Interpret(context) + right.Interpret(context);
}
}
// 非終結(jié)符表達(dá)式 - 減法
public class SubtractExpression : Expression
{
private Expression left;
private Expression right;
public SubtractExpression(Expression left, Expression right)
{
this.left = left;
this.right = right;
}
public override int Interpret(Dictionary<string, int> context)
{
return left.Interpret(context) - right.Interpret(context);
}
}
// 非終結(jié)符表達(dá)式 - 乘法
public class MultiplyExpression : Expression
{
private Expression left;
private Expression right;
public MultiplyExpression(Expression left, Expression right)
{
this.left = left;
this.right = right;
}
public override int Interpret(Dictionary<string, int> context)
{
return left.Interpret(context) * right.Interpret(context);
}
}
// 表達(dá)式解析器
public class ExpressionParser
{
private Dictionary<string, int> variables = new Dictionary<string, int>();
// 解析表達(dá)式字符串
public Expression Parse(string expression)
{
// 這里簡(jiǎn)化處理,實(shí)際應(yīng)用中需要更復(fù)雜的解析邏輯
if (expression.Contains("+"))
{
string[] parts = expression.Split('+');
return new AddExpression(Parse(parts[0]), Parse(parts[1]));
}
else if (expression.Contains("-"))
{
string[] parts = expression.Split('-');
return new SubtractExpression(Parse(parts[0]), Parse(parts[1]));
}
else if (expression.Contains("*"))
{
string[] parts = expression.Split('*');
return new MultiplyExpression(Parse(parts[0]), Parse(parts[1]));
}
else if (int.TryParse(expression, out int value))
{
return new ConstantExpression(value);
}
else
{
return new VariableExpression(expression);
}
}
// 設(shè)置變量值
public void SetVariable(string name, int value)
{
variables[name] = value;
}
// 獲取當(dāng)前變量表
public Dictionary<string, int> GetContext()
{
return variables;
}
}
// 測(cè)試代碼
public class MathInterpreterTest : MonoBehaviour
{
void Start()
{
ExpressionParser parser = new ExpressionParser();
// 設(shè)置變量
parser.SetVariable("x", 10);
parser.SetVariable("y", 5);
// 解析并計(jì)算表達(dá)式
TestExpression(parser, "x+y"); // 10 + 5 = 15
TestExpression(parser, "x-y"); // 10 - 5 = 5
TestExpression(parser, "x*y"); // 10 * 5 = 50
TestExpression(parser, "x+y*2"); // 10 + (5 * 2) = 20
}
void TestExpression(ExpressionParser parser, string expression)
{
Expression exp = parser.Parse(expression);
int result = exp.Interpret(parser.GetContext());
Debug.Log($"{expression} = {result}");
}
}
示例2:簡(jiǎn)單AI行為腳本解釋器
using UnityEngine;
using System.Collections.Generic;
// 抽象行為表達(dá)式
public abstract class AIActionExpression
{
public abstract void Interpret(AIContext context);
}
// 移動(dòng)行為
public class MoveAction : AIActionExpression
{
private string direction;
private float distance;
public MoveAction(string direction, float distance)
{
this.direction = direction.ToLower();
this.distance = distance;
}
public override void Interpret(AIContext context)
{
Vector3 moveVector = Vector3.zero;
switch (direction)
{
case "forward":
moveVector = context.AITransform.forward * distance;
break;
case "back":
moveVector = -context.AITransform.forward * distance;
break;
case "left":
moveVector = -context.AITransform.right * distance;
break;
case "right":
moveVector = context.AITransform.right * distance;
break;
case "up":
moveVector = context.AITransform.up * distance;
break;
case "down":
moveVector = -context.AITransform.up * distance;
break;
}
context.AITransform.position += moveVector;
Debug.Log($"AI移動(dòng): {direction} {distance}米");
}
}
// 等待行為
public class WaitAction : AIActionExpression
{
private float seconds;
public WaitAction(float seconds)
{
this.seconds = seconds;
}
public override void Interpret(AIContext context)
{
Debug.Log($"AI等待: {seconds}秒");
// 實(shí)際游戲中可以使用協(xié)程實(shí)現(xiàn)等待
}
}
// 攻擊行為
public class AttackAction : AIActionExpression
{
private string target;
public AttackAction(string target)
{
this.target = target;
}
public override void Interpret(AIContext context)
{
Debug.Log($"AI攻擊: {target}");
// 實(shí)際游戲中這里會(huì)實(shí)現(xiàn)攻擊邏輯
}
}
// AI行為序列
public class ActionSequence : AIActionExpression
{
private List<AIActionExpression> actions = new List<AIActionExpression>();
public void AddAction(AIActionExpression action)
{
actions.Add(action);
}
public override void Interpret(AIContext context)
{
foreach (var action in actions)
{
action.Interpret(context);
}
}
}
// AI上下文
public class AIContext
{
public Transform AITransform { get; set; }
public Dictionary<string, object> Variables { get; } = new Dictionary<string, object>();
}
// AI腳本解析器
public class AIScriptParser
{
public AIActionExpression Parse(string script)
{
ActionSequence sequence = new ActionSequence();
// 分割腳本為多行
string[] lines = script.Split(new[] { '\n', ';' }, System.StringSplitOptions.RemoveEmptyEntries);
foreach (string line in lines)
{
string trimmedLine = line.Trim();
if (string.IsNullOrEmpty(trimmedLine)) continue;
// 分割命令和參數(shù)
string[] parts = trimmedLine.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 0) continue;
string command = parts[0].ToLower();
switch (command)
{
case "move":
if (parts.Length >= 3)
{
string direction = parts[1];
if (float.TryParse(parts[2], out float distance))
{
sequence.AddAction(new MoveAction(direction, distance));
}
}
break;
case "wait":
if (parts.Length >= 2 && float.TryParse(parts[1], out float seconds))
{
sequence.AddAction(new WaitAction(seconds));
}
break;
case "attack":
if (parts.Length >= 2)
{
sequence.AddAction(new AttackAction(parts[1]));
}
break;
}
}
return sequence;
}
}
// AI控制器
public class AIController : MonoBehaviour
{
public string aiScript = @"
move forward 5
wait 2
attack player
move back 3
wait 1
";
private AIContext context;
private AIActionExpression behavior;
void Start()
{
context = new AIContext { AITransform = transform };
AIScriptParser parser = new AIScriptParser();
behavior = parser.Parse(aiScript);
// 執(zhí)行AI腳本
behavior.Interpret(context);
}
}
示例3:對(duì)話條件解釋器
using UnityEngine;
using System.Collections.Generic;
// 抽象條件表達(dá)式
public abstract class ConditionExpression
{
public abstract bool Interpret(DialogueContext context);
}
// 變量條件
public class VariableCondition : ConditionExpression
{
private string variableName;
private int expectedValue;
private string comparison; // "==", ">", "<", etc.
public VariableCondition(string variableName, string comparison, int expectedValue)
{
this.variableName = variableName;
this.comparison = comparison;
this.expectedValue = expectedValue;
}
public override bool Interpret(DialogueContext context)
{
if (!context.Variables.ContainsKey(variableName))
{
Debug.LogWarning($"變量 {variableName} 未定義");
return false;
}
int actualValue = context.Variables[variableName];
switch (comparison)
{
case "==": return actualValue == expectedValue;
case "!=": return actualValue != expectedValue;
case ">": return actualValue > expectedValue;
case "<": return actualValue < expectedValue;
case ">=": return actualValue >= expectedValue;
case "<=": return actualValue <= expectedValue;
default:
Debug.LogWarning($"未知比較運(yùn)算符: {comparison}");
return false;
}
}
}
// 邏輯與條件
public class AndCondition : ConditionExpression
{
private ConditionExpression left;
private ConditionExpression right;
public AndCondition(ConditionExpression left, ConditionExpression right)
{
this.left = left;
this.right = right;
}
public override bool Interpret(DialogueContext context)
{
return left.Interpret(context) && right.Interpret(context);
}
}
// 邏輯或條件
public class OrCondition : ConditionExpression
{
private ConditionExpression left;
private ConditionExpression right;
public OrCondition(ConditionExpression left, ConditionExpression right)
{
this.left = left;
this.right = right;
}
public override bool Interpret(DialogueContext context)
{
return left.Interpret(context) || right.Interpret(context);
}
}
// 非條件
public class NotCondition : ConditionExpression
{
private ConditionExpression condition;
public NotCondition(ConditionExpression condition)
{
this.condition = condition;
}
public override bool Interpret(DialogueContext context)
{
return !condition.Interpret(context);
}
}
// 對(duì)話上下文
public class DialogueContext
{
public Dictionary<string, int> Variables { get; } = new Dictionary<string, int>();
}
// 條件解析器
public class ConditionParser
{
public ConditionExpression Parse(string conditionStr)
{
// 這里簡(jiǎn)化處理,實(shí)際應(yīng)用中需要更復(fù)雜的解析邏輯
if (conditionStr.Contains("&&"))
{
string[] parts = conditionStr.Split(new[] { "&&" }, System.StringSplitOptions.RemoveEmptyEntries);
return new AndCondition(Parse(parts[0]), Parse(parts[1]));
}
else if (conditionStr.Contains("||"))
{
string[] parts = conditionStr.Split(new[] { "||" }, System.StringSplitOptions.RemoveEmptyEntries);
return new OrCondition(Parse(parts[0]), Parse(parts[1]));
}
else if (conditionStr.StartsWith("!"))
{
return new NotCondition(Parse(conditionStr.Substring(1)));
}
else
{
// 解析變量條件 如: "health > 50"
string[] parts = conditionStr.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
{
string varName = parts[0];
string op = parts[1];
if (int.TryParse(parts[2], out int value))
{
return new VariableCondition(varName, op, value);
}
}
}
throw new System.Exception($"無法解析條件: {conditionStr}");
}
}
// 對(duì)話選項(xiàng)
public class DialogueOption
{
public string Text { get; }
public ConditionExpression Condition { get; }
public DialogueOption(string text, ConditionExpression condition = null)
{
Text = text;
Condition = condition;
}
public bool IsAvailable(DialogueContext context)
{
return Condition == null || Condition.Interpret(context);
}
}
// 測(cè)試代碼
public class DialogueConditionTest : MonoBehaviour
{
void Start()
{
DialogueContext context = new DialogueContext();
context.Variables["health"] = 75;
context.Variables["hasKey"] = 1;
context.Variables["karma"] = -10;
ConditionParser parser = new ConditionParser();
TestCondition(parser, context, "health > 50"); // true
TestCondition(parser, context, "hasKey == 1"); // true
TestCondition(parser, context, "karma >= 0"); // false
TestCondition(parser, context, "health > 50 && hasKey == 1"); // true
TestCondition(parser, context, "health > 50 || karma >= 0"); // true
TestCondition(parser, context, "!hasKey == 1"); // false
// 創(chuàng)建對(duì)話選項(xiàng)
DialogueOption option1 = new DialogueOption("攻擊敵人", parser.Parse("health > 50"));
DialogueOption option2 = new DialogueOption("和平解決", parser.Parse("karma >= 0"));
DialogueOption option3 = new DialogueOption("逃跑", null); // 無條件
Debug.Log($"選項(xiàng)1可用: {option1.IsAvailable(context)}"); // true
Debug.Log($"選項(xiàng)2可用: {option2.IsAvailable(context)}"); // false
Debug.Log($"選項(xiàng)3可用: {option3.IsAvailable(context)}"); // true
}
void TestCondition(ConditionParser parser, DialogueContext context, string conditionStr)
{
ConditionExpression condition = parser.Parse(conditionStr);
bool result = condition.Interpret(context);
Debug.Log($"{conditionStr} = {result}");
}
}
在Unity中的實(shí)現(xiàn)建議
- 結(jié)合ScriptableObject:可以將表達(dá)式配置為ScriptableObject,便于在編輯器中設(shè)置
- 使用解析器生成器:對(duì)于復(fù)雜文法,考慮使用ANTLR等解析器生成器
- 限制文法復(fù)雜度:保持解釋的語言簡(jiǎn)單,避免過度設(shè)計(jì)
- 緩存解析結(jié)果:對(duì)于頻繁使用的表達(dá)式,可以緩存解析結(jié)果提高性能
- 與事件系統(tǒng)結(jié)合:將解釋結(jié)果轉(zhuǎn)換為游戲事件,降低耦合度
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C#中Entity Framework常見報(bào)錯(cuò)匯總
給大家總結(jié)了C#中Entity Framework常見報(bào)錯(cuò),以及處理這些錯(cuò)誤的方法,希望能夠?yàn)槟闾峁┑綆椭?/div> 2017-11-11
一文搞懂C#實(shí)現(xiàn)讀寫文本文件中的數(shù)據(jù)
這篇文章重點(diǎn)給大家介紹C#實(shí)現(xiàn)讀寫文本文件中的數(shù)據(jù)的一些知識(shí),讀取.txt文件數(shù)據(jù)的實(shí)例代碼及寫入讀取過程完整代碼,感興趣的朋友跟隨小編一起看看吧2021-06-06
unity實(shí)現(xiàn)場(chǎng)景跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)場(chǎng)景跳轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
C#中文隨機(jī)數(shù)實(shí)現(xiàn)方法
這篇文章主要介紹了C#中文隨機(jī)數(shù)實(shí)現(xiàn)方法,涉及C#針對(duì)中文及隨機(jī)數(shù)的相關(guān)操作技巧,需要的朋友可以參考下2015-06-06最新評(píng)論

