Roblox 动画(R6 / R15)
您将动作编写为一个小的 JSON 动作规范:一组用解剖学术语描述的关键姿势(如“右上臂向前90°”、“膝盖弯曲60°”)。集成的引擎会自动处理手动容易出错的部分:两种角色模型的Roblox关节数学、插值、保持脚部贴地、3D预览器、合理性警告以及烘焙关键帧导出。
用户通过观看来评判结果,因此循环为:规划阶段 → 编写规范 → 构建 → 自行查看故事板 → 修正 → 交付预览 + 关键帧。 不要跳过查看步骤;在纸面上看起来正确的姿势编号实际上往往是错误的(手臂穿过胸部,踢腿从未到达球)。
工作流程
- 确定动作。 角色模型(R6或R15;只有在确实无法推断时才询问,否则默认使用R15并说明),惯用手,循环或单次,大致时长,以及需要的游戏逻辑挂钩(打击帧、球释放、脚步声)。对于明确的要求,立即开始构建。
- 在编写数字之前将其拆分为阶段。 几乎每个动作都是:准备 → 预期 → 动作 → 跟随 → 恢复。阅读
references/motion-cookbook.md中您要做的类型(战斗、体育、循环动作、特技、R6特有内容);其中有时间表和姿势食谱。 - 将规范写入
<name>.json。 从examples/中最接近的文件开始,而不是从零开始。 - 构建:
node <skill>/scripts/ranim.cjs <name>.json --out out(可以传递多个规范;它们在一个预览中显示为标签页)。只需要Node,无需安装包。 - 读取报告(打印在stdout上的内容)。它列出了每个关键帧头部、手和脚的实际位置(studs:R = 角色右侧,U = 地面以上,F = 前方)并打印
WARN行。使用这些位置检查意图:拳头真的在胸部前方吗?脚在球的高度吗? - 使用读取工具查看
out/storyboard-<Name>.png(每个关键帧从3/4视图和侧视图)。如果没有生成PNG,则使用无头浏览器截图out/preview.html或读取SVG。将每个面板与您计划的阶段进行比较。 - 修正并重新构建,直到姿势清晰可读,并且警告消失或已明确接受。通常需要两到三轮。
- 交付: 交互式
out/preview.html(作为工件发布或发送文件),加上out/<Name>.rbxmx和out/<Name>.lua。如果可能,请内联显示故事板图片。简要告知用户如何将其导入Studio(见下文),以及存在哪些动画事件。
动作规范
{
"name": "JabCross", // 成为KeyframeSequence名称和文件名
"title": "刺拳-交叉组合", // 预览器中显示
"rig": "R15", // "R15" 或 "R6"
"priority": "Action", // Idle | Movement | Action | Action2..4 | Core
"loop": false,
"description": "预览旁边显示的一两句话。",
"keys": [
{ "t": 0, "label": "防御", "note": "在阶段列表中显示。",
"pose": { "UpperTorso": { "turnR": 10, "bendFwd": 8 },
"LeftUpperArm": { "fwd": 50, "out": 8 }, "LeftLowerArm": { "bend": 120 },
"LeftFoot": "flat" } },
{ "t": 0.1, "label": "刺拳", "ease": "out", "copy": "防御",
"pose": { "LeftUpperArm": { "fwd": 86, "in": 22 }, "LeftLowerArm": { "bend": 6 } } },
{ "t": 0.8, "label": "恢复", "copy": "防御" }
],
"markers": [ { "t": 0.1, "name": "Hit", "value": "刺拳" } ],
"props": [ { "type": "ball", "radius": 0.6, "color": "#e07a2c", "keys": [ ... ] } ]
}
(注释仅用于说明;文件必须是纯JSON。)
可选的顶层字段:fps(默认为30;非常快的动作使用60),duration(默认为最后一个关键帧的时间),grounding("off" 表示手动控制高度),mirror: true(左右翻转整个片段,例如左脚版本),endKeyframe: true。
关键帧
t秒(对齐到帧)。第一个关键帧在0。pose将关节名称映射到通道(角度单位)。关键帧中未列出的关节仅通过该关键帧进行插值,因此中间关键帧可以只提及相关的关节。从未提及的关节保持静止状态,并且不会被导出;第一次在关键帧0之后出现的关节从静止状态(0°)开始。copy:先前关键帧的标签或索引。姿势从副本开始,pose覆盖其中的部分。"mirror": true与copy配合使用时,会交换左右两侧,这是通过半步长制作步行/跑步循环的方法。ease控制 离开 此关键帧的段曲线:smooth(默认,自然缓动,无过冲),linear,in(开始慢,结束快:释放打击的蓄力),out(开始快,结束后稳定:冲击),inOut,hold,overshoot,anticipate,bounce,elastic。lift:此关键帧时身体最低点离地高度(studs)。默认0。这是制作跳跃的方式;请参阅接地。
关节和通道
角度是解剖学意义上的,同一个词表示身体两侧的相同含义,因此镜像姿势不需要改变符号。每个通道都有一个相反方向的别名;使用阅读更自然的那个。
| 关节 | 通道(相反方向的别名) |
|---|---|
根:LowerTorso(R15),Torso(R6) | bendFwd(bendBack),turnL(turnR),leanR(leanL),moveFwd(moveBack),moveR(moveL),moveUp(moveDown),单位studs |
脊柱:UpperTorso,Head | bendFwd(bendBack),turnL(turnR),leanR(leanL) |
上肢:Left/RightUpperArm,Left/RightUpperLeg;R6 Left Arm,Right Arm,Left Leg,Right Leg | fwd(back)身体前后摆动肢体,out(in)远离/靠近身体,twistOut(twistIn)沿其自身长度旋转 |
Left/RightLowerArm,Left/RightLowerLeg | bend:肘部和膝盖的自然弯曲方向(0 = 伸直) |
Left/RightHand,Left/RightFoot | fwd(back),out(in),twistOut(twistIn)。脚部也接受字符串 "flat" |
地标:手臂 fwd: 90 笔直向前,fwd: 180 笔直向上,out: 90 是T形姿势。腿 fwd: 90 是齐腰高的前踢。旋转是父子关系组合的,因此胸部的旋转会带动手臂;将 Head 向相反方向旋转以保持眼睛注视目标。
R6有六个部位,没有肘部、膝盖、手或脚,因此轮廓来自躯干和整个肢体角度;稍微夸张一些。R6肢体名称包含空格("Right Arm")。
接地
默认情况下,引擎每帧降低或提升根关节,使身体最低点恰好位于地面上方 lift studs处。后果:
- 蹲下、弓步和劈叉会自动降低臀部;对于站立姿势,永远不要手动调整臀部高度。
- R15脚部的
"flat"会解析脚踝,使脚底平贴地面,无论腿部在做什么。对于每只着地的脚都使用它;对于空中的脚或踮起的脚,给出明确的角度("back": 40= 脚趾尖)。 - 跳跃是空中关键帧的
lift值(起跳小,最高点最大,落地0)。空中的身体旋转作用于根关节:后空翻是根关节的bendBack在关键帧之间从0→360(角度是连续的,因此360是完整转体,而不是“回到0”)。 - 接地开启时,根关节上的
moveUp被忽略。仅在游泳或攀爬等与地板无关的情况下设置"grounding": "off"。 - 游戏内的跳跃和冲刺通常由角色控制器移动,而不是动画。对于在游戏跳跃期间播放的动画,摆出身体姿势并将
lift保持在0。对于自包含的表情、带跳跃的攻击和空翻,使用lift和moveFwd。如果片段结束时位置与开始不同,报告会发出警告,因为Roblox会在轨道停止时将身体弹回原位。
标记和道具
markers成为KeyframeMarkers,在游戏中通过track:GetMarkerReachedSignal("Hit")读取。在每个游戏逻辑关心的帧上放置一个:命中窗口、球接触或释放、脚步声、声音提示。props是 仅预览(不被导出),用于根据交互对象判断动作。type: "ball"带radius,或其他任何类型的盒子,带size: [x, y, z](剑是细长盒子)。at可以是部件名称(道具跟随该部件;offset: [right, up, fwd]在部件自身轴中)或固定点[right, up, fwd](从角色地面点起的studs)。使用多个keys时,道具在锚点之间移动;arc为离开该关键帧的段添加抛物线上升(射门或传球)。请参阅examples/jump_shot.json和examples/soccer_kick.json。
什么算做好的动画
- 可读的轮廓。 在故事板中,每个关键帧应仅从其轮廓就可识别。如果两个相邻的关键帧看起来相似,则其中一个没有作用。
- 时间上的对比。 慢节奏的预期,非常快的动作(2到4帧),短暂保持在跟随阶段,较慢的恢复。关键帧之间均匀间隔是最常见的导致动作感觉无重量的原因。
- 整个身体都参与其中。 力量从脚和臀部开始,然后是胸部,肩膀,最后到手或脚。在蓄力时给臀部和胸部一个反向旋转,并让远处的手臂平衡近处的手臂。
- 双脚保持不动。 着地的脚在报告中保持其R和F数值在关键帧之间不变。如果必须移动,请在其间的关键帧中抬起(一步)。
- 零警告,或者为保留的每个警告给出理由。限制是自然的关节活动范围;超出限制通常意味着意图使用不同的通道。
将其导入Roblox Studio
向用户提供两种方法以及事件名称。
- 命令栏(最快): 在资源管理器中选中角色模型,打开“视图”→“命令栏”,粘贴
<Name>.lua的全部内容,按回车。KeyframeSequence会出现在角色模型的AnimSaves中(如果未选中任何对象,则在ServerStorage中),动画编辑器可以加载并对其进行微调。 - 文件: 将
<Name>.rbxmx拖入Studio视口,或右键单击服务→从文件插入。 - 发布: 要在实时游戏中播放,必须上传:在动画编辑器中打开并发布,或右键单击KeyframeSequence→保存到Roblox,然后在
Animation对象中使用生成的资产ID,并使用Animator:LoadAnimation。对于在Studio中的快速本地测试,game:GetService("KeyframeSequenceProvider"):RegisterKeyframeSequence(seq)返回一个临时ID。Studio的菜单会不时更改;如果某一步与用户看到的不匹配,请说明并查找当前措辞,而不是坚持原有说法。
导出是烘焙的:每帧一个关键帧,带有线性缓动,因此Studio在任何该类型的角色模型上都能精确播放预览显示的内容。仅导出规范中动画化的关节,因此仅上半身的动画会保持下半身自由,以便进行默认行走。R15输出带有 AuthoredHipHeight,因此可以重新定位到其他比例的角色。
文件
scripts/ranim.cjs— 引擎、预览器和导出器集成在一个文件中。也可以作为模块使用:const R = require('.../ranim.cjs'); const c = R.compile(spec); R.report(c); R.sample(c, t)。scripts/selftest.cjs— 检查关节数学与Roblox的Motor6D公式。编辑引擎后运行它。examples/—soccer_kick,jump_shot(带球道具的体育),jab_cross(R15战斗),r6_sword_slash(R6带武器战斗),backflip(全身旋转和跳跃),r6_run(镜像循环)。references/motion-cookbook.md— 按类型分的时间表和姿势食谱。编写前阅读相关章节。