Left 4 Dead 2

Left 4 Dead 2

Mage Staff (Goldenglow's)(Arknights)(15-Round Hunting Rifle)
您🛰  [developer] 12 Feb @ 2:09am
在不使用粒子系统的情况下给武器加特效在技术上介于可行和不可行之间。
我没找到让骨骼根据动画淡入淡出的参数,引擎似乎也不允许骨骼在XYZ轴上缩放,因此特效的入场和出场动画只能通过背面剔除来实现。这会让某些特效在技术上无法实现。

虽然有个叫 Cycle 的材质代理(material proxy)据称可以根据动画的播放进度来输出变量,但是我将 resultVar 挂载到 $alpha 透明度变量后发现,尽管纹理可以随着时间流逝而淡出(表明 cycle 材质代理正常工作),但是影响 cycle(resultVar)的却不是当前播放的动画。
可能是因为我用 subtract 从 idle 动画继承某些特效的过程干扰了该材质代理,使其根据 subtract 所引用的动画的播放进度来输出 resultVar,而非从当前播放的动画(例如reload)的播放进度来输出相应的变量。

考虑到我这里必须从 idle 继承某些特效,我没有继续调试这个材质代理,所以不太确定这个初步结论(subtract 会干扰 cycle 材质代理)是否准确。

另一方面,不使用粒子系统貌似会让动画之间没法平滑切换,例如视频中的射击动画、装弹动画与“推”动画之间的冲突:从射击或装弹动画切换到“推”动画会让游标及附着到游标上的“粒子特效”瞬间消失,这是因为在“推”动画中,相关骨骼被移动到了视图不可见的位置上。

理论上也许可以用 subtract 使“推”动画从装弹或射击动画那里继承骨骼的位置和角度(关键帧信息),但是(重复一遍)我这里要用 subtract idle,所以没试过这种方法是否可行。
让“推”动画使用 $weightlist { 游标骨骼1 0, 游标特效组1 0, ...} 禁用“推”动画对游标骨骼和特效骨骼的影响似乎并不会缓解这个问题(禁止“推”动画对装弹动画和射击动画相关骨骼产生任何影响),尽管 $noanimation 有效(禁用“推”动画对所有骨骼的影响)。
这可能不是因为 动画.smd 里面存在相关骨骼的关键帧占位符,因为在创建自定义“推”动画(使用含有相关顶点组关键帧信息的“推”动画)之前所使用的原版“推”动画(不含相关顶点组的关键帧信息)也存在这个问题,表明通道存在与否并不是产生这个问题的原因。我猜可能是因为“推”动画的 subtract idle 会使其继承 subtract 对象“idle”动画所使用的 weightlist,不过我没有进一步地调试这个参数以检验这一假设是否成立。

恕我无知,唯一能够实现淡出(也包括淡入)的方法可能只有利用骨骼的运动来变形面,利用背面剔除的机制来让纹理逐渐消失。
然而,引擎限制了骨骼(确切地说是顶点组)的最大数量:顶点组不超过128个。这一限制会导致基于骨骼的动画特效存在天花板。

通过堆料来模拟粒子系统的视觉特效存在数量上的局限性,例如,每个游标均各自使用了 11 个额外骨骼来实现相关粒子特效,三个游标总计使用了 33 个与游标本身无关的骨骼,合计 36 个顶点组。这把武器在完成时一共使用了 110 个顶点组,只要再加两个游标,这把武器就可能会因为达到 crowbar 的硬性限制而无法编译。
模型也存在材质数量上的限制:不超过32个材质。材质方面的限制应该问题不大,虽然这里用满了 32 个材质,不过主要原因是我在弄纹理时误以为预算充裕到可以将模型的每一个部分都分别指定一个单独的材质,因此使用了大量的材质来提高模型的纹理分辨率并降低工作量,而非以平铺的形式投射到少数几个材质上,这导致我在制作的后期没办法继续添加特效材质。
Last edited by 您🛰; 12 Feb @ 2:09am
< >
Showing 1-4 of 4 comments
桜流し 12 Feb @ 10:11am 
好强的思维逻辑,学习一个
请查询此VDC文章而为游戏增加粒子。粒子清单文件(particle manifest)并不类似声音脚本(sound script),它不会与游戏本身或其它模组产生冲突呀(具体的“particles\particles_manifest.txt”文件行为与VScript脚本的“director_base_addon.nut”或“scriptedmode_base_addon.nut”相似,许多带有脚本的模组通常包含以上任一文件,但并不会见到多个包含以上文件的模组出现冲突。)。参考此文章以创建1个粒子,查阅此文档以创建动画粒子。若仍然不知道如何设置粒子,可以选择联系这里呀。

由于游戏不支持对骨骼进行缩放(SMD文件仅对动画储存位置与旋转信息),请使用粒子系统实现功能,它允许对效果有更多的控制,不应使用骨骼动画。

对于推击动画,或许“addlayer”将会有所帮助呢,但正如上述所说,若已经使用粒子系统,请不要再使用这个蹩脚的方法来处理复杂效果。

对于材质插槽数量问题,若不想对其进行优化,请尝试使用NekoMDL模型编译器以提升限制至128个材质插槽;若可以优化,只需简单地将多个纹理置入同1个图像中,为那些平面设置相同的材质,并设置它们的UV布局即可。不过,正如其上所说,对于复杂的效果,使用粒子系统更好。

另外,“Cycle”材质代理无法获取当前播放的动画,仅获取当前动画的进度,并非“subtract”影响,因此它并不十分有用。
使用“subtract <anim> <frame num>”与“weightlist <weight list name>”等动画与序列选项,它们仅仅影响当前动画/序列,不会有任何继承关系
“subtract”动画选项与“delta”序列选项是混合动画的1种方法(VDC文章),它表示“此动画将减去subtract选项所指定的动画”,通常表明“此动画将被作为增量动画使用”。如此行为的目的涉及混合序列(或被称为“动画混合”)的概念:“动画混合仅仅意味着在1个骨架网格上的2个或多个动画之间进行平滑过渡”,它主要处理多个动画之间的过渡,而非同时播放多个动画。
Last edited by 墨染月𝑴𝑹𝒀; 17 Feb @ 6:29am
您🛰  [developer] 17 Feb @ 11:44am 
Originally posted by 墨染月𝑴𝑹𝒀:
粒子清单文件(particle manifest)并不类似声音脚本(sound script),它不会与游戏本身或其它模组产生冲突呀

奇怪的是,https://developer.valvesoftware.com/wiki/Particles_manifest.txt 声称打包的粒子清单会重新覆盖粒子清单,上下文暗示粒子清单与粒子清单彼此之间并不能共存,并没有像引文那样指出这玩意可以以增量的形式与其他粒子清单合并或叠加,我之前因此直接忽略了有关内容。

Originally posted by 墨染月𝑴𝑹𝒀:
请不要再使用这个蹩脚的方法来处理复杂效果。

其实挺好玩的,“推”的那个动画用的特效我都没想到竟然那么便宜就能实现,下次还敢(不是)

Originally posted by 墨染月𝑴𝑹𝒀:
若可以优化,只需简单地将多个纹理置入同1个图像中

虽然可以合并纹理,但是合并纹理在某些情况下并不理想,例如:
1)纹理图像有最大尺寸限制:4096x4096
将两个 4096x4096的图像合并为 8192x4096 应该是不可行的。除非有方法提高这个上限,否则只能通过降低纹理分辨率以使其适应 4096x4096 的限制,这样也会降低纹理质量。

