• 👋 好久不见,老朋友,序序新:964014008

机制 【搬运】无敌帧机制详解

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
我搬我自己。顺便剪除了原帖中的一些废话。


引入:

众所周知,terraria中存在无敌帧这么一个机制:当你用某些武器攻击到怪物之后,怪物会获得一段时间的无敌帧,在这一段时间内怪物“不会受到伤害”。
因此,用迷你鲨配合陨石子弹时射击某一个怪物时,连续的两发子弹只有一发能击中怪物;
因此,用虎爪站撸世纪花时要特别注意不能挂雨云或者召唤小蜘蛛,否则虎爪会打不出应有的伤害。

按照常识,我们知道那些能穿透敌人的子弹、魔法或剑气一般会给敌人造成无敌帧,许多召唤物也会给怪物造成无敌帧。

但是在游戏过程中,细心的玩家可能会发现以上的经验会有些例外,比如说:
玩家A在打月总时放出了一条星尘龙,并且手里拿着耳膜击碎者+叶绿弹输出,他发现星尘龙尽管会造成无敌帧,但并没有影响到叶绿弹的输出。
玩家B在毕业之后造了一堆夜明弹玩耍,他发现虽然夜明弹会穿透,但它们之间并不会互相影响输出。这个特性与陨石子弹非常不同。
更细心的玩家C发现即使是造成无敌帧的子弹\魔法\剑气\召唤物,它们造成无敌帧的时间都不一定完全一样:比如说棱镜造成的无敌帧时间明显就比鲨龙卷召唤物的碰撞造成的无敌帧时间短很多。

以及还有一些问题:
燃烧、霜火、诅咒焰等debuff会不会造成骗伤?
多人联机时玩家A的攻击造成的无敌帧会不会影响玩家B的输出?

这一切都需要我们对无敌帧机制进行更深入的研究。

说明:

NPC、抛射体:
首先需要对这个帖子里所说的NPC和抛射体的概念做一些说明。
这里的NPC不仅包含电工妹和哥布林工匠等城镇NPC,还包含所有的怪物,甚至一部分怪物打出的魔法——比如地牢的黑暗法师打出的水弹,猪鲨吐出的泡泡。
抛射体即projectile,顾名思义,抛射体就是按照一定轨迹飞出去的东西,比如子弹、各种魔法、各种剑气等等。
注意:召唤物、各类长矛、悠悠球都是抛射体。

伤害途径:
然后来仔细思考一下玩家对怪物造成伤害的主要途径:
1.用剑等近战武器挥砍。
2.用枪械、魔法书等武器扔出抛射体,抛射体伤害怪物。

需要注意的是剑、镐、斧等大多数近战武器都属于第一类。但悠悠球、各类长矛武器以及Arkhalis剑都属于第二类。
手雷等爆炸物也属于第二类。

为了以下行文方便,我们把第一类统称近战武器,第二类统称抛射体。

普通无敌帧的内部实现:
游戏中每个怪物都拥有一个计时器immune,这是一个长度为256的自然数列。每个tick(1/60秒),数列中的每个值都会下降1,直到0为止。
于是怪物A的immune中的第k个数n表示怪物A对玩家k的无敌帧还会持续n个tick。
没错,游戏中不同玩家对怪物(即使是同一个怪物)造成的无敌帧是独立计算,互不影响的。
这是游戏中最普遍的无敌帧。

当然,游戏里还有两类无敌帧,这两类无敌帧与下面会说到的两类特殊抛射体绑定,所以详细的之后再说。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
抛射体造成的无敌帧:

与无敌帧有关的抛射体属性主要是抛射体的穿透能力,在源码里相应的变量是penetrate。游戏中的每个抛射体都拥有自己的penetrate值,penetrate若为正数k,则表明它能击中k个怪物(穿透k-1个),如果是-1,则表明它能击中在自己行进路线上的所有敌人。
比如说小鬼召唤物发射的火球、利刃台风就是penetrate=-1的抛射体。注意那些能造成范围伤害的抛射体,比如月耀、炸弹等也penetrate=-1。

