在实际游戏中,逻辑线程需要对渲染对象做许多操作,比如添加与删除,改变渲染对象的属性等等,而由于在先前的设计中,逻辑线程与渲染线程相互独立,如果只是改变某一共享数据,没有问题,但如果操作影响到了场景结构,例如实体的添加与删除,则必须进行线程同步,这又违背了FlagshipEngine的设计初衷——避免繁重的逻辑计算影响渲染速度。
解决办法其实在上一篇中已经提到了,仍然是利用天然的同步机制——Windows消息,添加实体时,逻辑线程只是new了一个Entity对象,设置这个对象的初始共享数据,比如位置信息,同时向渲染线程发送一条WM_ADDENTITY的自定义消息,将Entity指针作为wParam传递。渲染线程接受到消息后调用Entity的UpdateScene方法,更新Entity在场景树中的位置,并加载资源。
删除也是一样,逻辑线程向渲染线程发送WM_DELETEENTITY消息,并不再使用该Entity指针,渲染对象则处理改消息,将此Entity从场景中删除并卸载资源。
这里有一个非常危险的情况,前面一篇提到,资源加载也是通过消息传递实现的,同样是传递的资源指针,如果逻辑线程添加了一个Entity,还没加载就删掉了它,则资源加载线程会拿到一个过期指针,一切就结束了。。。
解决这一问题,最稳妥的方法是消息的wParam并不传递指针,而是传递该Entity或资源的唯一ID,这样的话即使ID过期,也可轻松忽略掉这条消息,坏处是每次消息处理都的从全局的map里检查是否存在此ID对应的Entity或资源,这可是笔不小的开销。
第二种方案,我们仍然传递指针,只是在接受到WM_DELETEENTITY消息时,检查该Entity是否已经加载完成,如果没有完成,则重新将此消息加入消息队列,下个渲染帧再次判断。
FlagshipEngine的多线程设计大致就是如此了。