2)多个材质可以使用相同的纹理文件,并通过调整材质参数来让多个使用相同纹理的对象具有不同的视觉效果

例如,ItemPickup 动作中出现的蝴蝶使用了与玻璃球相同的纹理文件(蓝色天空全景图),不过它们却分别使用了不同的材质文件

材质定义了纹理的滚动速度,蝴蝶材质的纹理滚动速度快于玻璃球(textureScrollRate:1.35 vs 0.15),而如果使用相同的材质(将两个材质合并为一个),则会导致蝴蝶纹理的滚动速度与玻璃球纹理的滚动速度一样慢,或是玻璃球的纹理滚动速度与蝴蝶的纹理一样快。

或者,(举例)假如要让游标具有不同的颜色,一个具有红色色调,一个具有绿色色调,一个具有蓝色色调,那么:
A)要么修改basetexture并创建额外两个纹理文件,通过修改纹理(而非材质)来修改变更游标的颜色;
B)要么分别为三个游标创建不同的材质(三个vmt文件)并让它们分别使用不同的 $color 键值,如:
#vmt1 -- 游标1 $color "[ 1 0.5 0.5 ]" $basetexture "cursor.vtf"
#vmt2 -- 游标2 $color "[ 0.5 1 0.5 ]" $basetexture "cursor.vtf"
#vmt3 -- 游标3 $color "[ 0.5 0.5 1 ]" $basetexture "cursor.vtf" // 实际上我并没有修改游标的颜色或是增加游标的材质使用量,这只是个例子。

