Skip to content

原版开发常识汇总(其一)

Rainbow_

Rainbow_

  • 这是一本指南式的工具书,记录了作者对原版开发的整体概括
  • 目标是让读者快速查缺补漏,了解整体情况,以及在困扰怎么做某些东西时能按图索骥
  • 为此本文尽量简短,降低阅读成本。
  • 所以本文只提供三行以内的实现,更复杂的内容在此仅作为关键词集合和索引,读者需自行检索 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使用calculatorcalc.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.bodyarmor.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 gametimegametime更新还要早。如果不处理,就约等于是在同一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 没有 elseelif 的说法。但是:
补充说明

你可以新开一个函数(设为 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的简单检测无法获取到的参数
    • 玩家按键输入的检测也需要依赖谓词
      不过现在只能检测运动相关的按键WASD Space Shift Ctrl\

!就这点也是 OJNG 更新矿车的时候才顺便施舍的!(entity_properties的玩家实体子谓词{"type_specific": {"type":"player", "input":{}}

  • 在整个游戏的运行中广泛使用,到了见缝插针的程度。所以原版很多条件判定~(只要是数据驱动的)~都是用谓词。可以拿到自己的系统里用。
    • 解压version/版本.json即可拿到原版的数据文件,在/data下。 这同时也能拿到原版的内置资源,在/asset
进度谓词

有一些谓词只在特定的场景下存在。比如进度里的伤害谓词

  • 伤害谓词
    • 存在于player_hurt_entity等与伤害相关的进度里
    • 能检测受到的伤害的数值、是否被阻挡、被防御机制降低前的伤害总量等
    • 而谓词文件和内联谓词中只有伤害类型谓词没有伤害谓词

记分板准则
  • trigger可以被玩家变更数值,但每次变更后都需要用/scoreboard players enable赋予玩家一次性权限
  • health food air level xp等等准则会自动变更,就不需要每次检测都读取玩家NBT了。
    • 可以用来做击杀榜、死亡榜、玩家血量显示等简单的分数效果
    • 同时,health等和玩家状态绑定的分数是不能被/scoreboard players修改的
  • minecraft.开头的都是 统计信息复合准则, 可以伴随对应的统计信息自动增加。
    • 玩家自身相关的是minecraft.custom:开头
    • 例:在玩家累计爬梯子或藤蔓1000米后, 使用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_use default_block_use item_used_on_block
  • 检测玩家合成物品 recipe_crafted
用途

进度一般会用来做这些效果

  • 右键交互:用consume_itemusing_itemconsumableblocks_attacks等数据组件(物品组件)配合,能检测玩家使用该物品右键。

    • 消耗物品准则consume_item + 消耗品组件consumable会在玩家长按一定时间后触发。此时也能触发 使用冷却组件 use_cooldown
    • 使用物品组件using_item + 消耗品组件consumable可以持续监测玩家是否在持有该物品并按住鼠标右键。可以灵活检测单击、长按n秒、长按n秒后放下等条件。
      • 有时会将消耗时间设为近乎无限长,就不用调整了。检测任意时间长按的时候很方便。
      • 但是会让手上的动画变得非常缓慢。
  • 与交互实体的交互

    • 左键玩家伤害实体准则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效果可以完美解决控制玩家动量的问题。这可以用来做冲刺闪身二段跳 等效果。一般配合ticklocation_changed魔咒组件使用。
  • 影响玩家状态(点燃玩家、令玩家饥饿):这两个别的实现方式效果不好的功能,使用魔咒可以更完美的达成。

Powered by Vitepress and Github Pages