Template:Bedrock Edition Developer Documentation
你可以帮助我们来翻译此条目,但请勿使用机器翻译。
这是基岩版1.12.0的动画文档。
版本:1.12.0.28
概述
当前Minecraft的JSON规范如下:
- 字段应用小写和下划线来表示(无空格)。
- 在定义的目录和子路径下所有的json文件都将会被动画系统所读取使用。
开始
v1.7Beta升级至v1.8
我们根据反馈以及沿着路线来推进技术对代码进行了很小的修改和整理。如果需要升级以前的脚本,您需要对你的所有molang脚本执行以下步骤:
- entity.flags.foo --> query.foo
- entity.member.foo --> query.foo
- entity.foo --> variable.foo
- params.foo --> global.foo
- 一般的规则是
query表示脚本所运行实体的只读值而variable表示用户创建的读写值。 - 我们为所有名称采用了蛇形命名法。如果你愿意的话也可以使用大写字母,因为我们不区分大小写,虽然我们一般建议使用蛇形命名法。
- 之前在生物上设置的一些变量被更改为使用
query.foo的格式。浏览下方的更新列表来查看新内容和更改。
v1.8Beta升级至v1.10
1.10主要的三大改动如下:
- 动画具有在任意深层次结构中可以引用别的动画的能力。
- 动画控制器的参数部分替换成了
variables部分。 - 在实体定义文件中,动画控制器现在列在
animation部分中,并且添加了scripts animate部分以定义要播放的根动画。
v1.8文件格式向后兼容v1.10,因此你不需要_need_来改变任何东西(尽管我们建议按照v1.10的指导重构你的文件,因为新格式会有轻微的性能提升,并使它更容易理解。
添加动画
实体定义
为了让实体具有动画, 你必须将animations和scripts/animate部分添加到实体的实体定义文件中。
你能在这里看见pig.json的的实体定义:
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
"identifier": "minecraft:pig",
"min_engine_version": "1.8.0",
"materials": { "default": "pig" },
"textures": {
"default": "textures/entity/pig/pig",
"saddled": "textures/entity/pig/pig_saddle"
},
"geometry": {
"default": "geometry.pig.v1.8"
},
"animations": {
"setup": "animation.pig.setup",
"walk": "animation.quadruped.walk",
"look_at_target": "animation.common.look_at_target",
"baby_transform": "animation.pig.baby_transform"
},
"scripts": {
"animate": [
"setup",
{ "walk": "query.modified_move_speed" },
"look_at_target",
{ "baby_transform": "query.is_baby" }
]
},
"render_controllers": [ "controller.render.pig" ],
"spawn_egg": {
"texture": "spawn_egg",
"texture_index": 2
}
}
}
}
注: 因为猪的行走动画与牛、羊的行走动画相同,所以应使用animation.quadruped.walk而不是定义自己的。如果你想自定义一个新的行走动画,你可以修改这一行以指向你自定义的行走动画。
动画被规定为短名称,后可跟其完整的资源名称。该短名称能用于动画控制器和scripts/animate列表,其后的完整名称用于动画文件。
在scripts/animate部分,你需要列出播放的动画以及播放顺序。你能直接指定动画,也能指定混合表达式(blend expression)。
动画控制器
我们需要能够控制动画的播放方式、播放时间以及与其他动画的交互方式。 在实体定义“scripts”中管理大量动画的同时,对动画进行分组动画。控制器示例:
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.my_mob.move": {
"initial_state": "moving",
"states": {
"moving": {
"animations": [
"wag_tail",
"wiggle_ears",
{ "walk": "query.modified_move_speed" }
],
"transitions": [
{ "grazing": "query.is_grazing" }
]
},
"grazing": {
"animations": [ "grazing" ],
"transitions": [
{ "moving": "query.all_animations_finished" }
]
}
}
}
}
}
动画
在每帧开始时,骨架将从其几何定义重置为其默认姿势,然后按顺序为每个通道添加动画。注意通道(x,y,z),动画数据可以是原始数据。
默认情况下,旋转以度为单位,采用先x后y最后z的格式。
"rotation": [90.0, 0.0, 0.0]
也可以是运行时才计算的脚本:
"rotation": ["cos(query.anim_pos * 38.17) * 80.0 * query.anim_speed", 0.0, 0.0]
下面是原版资源包中的animation文件夹中的quadruped.animation.json示例:
{
"format_version": "1.8.0",
"animations": {
"animation.quadruped.walk": {
"anim_time_update": "query.modified_distance_moved",
"loop": true,
"bones": {
"leg0": { "rotation": [ "Math.cos(query.anim_time * 38.17) * 80.0", 0.0, 0.0 ] },
"leg1": { "rotation": [ "Math.cos(query.anim_time * 38.17) * -80.0", 0.0, 0.0 ] },
"leg2": { "rotation": [ "Math.cos(query.anim_time * 38.17) * -80.0", 0.0, 0.0 ] },
"leg3": { "rotation": [ "Math.cos(query.anim_time * 38.17) * 80.0", 0.0, 0.0 ] }
}
}
}
}
动画层次
动画是基于通道的(旋转、位置或缩放),在此范围内,它们是关键帧:
EntityAnimation: animation name __BoneAnimation[]: bone name to animation for this animation ____AnimationChannel[]: rotation, scale, or translation to animate ______KeyFrame[]: the value for the channel to be at, at a specific time
以上所有概念都在下面详细的自下而上的方法中描述。
名称
所有名称(动画、骨骼、状态),都必须以字母开头,并且只包含字母数字、下划线或句点。建议使用全部小写的名称
变换
- 操作顺序:对顶点进行缩放、旋转和平移.
- 假设动画数据是分层的,并按名称应用于骨骼,将动画数据中的骨骼名称与目标几何体的骨架匹配。.
- 不是所有的骨头都需要动画.
- 可以设置目标几何体中不存在的骨骼的动画(忽略丢失的骨骼).
- 对于每个比例、旋转、位置,都可以使用单个值单独或均匀地设置字段。例如,它们是等效的:
"scale": [2.0, 2.0, 2.0] "scale": 2.0 "scale": [2.0]
通道(旋转、位移、缩放)方法
引擎分别跟踪旋转、位置和缩放的动画。在一个通道中,一个或多个关键帧是从动画开始的任意时间(以秒为单位)指定的。
实体动画格式示例
动画的JSON格式如下:
注:与几何格式匹配,单位为1/16米。
"<animation_name>": {
// optional
"loop": <bool> // default = false. Should the animation loop back to t=0.0 when it finishes?
"blend_weight": <expression> // default = "1.0". How much this animation is blended with the others. 0.0 = off. 1.0 = fully apply all transforms. Can be an expression - see the Animation Controller section below
"animation_length": <float> // default = time of last key frame. At what time does the system consider this animation finished?
"override_previous_animation": <bool> // default = false. Should the animation pose of the bone be set to the bind pose before applying this animation, thereby overriding any previous animations to this point?
// required
"bones": [
{
"<bone_name>": { // must match the name of the bone specified in the geometry skeleton
// various flavours of setting data
// omitting a channel skips that channel for this animation of this bone
// any number of floats below can be replaced by a string expression as described above; you don't have to replace all the floats on a line with expressions, only the ones you want to be expression-based
"position": 1.0, // set x, y, and z to 1
"position": [1.0], // set x, y, and z to 1
"position": [1.0, 2.0, 3.0], // set x=1 , y=2 , and z=3
"rotation": 45.0, // set x, y, and z to 45 degrees
"rotation": [45.0], // set x, y, and z to 45 degrees
"rotation": [30.0, 0.0, 45.0], // set x, y, and z to the respective values (in degrees)
// note: only uniform scaling is supported at this time
"scale": 2.0, // scales the bone by 2.0
"scale": [2.0], // scales the bone by 2.0
// Key frame data is described below
// Note that any of the above styles of values will work for "pre" and "post", and "pre" does not have to have the same format as "post"
"rotation": {
"0.0": [80.0, 0.0, 0.0],
"0.1667": [-80.0, 0.0, 0.0],
"0.333": [80.0, 0.0, 0.0]
}
// For discontinuous channel curve, you can specify a different value when interpolating to/from this key frame
"rotation": {
"0.3": { // the key field is the time stamp for this key frame: the value can be any of the above examples
"pre": [30.0, 0.0, 45.0], // when interpolating towards this key frame from the previous, use this value
"post": "180.0 * Math.Sin(global.key_frame_lerp_time)" // when at interpolating away from this key frame to the next, use this value
}
}
// another example
"rotation": {
"0.0": [80.0, 0.0, 0.0], // start at an x rotation of 80 degrees
"0.4": {
"pre": [80.0, 0.0, 0.0], // stay at 80 until 0.4 seconds have elapsed
"post": [0.0, 0.0, 0.0], // discontinuously pop the x rotation to 0.0 degrees
},
"0.8": [-80.0, 0.0, 0.0] // using the previous frame's lerp mode, lerp to a x rotation of -80 degrees by 0.8 seconds
}
}
]
}
关键帧
关键帧为特定通道在指定时间转换为特定骨骼定义两个值,一个值是随着时间接近关键帧时间,另一个值是从该关键帧时间开始的。
插值
目前只支持线性插值。关键帧“pre”和“post”设置允许控制任何关键帧的插值曲线。
- 连续示例:
此示例在1秒内围绕Y轴1旋转骨骼“头部”。
"head": {
"rotation": {
"0.0":[0, 0, 0],
"0.5": [ 0, 180, 0],
"1.0": [0, 360, 0]
}
}
- 单个示例:
不连续仅仅意味着关键帧之间不会有平滑的过渡。如果你想马上发生什么事,这很有用。
此示例缩放骨骼“头”:
1.从0到0.5秒(在“pre”标签中),头骨在所有尺寸上都设置为1的正常比例[x,y,z]
2.在0.5秒时,骨骼将立即扩大到正常大小的2倍,然后
3.从0.5秒到1秒(“post”),骨骼将重新缩放到其所有尺寸的正常比例1。
Note In the larger example above of the file format, "pre" and "post" can also be defined by a MoLang expression that calculates that value at runtime. Allowing you to have a mathematically defined curve instead of being purely linear.
"head": {
"scale": {
"0.5": {
"pre": [1, 1, 1],
"post": 2.0
}
"1.0": [ 1.0 ]
}
}
动画控制器
Animation controllers decide which animations to play when. Each controller contains a list of states that play one or more animations, each of which can be blended on one or more parameters. Controller files are stored as JSON in the animation_controllers folder
Animation Controller Format:
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.sheep.move": {
"states": {
"default": {
"animations": [
{ "walk": "query.modified_move_speed" }
],
"transitions": [
{ "grazing": "query.is_grazing" }
]
},
"grazing": {
"animations": [ "grazing" ],
"transitions": [
{ "default": "query.all_animations_finished" }
]
}
}
}
}
}
状态
A state defines a group of animations to process (each of which can have it's own blend value). Each state has an optional variables section, listing any number of variables that referenced animations can use. Each state also has one or more animations, using the name given in the entity's definition json.
- State Variables
Variables are either set by the game or by a user defined script that can be found in the entity definition json found in definitions/entity/<entity_name>.json. Variables have their value set by a Molang Expression. They can also have their value remapped via a linearly-interpolated curve.
For Example:
This defines a controller with a single state.
It will create a variable variable.ground_speed_curve that lives on the entity only while processing the animation controller for that frame.
It will take the value of query.ground_speed, then remap it to between 0.2 and 0.7 based on the value of query.ground_speed going from 0.0 to 1.0.It will play one animation walk that will blend from 0.0 to 1.0 as the ground speed increases from stopped to 2.3 m/s.
The remap curve can have any number of entries.
The animation controller will then play the entity-referenced wiggle_nose animations, followed by the walk animation, scaling the latter by the value of variable.ground_speed_curve
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.sheep.move": {
"states": {
"default": {
"variables": {
"ground_speed_curve": {
"input": "query.ground_speed",
"remap_curve": {
"0.0": 0.2,
"1.0": 0.7
}
}
},
"animations": [
"wiggle_nose",
{ "walk": "variable.ground_speed_curve" }
]
}
}
}
}
}
- User-Defined Script Example
This script will set foo to the result of the sine of query.life_time to later be used in the animation or animation controller.
Note: "pre_animation" tells the script to figure out the values of those variables once a frame, before animation occurs, so that the animation can use those values in their own formulas. If a variable didn't exist, it will create a new variable and its default value will be 0.0
In definitions\entity\tiger.json:
{
"custom:tiger":{
"scripts":{
"pre_animation": {
"variable.foo = math.sin(query.life_time)"
}
}
}
}
Note in this example that because foo is equal to a sin wave, that its values will range from -1 to 1. This means that you will have a period from 0 to -1 to 0 where only "base_pose" will play and then an equal amount of time where Walk will play on top of base_pose as foo goes from 0 to 1 back to 0. Base_pose will have a blend value of 1.0.
"controller.animation.tiger.move": {
"states": {
"default": {
"animations": [
//animations are ADDITIVE unless otherwise specified
//in this case, base_pose will always be playing in the default state
//walk will play as well if Entity.foo is greater than 0.0
"base_pose",
{ "walk": "variable.foo > 0.0" }
]
}
}
}
状态过渡
A state can specify any number of transition scripts, listed in order. Each transition has a target state to switch to, and a script for whether it should switch or not. For each transition in order, evaluate the script, and if it returns non-zero, switch to the specified state immediately.
NOTE: Only one transition will be processed per frame.
"<controller_name>": {
"states": {
"<state_name>": {
...
"transitions": [
// Evaluate the below expressions in order.
// The first to return non-zero is the state to transition to.
// If all are zero, then don't transition.
{ "<target_state_name_A>", "<expression>" },
{ "<target_state_name_B>", "<expression>" },
...
]
}
},
...
}
For example:
"controller.animation.tiger.move": {
"states": {
"default": {
"animations": [ "base_pose", "walk" ],
"transitions": [
{ "angry": "query.is_angry" }, // transition to angry state if query.is_angry returns true
{ "tired": "variable.is_tired" } // transition to tired state if variable.is_tired returns true
]
},
"angry": {
"animations": [ "roar", "extend_claws" ],
"transitions": [
{ "default": "query.any_animation_finished" } // transition back to default state when either the roar animation or extend_claws animation finishes
]
},
"tired": {
"animations": [ "yawn", "stretch" ],
"transitions": [
{ "default": "query.all_animation_finished" } // transition back to default state when the yawn and stretch animations have both finished
]
}
}
}
状态混合
If you would like there to be a cross-fade between states when transitioning, simply set "blend_transition" to the time you would like the system to take in blending between the two states. This is done as a simple lerp between the two states over the time specified.
For example:
"controller.animation.tiger.move": {
"states": {
"default": {
"animations": [ "base_pose", "walk" ],
"transitions": [
{ "angry": "query.is_angry" } // transition to angry state if query.is_angry returns true
],
"blend_transition": 0.2 // when transitioning away from this state, cross-fade over 0.2 seconds
},
"angry": {
"animations": [ "roar", "extend_claws" ],
"transitions": [
{ "default": "query.any_animation_finished" } // transition back to default state when either the roar animation or extend_claws animation finishes
]
}
}
}
渲染控制器
The Render Controller needs an identifier and needs to follow the format of "controller.render.<name>". This name needs to match the name set in the Client Entity Definitions JSON.
Render Controllers are a way for the player to determine what renders on the entity. Players can set the geometry, materials, textures, and part visibility of the entity. In addition to setting the keys directly, players can use arrays to have the entity choose between different options.
开始
To begin create a new folder named "render_controllers" in the root of the Resource Pack you want to add the new Render Controller JSON inside.
Example render controllers JSON for the ocelot:
"format_version": "1.8.0",
"render_controllers": {
"controller.render.ocelot": {
"arrays": {
"textures": {
"Array.skins": ["Texture.wild", "Texture.black", "Texture.red", "Texture.siamese"]
}
},
"geometry": "Geometry.default",
"materials": [{ "*": "Material.default" }],
"textures": ["Array.skins[query.variant]"]
}
}
示例
Example Array for geometry from the sheep JSON
"arrays": {
"geometries": {
"Array.geos": ["Geometry.default", "Geometry.sheared"]
}
},
"geometry": "Array.geos[query.is_sheared]",
Example Array for materials from the spider JSON
"arrays": {
"materials": {
"Array.materials": ["Material.default", "Material.invisible"]
}
},
"materials": [{ "*": "Array.materials[query.is_invisible]" }],
Example Array for textures from the villager JSON
"arrays": {
"textures": {
"Array.skins": ["Texture.farmer", "Texture.librarian", "Texture.priest", "Texture.smith", "Texture.butcher"]
}
},
"textures": ["Array.skins[query.variant]"]
Example with color for tinting of parts from Armor 1.0 render controller JSON:
"format_version": "1.8.0",
"render_controllers": {
"controller.render.armor.chest.v1.0": {
"arrays": {
"materials": {
"array.armor_material": [
"material.armor",
"material.armor_enchanted",
"material.armor_leather",
"material.armor_leather_enchanted"
]
},
"textures": {
"array.armor_texture": [
"texture.leather",
"texture.chain",
"texture.iron",
"texture.diamond",
"texture.gold"
]
}
},
"geometry": "geometry.armor",
"materials" : [
{ "body": "array.armor_material[query.armor_material_slot(1)]" },
{ "leftarm": "array.armor_material[query.armor_material_slot(1)]" },
{ "rightarm": "array.armor_material[query.armor_material_slot(1)]" }
],
"part_visibility" : [
{ "*": 0 },
{ "body": "query.has_armor_slot(1)" },
{ "leftarm": "query.has_armor_slot(1)" },
{ "rightarm": "query.has_armor_slot(1)" }
],
"color": {
"r": "query.armor_color_slot(1, 0)",
"g": "query.armor_color_slot(1, 1)",
"b": "query.armor_color_slot(1, 2)",
"a": "query.armor_color_slot(1, 3)"
},
"textures": ["array.armor_texture[query.armor_texture_slot(1)]", "texture.enchanted"]
}
}
Example with overlay_color from Wither Boss render controller JSON:
"format_version": "1.8.0",
"render_controllers": {
"controller.render.wither_boss": {
"arrays": {
"textures": {
"Array.wither_state": ["Texture.invulnerable", "Texture.default"]
}
},
"geometry" : "Geometry.default",
"materials" : [{ "*": "Material.default" }],
"textures" : ["Array.wither_state[variable.display_normal_skin]"],
"overlay_color" : {
"r": "variable.is_invulnerable ? 1.0 : this",
"g": "variable.is_invulnerable ? 1.0 : this",
"b": "variable.is_invulnerable ? 1.0 : this",
"a": "variable.is_invulnerable ? query.overlay_alpha : this"
}
}
}
Example with part_visibility for turning on and off visibility of parts from Llama JSON:
"format_version": "1.8.0",
"render_controllers": {
"controller.render.llama": {
"arrays": {
"textures": {
"Array.base": ["Texture.creamy", "Texture.white", "Texture.brown", "Texture.gray"],
"Array.decor": ["Texture.decor_none", "Texture.decor_white", "Texture.decor_orange", "Texture.decor_magenta", "Texture.decor_light_blue", "Texture.decor_yellow", "Texture.decor_lime", "Texture.decor_pink", "Texture.decor_gray", "Texture.decor_silver", "Texture.decor_cyan", "Texture.decor_purple", "Texture.decor_blue", "Texture.decor_brown", "Texture.decor_green", "Texture.decor_red", "Texture.decor_black"]
}
},
"geometry": "Geometry.default",
"part_visibility": [{ "chest*": "query.is_chested" }],
"materials": [{ "*": "Material.default" }],
"textures": [
"Array.base[query.variant]",
"Array.decor[variable.decor_texture_index]",
"Texture.decor_none"
]
}
}
NOTE: The arrays for Materials and Part visibility are applied in the order specified. Materials and Part visibility specified later in the file will override previous materials or parts.
Material array example from Horse render controllers. Saddle will override Mane, which will override TailA, etc.:
"materials": [
{ "*": "Material.default" },
{ "TailA": "Material.horse_hair" },
{ "Mane": "Material.horse_hair" },
{ "*Saddle*": "Material.horse_saddle" }
],