自学内容网 自学内容网

unity 2d 近战攻击判定的三种方式以及精确获取碰撞点

精确获取碰撞点

核心是获取武器碰撞盒最顶点,然后获取敌人碰撞盒距离该点最近的点

/// <summary>
    /// 获取获取武器前端位置   碰撞盒最左或最右顶点
    /// </summary>
    /// <param name="collider"></param>
    /// <param name="facingRight"></param>
    /// <returns></returns>
    public static Vector3 GetCollider2DEdgePoint(Transform deployer, bool facingRight)
    {
        var collider = deployer.GetChild(0).GetComponentInChildren<CapsuleCollider2D>();
        // 获取碰撞器的包围盒
        Bounds bounds = collider.bounds;

        // 计算面向的方向向量(右或左)
        Vector2 attackDirection = facingRight ? Vector2.right : Vector2.left;

        // 根据面向方向和碰撞器的边界来获取边缘点
        Vector3 edgePoint = bounds.center + (Vector3)(attackDirection * bounds.extents.x);

        // 如果需要在局部空间中获取边缘点,需要将世界坐标转换为局部坐标
        // edgePoint = deployer.InverseTransformPoint(edgePoint);

        return edgePoint;
    }

//使用
 public void CreateHit(Collider2D other, CharacterStats target, bool isCrit = false)
 Vector3 edgePoint = Tool.GetCollider2DEdgePoint(transform, Entity.facingRight);
        var hitPosition = other.ClosestPoint(edgePoint);

1. 给攻击帧添加碰撞盒

优点:配置直观,无需事件触发
缺点:无法定制,效率低

检测放在子物体,可以控制旋转
在这里插入图片描述

添加触发器事件

注意OnTriggerEnter2D只会在挂载了collider的组件上触发

protected virtual void OnTriggerEnter2D(Collider2D other)
    {
        Debug.Log(this.name + "命中" + other);
        entity.AttackTrigger(other);
    }
 
在角色脚本执行攻击检测
 public override void AttackTrigger(Collider2D other)
    {
        if (other.gameObject.layer == LayerMask.NameToLayer("Enemy"))
        {
            base.AttackTrigger(other);
            AttackColliderCheck<Enemy>(other, (enemy) => enemy.BeDamage(stats));
        }
    }

2. 为每个攻击动画状态使用射线检测

优点:效率高于碰撞盒,可以定制,比如增加攻击范围
缺点:配置麻烦不直观

部分代码,仅供参考

定义每个动画的检测范围
[Header("攻击状态对应的碰撞盒")]
        public Dictionary<Entity.States, AttackRanges> attackRangeDict = new();

//索引是攻击的状态 + 连击数,获取对应攻击的范围
[System.Serializable]
public class AttackRanges
{
    /// <summary>
    /// 对应的连击次数  作为索引
    /// </summary>
    public AttackRange[] ranges = new AttackRange[1];

    //索引器
    public AttackRange this[int index]
    {
        get
        {
            // return ranges.FirstOrDefault(x => x.combo == index);
            //如果没有,默认用第一个
            if (index >= ranges.Length)
                return ranges[0];
            return ranges[index];
        }
    }
}


[System.Serializable]
public class AttackRange
{
    public SerializableVector2 offset = new();
    public SerializableVector2 size = new();
    public float rotation = 0;

    public UnityEngine.Vector2 Offset => offset.ToVector2();
    public UnityEngine.Vector2 Size => size.ToVector2();
}
添加帧事件
private void AttackTrigger()
    {
        entity.AttackTrigger();
    }
执行检测与绘制范围
 /// <summary>
    /// 攻击检测
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="action"></param>
    protected void AttackColliderCheck<T>(Action<T> action) where T : Entity
    {
        AttackRange attackRange = stats.attackRangeDict[States.PrimaryAttack][ComboCounter];
        var offset = facingRight ? attackRange.offset.ToVector2() : new Vector2(-attackRange.offset.x, attackRange.offset.y);
        var rotation = facingRight ? attackRange.rotation : -attackRange.rotation;

        Collider2D[] colliders = Physics2D.OverlapBoxAll(offset, attackRange.size.ToVector2(), rotation);

        foreach (Collider2D hit in colliders)
        {
            if (hit.TryGetComponent<T>(out T entity))
            {
                action?.Invoke(entity);
            }
        }
    }

// 默认情况下就会在编辑模式下运行
    protected virtual void OnDrawGizmos()
    {
        // Vector3 boxCenter = groundCheck.position + Vector3.down * groundCheckDistance / 2;
        // Gizmos.DrawCube(boxCenter, new Vector3(groundCheckWidth, groundCheckDistance, 0));
        Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。
        Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));//绘制一条从 from(前面的) 开始到 to(后面的) 的线。

        if (stats == null) return;
        Gizmos.color = Color.red;
        var attackRange = stats.attackRangeDict.ContainsKey(States.PrimaryAttack) ? stats.attackRangeDict?[States.PrimaryAttack]?[ComboCounter] : new AttackRange();

        Vector3 center = (Vector3)attackRange.Offset;
        // 设置 Gizmos 的变换矩阵
        Matrix4x4 oldMatrix = Gizmos.matrix;
        Gizmos.matrix = Matrix4x4.TRS(center, Quaternion.Euler(0, 0, attackRange.rotation), Vector3.one);
        Gizmos.DrawWireCube(transform.position + (Vector3)attackRange.Offset, (Vector3)attackRange.Size);
        // 恢复原来的矩阵
        Gizmos.matrix = oldMatrix;
    }

3. 所有动画共用一个射线检测

优点:简单
缺点:不精确

不推荐使用

在帧事件触发,调用检测方法即可
 /// <summary>
    /// 攻击检测
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="action"></param>
    protected void AttackColliderCheck<T>(Action<T> action) where T : Entity
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(attackCheck.position, attckCheckRadius);

        foreach (Collider2D hit in colliders)
        {
            if (hit.TryGetComponent<T>(out T entity))
            {
                action?.Invoke(entity);
            }
        }
    }

原文地址:https://blog.csdn.net/zhuanggenhua/article/details/142700437

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!