基础GamePlay知识-扇形和点/扇形和圆碰撞检测

将会持续更新gameplay的一些基础知识,一同学习。

扇形检测

扇形检测是Gameplay里面很常见的场景。比如荒野乱斗中,大部分的近战角色都是扇形攻击。在扇形范围内就认为是受击。
扇形检测只有两个参数,一个是扇形的角度一个是扇形的半径大小。
在这里插入图片描述

效果

在这里插入图片描述

获取鼠标朝向

技能必然是和鼠标朝向一致的,所以学习检测务必先学一下怎么得到鼠标朝向,以及得到朝向对应的旋转角度。
思路是利用Camera.main.ScreenPointToRay(Input.mousePosition)方法,能够得到相机朝向屏幕空间下某点的射线,从而得到射线的碰撞点,然后计算角色朝向碰撞点的方向,这时候碰撞点的横坐标x和轴坐标z是已知的,所以用反三角函数Atan2可以得到旋转到碰撞点的旋转角度。
值得一提的是Camera.main.ScreenPointToRay(Input.mousePosition)在诸如用户注视某个物品/3dui的交互上面都可以使用。

    private void GetSkillDirection(Color color)
    {
        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            //射线检测的是平面点的位置,实际需要的是平行于平面的方向,所以修改y坐标
            Vector3 horizontalPoint = new Vector3(hit.point.x, hit.point.y,0);
            Debug.DrawLine(transform.position, horizontalPoint, color);
            skillDirection = (horizontalPoint - transform.position).normalized;
            skillAngle = Mathf.Atan2(skillDirection.y, skillDirection.x) * Mathf.Rad2Deg;
        }
    }

尝试利用画出一个扇形

为了方便我们看检测的结果,先实现一个绘制扇形的工具将技能范围画出来,这里提供使用Debug.DrawLine和使用LineRender两种办法。需要注意如果用LineRender的SetPostition方法,需要在组件里面设置好Position数组的大小,防止出现数组越界的报错。
在这里插入图片描述
利用LineRender和Debug.DrawLine的思路是一样的,先绘制扇形的中心点以及左边界点,再从左边界点往另一个边界以某个角度间隔绘制一个个点,连接起来就是一个圆弧

    private void DrawFan_DebugDrawLine(float radius, int euler)
    {
        int segments = 100; //圆弧那一段需要用多少个点来表示
        float deltaAngle = euler * 1.0f / segments;

        Vector3[] vertices = new Vector3[segments + 2];
        vertices[0] = transform.position;
        for (int i = 1; i < vertices.Length; i++)
        {
            float curAngle = -euler / 2 + deltaAngle * (i - 1) + skillAngle;
            //从-1/2扇形角度开始绘制,每次偏移deltaAngle
            Vector3 pos = Quaternion.Euler(0f, 0f, curAngle) * transform.right * radius + vertices[0];
            vertices[i] = pos;
        }

        // 画圆弧
        for (int i = 1; i < vertices.Length - 1; i++)
        {
            Debug.DrawLine(vertices[i], vertices[i + 1], showColor);
        }

        // 画两条边
        Debug.DrawLine(vertices[0], vertices[vertices.Length - 1], showColor);
        Debug.DrawLine(vertices[0], vertices[1], showColor);

    }
    
    private void DrawFan_LineRender(float radius, int euler)
    {
        ResetLinerRenderPoints();
        m_LineRenderer.startColor = showColor;
        m_LineRenderer.endColor = showColor;
        m_LineRendererPoints.Add(transform.position);
        //每一度一个点,绘制思路和debug.DrawLine相同
        for (int angles = -euler / 2; angles <= euler / 2; angles++)
        {
            m_LineRendererPoints.Add(Quaternion.Euler(0,0, angles + skillAngle) * transform.right * radius + transform.position);
        }
        m_LineRenderer.SetPositions(m_LineRendererPoints.ToArray());
    }
    
    private void ResetLinerRenderPoints()
    {
        m_LineRendererPoints.Clear();
    }

扇形和点的碰撞检测

