自定义语法的实现主要通过以下模块协作完成,涉及从文本输入到脚本编译和执行的完整流程。以下是核心逻辑的总结:
1. 文本处理与分词(Tokenizer.cs)
- 功能:将输入的脚本文本分解为
Token,识别关键字、方法、参数、块等。 - 实现:
- 通过
Tokenizer类处理逐行文本,识别Cycle(如 Now、Once、Loop)、Method、Statement、Comment等 token 类型。 - 使用正则表达式(
KeywordRegex)在分词前替换全局关键字(如中文“若”替换为“if”),支持中文编程和简易语法。 - 支持中文标点转换为英文标点(如“:”转为“:”),统一格式。
- 处理块结构(
{和}),支持嵌套块(如方法定义或条件语句)。 - 跳过引号内的内容,避免误替换字符串中的关键字。
- 通过
- 关键点:
- 全局替换表(
ScriptSyntax.GlobalVariables)简化了语法扩展,允许中文编程和自定义简写。
- 全局替换表(
2. 语法解析(CommonParser.cs 和 UnifiedCompiler.cs)
- 功能:将 token 解析为
Command和ScriptStatement,构建ScriptStructure。 - 实现:
CommonParser.ParseTokens:遍历 token,识别周期类型(Now、Once、Loop、Import、Value、Method),解析方法调用和参数。- 支持嵌套命令(
NestedCommands)和显式目标对象(target参数)。 UnifiedCompiler:负责具体语句的识别,转换为Command对象,处理方法调用、赋值、移动等。- 参数以字符串形式存储,延迟类型解析到运行时,增加灵活性。
ScriptStructure存储解析结果,包含各类命令(Now、Once、Loop)、导入语句、变量声明、方法定义和剩余语句。
- 关键点:
- 解析支持简易语法(如
move up 1转为Move命令)。 - 保留未识别的语句(
RemainingStatements),允许混合原生 C# 代码。 - 方法定义(
MethodDefinition)支持自定义方法,解析方法签名和体内的语句。
- 解析支持简易语法(如
3. 代码生成(CodeGenerator.cs)
- 功能:将
ScriptStructure转换为可执行的 C# 脚本,分为MainScript(Once 和 Loop)和NowScript(Now)。 - 实现:
GenerateMainScript:- 生成
Start方法(Once 命令)和Update方法(Loop 命令)。 - 包含导入语句(
using)、变量声明(ValueStatements)和自定义方法。 - 支持协程(
IEnumerator)和普通方法调用,自动选择StartCoroutine或直接调用。
- 生成
GenerateNowScript:- 生成一次性执行的脚本,包含
ExecuteCommands协程,依次执行 Now 命令。 - 在执行完后销毁自身(
Destroy(this))。
- 生成一次性执行的脚本,包含
SerializeCommand:将Command序列化为方法调用,处理参数类型(如Vector3、字符串等)和默认值。ConvertValueToCSharp:将字符串参数转换为 C# 表达式(如(1,2,3)转为new Vector3(1,2,3))。
- 关键点:
- 区分协程和普通方法调用,避免 Loop 中重复启动协程。
- 使用
RawParameters保留原始参数格式,确保自定义方法调用正确。 - 生成的脚本以唯一命名(
Guid)避免冲突。
4. 脚本编译与附加(ScriptCompiler.cs)
- 功能:将生成的 C# 代码编译为程序集并附加到
GameObject。 - 实现:
- 使用
Microsoft.CodeAnalysis编译 C# 代码,引用 Unity 和系统程序集(cachedReferences)。 - 异步编译(
Task.Run和协程),避免阻塞主线程。 - 检查脚本内容是否变化,避免重复编译(
lastCompiledScript)。 - 移除旧脚本组件(
GeneratedScript或TempNowScript),附加新编译的组件。 - 缓存编译结果(
compiledScripts),支持后续访问。 ParseScript:整合 token 解析,处理 Method 块,生成完整ScriptStructure。
- 使用
- 关键点:
- 编译优化显著(异步+缓存),无卡顿(2025/4/14 记录)。
- 支持直接编译 C# 脚本(
CompileCSharpScript),跳过原生解析。 - 方法定义解析(
ParseMethodDefinition)使用正则匹配签名,确保格式正确。
5. 运行时支持(ScriptFunctions.cs)
- 功能:提供自定义方法库,供生成的脚本调用。
- 实现:
- 包含移动(
Move、MoveCoroutine)、旋转(Rotate)、缩放(Scale)、物理(Force、AddCollider)、事件(OnCollision、OnKeyPress)等方法。 - 支持协程和普通版本,适应不同周期(Now 使用协程,Loop 使用普通)。
print方法支持彩色输出(ScriptOutputDisplay),解析颜色字符串(如"red"或(1,0,0))。- 事件处理通过辅助类(
CollisionHandler、KeyPressHandler等)实现动态注册。
- 包含移动(
- 关键点:
- 方法抽象化,减少脚本复杂度(如
Move封装平滑移动逻辑)。 - 物理和事件方法扩展了交互能力(如碰撞检测、鼠标点击)。
- 方法抽象化,减少脚本复杂度(如
6. 用户交互(ObjectSelectionManager.cs 和 ScriptOutputDisplay.cs)
- 功能:提供脚本编辑和输出界面,支持运行时交互。
- 实现:
ObjectSelectionManager:- 允许选择场景对象(
HandleSelection),高亮显示(highlightMaterial)。 - 显示 UI 面板(
ShowUI),编辑脚本(TMP_InputField)。 - 保存脚本到
ES3,支持持久化(SaveInput)。 - 触发编译(
ProcessScriptAndSave),附加到选中对象。
- 允许选择场景对象(
ScriptOutputDisplay:- 显示脚本输出(
AddOutput),支持颜色和重复计数。 - 自动清空输出(
ClearOutput),确保界面干净。
- 显示脚本输出(
7. 语法定义(ScriptSyntax.cs)
- 功能:定义自定义语法的关键字、方法名、方向映射和替换规则。
- 实现:
Keywords和MethodNames:定义保留字和可调用方法。DirectionMap:映射方向(如up到Vector3.up)。GlobalVariables:定义替换规则(如引入转为Import,红转为"red")。CycleTypes:支持 Now、Once、Loop、Import、Value、Method 周期。
此方悬停