前言:我们在做动作游戏的时候常常会进行攻击检测,又根据不同的攻击类型实现不同的检测方式,我整理了比较常见的攻击检测逻辑以供学习

首先:动作游戏的攻击框架(无论何种攻击)都需要遵循以下特点

1,攻击时间

2,命中放信息

3,过滤自身,不可被攻击的物体,重复的攻击

4,生成HitData(攻击信息)

5,传输信息到被攻击方处理格挡,防御,护盾,扣血等等

常见刀剑攻击

常见碰撞体触发器检测攻击,攻击逻辑为

由weaponHibox(剑检测碰撞)

检测到HurBox(检测到碰撞到的物体)

生成HitData(攻击数据,结构体,用于计算攻击结果)

DamageReceiver(用于处理被攻击事件,常见于敌人身上)

接下来逐步拆解步骤

1,HitData:攻击信息包

此信息包主要包括

//攻击者

//基础伤害

//被命中位置的坐标,用于生成特效

//攻击者指向被攻击者的方向,用于击退效果

//击退力度,用于数值处理

攻击信息包为结构体,方便调用

2,DamageReceiver:真正处理生命值的事件,挂于被攻击方主体上

负责处理角色的流血,受伤,死亡状态

////基本变量

基本逻辑为:

最大生命值

当前生命值(对外提供但只读加密)

是否死亡(对外提供但只读加密)

是否进入无敌状态

///运行逻辑方法

//awake

游戏开始时恢复最大生命值

public bool TryReceiveHit(HitData hitData)

传入受伤害数据包进行检测,并处理承受攻击,首先检测角色是否处于死亡或着无敌状态,

限制伤害不可为复数以免增加生命值

处理受伤扣血,并限制血量不会小于0

根据位置播放命中特效

根据方向进行击退

检测死亡

命中特效播放方法

根据传入位置调用对象池播放特效

击退方法,传入方向和数值

建议移动方法用角色控制器

死亡方法

将检测死亡bool值设置

后续可播放死亡动画

设置无敌状态,传入一个bool值进行赋值

3,Hurtbox:敌人的受击部位

这个脚本用于碰撞检测不同的受击部位

在不同的位置放置此脚本,用于检测不同位置的伤害处理

///变量

该位置处于哪个DamageReceiver,也就是说受到攻击后在哪进行扣血判定,属于哪个角色的

该位置伤害倍率

加密所属角色信息,外部只能只读不可修改

方法详解

private void Awake()

用于初始化加载,父类查找DamageReceiver进行赋值

接受原始数据,并根据部位伤害进行伤害计算然后转入

新建一个HitData将原始数据赋值赋值,以免污染原始数据

将处理后的数据传入DamageReceiver的TryReceiveHit进行攻击判定以及死亡判定

4,WeaponHitbox:完整武器检测代码

此脚本放在武器上用于检测触发来实现攻击掉血机制

///基础变量

攻击者跟节点的位置,用于根据根节点来过滤碰撞避免重复攻击

用于检测的Collider

攻击基础伤害

攻击击退力度

允许攻击的层,为了避免多次物体进入触发器扰乱代码逻辑

bool值检测,当前是否可以攻击,避免在非公机时期触碰到角色出现攻击事件

private readonly HashSet<DamageReceiver> hitTargets = new();

新建一个HashSet结构列表用于过滤进入的物体,防止一次攻击多次触发同一个角色的受击脚本

自动获取物体的Collider

private void Awake()

再次尝试自动获取

检测Trigger是否开启,如果没开启将其开启

关闭碰撞体,以免莫名其妙发生触碰

public void BeginAttack()

攻击动画帧刚开始时调用,清除上一刀数据,将攻击bool值设置为true,开启剑的Trigger

 public void EndAttack()

攻击动画结束时调用,设置不可以攻击,设置trigger禁用

private void OnTriggerEnter(Collider other)

剑体触发检测代码

保险判断,是否处于可以攻击状态

进行层检测以免非可攻击层物体进入碰撞体

重点代码:

Hurtbox hurtbox = other.GetComponent<Hurtbox>();

前面说了在身体每个部位放置HurBox脚本用来检测被攻击,在这里获取到被攻击位置的HurtBox

检测,如果HurtBox为空代表不是有效的受击位置

DamageReceiver target = hurtbox.Owner;

获取到被攻击的角色

if (attackerRoot != null &&
            target.transform.root == attackerRoot.root)
        {
            return;
        }

需要过滤攻击者自身,因为手拿着剑,以免发生触碰,检测如果根节点相同则退出

 if (!hitTargets.Add(target))
            return;

上面我们说了,避免重复一刀出现多次伤害判定,将主题传入,如果失败则代表以及受到攻击

 Vector3 hitPoint = other.ClosestPoint(
            hitboxCollider.bounds.center
        );

传入剑上的碰撞体中心的位置,在角色的位置身上找到距离碰撞点最近的位置

 HitData hitData = new HitData
        {
            attacker = attackerRoot.gameObject,
            damage = baseDamage,
            hitPoint = hitPoint,
            hitDirection = hitDirection,
            knockback = knockback
        };

创建初始攻击数据包

发起攻击者

基础伤害

被击打位置

击打方向

击退力度

5,通过动画事件控制攻击时间

public class PlayerCombatAnimationEvent : MonoBehaviour

这个脚本出要处理动画在运行至一定阶段后出现的事件控制,比如我们之前在武器的脚本中写了开始攻击和结束攻击的脚本用于控制动画状态,在unity中一般在角色的动画位置打关键帧后将事件拖动上去便于运行,为了降低代码的耦合性,我们将此类事件单独为一个脚本

主要变量:

放置攻击检测的武器本身

主要方法

攻击动画开始播放帧方法,调用攻击检测脚本的BeginAttack方法设置开启可攻击状态

攻击动画结束播放帧方法,调用攻击检测脚本的EndAttack方法关闭攻击状态

6,一次攻击的完整执行

玩家播放攻击动画
    ↓
动画进入第8帧
    ↓
AnimationEvent_BeginAttack
    ↓
WeaponHitbox.BeginAttack
    ↓
清空 hitTargets
    ↓
isAttacking = true
    ↓
开启 hitboxCollider
    ↓
敌人 Body Collider 进入
    ↓
OnTriggerEnter(other)
    ↓
检查 Layer
    ↓
找到 Hurtbox
    ↓
取得 DamageReceiver
    ↓
过滤攻击者自己
    ↓
HashSet 判断是否重复
    ↓
计算 hitPoint
    ↓
计算 hitDirection
    ↓
创建 HitData
    ↓
Hurtbox 添加部位倍率
    ↓
DamageReceiver 扣血
    ↓
动画进入第16帧
    ↓
AnimationEvent_EndAttack
    ↓
关闭 hitboxCollider

这就是动作游戏攻击的基本逻辑需求,在逻辑上更加严谨低耦合,核心主旨为多判定,低耦合,方便添加与修改

更多推荐