对于(A)第一种方法,这要么以提高纹理大小为代价维持纹理质量(游标纹理乘以三),要么降低纹理分辨率并维持纹理大小(除以三,然后平铺并重新映射UV);
对于(B)第二种方法,则是以增加材质使用数量为代价复用纹理文件,并通过修改材质参数来实现不同的效果(如彼此之间具有各不相同的透明度等等)。

在某些情况下增加材质使用数量可能是没办法避免的。

Originally posted by 墨染月𝑴𝑹𝒀:
另外,“Cycle”材质代理无法获取当前播放的动画,仅获取当前动画的进度
VDC条目中对此材质代理的解释好像有点问题。

有关的“动画”既可以指地图上的实体的动画(例如c1m4赛车、c3m5救援船、c11m4撞栅栏的面包车),也可以指w模的idle动画(w模本身是个单独的实体)、v模的武器动作、逐帧播放的vtf纹理等(我没摸过GIF纹理,因为太贵了)。
我不太明白影响这个材质代理的输入究竟是哪个,当时只是随便试了一下。

Originally posted by 墨染月𝑴𝑹𝒀:
使用“subtract <animation> <frame num>”与“weightlist <weight list name>”等动画与序列选项,它们仅仅影响当前动画/序列,不会有任何继承关系。
嗯……我先具体解释一下这部分吧。

subtract 的行为貌似是计算当前动画与目标动画之间的偏移量,即“提取彼此之间的差异”。例如,idle 和 shoot 动画里的武器位置相同,shoot 序列含有 subtract idle 0,大致的意思上该序列是忽略与 idle 相同的部分,只保留不同的部分(实际情况要复杂一些,比如细微的差异并不会像合并顶点那样被忽略)。

在这种情况下,shoot 会因为 subtract 删除了与 idle 相同的数据而只有游标位置、便宜粒子和左手姿态之类的信息,与 idle 相同的数据被“减去”了。(这是个有些过度简化的说法)
delta 的意思则是将剩下的偏移量应用到正在播放的动画上。

(例如)具体来说:
1)shoot 动画里左手手臂位于三维坐标(XYZ)"[1 5 2]", idle 动画里左手手臂位于三维坐标 "[0 3 1]";
2)计算偏移量:"[1 5 2]" 减去 "[0 3 1]" 为 "[1 2 1]";
3)(猜测)将偏移量作为序列($sequence)保存,应该不会修改 .smd 文件的相关数据;
4)delta:应用偏移量到当前播放的动画,使左手根据计算后的偏移量偏移 "[1 2 1]"。

我直接放插件日志文件里的偏移量好了:
// KeyframeInsertionMonitor.log // (1)和(2) ValveBiped.Bip01_L_Hand Location before = -3.814697265625e-06, -9.5367431640625e-07, -6.67572021484375e-06 Rotation before = -62.54338653164644, 7.709869723786203, 29.706476870339344 Location after = 4.045010089874268, -0.42440399527549744, 1.2808799743652344 Rotation after = -38.863998358520575, 14.61780010693204, 11.545599801280522 Location offset = 4.045013904571533, -0.42440304160118103, 1.2808866500854492 Rotation offset = 23.67938817312586, 6.907930383145836, -18.16087706905882 // (blender)上述的偏移量是姿态变更应用之前和之后的计算结果。blender不支持将对某帧的修改应用到其他帧上,所以需要计算偏移量,再将偏移量应用到别的关键帧上。 //(4)应用偏移量到某个关键帧上的特定骨骼,如ValveBiped.Bip01_L_Hand。

