实战贴:斗战神龙女链刃骨骼设置
本文与大家分享“龙女链刃骨骼“架设遇到的一些问题和具体架设步骤。
1 架设前需要思考和解决的问题
1.1 链刃的表现效果能否达到策划要求。
对于效果,策划要求尽量接近战神奎爷的链子效果。记得之前有个战神的动画师来这边讲课,当时问了他,战神里链子的实现方式,他说是程序模拟的,具体细节不清楚。我和程序同事沟通了下,感觉完全用程序模拟不太好实现,最后还是决定用骨骼动画来制作。我的思路是把链子的骨骼依附在一个曲线上,然后再使用四个控制器来控制这个曲线的形态。初步架设好给动画同事测试了一下,动画同事感觉还行,基本形态都可以实现。
(当时龙女模型还没有做好,拿的神将的模型做的测试,链子也是临时的)
1.2 手持状态和链刃状态如何切换。
龙女武器有手持和链刃两个状态。手持状态时链子会缩在手里,武器回到手上,跟着手运动。 链刃状态时 武器会脱离手, 手和武器中间有链子链接。为了方便切换这两个状态,我给控制曲线的dummy加了position constraint和orientation constraint 控制器,通过调整控制器权重来切换这两个状态。
链刃模式:
手持模式:
备注:P_Hand_R 是手上的挂点, weaponCtrlR02是链子的控制器。状态切换是通过脚本执行的:
1.3 如何降低骨骼数带来的性能开销。
主城里同屏玩家数会很多,如果每个龙女手里都握着几十根骨骼的链子,会影响性能。为了减小这一开销,我们采用手持模式下链子不加载不显示,当使用技能时链子才加载显示出来。导出时,链子也是单独导出成一个模型和一套动画的。
没有加载链刃:
加载链刃:
2 架设步骤:
2.1 创建曲线
打开0.max 。这个文件里包含龙女的基础骨骼和链子武器模型。首先创建两个曲线匹配链子模型。
选择曲线,把曲线三等分。
最后把曲线顶点类型改成smooth。
2.2 创建dummy
接上或打开 1.max。接下来个曲线创建几个dummy。具体步骤如下:
给曲线加一个Spline IK Control。
点击Create Helpers生成dummy。
修改下link types 和Helper Size
对dummy统一命名:
这样就可以通过移动这些dummy来调整曲线形态了。
2.3 创建依附于曲线的point
接上或打开2.max。接下来就是创建骨骼,并让骨骼依附与曲线了。普通的线性IK可以让骨骼依附与曲线,但是线性IK无法改变链子的长度。我这里变通了一下做法,我创建了一些point,让point平均发布在曲线上,并使用path constraint 控制器让point跟着曲线运动。然后使用这些point去约束骨骼。我们先创建并设置这些point:
创建一个point,并把position控制器改为 path constraint。
Add Path, 选取右边的曲线。
删除掉path的关键帧。
勾上follow,这样point的朝向会沿着曲线的切线。
一根链子我们使用十根骨骼,所以我们需要十一个point把这个曲线十等分,按住shift 移动point,沿曲线复制这些point,并调整Along Path。
选择这些point,统一命名
2.4 创建链子骨骼
接上或打开3.max。在创建链子骨骼之前我先讲解下如何通过两个point来约束骨骼。
首先创建一段骨骼。然后再创建两个point对齐到骨骼的首尾。
把骨骼的position控制器设置为position constraint,然后add position target,拾取左边的point。
把骨骼的rotation 控制器设置为 LookAt constraint, 然后add lookAt target,拾取右边的point。
这是我们移动两个point看看效果,我们发现我们可以通过两个point控制骨骼的位移和旋转,但是骨骼不会根据两个point的距离来改变缩放,接下来我们就来设置缩放。
把骨骼的Scale 控制器设置为 ScaleXYZ。
我们只要控制X轴向的缩放,所以我们把ScaleXYZ下的X Scale控制器设置成float script。
这时会弹出一个窗口,先关闭掉。
接下来我们需要设置X Scale 的属性,为了方便大家操作,我把左边的point名字改为PointL,右边的改为PointR。
执行一句脚本 distance $pointL $pointR 得到 两个point之间的初始距离。记录下这个数字。
然后选择骨骼,右键X Scale 在右键菜单里选择 properties。弹出对话框:
把下面一段脚本复制把Expression下,最后一个数字36.2616是两个point的距离,替换成之前记录的那个数字。然后点击 Evaluate。
dependsOn $PointL $PointR
dis = distance $PointL $PointR
dis/36.2616
这时候再移动这两个point, 骨骼就会有缩放了:
如果我们要这样手动创建和设置每一根链子骨骼,会花费很长时间。这里我写了一小段脚本辅助创建骨骼。首先选择一边曲线上所有point。
执行脚本 createRigging.ms。 或者直接把这个文件拖入到max窗口。这样一边的链子就创建好了。
最后设置一下骨骼的名字 并把父级设置为手上的虚拟体P_Lianzi_R。
2.5 添加操作手柄
接上或打开4.max。接下来为链子添加操作手柄,我们通常在操作手柄上key帧控制链子。
首先创建一个样条曲线,什么形状都可以。 我选择Donut形状。
把这个Donut对齐到Help_LianziR01, 在复制两个,分别对齐到Help_LianziR02,Help_LianziR03。把这三个Donut 名字改为 Ctrl_LianziR01,Ctrl_LianziR02,Ctrl_LianziR03。
选择Help_LianziR00,Help_LianziR01,Help_LianziR02,Help_LianziR03,alt + 鼠标右键, 选择Freeze Transform。弹出对话框点“是”。
把Help_LianziR00的position list 控制器下的 Available设置为Position Constraint。Add Position Target 拾取 P_Lianzi_R。
把Help_LianziR01的positionlist控制器下的 Available设置为Position Constraint。Add Position Target 拾取 P_Lianzi_R和Ctrl_LianziR01。并把P_Lianzi_R的权重设置为0, Ctrl_LianziR01的权重设置为100。Help_LianziR02,Help_LianziR03也和Help_LianziR01 做相同设置。
复制Ctrl_LianziR03 把名字改为 Ctrl_LianziR04。Ctrl_LianziR04是用来控制前端武器的旋转。把Ctrl_LianziR04对齐到P_Hand_R。
选择 Ctrl_LianziR01,Ctrl_LianziR02,Ctrl_LianziR03,Ctrl_LianziR04。alt + 鼠标右键, 选择Freeze Transform。弹出对话框点“是”。
把Ctrl_LianziR04的position list 控制器下的 Available设置为Position Constraint。Add Position Target 拾取 Line_PathR10
把Ctrl_LianziR04的rotation list 控制器下的zero euler xyz设置为orientation Constraint。Add orientation Target 拾取 P_Lianzi_R,然后在点击add World as target。把P_Lianzi_R的权重设置为0, world的权重设置为100。
然后再把available控制器设置为 Euler xyz,并激活它。
这是再次把Ctrl_LianziR04 对齐到 P_Hand_R。
选择P_Hand_R,alt + 鼠标右键, 选择Freeze Transform。弹出对话框点“是”。
选择P_Hand_R ,把P_Hand_R的position list控制器下的 available设置为Position Constraint。Add Position Target 拾取 Line_PathR10
把P_Hand_R的rotation list控制器下的available 设置为Orientation Constraint。Add Orientation Target 拾取 Ctrl_LianziR04。
现在右边的链子已经设置完成。左边进行相同的操作。
6. 接上或打开5.max。接下来分别选择链子模型,添加skin修改器,把骨骼添加到skin修改器,调整点的权重。6.max为最终完成文件。
3 辅助脚本工具介绍:
把longnvWeapon.ms 拖入到max视窗会弹出一个窗口。
LockToHand 手持状态。
UnLockToHand 链刃状态。
ResetCtrl 把链子缩到手里,方便手持状态和链刃状态见的切换。
selectDummy 选择dummy, 可以查看上面的关键帧。
4 导出相关
导出时最好先把链子的动画bake成逐帧的, 然后再把哪些曲线,point,dummy ,手柄删除掉。
下面这段脚本就是我们导出时需要执行的。
fn bakeLianzi =
(
lianziBones = $B_Lianzi* as array
append lianziBones $P_Hand_L
append lianziBones $P_Hand_R
animationData = #()
for b in lianziBones do
(
eachData = #()
for f = animationrange.start to animationrange.end do
(
tempTransformData = at time f b.transform
append eachData tempTransformData
)
append animationData eachData
)
for b = 1 to lianziBones.count do
(
lianziBones[b].transform.controller = PRS()
lianziBones[b].pos.controller = Position_Expression ()
lianziBones[b].pos.controller = position_xyz()
lianziBones[b].rotation.controller = linear_rotation ()
lianziBones[b].rotation.controller = Euler_xyz()
lianziBones[b].scale.controller = bezier_scale ()
frameTime = 1
animate on
(
for f = animationrange.start to animationrange.end do
(
at time f lianziBones[b].transform = animationData[b][frameTime]
frameTime += 1
)
)
)
delete $line*
delete $Help_Lianzi*
delete $Ctrl_Lianzi*
)
bakeLianzi()
5 小结
链子的动画最终是有操作手柄间接驱动的。中间还有 dummy,曲线和point。
操作手柄 —> dummy—>曲线—>point—>链子骨骼。
创建的过程中运用了几种常见的控制器,大家最好先熟悉和掌握这几种控制器的使用。在设置过程中保持对物体命名的好习惯,这样方便后面脚本使用。
本来只想简单描述下几个关键的步骤,没想到写的过程中一发不可收拾。但感觉后面有些还是不够详细,如果都展开的内容太多了。大家如果遇到不明白的地方可以看看提供的几个过程文件。最后附送我们实际游戏里用的一些动画资源作参考,希望对大家有所帮助。
大家可下载附件视频看下版本中的效果。附件中还包含max文件和脚本文件。