penetrate为正数时,每击中一次怪物都会下降1,到0的时候这个抛射体就会消失。

从这一方面能把抛射物分成两类——穿透的(初始的penetrate不为1)和不穿透的(初始的penetrate=1)。注意,虽然穿透的抛射体在还剩一次击中怪物的能力时penetrate=1,但还是被认为是穿透型抛射体,毕竟其初始penetrate>1嘛。

抛射物还有两个标签:
usesLocalNPCImmunity
usesIDStaticNPCImmunity

游戏中的700多种抛射体可以根据这俩标签的有无被分成三类:
第一类是这俩标签都没有(被设定为False)的
第二类是usesLocalNPCImmunity=True的
第三类是usesIDStaticNPCImmunity=True的

第一类抛射体占了绝大多数。
第二类抛射体仅仅有一小部分。
第三类仅有一种抛射体,即699号抛射体,食人魔掉落的关刀的本体。

这三类抛射体计算无敌帧、以及被无敌帧影响时的机制不尽相同。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
第一类抛射体:
第一类抛射体内的大多数穿透抛射体在穿透怪物(击中当前怪物的时候penetrate不是1)时,会对怪物造成10tick(即1/6秒)的无敌帧。
但有一些例外,见下表:
2019-04-08 (1).png
注意鲨龙卷召唤物指的是鲨龙卷本体的碰撞、至于它发射出的小鲨鱼,那东西不穿透也不造成无敌帧。

当第一类抛射体碰到怪物时,如果怪物对当前玩家的immune无敌帧为0,或者抛射体是不穿透型抛射体,那么抛射体就能击中怪物。否则不行。
第一类抛射体的特点是互相影响,比如陨石子弹A对怪物造成的无敌帧会影响陨石子弹B的杀伤。
当然,非穿透抛射体不受影响,所以如果你用泡泡枪站撸世花,就不需要担心世花骗伤的问题;所以用耳膜击碎者配合叶绿弹打月总时,召唤一条星尘龙出来也不会影响输出。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
第二类抛射体:
2019-04-08.png


第二类抛射体被发射出去后,会拥有一个计时器localNPCImmunity,这是一个长度为200的整数列,其中的第k个值表明这个抛射体对序号为k的怪物的“局部无敌时间”。
这个整数列中的所有大于0的值每秒都会减少60,直到0为止。而小于0的值则不变。
当第二类抛射体碰到怪物时,游戏会首先检测这个抛射体对这只怪物的局部无敌时间是否为0。如果不是,则无法击中;如果是,则继续检测怪物对发射这枚抛射体的玩家的immune(即普通的无敌帧)是不是0,以及这个抛射体是不是非穿透抛射体,如果以上至少有一个是成立的,则抛射体最终击中怪物。
第二类抛射体击中怪物时,绝大多数不会给怪物造成普通无敌帧,唯一的例外是喷发剑,上面那个表里已经显示出来了。

注意,局部无敌时间是抛射体的属性(而普通的无敌帧是怪物的属性),游戏中每一个抛射体都拥有独立计算的局部无敌时间,加之绝大多数第二类抛射体不会造成普通的无敌帧,这意味着第二类抛射体基本上不会互相影响(唯一的例外是喷发剑)。
所以说虽然夜明弹是穿透的,但它们并不会像陨石弹那样互坑。
大家还可以注意到一部分第二类抛射体的localNPCImmunity是-1,这意味着这个抛射体无法再次击中已经击中过的怪物,而那些localNPCImmunity大于0的抛射体,则表明它在击中某个怪物之后一段时间,可以再次击中这只怪物(典型的就是星云奥秘)。

以及由于第二类抛射体碰到怪物时仍然会检测怪物的immune,所以普通无敌帧还是会影响第二类抛射体的输出。
简单来说,虽然第二类抛射体基本不会互相影响,但它们会受到第一类抛射体的影响
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
第三类抛射体:
只有那个大关刀的本体。