更通俗一些来讲的话,就是:
你有两张照片(两个动画),一个是昨天拍的照片,另一个是今天拍的照片,它们看起来就跟屏幕截图一样相同:

$sequence "今天拍的照片" { "anims\9管460同时齐射,大道都和平了.smd" activity "ACT_VM_SHOOT_SNIPER_LAYER" 1 subtract "昨天拍的照片" 0 delta }

你 subtract 了昨天拍的照片,它会计算昨天的照片(idle, subtract 的对象)和今天的照片(shoot)之间的区别,然后将与昨天相同的像素(山水物件之类的刺激物)从今天的照片里移除,只保留与昨天不同的部分。(虽然这么说技术上不准确,但是比较方便理解)

比如今天有一块臭豆腐在地上,它会将照片的背景剔除(透明度油漆桶),只保留与昨天不同的部分 —— 地上的臭豆腐。
* 顺便说一句,如果更换照片拍摄的位置(更换动画),或者说两张照片的拍摄位置、角度和对象都不同(不是屏幕截图而是手机拍屏),那么偏移量的计算会出现一些很奇怪和很严重的问题。

然后,delta 的意思是将这块臭豆腐(应用剔除后的动作)添加到你的视野里(即正在播放的动画)。

同理:
$sequence "Sniper_shoot_layer" { "anims\shoot.smd" activity "ACT_VM_SHOOT_SNIPER_LAYER" 1 subtract "ta_sniper_run_trans_idle" 0 delta }
上面的代码的意思是将 shoot 动画里与 subtract 的对象 idle 相同的部分剔除,然后保存到序列里(它不应该修改原始 .smd 动画文件)

应用透明度油漆桶后这个动画里应该只有游标的射击、便宜粒子和左手的动作,delta 使其将这部分以覆盖的形式应用到当前播放的动作上(比如 idle 和 run)。

只不过我这里用法完全相反,所以当时把上述的 subtract 行为误解成了继承。

例如,正常情况下:
1)新建一个与 idle 完全相同的序列,与 idle 区别是具有特斯拉闪电旋转效果;
2)subtract idle 意味着该新建的序列与 idle 相同的部分被忽略,序列本身只包括特斯拉闪电的旋转效果;
3)用 delta 将其与其他动画,如 idle 和 run 叠加。
4)尽管上述的代码并没有说明,但是 idle 和/或 run 动画实际上是一直在播放的 —— delta 意味着事件(如射击事件)调用的序列(如shoot_layer)将与正在播放的动画混合。deploy 动画无法使用 subtract idle & delta 的原因可能是 idle 动画还没开始播放。

然而我这边的问题是:
1)我怀疑本地 .qc 文件内的序列位置必须与在线服务器上的序列位置相同,比如 idle 序列位于 a_sniper_arms_vaims 下方,如果位于上方那么可能联机游戏会出现问题;
2)我怀疑额外的序列在联机游戏上不会被正确地使用,例如我在 .qc 文件最底部添加了一个额外的射击动画序列,然而它在联机游戏里却引起了动画方面的问题(暗示额外的序列会引起潜在的兼容性问题);
3)貌似有些序列在联机游戏中使用,但是却不会在本地服务器(测试环境)上使用,替换它们可能会产生类似的动画问题;
4)我不知道怎么把经过 subtract 处理的序列(如特斯拉线圈闪电效果)添加到正在播放的动画上(或者说看语法糊涂了),因为想要使用序列就需要添加 activity "ACT_VM_IDLE_SNIPER" 之类的参数到序列,而额外的 activity 又意味着该序列将作为额外的动画使用(多重动画可能会在多人游戏中引起兼容性问题)

现在想来,addlayer 好像可以将 subtract 的序列添加到其他序列里,但是……(1)(2)(3)。

于是……:
1)我将新建的、含有特斯拉闪电效果的序列直接用作 idle 序列;
2)其他动画通过 subtract idle 和 delta 将特斯拉闪电的旋转效果“继承”(添加)过来,比如 run、shoot、reload等等;(实际上却是将应用剔除后的序列动画藉由 delta 应用到正在播放的 idle 或 run 动画上)
3)被“继承”的特效除了特斯拉闪电效果之外还包括蝴蝶扇动翅膀涉及的骨骼运动,没放到 itempickup_loop 里,因为我觉得这么做方便一点。

