《我的世界》跑图神器,再远的风景也能尽收眼底!
上期有勇有谋的小松鼠们
有没有俘获大家的心呢?
今天我们一起来巩固
molang知识的应用
亲手制作一台超炫酷载具
跑图探险,想走就走!
本章内容
在本章中,我们将一起更详细地学习Molang,了解Molang在实体中的应用,并一起在实体中实践Molang。
前情提要:
第一节(认识Molang)
第二节(Molang在自定义实体中的常用场景)
(点击图片回顾该期教程)
第三节(使用配置功能创建基础实体)
第四节(自定义松鼠实体资源)
(点击图片回顾该期教程)
第五节(自定义松鼠实体行为)
(点击图片回顾上期教程)
本期内容:
●
挑战:制作一辆卡丁车
在本期教程中,发发菌会带大家手把手制作一辆卡丁车!这次的挑战主要是针对Molang的复习以及更多复杂应用的学习,所以卡丁车本身模型的精细绘制为次要目标,反之我们会将精力全部放在Molang相关的表达式的编写过程上(๑•̀ㅂ•́)و✧
首先,我们粗略地绘制一个卡丁车的模型,重点突出了卡丁车的四个轮胎骨骼,同时将所有的骨骼全部放在一个根组里。这样能够方便我们的后续操作。
其次通过新建纹理贴图的“填充”功能为其快速上色,之后我们便可以关注卡丁车的动画了。这也是我们本期挑战的重中之重。
现在我们将关注点转移到车轮上。通过对现实的观察,我们可以得知:
当一个卡丁车移动时,轮胎会旋转,而车身则相对不动;当一个卡丁车拐弯时,前轮的旋转角度将会相对于车身更加地大;当卡丁车在空中飞起或下降时,车身会整体后仰或前倾。
根据现实生活中卡丁车的效果,我们可以制作出游戏中卡丁车的动画。
移动动画
对于移动动画来说,我们只需要关注四个车轮的旋转情况,让车轮随着前进的距离进行旋转。
通过查阅文档,我们可以得知query.modified_distance_moved查询函数可以用于返回一个实体从进入世界到当前时间移动的总距离。我们可以使用这个查询来进行车轮向前移动的动画。
首先更改$yOz$面的转角,即X轴向所代表的字段,比如,将其设置为【50 * query.modified_distance_moved】。然后将四个轮胎设置为同样的值,这样可以保证四个轮子转速一致。
拐弯动画
在拐弯时,我们需要改变前轮的转角,继续关注前轮的旋转通道。
通过查阅文档可以得知,当实体在左右移动时会具备一个偏航角(Yaw)速度,这个速度可以通过query.yaw_speed查询函数得到。我们可以利用这一点来做到车轮的左右偏转。
将Y轴向的值改为例如math.clamp(query.yaw_speed * 35, -35, 35),这样可以在使其旋转的同时不要转得过于离谱。而math.clamp可以用于钳制(Clamp)一个值到指定的区间中。
起伏动画
接下来我们制作起伏动画。我们在看卡丁车下落时一般都有这样的直观感受:随着卡丁车在空中滑行时落得越来越快,车头会越来越靠下。
因此,我们对卡丁车的root骨骼,也就是控制着整体的骨骼的旋转通道来进行操作,使其随着竖直速度的变化而旋转。
通过查阅文档我们可以得知,实体的竖直速度可以用query.vertical_speed查询函数来获得。因此,我们可以在整体骨骼的X轴向上使用类似于math.clamp(query.vertical_speed * -7, -35, 35)的表达式来做到控制起伏。
不过值得注意的是,query.vertical_speed的方向与世界坐标的Y轴方向一致。也就是说,当卡丁车下落时,竖直速度的方向是向下的。为了使我们的骨骼向前转,我们需要对其乘一个负数。
这样,我们就完成了卡丁车基本动画的制作。不过不要着急导出动画,因为我们还要在这个基础上为其添加粒子效果!
(1)我们准备好尾气粒子和胎痕粒子,假设他们的短名称分别为gas和mark。
为了把他们挂接到实体上,我们需要为他们设置定位器(Locator)。定位器是模型中一个单点元素,代表模型中一个固定的位置。有了定位器,我们就可以将其他的一些小部件例如粒子挂接到模型的特定位置上。
(2)我们回到“编辑”模式,在右侧的“大纲”窗格上选中我们想加入定位器的骨骼,右键点击【添加定位器】。
(3)像其他元素一样,我们可以通过拖动预览窗中的坐标轴来改变定位器的位置。这里我们将其放在车轮的底部,代表胎痕粒子的起始位置。
(4)很快,我们便为四个车轮和尾气管的位置添加了定位器。现在可以添加粒子动画了。回到“动画”模式,着眼于下方的时间轴窗格。
(5)点击【动画效果】按钮,我们可以在时间轴中看到一个除了骨骼之外的额外动画主体,那便是包含了粒子、声音和命令的效果主体。点击“粒子”通道右侧的【+】按钮,为其添加一个0.0的关键帧。
尾气粒子
(1)点击关键帧,可以在左侧的“关键帧”窗格中看到除了“脚本”之外,多出了“效果”和“定位器”两个属性。
其中,定位器可以通过下拉菜单选择的方式来选取。
(2)现在,我们将尾气粒子的短名称挂接到pipe定位器上,这样只差尾气粒子在实体定义文件中的短名称定义,就添加完成了。
胎痕粒子
(1)我们点击“关键帧”窗格右侧的【+】按钮,在同一个关键帧处添加多个效果。
(2)通过重复上述过程,即可将四个轮胎的胎痕粒子也挂接完毕。
现在,我们可以导出几何、纹理和动画文件了,接下来,我们将这些资源挂接到实体客户端定义文件上。
在《我的世界》开发工作台中创建一个附加包组件,然后通过配置创建一个tutorial_demo:kart实体,并将我们在Blockbench项目中的几何、纹理和动画全部导出到我们的附加包组件的文件夹中。
现在,我们将实体客户端定义文件补全:
向上滑动阅览
{
"format_version": "1.8.0",
"minecraft:client_entity": {
"description": {
"identifier": "tutorial_demo:kart",
"materials": {
"default": "entity_alphatest"
},
"textures": {
"default": "textures/entity/kart/kart"
},
"geometry": {
"default": "geometry.kart"
},
"animations": {
"move": "animation.kart.move"
},
"animation_controllers": [
{ "move": "controller.animation.kart.move" }
],
"render_controllers": [
"controller.render.kart"
],
"particle_effects": {
"mark": "tutorial_demo:mark",
"gas": "tutorial_demo:gas"
}
}
}
}
除了上述操作之外,我们还需要单独制作一个动画控制器用于播放move动画:
向上滑动阅览
{
"format_version" : "1.10.0",
"animation_controllers" : {
"controller.animation.kart.move" : {
"initial_state" : "default",
"states" : {
"default" : {
"animations" : [
"move"
]
}
}
}
}
}
并制作一个基础的渲染控制器用于使实体正常渲染:
向上滑动阅览
{
"format_version": "1.8.0",
"render_controllers": {
"controller.render.kart": {
"geometry": "Geometry.default",
"materials": [ { "*": "Material.default" } ],
"textures": [
"Texture.default"
]
}
}
}
另外,我们也需要检查一下导出的资源文件,避免挂接失败。首先是几何文件:
向上滑动阅览
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.kart",
"texture_width": 128,
"texture_height": 128,
"visible_bounds_width": 3,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "root",
"pivot": [0, 1.5, -3.5],
"cubes": [
{"origin": [-7, 1, -4], "size": [14, 1, 1], "uv": [0, 24]},
{"origin": [-7, 1, 7], "size": [14, 1, 1], "uv": [0, 21]},
{"origin": [-7, 3.5, -7.5], "size": [14, 1, 19], "uv": [0, 0]},
{"origin": [-0.5, 4.5, -3.5], "size": [1, 6, 1], "uv": [0, 27]},
{"origin": [-3.5, 10.5, -5.5], "size": [7, 1, 5], "pivot": [0, 10.5, -3.5], "rotation": [-40, 0, 0], "uv": [26, 22]},
{"origin": [-0.5, 2, -4], "size": [1, 1.5, 1], "uv": [11, 0]},
{"origin": [-1, 2, 7], "size": [1, 1.5, 1], "uv": [6, 0]}
],
"locators": {
"pipe": [-2, 4, 11]
}
},
{
"name": "left_wheel",
"parent": "root",
"pivot": [7.5, 1.5, -3.5],
"cubes": [
{"origin": [7, 0, -5], "size": [1, 3, 3], "uv": [9, 11]}
],
"locators": {
"left_front": [7.5, 0, -3.5]
}
},
{
"name": "right_wheel",
"parent": "root",
"pivot": [-8, 1.5, -3.5],
"cubes": [
{"origin": [-8, 0, -5], "size": [1, 3, 3], "uv": [0, 8]}
],
"locators": {
"right_front": [-7.5, 0, -3.5]
}
},
{
"name": "left_back_wheel",
"parent": "root",
"pivot": [7.5, 1.5, 7.5],
"cubes": [
{"origin": [7, 0, 6], "size": [1, 3, 3], "uv": [6, 4]}
],
"locators": {
"left_back": [7.5, 0, 7.5]
}
},
{
"name": "right_back_wheel",
"parent": "root",
"pivot": [-7.5, 1.5, 7.5],
"cubes": [
{"origin": [-8, 0, 6], "size": [1, 3, 3], "uv": [0, 0]}
],
"locators": {
"right_back": [-7.5, 0, 7.5]
}
}
]
}
]
}
然后是动画文件:
向上滑动阅览
{
"format_version": "1.8.0",
"animations": {
"animation.kart.move": {
"loop": true,
"bones": {
"left_wheel": {
"rotation": ["50 * query.modified_distance_moved", "math.clamp(query.yaw_speed * 35, -35, 35)", 0]
},
"right_wheel": {
"rotation": ["50 * query.modified_distance_moved", "math.clamp(query.yaw_speed * 35, -35, 35)", 0]
},
"root": {
"rotation": ["math.clamp(query.vertical_speed * -7, -35, 35)", 0, 0]
},
"left_back_wheel": {
"rotation": ["50 * query.modified_distance_moved", 0, 0]
},
"right_back_wheel": {
"rotation": ["50 * query.modified_distance_moved", 0, 0]
}
},
"particle_effects": {
"0.0": [
{
"effect": "gas",
"locator": "pipe"
},
{
"effect": "mark",
"locator": "left_front"
},
{
"effect": "mark",
"locator": "right_front"
},
{
"effect": "mark",
"locator": "left_back"
},
{
"effect": "mark",
"locator": "right_back"
}
]
}
}
}
}
最后是两个粒子:
向上滑动阅览
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "tutorial_demo:mark",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particle/particles"
}
},
"curves": {
"variable.psize": {
"type": "catmull_rom",
"input": "variable.particle_age",
"horizontal_range": "variable.particle_lifetime",
"nodes": [1, 1, 0.83, 0, 0]
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "variable.radius = 2.5;"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_looping": {
"active_time": 2
},
"minecraft:emitter_shape_point": {},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1.5
},
"minecraft:particle_appearance_billboard": {
"size": ["0.12 * variable.psize", "0.12 * variable.psize"],
"facing_camera_mode": "emitter_transform_xz",
"uv": {
"texture_width": 128,
"texture_height": 128,
"uv": [56, 0],
"uv_size": [8, 8]
}
},
"minecraft:particle_appearance_tinting": {
"color": [0.01176, 0.01176, 0.01176, 0.9098]
}
}
}
}
向上滑动阅览
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "tutorial_demo:gas",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particle/particles"
}
},
"curves": {
"variable.psize": {
"type": "catmull_rom",
"input": "variable.particle_age",
"horizontal_range": "variable.particle_lifetime",
"nodes": [1, 1, 0.83, 0, 0]
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "variable.radius = 2.5;"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_looping": {
"active_time": 2
},
"minecraft:emitter_shape_point": {},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1.5
},
"minecraft:particle_appearance_billboard": {
"size": ["0.12 * variable.psize", "0.12 * variable.psize"],
"facing_camera_mode": "lookat_xyz",
"uv": {
"texture_width": 128,
"texture_height": 128,
"uv": [56, 0],
"uv_size": [8, 8]
}
},
"minecraft:particle_appearance_tinting": {
"color": [0.01176, 0.01176, 0.01176, 0.9098]
}
}
}
}
这样就可以保证我们的卡丁车的渲染不会出现任何问题了。接下来,我们为卡丁车添加实体行为组件。
接下来我们为卡丁车添加行为。比较重要的行为便是可以使其受到物理引擎作用的minecraft:physics,还有使其可被骑乘的minecraft:rideable:
向上滑动阅览
{
"format_version": "1.12.0",
"minecraft:entity": {
"description": {
"identifier": "tutorial_demo:kart",
"is_experimental": false,
"is_spawnable": false,
"is_summonable": true
},
"component_groups": {
},
"components": {
"minecraft:persistent": {},
"minecraft:physics": {},
"minecraft:rideable": {
"seat_count": 1,
"crouching_skip_interact": true,
"interact_text": "action.interact.ride.kart",
"family_types": [
"player"
],
"seats": [
{
"position": [
0,
0.2,
-0.3
],
"min_rider_count": 1,
"max_rider_count": 1
}
]
},
"minecraft:pushable": {
"is_pushable": false,
"is_pushable_by_piston": true
},
"minecraft:movement.basic": {},
"minecraft:jump.static": {},
"minecraft:input_ground_controlled": {},
"minecraft:collision_box": {
"width": 1,
"height": 1
},
"minecraft:type_family": {
"family": [
"kart",
"inanimate"
]
},
"minecraft:health": {
"value": 50
},
"minecraft:knockback_resistance": {
"value": 1,
"max": 1
},
"minecraft:movement": {
"value": 0.5
}
},
"events": {
}
}
}
我们可以看到,卡丁车各个部分的行为和动画皆按照我们预想的那样进行,这说明我们成功添加了一个卡丁车实体!
学会制作超轻便卡丁车
遗迹太偏?神殿太远?
都不是问题!
快跟着今天的教程实践起来
骑上小车去看看远方的风景吧!
下一期教程
我们的小松鼠又返场啦!
想看看它们有什么新变化?
下期不见不散哦~
前篇回顾
更多开发教程
从零开始做插件
全新皮肤诞生记
万物皆需材质包
针对“MC开发大师成长指南”
我们还设置了课程问卷
欢迎大家对课程提出宝贵的建议~
点击文末“阅读原文”来填写问卷吧!
将“我的世界Minecraft开发者”设为星标
↓第一时间掌握开发圈新鲜事↓
关注“我的世界Minecraft开发者”,世界在你手中
戳戳在看/点赞
走遍方块世界~