判断某个点在扇形内的办法
距离判断:
点和玩家的距离小于扇形半径。
角度判断:
设玩家到受击角色的向量为a,玩家技能朝向为b
方法一:判断a和b所形成的角度小于1/2的扇形夹角。
只需要拿到a和单位向量和b的单位向量进行点积,就能够得到a和b夹角的cos值,再利用Acos得到夹角大小。

设扇形左边界为left,扇形右边界为right
方法二:利用向量叉乘,如果a在扇形的左边界之外或扇形的右边界之外则不满足。
这里需要注意Unity的世界坐标系是左手系,叉乘的方向满足的是左手定则
a x left所得向量的y轴坐标大于0说明在左边界以左,
right x a所得向量的的y轴左边大于0说明在右边界以右

这里两个办法都实现一下

三角函数法

/// <summary>
/// 利用Acos检测扇形和点的碰撞
/// </summary>
/// <param name="fanPos">扇形中心位置</param>
/// <param name="fanRadius">扇形半径</param>
/// <param name="fanAngle">扇形圆心角</param>
/// <param name="fanDir">扇形中心朝向</param>
/// <param name="pointPos">点坐标</param>
/// <returns></returns>
    private bool Dectect_FanAndPoint_ACos(Vector3 fanPos, float fanRadius, float fanAngle, Vector3 fanDir, Vector3 pointPos)
    {
        float distance = Vector3.Distance(fanPos, pointPos);
        //距离超过检测半径
        if (distance > fanRadius)
        {
            return false;
        }
        Vector3 circleDir = (pointPos - fanPos).normalized;
        //两个单位向量的点乘等于其夹角的余弦值
        float enemyAngle = Mathf.Acos(Vector3.Dot(fanDir, circleDir)) * Mathf.Rad2Deg;
        //敌人朝向和技能朝向的夹角小于二分之一扇形角说明在扇形范围内
        if (enemyAngle <= fanAngle / 2)
        {
            return true;
        }
        return false;
    }

向量叉乘

    /// <summary>
    /// 利用叉乘检测扇形和点的碰撞
    /// </summary>
    /// <param name="enemyPos">敌人位置</param>
    /// <param name="leftBound">扇形左边界</param>
    /// <param name="RightBound">扇形右边界</param>
    /// <returns></returns>
    private bool Dectect_FanAndPoint_Cross(Vector3 fanPos, float fanRadius, float leftAngle, float RigthtAngle, Vector3 pointPos)
    {
        float distance = Vector3.Distance(fanPos, pointPos);
        //距离超过检测半径
        if (distance > fanRadius)
        {
            return false;
        }
        //扇形左边界
        Vector3 leftBound = Quaternion.Euler(0f, 0f, leftAngle) * Vector3.right;
        //扇形右边界
        Vector3 RightBound = Quaternion.Euler(0f, 0f, RigthtAngle) * Vector3.right;
        Vector3 circleDir = pointPos - fanPos;
        //注意左手系的叉乘是左手定则,2D情况下z是往里的
        bool isLeft = Vector3.Cross(circleDir, leftBound).z < 0 ? true : false;
        bool isRight = Vector3.Cross(RightBound, circleDir).z < 0 ? true : false;
        return !isLeft && !isRight;
    }

进阶:扇形和圆形的碰撞检测

效果

在这里插入图片描述

思路

我们根据圆心的位置进行分类讨论,前面说到可以根据叉乘来得知圆心在扇形的左侧以左或者是右侧以右,或者是中间
圆心在左侧以左或者右侧以右时,题目其实就化为圆心到线段的距离是否小于圆的半径
圆心在扇形中间时,可以视为是两个圆进行碰撞检测。
在这里插入图片描述

求点和线段的距离

这里参考了https://blog.csdn.net/zaffix/article/details/25160505的做法,思路很巧妙。
将P投影到AB方向上,投影小于零说明最短距离是PA,大于零说明P应该做垂线或者求PB,如果投影大小大于AB则PB为最短距离,否则PP’为最短距离。图示如下
在这里插入图片描述
求PP’可以利用叉乘的几何意义来做
在这里插入图片描述

求圆和圆之间的碰撞检测

这个就很简单了,不赘述
在这里插入图片描述

