本文仅简单演示下我个人在网易我的世界基岩版组件开发过程中制作 BOSS 用到的方法。
壹;要求技能
- Molang
- AnimationController
- 实体行为文件结构
- 实体资源文件结构
- Python(可选)
BP:行为包
RP:资源包
贰;哪些方式可以实现?
CustomGoal + AnimationController(RP) + Molang(动作会有延迟)
原理:通过对生物行为文件添加 CustomGoal 来控制技能的释放与暂停,并同时更新 Molang 来使 AnimationController 同步播放动画,但是这种方式动作播放会有延迟。
[推荐] AnimationController(BP) + AnimationController(RP) + Property
原理:通过行为文件修改 property,并开启 client_sync
来同步 property 给客户端,RP 的动画控制器通过 property 来同步技能动作,该方式延迟较低,如果对行为文件熟练的话,可以通过纯行为文件来实现技能效果。
叁;实现过程
这里采用推荐的方案二,BP 的动画控制器用来控制技能的动作切换(控制技能时长来同步客户端动作)
过程思考
在实现之前,我们需要简单梳理下需要用到东西:网易SDK事件(EntityDefinitionsEventServerEvent)、行为文件事件(event)、行为文件属性(property)。
在 BP(行为包) 中也可以实现 animation_controllers 和 animations,与 RP(资源包) 的区别是在 BP 中可以让生物执行命令。
如果你对基岩版的实体行为文件熟悉,就知道有 properties
可以设置生物属性,例如:不同颜色的羊。同理,我们也可以通过这个属性来设置当前生物释放的技能状态,再通过 aniamtion_controller
来调用行为文件事件取消技能释放的状态,当然,你也可以通过纯 Python 代码来控制技能状态,但这里按下不表。
实现流程
- 生物行为文件中定义一个随机释放技能的事件
example:random_skill
- 生物行为文件中创建一个 enum 类型的 property,值为技能编号与无状态(不懂可以看下方实现逻辑)
- 生物行为文件中定义一个无状态的事件
netease:example_skill_none
,用来修改生物技能释放状态为无技能 - 生物行为文件中写好对应技能的 event 来修改 property
- 通过 timer 定时调用
example:random_skill
事件 example:random_skill
中通过randomize
来实现随机技能再调用具体的技能事件- 通过事件修改技能状态的 property 为对应技能的值
- Python 代码中监听
EntityDefinitionsEventServerEvent
来同时实现技能逻辑(例如:爆炸、范围伤害、技能音效等) - 在
aniamtion_controller
中对应的动画控制器下调用netease:example_skill_none
来取消技能状态
实现逻辑
behavior_packs/entities/example_test_mob.json
{
"format_version": "1.16.0",
"minecraft:entity": {
"component_groups": {
},
"components": {
...,
// 创建一个定时器来定时触发随机技能的事件,这里间隔是 8-9 秒
"minecraft:timer": {
"looping": true,
"randomInterval": true,
"time": [
8,
9
],
"time_down_event": {
"event": "netease:random_example_skill",
"target": "self"
}
}
},
"description": {
"identifier": "example:test_mob",
"is_experimental": false,
"is_spawnable": true,
"is_summonable": true,
// 创建一个 property 来调整生物当前的技能状态,并通过 client_sync 同步给客户端。
"properties": {
"example:skill_state": {
"type": "enum",
"values": [
"none",
"skill1"
],
"default": "none",
"client_sync": true
}
},
// 定义动画控制器与动画
"animations": {
"base": "controller.animation.example_test_mob.beh_base",
"abyss:stone_skill1": "animation.example_test_mob.beh_skill1"
},
// 设置初始动画与动画控制器
"scripts": {
"animate": [
"base"
]
}
},
"events": {
// 随机技能事件的事件
"netease:random_example_skill": {
"randomize": [
{
"weight": 1,
"filters": {
"test": "has_target"
},
"trigger": {
"event": "netease:example_skill_1_start",
"target": "self"
}
}
]
},
// 修改技能 property 为无技能
"netease:example_skill_none": {
"set_property": {
"example:skill_state": "none"
}
},
"netease:example_skill_1_start": {
"set_property": {
"example:skill_state": "skill1"
}
}
}
}
}
behavior_packs/animation_controllers/example.animation_controller.json
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.example_test_mob.beh_base": {
"initial_state": "default",
"states": {
"default": {
"transitions": [
{
"beh_skill1_pre": "query.property('example:skill_state') == 'skill1'"
}
]
},
"beh_skill1_pre": {
"animations": [
"example:skill1"
],
"transitions": [
{
"default": "query.anim_time > 3.5"
}
],
// 动作播放结束(即技能释放结束)后,调用 netease:example_skill_none 来设置状态为无技能
// 作用是避免技能动作重复触发
"on_exit": [
"@s netease:example_skill_none"
]
}
}
}
}
}
behavior_packs/animations/example_test_mob.animation.json
{
"format_version": "1.8.0",
"animations": {
"animation.example_test_mob.beh_skill1": {
"animation_length": 3.5
}
}
}
exampleScripts/modServer/serverSystem.py
# 监听 EntityDefinitionsEventServerEvent 的逻辑自行实现
def on_definitions_event(self, params):
"""
释放技能 2.5 秒后创建一个爆炸
下方的 entityUtil 是我自己封装的类,你可以将这里修改为你自己的逻辑
"""
if not params['eventName'].startswith("netease:example_skill_1_start"):
return
entity_id = params['entityId']
def _internal_skill1():
# 获取生物面前方块
pos = entityUtil.get_sight_pos(entity_id, 1.5)
src_pos = serverApi.GetEngineCompFactory().CreatePos(entity_id).GetPos()
# 维度
dimension_id = serverApi.GetEngineCompFactory().CreateDimension(entity_id)
# 创建爆炸
entityUtil.create_explosion(entity_id, pos, 3)
# 执行延迟逻辑
comp = serverApi.GetEngineCompFactory().CreateGame(serverApi.GetLevelId())
comp.AddTimer(2.5, _internal_skill1)
客户端动作也同步的话,只需要在动画控制器中加上 q.property('example:skill_state') == 'skill1' 即可。
肆;结尾
实现的方式有很多,这里只演示了我觉得最方便的一种方式,如果你技术力可以的话,也可以封装自己的 Entity
类,再通过 CustomGoal
或监听 tick 事件来调用自己的代码逻辑释放技能与冷却控制,使用 TriggerCustomEvent
来触发生物行为事件,该方法自定义性与复用性更高,如果你有其他更好的方法,可以在评论区一起探讨。