原版开发常识汇总(其一)
- 这是一本指南式的工具书,记录了作者对原版开发的整体概括。
- 目标是让读者快速查缺补漏,了解整体情况,以及在困扰怎么做某些东西时能按图索骥。
- 为此本文尽量简短,降低阅读成本。
- 所以本文只提供三行以内的实现,更复杂的内容在此仅作为关键词集合和索引,读者需自行检索 Wiki 或香草图书馆等网站获取详细教程。
- 如果有疏漏,或者错误,可以在下面指出,或者与作者(下称“我”)合作编辑。
- 本文可能含有自造词汇等私货,或临时捏造一些词汇,请自行甄别。
原贴地址:https://etis.vcsofficial.site/d/131
使用哪些工具?
打◇开头的为强烈建议使用的工具
|| 数据包编辑
◇ VSCode
工具简介
扩展齐全的代码编辑器。也可以打开图片之类的,只要别太大。
可以使用 Git 和 Github 进行源代码管理(备份、团队协作)。
可以使用内置的liveshare功能进行合作编辑,类似石墨文档、腾讯文档等云文档的编辑体验。
如果是在服务器上开发,也可以用 ssh-remote 插件远程连接到服务器进行编辑。
- ◇
Datapack Helper Plus by Spyglass插件(中文名:大憨批)- 提供函数和各种文件的自动补全
- (虽然名字里是 Datapack 但资源包也有补全)
- 没错,中文名真的叫大憨批。或许是MCBBS命令/数据包版块喜闻乐见的凑首字母环节。
Insert Numbers Pro插件- 在穷举的时候比较有用。可以批量插入命令
- 鼠标中键拖动选中多行,然后
Ctrl+Alt+N即可弹出补全格式窗口。
Markdown All In One插件- 方便写文档
- 没什么,只是想让你知道...
MCFPP
狐狐(Alumopper)开发中的高级数据包语言。
- 是原创语言,有接近高级编程语言的语法
- 也可以直接使用数据包的语法,仅作为方便穷举的工具
- 最大的问题没有 VSCode 插件,所以也没有自动补全
Beet/Bolt
伊桑(Ethanout)力荐的开发脚本,可能比MCFPP这样的重型工具更简单轻便。
似乎已经是国外主流的数据包脚本语言了。
- 可以用Python和mcfunction的混合语法控制命令和函数
其他
参阅下文有关章节
|| 美术资源制作
◇ Blockbench
工具简介
Mojang死丢丢都在使用的MC体素建模软件。
用于建模、绘制材质、制作动画。也可以画皮肤。除了原版开发,你也可以用它来制作 Yes Steve Model 模型。
- ◇
Animated Java插件- 能输出可在原版Java版MC运行的骨骼动画包。
其他像是笔刷之类的插件也可以下一点,内置有插件市场。
使用技巧
- 先建模完成,再全选所有元素,按创建纹理,就可以自动生成一张绑定好了 UV 的纹理~(旧译:材质)~了。
- 元素尺寸尽量不要是精细度的非整数倍,除非你那一块不打算上纹理,保持纯色。
文件-编辑会话选项理论上可以让你和其他人联机一起做模型,但是实测国内似乎用不了。文件-转换项目可以把通用模型.bbmodel转换为Animated Java模型。- 似乎也有插件可以把
.obj文件转化为.bbmodel文件。- 因此也有人在
Blender搅拌机里把动画之类的搅好再传回Blockbench。
- 因此也有人在
Aseprite、 PS 等其他贴图绘制软件
可选。 Blockbench 自带的绘画模式功能可能会有点少。或许可以用插件补全一部分。
总之备一个专门画像素画的软件用来画点像素画、制作 UI 之类的也不错。
Objmc
一个使用着色器驱动,在 MC 里**直接渲染 obj 模型及动画**的项目
由于着色器的向上兼容性很差,并且还会让玩家没法用 opti 或 modapi 客户端安装光影包,而这个项目的更新并不够迅速,因此大部分情况下还是更推荐使用Animated Java
(其实着色器就是光影,光影就是着色器)
◇ Axiom Fabric Mod
- 做建筑地形的神,上手约等于无门槛,随便试试就知道怎么用了。
- 可以编辑、组合展示实体
- B 站有教程
- 更新很快,最新版本发布后大约 3~7 天就会更新新版本。
- 如果你一定要追着快照版开发,也可以先升级,后迁移地形地物,问题不大。
至于原版洁癖的问题,我的评价是:发布和游玩层面可以坚持纯净,开发方面也坚持纯原版的话,要做到接近的开发效率就很麻烦了,还得自己开发工具。而且如果不用自己当小六边形了,总不能让来帮忙的美工也学着用自制的都不一定有UI的简易工具吧。
World Machine
地形生成工具
- 一般似乎是用于生成灰度图,然后导入到 Axiom 等其他工具,再“印刷”到地图上。
数据存在哪?
|| 分数(整数变量)
记分板 /scoreboard
- 记分板分数项相当于一个 int 变量。
“分数”和“记分项”都不太能指代“一个分数持有者在某个记分项的分数”,所以这里临时用“记分板分数项”(或“记分项”)指代。
- 只能存整数,计算结果向下取整。
- 指向的玩家可以不存在,也可以不是玩家。
- 尽管子命令叫
players并且自动补全只能补全玩家。 - 所以 Wiki 中使用的词是
分数持有者(Score Holder)而不是玩家。
- 尽管子命令叫
- 临时数据用 # 开头可以不在侧边栏显示,同时性能上似乎也有一定优化。
- 除了加减及设置固定数值之外的所有运算,都需要使用
scoreboard players operation子命令。operation只能让分数项与分数项之间进行运算。譬如要运算a*2,就必须形如a score *= 2 score- 因此我一般会在
load函数中注册大量常数备用。
- 创建一个临时记分板很有用,可以省去思考一些小步骤的分数存在哪里的问题。
- 作为参考,我会使用
_作为所有临时存放位置的名称,如score return _而小豆娘会使用int。Dahesor使用calculator或calc.namespace - 记分板准则有很多种。
- 一般使用
dummy来存放变量。 trigger允许玩家自行修改,但需要使用scoreboard players enable赋予玩家- 其他一些像是
health等准则会让分数自动变化。包括所有可以在 ESC 菜单的“统计”里找到的内容。
我们一般用
n条scb来衡量并表示一条命令或一个模块的消耗。这是指一次scoreboard player add | remove的能耗,是一个足够小且稳定的基准。
|| NBT(键值对)
1. 纯 NBT
NBT存储 /data modify storage
- 可以存储的数据类型非常多。一般使用 NBT 来存储和管理数据,记分板大部分情况下只用来进行暂存和运算,除了在数据强绑定实体的情况下会用来存数据(如存储分数、实体坐标,且需要随时计算更新)。
- 相对于实体 NBT 而言效率很高。
data modify storage name path set value "233"的消耗与单条简单记分板相差无几。- 但是效率与路径的层数、复杂度挂钩。如操作
storage data a.b.c的消耗会比storage data a高一些。
- 但是效率与路径的层数、复杂度挂钩。如操作
我暂时没有精确的数据,不过暂时理解成每层增加10%~30% 的损耗就行了。大部分情况下不需要担心这个。
2. 非玩家实体
1.21.5+
所有实体的 NBT 路径 entity @s data
AI生物
entity @s (ArmorItems(旧 1.21.4-) | equipments(1.21.5+))
物品相关实体
entity @s (item | Item) (区别是大小写)
3. 玩家
Storage 玩家空间
在 NBT 存储 中建立玩家专属的路径,称之为玩家空间。 如:
VDC>pllib模块使用storage vdc:pllib 0[{uid:1, name:"VRainbow_",UUID:{...} }]这样的形式来存储玩家空间。- 也可以使用宏函数来创建并指引路径,形如
storage proj:player $(name).skill
优点是方便管理和使用。即使玩家下线也能够访问和处理其数据。
缺点是如果人来人往,这个NBT存储的文件会越来越大。
- 如果大到一定程度(对于小型私有服务器或局域网联机地图而言,除非数据溢出否则基本上不可能),如20MB , 会导致游戏周期性地在自动保存时造成一瞬的卡顿,可能持续数百毫秒。
隐形物品
在玩家的物品栏中放置不影响游戏体验的隐形物品,并将数据存放在其中 (用data entity @s <item>.components.minecraft:custom_data.<nbt> if items ... *[custom_data~{}] item modify ... 来判定或操作。这里< >是指代某个路径)
- 如
VDC>pllib模块的前身, 1.15 ~ 1.19 的EDLib前置,就是在末影箱中放置两个物品,互为备份。 - 1.20.5+ | 现在更多的是使用
armor.body或armor.saddle(1.21.5+)槽位- 用
item命令置入并修改其内容 - 打开
保存物品栏(死亡不掉落)以避免其意外丢失。如果只存放临时数据,也可以使用消失诅咒。- 是的,这个槽位也可以用来令一些魔咒生效。因此可以用来附魔一些希望绑定玩家自身而非可穿戴物品的效果。
- 1.21.2+ | 在
equippable数据组件~~(物品组件)~~更新后,修改物品的equippable.slot可以允许其放入这两个原本不能放入的格子,因此也可以使用别的物品。- 在开发中的『悬线Proj.』中,我使用的是命名牌,并将其称为“角色铭牌”。
- 用
- 这个方案也可以与玩家空间并行,用于:
- 存储临时数据:不一定更快,但或许方便。如果使用收纳袋等物品原型,还可以用来折叠一些物品的同时又能用
if items *[bundle_contents~[{}]]判定或使用物品修饰器来操作。 - 存储一些确定是只要玩家下线就不会再用到的数据。
- 反例:玩家的基础信息(用于对照查表)、即使下线了也可以被管理(增删重置、升级版本)的数据。
- 正例:玩家的局内数据、确定不会更新或可以上线后再更新的数据(如折叠的物品栏)。
- 存储临时数据:不一定更快,但或许方便。如果使用收纳袋等物品原型,还可以用来折叠一些物品的同时又能用
怎么在需要的时候触发效果(系统入口和广播事件汇总)
|| 高频循环检测 + 条件判定
周期执行方式
简单顺序:tick函数 >> load函数添加的schedule >> 手动添加的schedule >> 进度触发的函数 >> 魔咒触发的函数
点我查看详情(可能需要科技)
Tick 函数集 (tick.json 函数标签内函数)
这也是个私货捏造词
- Tick 函数集会每tick执行
- 在游戏的主程序循环里执行的非常早,比
time query gametime的gametime更新还要早。如果不处理,就约等于是在同一tick下最后执行的。 - 如果需要进一步区分多个数据包的tick函数的执行优先级,可以在Tick函数集内调用别的函数标签,其他数据包的tick函数再挂在子级函数标签下。
Schedule 循环
/schedule function <This> 1t 在函数中用schedule为自身定时,可以令其循环执行。
- 执行顺序与
schedule function执行的顺序相同。 - 加入条件判定可以自动终止循环
- 也可以用
/schedule clear来主动终止循环
- 也可以用
- 如果你不将
/schedule function函数在<This>执行,而是在Tick函数集里定时,那么这个循环每Tick都会被后置理论上是,没经过完整测试。 - 可以修改时间参数来延长执行周期
- 可以每隔 1 tick 或数个 tick 执行一个这种循环,并将一些瞬时执行的函数分成多块跨 tick 执行。
- 这可以平滑计算压力,对 tps 火焰图进行削峰处理。说人话就是:降低1% low帧,但是tps。
魔咒循环
使用tick魔咒效果组件的物品,并将物品放在目标实体的可触发的槽位上(如前文提到的 armor.body)。效果设为执行函数。
- 自带执行上下文
- 可以略过初步筛选实体的步骤,只有需要检查的实体会执行
- 使用物品修饰器
{function:"set_enchantment"}移除附魔(将等级设为0)或干脆删除物品即可终止循环。
进度循环
- 和魔咒循环类似
- 不需要物品
- 只能用于玩家
- 赋予玩家这个进度而不使用
revoke重置状态即可终止循环 - 据说
/advancement revoke其实消耗不算低,在100 scb左右。
条件判定
通过这些条件判定该 tick 是否执行特定效果。
execute (if|unless)
最常用的条件判定方式。也可以调用谓词。这里就列出一点容易疏漏的点得了。
if data如果在整条指令的末尾,会返回 NBT 数量。
补充说明
所以可以使用形如execute store result score return _ if data storage _ List[{type:"b"}]的命令来获取列表中符合条件的的元素数量。
而data get ... List(注意后面没有[])只能得到总的元素数量,如果指定路径的是List[]则会报错
if items的效率要比if data <target> item{}要高不少。功能也要强一点。
补充说明
使用通配符 * 来指代任意槽位或物品
用 ~ 替代等于号 = 以模糊匹配。 如 *[custom_data~{ a:b }] 可以匹配到 {a:b, c:d}。反之使用 = 则只能匹配到 {a:b} 而不能匹配到前述组件。
一些组件可以指定数值范围而非固定数值:*[count~{min:1,max:24}]*[enchantments~[{id:"sharpness",level:{min:3}}]]if function 可以在句中执行函数,根据函数的返回值决定是否继续执行。
不能执行宏函数,要套一层壳
没有返回值也不会通过
if/unless没有else和elif的说法。但是:
补充说明
你可以新开一个函数(设为 cond.mcf),并在函数开头写上execute if/unless ... run return run function pass_func,这样function pass_func就会在判定通过时执行,而下文就是 else 部分了。(return运行时会中止函数,不执行后面的内容)
也可以...run return 1 然后在上一层函数或命令中用... if function cond run function pass_func调用这个函数。这样当if function cond没有通过时,会执行cond.mcf,而通过时会执行function pass_func。
只有套娃或者整理强迫症的时候会这样写吧。if blocks除了检测区域是否符合特定的结构(用来做多方块结构之类的),也可以指定一个全是空气的工具区域,用来检测目标地点是否完全无遮挡。execute at @s if blocks ~ ~ ~ ~ 320 ~ x ~ z masked run say 上方无遮挡,其中从 x -64 z 到 x 320 z 的区域是一个空气柱。
目标选择器
- 选择器与
if entity的抉择:
详情
虽然一般更常见的形式是
execute as @e run function a
#------
# a.mcf
...if entity @s run function...
....scoreboard players operation @s...但在所有实体都要过一遍判定的情况下,使用 @e 更好。
也就是说clear @a[scores={a=1}] 要比execute as @a if score @s a matches 1 run clear @s更好。\
这是由于 execute 会创建很多分支,带来额外开销。
我们不建议的其实是
execute as @e[tag=a] if data entity @s item run say 1
execute as @e[tag=a] if data entity @s item run say 2
execute as @e[tag=a] if data entity @s item run say 3这样的重复判断。
- 变量
@a可以选中死亡的玩家,但@e[type=player]不能。这是一种检测玩家是否复活的方法。@s可以抓到世界外(未加载区块)的实体,以及死亡实体。但要在其他选择器不可选中前构成这个上下文。这个技巧在还没有宏的时候被用来实现传送到存档点(如:家坐标)位置一类的效果。
案例
tag @s add temp
execute as 0-0-0-0-3 run function a
#------
# a.mcf
# 坐标 <x y z> 未加载
execute store result entity @s Pos[0] run scoreboard players get @p[tag=temp] home_x
execute store result entity @s Pos[1] run scoreboard players get @p[tag=temp] home_y
execute store result entity @s Pos[2] run scoreboard players get @p[tag=temp] home_z
tp @p[tag=temp] @s
#------
tag @s remove temp- 参数
- 进行判定的坐标点和实体都会跟着
/execute的子命令变化
不一定是执行者或选择目标的位置 distance参数是球形判定
判定的是实体的原点^(碰撞箱底部的对角线交点)^在不在以判定点为球心的范围内- 与
distance不同,[dx=0.0,dy=0.0,dz=0.0]是长方体范围,判定的是碰撞箱相交。
判定范围的棱长是 dx+1 dy+1 dz+1,也就是说上述的参数会框定一个1x1x1的正方体范围
也就是说,如果你需要在实体的任意部位与判定范围相交即通过,就选择 dxdydz。 - 检测某一点是否在实体的碰撞箱内,可以用两个
distance相夹的与门判定
形如execute at @s as @n if entity @s[dx=0, dy=0, dz=0] if entity @s[x=~0.999, y=~-0.999, z=~-0.999, dx=0, dy=0, dz=0] - 角度相关的,
x_rotation才是检测俯仰旋转y_rotation才是检测左右旋转。x_rotation往上看是负数,往下看才是正数。
- 进行判定的坐标点和实体都会跟着
谓词
在几乎所有数据文件(.json文件)中都会用到的“判定条件”
Xiao2 按
谓词是逻辑学里面那个,一阶谓词演算那种。简单来说就是bool函数,返回值为true or false的函数。
predicate直接翻译成“条件函数”得了
只不过我们先接触到mc的predicate,有机会才会学逻辑学或者离散数学什么的,或者java里lambda的predicate
那里确实叫“谓词”
谓词这个翻译太不直观了,我还是更喜欢旧翻译“断言”。更整活一点就“屁瑞迪卡特”。
这个名字可以简单理解为:“做一段描述(省略主语的陈述句),如果当下的情况符合描述则通过,不符合描述则不通过。”
主语(上下文context)即当下的情况,由调用谓词的组件给出。在很多XX定义格式的子条目下都可以看到战利品上下文的标注,即是这段描述(谓词)的主语(上下文context)
配合高频循环检测的使用方法是 /execute (if|unless) predicate。写在内联的战利品表、物品修饰器里也不是不行。
- 可以检测光照等级、伤害类型、天气等通过
execute if|unless的简单检测无法获取到的参数- 玩家按键输入的检测也需要依赖谓词
不过现在只能检测运动相关的按键WASDSpaceShiftCtrl\
- 玩家按键输入的检测也需要依赖谓词
!就这点也是 OJNG 更新矿车的时候才顺便施舍的!(
entity_properties的玩家实体子谓词{"type_specific": {"type":"player", "input":{}})
- 在整个游戏的运行中广泛使用,到了见缝插针的程度。所以原版很多条件判定~(只要是数据驱动的)~都是用谓词。可以拿到自己的系统里用。
- 解压
version/版本.json即可拿到原版的数据文件,在/data下。 这同时也能拿到原版的内置资源,在/asset
- 解压
进度谓词
有一些谓词只在特定的场景下存在。比如进度里的伤害谓词
- 伤害谓词
- 存在于
player_hurt_entity等与伤害相关的进度里 - 能检测受到的伤害的数值、是否被阻挡、被防御机制降低前的伤害总量等
- 而谓词文件和内联谓词中只有
伤害类型谓词没有伤害谓词
- 存在于
记分板准则
trigger可以被玩家变更数值,但每次变更后都需要用/scoreboard players enable赋予玩家一次性权限healthfoodairlevelxp等等准则会自动变更,就不需要每次检测都读取玩家NBT了。- 可以用来做击杀榜、死亡榜、玩家血量显示等简单的分数效果
- 同时,
health等和玩家状态绑定的分数是不能被/scoreboard players修改的
minecraft.开头的都是 统计信息复合准则, 可以伴随对应的统计信息自动增加。- 玩家自身相关的是
minecraft.custom:开头 - 例:在玩家累计爬梯子或藤蔓
米后, 使用 minecraft.custom:minecraft.climb_one_cm准则的记分项就会增加到100000 - 旧版本常用的胡萝卜钓竿右键触发就是用记分板准则+高频检测实现的。
- 玩家自身相关的是
|| 条件触发(广播事件)
一些只在满足特定条件的时候触发的效果。
- 尽量写成这样的形式,在系统平稳运行的时候就不会执行相关的任务。这能够大大降低系统运行时的负载,也就是说大部分情况下是一种优化。
广播事件的意思是,这个事件发生时会进行一次“广播”。
- “广播”会让相关的系统被触发,继而判定是否需要进行一些对应的处理。
函数标签广播
在一些封装好的前置模块里,有时会使用 函数标签来作为接口。使用者往这个标签里添加函数,就可以使自己的函数在特定流程中被调用。
进度准则
监测一些只能由玩家作为主体的事件。触发后可以由玩家执行函数、给予玩家战利品奖励、给予经验值。
- 触发后用
advancement revoke移除该函数以再次触发 - 也可以反其道而行之,给予玩家该进度并不移除,来防止玩家在不需要的时候触发该进度。
- 设定一个父系进度,并在主循环里写个
advancement revoke @a from <父系进度>就可以一次性处理所有需要自动重置的进度。 - 一次
advancement revoke only的消耗大约是100 scb ±30。这个数据非常粗略,仅供大致考量。
基本作用
基本上看看原版有哪些进度就知道可以做出哪些效果了。下面挑一些常用的说说
- 检测玩家物品栏变动
inventory_changed - 检测玩家造成伤害
player_hurt_entity - 检测玩家吃东西、用药等
consume_item - 检测玩家使用物品(长按)
using_item - 检测玩家右键方块
any_block_usedefault_block_useitem_used_on_block - 检测玩家合成物品
recipe_crafted
用途
进度一般会用来做这些效果:
右键交互:用
consume_item或using_item与consumable或blocks_attacks等数据组件(物品组件)配合,能检测玩家使用该物品右键。- 消耗物品准则
consume_item+ 消耗品组件consumable会在玩家长按一定时间后触发。此时也能触发 使用冷却组件use_cooldown - 使用物品组件
using_item+ 消耗品组件consumable可以持续监测玩家是否在持有该物品并按住鼠标右键。可以灵活检测单击、长按秒、长按 秒后放下等条件。 - 有时会将消耗时间设为近乎无限长,就不用调整了。检测任意时间长按的时候很方便。
- 但是会让手上的动画变得非常缓慢。
- 消耗物品准则
与交互实体的交互
- 左键:玩家伤害实体准则
player_hurt_entity+/execute as <interaction> on attacker - 右键:玩家与实体交互准则
player_interacted_with_entity+/execute as <interaction> on target
- 左键:玩家伤害实体准则
魔咒效果组件
- 魔咒的效果与物品绑定。用来写一些与物品相关的效果很合适。
由于Mojang特有的东一块西一块,往往需要组件和魔咒拼一起才能覆盖物品定制需求。有时候还需要进度的参与。 - 有一些功能的实现,虽然在设计层面上与物品无关,却也需要藉由魔咒才能实现,没有独立的命令来直接实现。
- 但是魔咒必须持有或装备才能触发。在这种情况下,可以使用
armor.body(马铠) 或armor.saddle(马鞍)来触发。 - 对于一般认知下没有这些槽位的实体,这些槽位仍然能置入物品并生效。包括玩家
- 但是魔咒必须持有或装备才能触发。在这种情况下,可以使用
- 除了本章主题的“条件触发”,魔咒还有很多被动类的效果。不过“装备时生效”的被动也算是条件触发罢。
- 同一个魔咒组件触发的函数等的效果是按列表顺序执行的。
基本作用
魔咒可以做一些装备时生效的被动:
- 免疫伤害(对特定伤害类型无敌)
damage_immunity - 设置数值
- 射箭消耗的箭数
- 当次近战攻击的基础攻击力
- 护甲减免系数(护甲提供的减伤)和魔法提供的减伤
- 经验修补效率
- 击退效率
- 杀敌时的经验掉落数
- 弹射物数量
- 非玩家生物爆装备的概率
魔咒可以在这些情况下条件触发:
- 装备时高频触发
tick - 攻击或受到攻击触发
post_attack- 包括火焰附加、荆棘、引雷等效果
- 生成弹射物时触发
projectile_spawned- 如射箭、发射三叉戟。部分弹射物似乎不会触发。
- 穿刺武器左键
post_piercing_attack^(1.21.11) - 玩家开始破坏(左键)方块
hit_block - 移动到其他方块、落地、装备的瞬间、从旁观模式切回来时触发
location_changed
魔咒可以触发这些效果: (上文提到的条件触发组件都可以用) (只列出不可替代性较强的效果)
- 执行函数
run_function - 施加消耗度(令玩家饥饿)
apply_exhaustion - 施加冲量(改变实体Motion)
apply_impulse- 一般用来控制玩家的动量。实体的直接
/data就可以了 - 只能使用局部坐标系
^u ^v ^w。想要使用绝对坐标系或局部坐标~x ~y ~z需要依赖前置。
- 一般用来控制玩家的动量。实体的直接
- 产生爆炸效果
explode - 点燃实体
ignite - 生成圆盘或圆柱状的方块结构
replace_disk - 召唤实体
summon_entity
用途
进度一般会用来做这些效果:
- 左键检测(包括空挥):用
post_piercing_attack魔咒组件,配合piercing_weapon物品组件,可以检测玩家点击左键。这个效果需要主手持有带附魔的物品才能生效。 - 左键方块:在玩家可以破坏指定方块的情况下(指向方块时有框),可以检测到玩家点击这个方块。可以配合
can_destory物品组件,以及调整挖掘速度的组件或实体属性使用。- 譬如我的快照介绍视频中随手搓的火焰附加剑点燃火堆、点击地面召唤雪傀儡。
- 检测击中或被击中:用
post_attack魔咒,可以检测一次攻击的产生。- 简单的效果例如以前很难完美处理的干预被箭或雪球击中的实体(被雪球击中会跳起来之类的小玩具效果)、玩家挨打时随机传送
- 复杂的比如虚拟血量系统、架空攻防体系
- 进度和魔咒的生效都非常即时。我的意思是触发效果的时候箭还没消失呢
- 效果是顺序执行的,所以写在上面的函数会先触发。
- 控制玩家移动:用
apply_impulse效果可以完美解决控制玩家动量的问题。这可以用来做冲刺、闪身、二段跳 等效果。一般配合tick或location_changed魔咒组件使用。 - 影响玩家状态(点燃玩家、令玩家饥饿):这两个别的实现方式效果不好的功能,使用魔咒可以更完美的达成。