检测函数

 	/// <summary>
    /// 检测圆形和扇形的碰撞
    /// </summary>
    /// <param name="circlePos">圆心坐标</param>
    /// <param name="radius">半径</param>
    /// <param name="fanPos">扇形圆心坐标</param>
    /// <param name="fanRadius">扇形坐标</param>
    /// <param name="leftAngle">扇形左边界角度</param>
    /// <param name="RigthAngle">扇形右边界角度</param>
    /// <returns></returns>
    private bool Dectect_FanAndCircle(Vector3 circlePos, float radius, Vector3 fanPos, float fanRadius, float leftAngle, float RigthAngle)
    {
        Vector3 enemyDir = circlePos - fanPos;
        Vector3 leftBound = Quaternion.Euler(0f, 0f, leftAngle) * Vector3.right * fanRadius;
        //圆心在扇形左边
        bool isLeft = Vector3.Cross(enemyDir, leftBound).z < 0 ? true : false;
        if (isLeft)
        {
            return GetMinDistanceFromPointToLineSegment(circlePos, fanPos, fanPos + leftBound) > radius ? false : true;
        }
        Vector3 RightBound = Quaternion.Euler(0f, 0f, RigthAngle) * Vector3.right * fanRadius;
        bool isRight = Vector3.Cross(RightBound, enemyDir).z < 0 ? true : false;
        //圆心在扇形右边
        if (isRight)
        {
            return GetMinDistanceFromPointToLineSegment(circlePos, fanPos, fanPos + RightBound) > radius ? false : true;
        }
        //圆心在两者夹角
        return radius + fanRadius > Vector3.Distance(fanPos, circlePos);
    }
    /// <summary>
    /// 检测点和线段的碰撞
    /// </summary>
    private float GetMinDistanceFromPointToLineSegment(Vector3 point, Vector3 starPos, Vector3 endPos)
    {
        Vector3 start2Point = point - starPos;
        Vector3 line = Vector3.Normalize(endPos - starPos);
        float projection = Vector3.Dot(start2Point, line);
        //投影在线段起始位置的前面
        if (projection <= 0f)
        {
            return start2Point.magnitude;
        }
        //投影在线段终点位置的后面
        if (projection > start2Point.magnitude)
        {
            return (point - endPos).magnitude;
        }
        //投影在线段中间,等价于求点到直线的距离,利用叉乘法
        float area = Vector3.Cross(start2Point, endPos - starPos).magnitude; //求平行四边形面积
        float distance = area / (endPos - starPos).magnitude;
        return distance;
    }
  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE4中的Gameplay框架是一个强大的工具集,用于开发和实现游戏玩法和用户交互。该框架提供了许多功能和组件,以帮助游戏开发者快速构建出丰富、流畅的游戏体验。 UE4的Gameplay框架主要由以下几个方面组成: 1.输入系统:该系统可以处理各种输入设备的操作,如鼠标、键盘和游戏手柄。开发者可以轻松地设置和管理输入映射和按键绑定。此外,还提供了鼠标和手柄的即时响应和移动方向控制功能,使玩家能够自由操作游戏中的角色。 2.角色控制器:角色控制器是游戏玩家在游戏中扮演的角色,他们的控制是通过输入系统和蓝图来实现的。游戏玩家可以移动角色、执行动作、攻击敌人等。角色还可以通过动画系统实现自然的运动和交互。 3.人工智能:UE4的Gameplay框架提供了内置的人工智能系统,可以对NPC和敌人进行编程控制。开发者可以设置敌人的行为模式、路径寻找和攻击策略,让游戏中的敌人具有更真实和智能的表现。 4.物理模拟:UE4的Gameplay框架使用了物理引擎来实现真实的物理模拟效果,比如碰撞、重力和刚体运动等。这使开发者能够创建更真实和具有交互性的游戏世界,使玩家可以与环境进行互动。 总之,UE4的Gameplay框架提供了强大而灵活的工具,帮助开发者轻松地构建出丰富多样的游戏玩法和用户交互。无论是开发动作冒险游戏、射击游戏还是角色扮演游戏,该框架都能满足开发者的需求,并带来令人惊叹的游戏体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值