与那个东西有关的是perIDStaticNPCImmunity,这个变量是个714(游戏内抛射体总数)*200(游戏内最大NPC数量)的整数矩阵,这是个游戏的内置变量,不属于任何一个NPC或者抛射体。关刀碰到怪物(假设怪物的序号是k)时,会检测矩阵第700行(因为关刀序号是699,是第700个抛射体),第k+1(加了1是因为C#的序号是从0开始排的)列的数字是否小于等于GameUpdateCount,如果是则继续检测怪物对发射这枚抛射体的玩家的immune(即普通的无敌帧)是不是0,以及这个抛射体是不是非穿透抛射体,如果以上至少有一个是成立的,则关刀最终击中怪物。
关刀击中怪物后,也会在矩阵的相应位置赋值GameUpdateCount+36.
这个大矩阵内的数值本身是不会下跌的(不像immune和localNPCImmunity),但因为GameUpdateCount会上涨(速率应该也是每秒60),所以起到的倒计时效果是一样的。

所以关刀的特性和第二类抛射体差不多,但有一点不一样:不同玩家的关刀是会互相影响输出的。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
剑、锤、斧及虎爪等近战挥舞武器的无敌帧机制:

游戏每时每刻都会检测每个怪物对某个玩家的immune(普通无敌帧)是不是0,以及玩家的attackCD是不是0,如果以上都成立,并且武器碰到了怪物,则对怪物造成伤害。
玩家的attackCD=玩家itemAnimationMax/3
而itemAnimationMax是对应武器的useAnimation*玩家的meleespeed(为实际近战攻速的倒数),useAnimation是武器的自身属性。
所以实战中,挥舞一次剑能打到的怪物数量是有限的。
并且挥舞武器受普通无敌帧的影响。

此外,当挥舞武器给怪物造成伤害时,会给怪物对当前玩家的一个比较短的普通无敌帧,持续时间是当前玩家的itemAnimation。
这个数值在你刚开始挥舞武器的时候与itemAnimationMax一样,然后下降,如果没理解错的话,当你挥舞完成的时候,这个值会降到0.
这个无敌帧的存在应当只是为了保证玩家每一次挥砍,只能对同一个怪物造成一次伤害。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
一些比较杂乱的内容:
1.着火、诅咒焰以至于星尘细胞吸附、破晓等烧血debuff通过NPC.UpdateNPC_BuffApplyDOTs函数直接作用于NPC生命值,不受无敌帧影响也不造成无敌帧。
2.部分抛射体在击中怪物后属性会改变——主要是那些击中目标后爆炸的抛射体比如爆炸子弹、月耀等。改变的属性主要是抛射体的大小,不过有时候penetrate属性也会变,这造成了一些搞笑的现象:爆炸子弹自身penetrate是1,但击中怪物后penetrate变成-1,长宽变成80像素(16像素是一格)然后再造成一下伤害,于是乎...爆炸子弹本体伤害不受无敌帧影响,但是爆炸的范围伤害会受到无敌帧影响...同样的现象在星云烈焰上也能观察到(星云烈焰的爆炸是范围伤害,范围是50*50,单位是像素)。
 

AingTii

Lv3
LV
0
 
IP属地
未知
2019/01/30
11
23
  • · 发布于未知
参考来源:
1.抛射体的无敌帧部分主要来自Projectile.cs 中的 Damage函数。
2.近战武器的无敌帧部分主要来自Player.cs 中的 ItemCheck函数。

3.相关变量的更新主要来自于:
NPC.cs 中的 UpdateNPC函数
Projectile.cs 中的 Update函数
 

dcfhft

Lv4
管理成员
版主
LV
0
 
IP属地
未知
2018/08/28
142
38
勋章
4
  • · 发布于未知
支持!
 

乔伊榕宴

Lv3
LV
0
 
IP属地
未知
2019/08/23
7
3
勋章
1
  • · 发布于未知
是真的可以学东西,厉害
 

pinkie_pie

Lv2
LV
0
 
IP属地
未知
2019/09/04
3
0
  • · 发布于未知
感谢搬运!:wink:
 
顶部