unity3d支持typescript开发

  1. unity3d支持typescript开发(一)
  2. unity3d支持typescript开发(二)
  3. unity3d支持typescript开发(三)
  4. unity3d支持typescript开发(四)
  5. unity3d支持typescript开发(五)
  6. unity3d支持typescript开发(六)

前言

由于之前的工作是在游戏公司做unity框架,该框架使用的是lua+unity的方案,而最近频繁接触typescript后,就有了想要在unity框架内支持typescript的想法.

早期的unity是支持javascript后面简写为js的,但是后来被unity抛弃了,其实让unity支持typescript后面简写为ts跟支持lua是很相似的,只需要找到一个可以在.net环境下执行js的解释器即可,而js可以通过编译ts来获得,那么即可支持ts了.

那么此次我们来完成一个示例,首先unity启动自会后会加载一个prefab,然后加载对应路径的js代码并调用onStart方法,该js方法内会调用console.log,而该函数会转而调用unity内的Debug.Log方法.

.net库

https://github.com/sebastienros/jint 是比较符合当前条件的.net库, 但是该库只提供了基础的js环境, 而该环境中并不包含console对象.

初始化jint

首先在canvas下添加一个脚本,在该脚本内初始化jint,当Awake方法被触发的时候,加载prefab, C#代码如下:

class DemoCanvas : MonoBehaviour
{
    private readonly Engine m_Engine = new Engine();

    private void Awake()
    {
        var prefab = Resources.Load<Component>("demo/Index");
        Instantiate(prefab, this.transform);
    }
}

执行js脚本

由于上文中我们将Jint的初始化代码放在了Canvas内,因此js脚本的加载执行就不能参考canvas的流程来了,因为Instantiate的时候Awake会被触发如果这时候要调用Jint.Engine的话则需要将该字段开放,这样循环引用并不好,因此此处改为在prefab内开放一个方法用于初始化, C#代码如下:

// js
console.log('hello world');

// C#
class DemoView : MonoBehaviour
{
    public void EvalScript(Engine engine)
    {
        var js = Resources.Load<TextAsset>("demo/index");
        engine.Execute(js.text);
    }
}

// DemoCanvas
public void Awake()
{
    var prefab = Resources.Load<Component>("demo/Index");
    Instantiate(prefab, this.transform).GetComponent<DemoView>().EvalScript(this.m_Engine);
}

运行以后出现了错误,这是由于Jint并没有提供console这个对象,因此需要自己扩展一个console对象.

console对象

阅读Jint的源码内我们可以发现,Jint的对象需要继承自ObjectInstance,然后调用FastAddProperty给该对象添加一个属性,该方法的定义如下:

public void FastAddProperty(string name, JsValue value, bool writable, bool enumerable, bool configurable)

除了JsValue以外,其他的参数字面上跟js是一致的,这里就不解释了.由于console.log是一个函数,因此这里JsValue需要使用ClrFunctionInstance,ClrFunctionInstance的定义如下:

public sealed class ClrFunctionInstance : FunctionInstance
{
    public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func);
    public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func, int length);

    public override JsValue Call(JsValue thisObject, JsValue[] arguments);
}

构造函数中有两个,其中一个包含了一个int length的参数,因为console.log是不限定参数个数的,因此我们使用另外一个不包含length的构造函数,C#代码如下:

class ConsoleInstance : ObjectInstance
{
    public ConsoleInstance(Engine engine) : base(engine)
    {
        this.Prototype = engine.Object.Prototype;
        this.FastAddProperty("log", new ClrFunctionInstance(this.Engine, Log), true, false, true);
    }

    public JsValue Log(JsValue thisObject, JsValue[] arguments)
    {
        Debug.Log(arguments[0].ToString());
        return JsValue.Null;
    }
}

// DemoCanvas
public void Awake()
{
    var console = new ConsoleInstance(this.m_Engine)
    {
        Prototype = this.m_Engine.Object.PrototypeObject
    };
    this.m_Engine.SetValue("console", console);

    // 略
}

这里为了简化log方法内参数的判断,代码改为只对第一个值进行ToString的处理.

结尾

到了这里我们就已经完成了unity支持执行js脚本了,但是离支持ts还是有点远的.由于时间比较零碎,因此其余的部分会在有空的时候继续提供.如果文章中有任何错误或者疑问欢迎提出,如果文章对你有帮助也欢迎打赏,谢谢.