原神NPC动作机制反推
前言
主角与NPC之间的交互,是虚拟世界中为NPC赋予灵性的重要环节。在传统RPG游戏中,往往通过对话、剧情演出等方式塑造NPC的人物形象,而在动作游戏中,NPC的动作表现受环境的反馈而对应变化也是其真实性的表现。
本文的主要内容是根据《原神》游戏内的实机表现,来反推其NPC动作系统中人物碰撞动作、人物受惊动作的实现机制。
从人物碰撞动作说起:
碰撞检测
首先,根据我对角色技能动作/表现动作的观察,原神的动作实现机制应该是网游中比较常见的Code Driven Locomotion,只是IK做的比较好,另外估计还自研了Root Motion Extractor将动画数据提取并应用到角色身上。这一方案下,对于角色位置信息的维护应该都是基于模型中心点来判断,那么关于角色碰撞大概率就是直接以空间距离来进行判定。
经测试,
案例1:从墙上往NPC头上跳/飞行时,如果按住方向轮盘触发输入则会触发碰撞动画,否则不会触发碰撞动画
案例2:当NPC主动朝你的方向走来并进入碰撞距离时,不会触发碰撞动画
案例3:当角色处于碰撞范围内时,朝任意方向发起跑步指令不会触发碰撞动画
案例4:以行走状态进行位移任何情况下不会触发碰撞动画
案例5: 处于特殊动作状态的NPC不受碰撞行为影响(如剧情任务中,蒙德城中受惊的NPC)
结合上面的测试案例,我们可以大胆推断如下几点: - 以一个巡逻士兵为例,Idle状态下无交互一定时间后进入Patrolling(巡逻)状态,这两个状态下如果受到碰撞进入RunInto(被碰撞)状态,如果被惊吓进入Threatened(被惊吓)状态,RunInto与Threatened状态下无交互一段时间后返回Idle状态或Patrolling状态。
写个伪代码的行为树实现: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26enum STATUS = {
Idle,
Patrolling,
RunInto,
Threatened
}
status = STATUS.Idle
Loop:
//RunInto和Threatened两种状态下不可交互
If status == STATUS.Idle or STATUS.Patrolling:
If RunInto():
status = STATUS.RunInto
PlayAnim(Anim.RunInto, playerPosition)
WaitForSeconds(3)
status = STATUS.Idle
Elif Threatened():
PlayAnim(Anim.Threatened, playerPosition)
status = STATUS.Threatened
WaitForSeconds(3)
status = STATUS.Idle
If status == STATUS.Idle:
If AsyncWaitForSeconds(someTime):
status = STATUS.Patrolling
Patrol()
- 当角色首次进入碰撞的范围时,若满足条件(角色处于奔跑状态),根据对方的位置改变自己的朝向并播放对应的动画(原神中有前向被撞和后向被撞两种动画)。伪代码:
1 | // 表示玩家是否首次进入碰撞区域 |
受惊吓的逻辑和被碰撞有些类似,条件是:
- 主角距NPC距离在X米内(目测3米)
- 主角面朝NPC(主角的前向180度角内)
- 主角进行攻击





