最近有个需求是想让程序解析策划编辑一个文本生成一段CG,内容使用大致是这样
cgSetCameraEx(118.6324,30.71189,75.55666,45,-45,0,0)
cgCloneMyPlayer(1)
cgSetPosDir(1,109,80,0)
cgSetCameraEx(114.18,23.17,96.9,11.51974,-61.49661,-3.384705,3.5)
cgCreateActor(2,25,108,100,90)
cgWait(0.2)
cgMove(1,109.7,100,9,false)
cgWait(3)
cgFaceToActor(1,2)
cgWait(1.5)
cgPopTalk(1,1,2)
我的做法是借助一个第三方库NCalc,把自定义的函数注册到解析器里,让解析器去Load这段文本
using UnityEngine;
using System.Collections;
using NCalc;
using System.Collections.Generic;
using System;
//解析字符串表达式执行函数
public class CallFunctionScript
{
//缓存函数表
private readonly Dictionary> mRegisterFunctions = new Dictionary>();
//注册可被执行的函数
//注册的函数格式满足 public static object Func(Expression[] args)
//函数返回值不能是null,如果不需要返回值可以返回1
public void RegisterFunction(string funcName, Func func)
{
if (!mRegisterFunctions.ContainsKey(funcName))
{
mRegisterFunctions.Add(funcName, func);
}
else
{
UnityEngine.Debug.LogError("duplicate func " + funcName);
}
}
//反注册所有函数
public void UnRgisterAllFunction()
{
mRegisterFunctions.Clear();
}
//执行函数
public object Execute(string expression)
{
var exp = new Expression(expression);
//函数代理
exp.EvaluateFunction += delegate(string name, FunctionArgs args)
{
Func func;
if (mRegisterFunctions.TryGetValue(name, out func))
{
object result = null;
try
{
result = func(args.Parameters);
}
catch (Exception ex)
{
UnityEngine.Debug.LogError("Execute "+name + "() Error:" + ex.ToString());
}
finally
{
args.Result = result;
}
}
else
{
UnityEngine.Debug.LogError("Can't find function [" + name+"]");
}
};
try
{
return exp.Evaluate();//执行函数
}
catch (Exception ex)
{
Debug.LogError("Evaluate \"" + expression + "\" Error: "+ex.ToString());
return null;
}
}
}
//被注册的函数如这样
public static class CGFunctionWrapper
{
public static object cgCloneMyPlayer(Expression[] args)
{
if (args.Length < 1)
{
Logger.Error("cgCloneMyPlayer() args<[1]");
return 0;
}
PlayCG.Instance.cgCloneMyPlayer(
Convert.ToInt32(args[0].Evaluate())
);
return 1;
}
}
使用方法
public CallFunctionScript mExecutor = new CallFunctionScript();
mExecutor.RegisterFunction("cgCloneMyPlayer", CGFunctionWrapper.cgCloneMyPlayer);
mExecutor.Execute("cgCloneMyPlayer(1)");