在dfmc(https://bbs.craft.moe/d/2419-demodungeonframeworkmc)开发完毕之后,我开始试着挖掘脚本的潜力,发现脚本在制作些配置逻辑十分复杂的功能时很好用,典型的一种就是复杂的粒子运动。
粒子技能系统,或者说粒子抛射物,因为mc根本没有给出任何方便操作的API,只能从渲染单个粒子开始从头构建,因此方便直观地操作粒子构成形状,以及其运动就是粒子特效的关键。
我在18年做过一个unity的弹幕游戏demo,里面实现了各种弹幕和飞行轨迹,得益于unity的物体挂载组件式的脚本逻辑以及丰富的向量操作函数,构建这个弹幕系统十分方便。

仿照unity的逻辑,我在dfmc提供的lua框架中设计了一个简洁的抛射物控制系统。
下面以一个“魔法飞弹”作为例子来展示这个系统的逻辑,代码部分进行了简化。

抛射物的逻辑分为两个部分。
粒子物体
一个叫做“粒子物体(ParticleObject)”,这就相当于一个可以被随便移动旋转的物体模型,而它的样子则由一个“坐标组”构成(确切来说是包括坐标、颜色等信息在内的粒子信息组)。
这一坐标组的坐标和世界坐标没有任何关系,是以粒子物体的中心作为(0,0,0)原点的一个参考系。例如,在上图魔法飞弹中,红色粒子的位置就是(0,0,0)点始终不变,而周围三个绿色粒子则在yz平面上围绕x轴旋转(也就是每tick改变本地坐标)。
因此,在创建一个新的粒子物体形状时,不需要任何来自世界坐标的参数。魔法飞弹的初始位置,用基础三角知识,直接手动添加就可以。
-- 中心粒子
po["particles"]["center"] = maf.vec3(0, 0, 0)
-- 环绕1(正上方)
po["particles"]["s1"] = maf.vec3(0, 1, 0)
-- 环绕2(右下方)
po["particles"]["s2"] = maf.vec3(0, -0.5, math.sqrt(3) / 2)
-- 环绕3(左下方)
po["particles"]["s3"] = maf.vec3(0, -0.5, -math.sqrt(3) / 2)
我在网上找了个向量操作库,一个lua文件就能把向量操作傻瓜化。旋转逻辑就非常容易。我们每一tick,让每个粒子绕着(1,0,0)旋转一定的角度,由于红色粒子在原点,所以旋转之后还是原点,而绿色环绕粒子则发生略微的移动。
--转起来!
local r = maf.rotation.fromAngleAxis(rotateSpeed/20, 1, 0, 0)
for key, value in pairs(po["particles"]) do
value:rotate(r)
end
运动模式
粒子物体的坐标是本地参考系,与世界坐标无关。而之所以能让这个粒子物体在特定的位置出现、移动、甚至追踪生物,这是由另一半逻辑完成的,叫做“运动模式(MotionPattern)”。它储存实际的世界坐标,并负责计算路径。
在游戏中的每一Tick,运动模式会执行更新函数,并给出粒子物体原点应当处于的世界坐标。例如如果你希望制作一个直线运动的模式,那么更新函数中就可以写
position = position + speed/ 20
这样一句即可。
之后,粒子系统会根据这个返回的世界坐标和朝向,把粒子物体的坐标组执行向量加和与旋转,根据最后的结果,依次在游戏中生成粒子。而生成粒子,则通过luaJ引擎来引用一个粒子插件(ParticleNativeAPI)的函数完成。
也就是说,运动模式并不在乎粒子物体长什么样,它只负责移动这个粒子物体的整体。
而粒子物体也不在乎运动模式长什么样,它只负责储存自己各部分粒子的相对位置。
因此任何形式的粒子物体、运动模式就可以自由搭配,从而形成各种技能效果。
例如,我们可以画一个圆,然后在里面画两个三角形,构成一个粒子物体。
让武器攻击时,创建该粒子物体的一个实例,将其应用到直线运动模式,并把速度设为极小值。

就是这样一个效果。不仅如此,还可以通过碰撞逻辑,让它给上面的玩家提供一个buff效果,把它做成黑魔纹)而碰撞逻辑,也是十分自由的。
互动
lua可以将函数作为变量进行参数传递,因此该系统可以把碰撞时执行的逻辑写在各种地方,比较直观的,就是写在武器上,当武器创建一个粒子技能实例的时候,可以将一个自定义函数传递作为碰撞回调。例如上图中的魔法飞弹,注意当其击中僵尸的时候僵尸头上冒出了一个异常状态图标。该逻辑是由武器交给飞弹的函数完成,而非通过订阅mc的伤害事件完成的。
该函数通过调用异常状态系统(之前我拿lua写的),给了怪物一个“冰冻”状态,该状态的函数再调用mc服务器的API,给生物一个缓慢药水效果,同时引用另一个服务器插件(全息显示),给生物头上放置一个冰块作为图标显示。
基于lua的模块化框架,这个粒子系统十分精简,其实粒子系统本体文件其实只有不到100行代码,而粒子物体和运动模式基于需要可以随意创建和删改。同样的,开发自定义的异常状态模块、装备模块等,都可以利用这方面的优势。