迫害也需要指导理论:推拉机制及物理引擎详解
前言
方舟已经公测一年了,然而针对其推拉系统仍然没有较为深入的研究
目前较为普遍应用的仅有https://ngabbs.com/read.php?tid=17657755中针对推力算法的研究
本文将尝试从程序层面完全揭开方舟推拉机制的原理
因涉及反编译,部分研究过程不予放出
如有错误请各位大佬指正

底层:unity物理引擎
众所周知方舟是拿unity引擎写的,运动的底层自然也是使用的unity自带的一些功能
在unity引擎中,刚体运动使用的是Rigidbody类型的元件
Rigidbody可以设置质量,碰撞类型等等等
方舟中敌方单位使用的是二维的刚体,即Rigidbody2D
这个刚体是dynamic类型,拟真度较高,可以模拟刚体的碰撞(虽然游戏中并不会发生),惯性,重力...等等
但有一点很特殊:在方舟中,这些刚体的质量全部被设置成了1(kg)
也就是说,只要初始状态相同,施加的力相同,这些刚体的运动会完全一致
很显然,方舟中是通过控制给Rigidbody2D施加力的大小和模式来控制不同的运动的
如何控制施加力的大小会在后面说到
给Rigidbody2D施加力的模式可以很多种,这里只介绍两个方舟中用到的
第一种是Force,可以视作常规的,通过在一段时间内持续给一个刚体施加一个固定数值的力来改变其动量
第二种是Impulse,可以看做瞬时力。其原理可近似看做直接改变刚体的动量
如果想不起动量的概念请回忆高中物理,这在之后很重要
动量=mv=F关于t的积分,在F恒定时可看做Ft
Impulse模式会将t默认为1,直接为刚体增加Ft的动量
在方舟通用的m=1的情况下可得,v=F

中层:状态机与摩擦力
中间层是鹰角自己发挥的部分,虽然是自由发挥但依然比较物理
首先需要介绍一下状态机的概念
大体可以理解为敌人存在很多种状态,处在不同的状态下会拥有不同的特征
比如成功的推拉本质上是对敌人施加力并使其进入unbalance状态
只有在这个状态下,敌人才会拥有物理碰撞箱,且每帧会更新速度并检测状态
按照程序算法,摩擦力=g*质量*摩擦系数,其中摩擦系数是0.5,g取默认值9.81,质量默认为1。求得摩擦力为4.9
程序每帧会根据摩擦力计算新速度并重设刚体速度
之后,程序会记录新速度
如果((新速度小于等于0.1 且 已超过unbalance保护时间 且 无有效拉力来源) 或 (拥有UNBALANCE_IMMUNE效果))
则立刻退出unbalance状态,退出unbalance状态时清空刚体运动速度

表层:概念声明
表层属于鹰角的自由发挥,这部分自由发挥使得方舟的推拉机制非常不物理
以下有很多很容易混淆的概念,先在此进行定义和声明
力:代表unity引擎施加的力,大小和方向需要通过计算,模式根据推拉的区别有所不同
质量:代表敌人的刚体在unity引擎中设置的质量,默认为1(kg)
重量等级:代表游戏中敌人的重量等级属性,无上下限
力量等级:代表游戏中见到的中等力度,大力等等级,其描述与等级的对应表如下

有效力量等级 = 来源力量等级 - 目标重量等级。值域为[-3, 3]

表层:推
推力本质上对敌人的刚体瞬间施加Impulse模式的力,直接改变其动量
推力的方向
推力有两种模式,一种是relative,另一种是directional
前者的方向为推力来源到推力目标(的向量)
后者的方向一般为推力来源的朝向
但是注意,在一定情况下,directional会被装换为relative:
当推力来源到推力目标的向量,与推力来源的朝向的夹角超过45度 或 推力来源与推力目标的坐标距离不足0.25
则directional推力会被转换为relative推力
推力的力度计算
directional推力转换为relative推力时有效力量等级将会减少,默认减少数值为2
有效力量等级将对应特定列表(位于gamedata中)中的编号
力的大小 = 对应后的值 * 0.02 * MOVE_MULTIPLIER(默认0.5)
下面将给出对应列表,力的大小将使用默认值进行计算

推力的执行
大致逻辑如下:
如 力不为0 且 目标不持有UNBALANCE_IMMUNE效果
则将目标切换至unbalance状态
随后以impulse模式,按照刚刚计算的方向和大小施加单次的力
最后将unbalance保护时间期限调至0.1s后

表层:拉
拉力本质上对敌人的刚体持续施加Force模式的力,通过力逐渐改变其动量
拉力的方向与力度计算
方向就是拉力目标到来源的向量,这个没啥好说的,关键是力度
和推力类似,拉力的有效力量等级将对应特定列表(位于gamedata中)中的编号
初始力的大小 = 对应后的值 * 1
下面将给出对应列表

细心的读者应该注意到我在力的前面加了个初始吧
钩子在命中目标时会记录下目标与来源的距离
在默认情况下(easeType=6,即easeInQuart)
某一瞬间的力 = (现距离/初始距离)^4 * 初始力
(现距离/初始距离)最大为1
因此钩子的力度会随着距离比例的接近急速衰减
拉力的执行
目前的机制中,拉力都是和钩子挂钩的
钩子钩中时:
如 力为0
则立刻停止钩子
如 目标不持有UNBALANCE_IMMUNE效果
则将目标切换至unbalance状态
然后将unbalance保护时间期限调至0.1s后
钩子是projectile,拥有最大持续时间
当有效力量等级>=-1时,钩子钩中后会将最大持续时间改为linkDuration(默认为1)
否则最大持续时间将被改为0.5
钩子勾住期间会时刻检查目标是否位于来源阻挡范围内
检查位于范围内且只有单个拉力来源时会立刻清空目标速度

一些结论
推力的公式可以沿用https://ngabbs.com/read.php?tid=17657755
位移=0.5*初速度^2/加速度
虽然机制上有一定的不准确但给出的公式没有问题
拉力的话,我列了个微分方程

据学数学的朋友说,这个方程存在解析解,但不是初等函数
因此写个模拟器计算大概还快一些
本文原载于NGA:https://ngabbs.com/read.php?tid=22221080
作者为本人
部分研究(反编译)过程未转载至专栏
本专栏以CC BY-NC-SA 4.0协议发布