影子坐标是一种在Ballance制图界中描述产生影子的一个名词。影子坐标与物体(CK3dEntity)有关而与物体的网格(CKMesh)无关。而在稳定的产生影子的方法被发现后,所谓的影子坐标说法不合理,更准确的说法应该是影子属性(Property)或者影子标识(Flag)。为了前后兼顾,因此本文仍然采用影子坐标一词。
历史
在早期的自制地图中,制图者已经注意到归入Shadow组中的物件,有些会产生影子,有些则不可以。在大量总结规律之后,得到如下结论:原版关卡的结构(即使该物件原先是机关示意模型)在归入物理化物件组和影子组后都可以产生影子,而制图者由外部建模并导入的结构不可以。之后便产生了“影子坐标”的说法,即:原版关卡的文件中,所有物体均已具有影子坐标,且复制已具有影子坐标的物体时,影子坐标可以被同时复制。并且制图者已经认识到影子坐标与物体相关而与网格无关,而且通过切换原版物体关联的网格可以强行让物体具有影子坐标,并在游戏中显示。
“影子坐标”最早的称呼来源不明。2010年时,加入影子时产生的此类奇特现象已经被发现,并于之后数月内逐渐广传,但至少至2014年,均无人给予此现象任何名称。有推测[来源请求]认为“影子坐标”的称呼最早来源于2016年NiceMelodies在其制图教程中对之的称呼。
再发现
2020年7月22日,yyc12345在制作完地图斯卡布罗集市之后一直念念不忘这种奇特的加影子的方式,希望探究影子到底是由什么引起的。
再发现的主要步骤是这样的:
- 打开一个原版关卡;
- 从外界导入一个自定义物体(CK3dEntity);
- 选择一个原版关卡物体(CK3dEntity);
- 将原版关卡的物体(CK3dEntity)对应的Mesh(CKMesh)关联到自定义物体(CK3dEntity)上;
- 清理其余所有无用组件,然后保存;
- 使用Virtools SDK编写独立的Virtools播放器,尝试读取物体(CK3dEntity)的一些属性来比较两者的不同。
比较的结果便是,两者的MoveableFlags不同,但是二者在Virtools 3.5环境下,通过遍历枚举VX_MOVEABLE_FLAGS所有可能的情况,发现两者完全一致,进一步研究之后发现原版物件(CK3dEntity)使用了一个不存在于Virtools 3.5中的VX_MOVEABLE_FLAGS中的标识。并且观察枚举VX_MOVEABLE_FLAGS,非常容易发现其并不连续,因此一个大胆的推论就是:影子使用了一个Virtools 3.5遗弃的VX_MOVEABLE_FLAGS标识来进行实现。
之后通过编写代码,为一些自定义物体加上此标识后,影子出现。也进行了许多额外的测试,包括将原版关卡中的物体的此标识去除观察影子是否消失来进行验证。
影子的实质
影子坐标的实质就是一个VX_MOVEABLE_FLAGS中被遗弃的标识(VX_MOVEABLE_RENDERCHANNELS)。在Virtools 2.1中起作用,而在Virtools 3.5中完全不可见。更准确的来说,此标识在Virtools 2.5到Virtools 3.0的升级中被新功能替换。
被遗弃的标识,VX_MOVEABLE_RENDERCHANNELS,在文档中做了如下描述:(User) If not set, additional material channels on the mesh used by this entity won't be rendered
。即其控制了是否渲染额外的Material Channels。Ballance中的影子是通过自定义Building Block:TT Shadow进行实现。而通过观察TT Shadow的反汇编代码,其与Virtools SDK附带的示例代码中的SimpleShadow极为相似,而通过阅读SimpleShadow的代码可以得知,阴影的实现是通过在对应物体的Mesh上添加Material Channel来进行实现的。因此,如果不启用VX_MOVEABLE_RENDERCHANNELS,影子所属的Material Channel将不会被渲染,呈现影子消失的状态。
属性被丢弃也注定了不可能通过Virtools 3.5的原生操作来实现自主添加影子,只能被动地借用原版具有影子坐标的物体(CK3dEntity)来实现影子。但是可以通过VSL和Virtools SDK编写的脚本与插件来实现。
使用Virtools SDK编写插件为物体加上影子的代码可以参见下文(C++):
CK3dEntity* obj = (CK3dEntity*)ctx->GetObjectByName("NAME"); DWORD objFlag = obj->GetMoveableFlags(); objFlag |=8; obj->SetMoveableFlags(objFlag);
使用Virtools内编写Action为物体加上影子的代码可以参见下文(VSL):
Entity3D ent = Entity3D.Cast(ac.GetObjectByName("NAME",NULL)); int f = ent.GetMoveableFlags(); if ( f / 8 % 2 == 1 ) { f = f-8;} else { f = f+8;} ent.SetMoveableFlags(f);