严格来说“继承”这个词在技术上确实是不准确的。当时受限于这个问题或者那个问题,人都麻了,只想着“这种我没试过但是想过的方法也许可以在那个情况下起作用(指修改 idle 动画的骨骼权重)”,然后乱写一通。
Last edited by 您🛰; 18 Feb @ 11:00am
Originally posted by 您🍂:
奇怪的是,https://developer.valvesoftware.com/wiki/Particles_manifest.txt 声称打包的粒子清单会重新覆盖粒子清单,上下文暗示粒子清单与粒子清单彼此之间并不能共存,并没有像引文那样指出这玩意可以以增量的形式与其他粒子清单合并或叠加,我之前因此直接忽略了有关内容。
《求生之路2》的行为不同,请参考上个评论中对《求生之路》添加粒子的文章。模组中包含“particles/particles_manifest.txt”应当不会在游戏中引发冲突。

Originally posted by 您🍂:
VDC条目中对此材质代理的解释好像有点问题。

有关的“动画”既可以指地图上的实体的动画(例如c1m4赛车、c3m5救援船、c11m4撞栅栏的面包车),也可以指w模的idle动画(w模本身是个单独的实体)、v模的武器动作、逐帧播放的vtf纹理等(我没摸过GIF纹理,因为太贵了)。

或许描述不清楚,但这里并不认为其中的“Animation”词语有歧义,因为该材质代理位于“Entity Data Access(实体数据访问)”分类中,它确实是指模型的动画,但不会是VTF纹理,亦不是指地图中的赛车、救援船等,它们的实现方式不是模型动画。

Originally posted by 您🍂:
1)我怀疑本地 .qc 文件内的序列位置必须与在线服务器上的序列位置相同,比如 idle 序列位于 a_sniper_arms_vaims 下方,如果位于上方那么可能联机游戏会出现问题;
2)我怀疑额外的序列在联机游戏上不会被正确地使用,例如我在 .qc 文件最底部添加了一个额外的射击动画序列,然而它在联机游戏里却引起了动画方面的问题(暗示额外的序列会引起潜在的兼容性问题);
3)貌似有些序列在联机游戏中使用,但是却不会在本地服务器(测试环境)上使用,替换它们可能会产生类似的动画问题;
4)我不知道怎么把类似的待机动画添加到正在播放的动画上(或者说看语法糊涂了),因为想要使用动画就需要用 activity "ACT_VM_IDLE_SNIPER" 之类的参数,subtract idle 并不意味着这部分特效会被添加到 subtract 的对象 idle 里。

1. 是的,通常来说,序列在QC中的声明必须与服务器相同。请将额外的序列放置在“item_end_layer”序列之后。
2. 额外的序列能够在联机游戏中被使用,部分序列的播放由服务器指定(服务器将传递客户端需要播放动画的索引,这正是额外的序列需要被放置在“item_end_layer”之后的原因);但部分序列则由客户端决定(例如“ACT_FIDGET(玩弄武器)”活动)。
3. 并非如此,出现此问题,可能由于序列在QC文件中定义的顺序问题(见第1条回答)。
4. 这里不能理解后段话的意思,不过,若指的是其它模组作者所制作的“检视武器”动画,请参考使用“ACT_FIDGET(玩弄武器)”活动(使用方法参考自mrFunreal的指南)。





subtract选项的完整参数为:subtract <anim> <frame(帧)>,因此,当使用“subtract idle 0”时,它的意义为:“当前动画每帧的数据将减去idle动画第0帧的数据,当前动画将成为idle动画第0帧的增量”
subtract idle 0: delta anim = current anim - idle (frame 0)


“idle”序列在实体生成时将立刻播放,“run”动画仅在玩家疾跑时播放。你或许注意到QC文件中的“$poseparameter move_x”(姿势参数)与“idle”序列中的“blend move_x”,姿势参数“move_x”的实际值由游戏源代码中继承CBasePlayerAnimState(基本玩家动画状态机类)的动画状态机对象所指定,QC文件中的“idle”则声明1个混合序列,在“run”与“idle”之间混合。
< >
Showing 1-4 of 4 comments
Per page: 1530 50