目录unity3d支持typescript开发(一)unity3d支持typescript开发(二)unity3d支持typescript开发(三)unity3d支持typescript开发(四)un
目录
- unity3d支持typescript开发(一)
- unity3d支持typescript开发(二)
- unity3d支持typescript开发(三)
- unity3d支持typescript开发(四)
- unity3d支持typescript开发(五)
- unity3d支持typescript开发(六)
前言
上一篇文章原本是要完成加载场景的,但是由于完成prefab
界面元素成员绑定的篇幅已经过长了,因此此篇文章首先完成上一篇的遗留.
加载进度
控制器
为了达到显示进度的效果,需要在_view.push
推送视图数据之后延迟一定的时间,然后再次推送即可,代码大致如下:
class TSControllerLoading { public onStart(): void { // 略 _view.push({ ... }, () => { setTimeout(this.onStart, 100); }); }}
定时器
为了让C#
端支持定时器,可以使用untiy
的帧,那么定时器需要继承自MonoBehaviour
,不存在的时候则动态创建,定时器使用单例模式+观察者模式,当需要使用定时器的地方,则可以通过定时器单例增加观察者,根据setTimeout
的需求,这里只需要增加一个只执行一次的延后观察者,然后在update
内遍历所有观察者并把已经完结的观察者移除掉,这里会出现一个问题,那就是有可能出现增加观察者的时候正好也在删除,那就会有锁的问题了,为了尽量避免用锁,可以分配一个专门增加列表,然后在update
内将增加的列表合并到总列表,然后清空增加列表,大致代码如下:
class TimerSubject : MonoBehaviour{ private readonly List<OnceTimerObserver> m_Observers = new List<OnceTimerObserver>(); private readonly List<OnceTimerObserver> m_ObserversToAdd = new List<OnceTimerObserver>(); private static TimerSubject s_Instnace; public static TimerSubject Instance { get { if (s_Instnace == null) { s_Instnace = FindObjectOfType<TimerSubject>() ?? new GameObject( typeof(TimerSubject).Name ).AddComponent<TimerSubject>(); } return s_Instnace; } } public void AddObserver(OnceTimerObserver observer) { this.m_ObserversToAdd.Add(observer); } private void Update() { if (this.m_ObserversToAdd.Count > 0) { this.m_Observers.AddRange(this.m_ObserversToAdd); this.m_ObserversToAdd.Clear(); } foreach (var item in this.m_Observers) item.Update(); this.m_Observers.RemoveAll(r => r.IsDone); }}class OnceTimerObserver{ private readonly float m_StartTime = Time.time; private bool m_IsDone = false; public Action Action { private get; set; } public float Interval { private get; set; } public void Update() { if (this.m_IsDone || Time.time < this.GetFireTime()) return; this.m_IsDone = true; this.Action(); } private float GetFireTime() { return this.m_StartTime + this.Interval; }}
WindowInstance
有了定时器就可以创建Jint
对象了,代码如下:
class WindowInstance : ObjectInstance{ public WindowInstance(Engine engine) : base(engine) { engine.SetValue( "setTimeout", new ClrFunctionInstance(this.Engine, this.SetTimeout) ); } public JsValue SetTimeout(JsValue thisObject, JsValue[] arguments) { TimerSubject.Instance.AddObserver(new OnceTimerObserver { Action = () => arguments[0].Invoke(), Interval = (float)(arguments[1].AsNumber() / 1000) }); return null; }}
支持Promise
控制器
由于Jint
暂时还不支持Promise
,因此需要自己扩展来支持,那么首先应该将代码改为async/await
方式的,大致代码如下:
// index.d.tsinterface View { push(viewModel: object): Promise<void>; // 略}class TSControllerLoading { public async onStart(): Promise<void> { while (true) { // 略 await _view.push({ ... }); await new Promise(s => { setTimeout(s, 100); }); } }}
Jint构造函数/对象/原型
在Jint
中,有3种对象:
Constructor
: 构造函数,包含实例化Instance
的方法,继承自FunctionInstance``IConstructor
Instance
: 对象(实例),在该对象上增加的方法相当于静态方法,例如:console.log
,继承自ObjectInstance
Prototype
: 原型链对象(不了解的可以网上查询相关的文章),继承自Instance
由于Promise
的实现比较复杂,该篇文章只为了满足以上业务的需求,因此只给出了Promise.then
实现,至于其他方法,大家可以自己实现.
PromiseInstance
从控制器中的代码可以看出,我们会使用到Promise
的构造函数以及Promise
实例中的then
方法,当该方法被调用的时候代表异步结束,因此只需要在PromiseInstance
上创建一个委托属性Action<JsValue, JsValue>
,该委托属性上的两个参数分别代表Promise
的resolve``reject
回调函数,大致代码如下:
class PromiseInstance : ObjectInstance{ public override string Class => "Promise"; public Action<JsValue, JsValue> ReadyAction { get; set; } public PromiseInstance(Engine engine) : base(engine) { } public override string ToString() { return this.Class; }}
PromisePrototype
由于控制器中使用到的是Promise
实例化后的then
方法,因此该方法需要定义在Prototype
上,当调用then
的时候则调用PromiseInstance.ReadyAction
,另外由于编译后的代码中会使用到instanceof
,因此必须要在原型对象上定义constructor
属性,大致代码如下:
class PromisePrototype : PromiseInstance{ private PromisePrototype(Engine engine) : base(engine) { } public void Configure() { this.FastAddProperty( "then", new ClrFunctionInstance(Engine, this.Then), true, false, true ); } private JsValue Then(JsValue thisValue, JsValue[] args) { thisValue.As<PromiseInstance>().ReadyAction(args[0], JsValue.Undefined); return null; } public static PromisePrototype New(Engine engine, GlobalPromiseConstructor ctor) { var obj = new PromisePrototype(engine) { Extensible = true, Prototype = engine.Object.PrototypeObject, }; obj.FastAddProperty("constructor", ctor, true, false, true); return obj; }}
PromiseConstructor
该对象主要的作用就是实例化PromiseInstance
以及将Promise
注入到js
代码中,大致代码如下:
class PromiseConstructor : FunctionInstance, IConstructor{ public PromisePrototype PrototypeObject { get; set; } private PromiseConstructor(Engine engine) : base(engine, null, null, false) { } public override JsValue Call(JsValue thisObject, JsValue[] arguments) { return null; } public void Configure() { } public ObjectInstance Construct(JsValue[] arguments) { return this.New( (s, f) => arguments[0].Invoke(s, f) ); } public PromiseInstance NewInstance(Action<JsValue, JsValue> taskAction) { return new PromiseInstance(Engine) { Extensible = true, Prototype = this.PrototypeObject, ReadyAction = taskAction }; } public static PromiseConstructor New(Engine engine) { var obj = new PromiseConstructor(engine) { Extensible = true, Prototype = engine.Function.PrototypeObject }; obj.PrototypeObject = PromisePrototype.New(engine, obj); obj.FastAddProperty("length", 1, false, false, false); obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false); return obj; }}
重构
修改ViewInstance.Push
让其返回PromiseInstance
实例并不够,因为instanceof
的判断不正确,因为PromiseInstance.PrototypeObject
来自不同的实例导致的,因此需要在DemoCanvas
上增加PromiseConstructor
的属性,然后ViewInstance.Push
内用PromiseConstructor.NewInstance
来创建PromiseInstance
,大致代码如下:
// ViewInstanceclass ViewInstance : ObjectInstance{ public JsValue Push(JsValue thisObject, JsValue[] arguments) { return this.m_Canvas.Promise.NewInstance((s, _) => { this.m_Canvas.Notify( arguments[0].AsObject() ); arguments[1].Invoke() });' } // 略}// DemoCanvasclass DemoCanvas : MonoBehaviour{ public PromiseConstructor Pormise { get; private set; } public DemoCanvas() { this.Promise = PromiseConstructor.New(this.m_Engine); this.m_Engine.SetValue("Promise", this.Promise); } // 略}
结尾
那么今天就到这里了,如果文章中有任何错误或者疑问欢迎提问,我会尽快回复的,如果文章对你有帮助也欢迎打赏,谢谢.