碰撞检测之OBB-OBB的SweepTest

提要

当物体在运动的时候,普通的每帧进行碰撞检测已经无法满足要求,比如子弹的运动


两帧的位置已经直接将中间的板子穿过了,所以 t 时刻和 t +1 时刻的检测都是失效的。这时候需要用到的就是sweep检测了。今天要处理的就是AABB的Sweep检测。



2D情况

如下图,当前位置是蓝色Box所在位置,目的位置是绿色框所在位置。



2D情况只用处理x,y方向的,利用SAP理论,分别在各个轴向计算可以移动的距离。代码如下

 public static Vector2 SweepTest(OBB from, OBB other, Vector2 movement)
{
        float deltaX = movement.x;
        float deltaY = movement.y;
        if (from.max.y > other.min.y && from.min.y < other.max.y)
        {
            float d1;


            if (deltaX > 0.0D && from.max.x <= other.min.x)
            {
                d1 = other.min.x - from.max.x;


                if (d1 < deltaX)
                {
                    deltaX = d1;
                }
            }
            else if (deltaX < 0.0D && from.min.x >= other.max.x)
            {
                d1 = other.max.x - from.min.x;


                if (d1 > deltaX)
                {
                    deltaX = d1;
                }
            }
        }


        if (from.max.x > other.min.x && from.min.x < other.max.x)
        {
            float d1;
            if (deltaY > 0f && from.max.y <= other.min.y)
            {
                d1 = other.min.y - from.max.y;
                if (d1 < deltaY)
                {
                    deltaY = d1;
                }
            }
            else if (deltaY < 0f && from.min.y >= other.max.y)
            {
                d1 = other.max.y - from.min.y;


                if (d1 > deltaY)
                {
                    deltaY = d1;
                }
            }
        }

        return Vector2(deltaX, deltaY);
}

输入是两个OBB,from是要运动的OBB,movement是要进行的位移,返回的是最终的位移。

简单说一下X方向的判断,

首先

if (from.max.y > other.min.y && from.min.y < other.max.y)

要判断的是两个OBB在Y方向的投影是否有重叠,如果没有就直接返回movement 的x分量,因为在X方向不可能发生碰撞。

接下来判断的是如果from在other的左边,看是否有足够的空间给它运动,没有的话直接贴到other的边边上。from在other的右边的情况做同样的检测。


3D情况

只要简单的扩展到3D情况就可以了。

    public static Vector3 SweepTest(Bounds from, Bounds other, Vector3 movement)
    {
        float deltaX = movement.x;
        float deltaY = movement.y;
        float deltaZ = movement.z;
        if (from.max.y > other.min.y && from.min.y < other.max.y && from.max.z > other.min.z && from.min.z < other.max.z)
        {
            float d1;

            if (deltaX > 0.0D && from.max.x <= other.min.x)
            {
                d1 = other.min.x - from.max.x;

                if (d1 < deltaX)
                {
                    deltaX = d1;
                }
            }
            else if (deltaX < 0.0D && from.min.x >= other.max.x)
            {
                d1 = other.max.x - from.min.x;

                if (d1 > deltaX)
                {
                    deltaX = d1;
                }
            }
        }

        if (from.max.x > other.min.x && from.min.x < other.max.x && from.max.z > other.min.z && from.min.z < other.max.z)
        {
            float d1;
            if (deltaY > 0f && from.max.y <= other.min.y)
            {
                d1 = other.min.y - from.max.y;
                if (d1 < deltaY)
                {
                    deltaY = d1;
                }
            }
            else if (deltaY < 0f && from.min.y >= other.max.y)
            {
                d1 = other.max.y - from.min.y;

                if (d1 > deltaY)
                {
                    deltaY = d1;
                }
            }
        }

        if (from.max.x > other.min.x && from.min.x < other.max.x && from.max.y > other.min.y && from.min.y < other.max.y)
        {
            float d1;

            if (deltaZ > 0.0D && from.max.z <= other.min.z)
            {
                d1 = other.min.z - from.max.z;

                if (d1 < deltaZ)
                {
                    deltaZ = d1;
                }
            }
            else if (deltaZ < 0.0D && from.min.z >= other.max.z)
            {
                d1 = other.max.z - from.min.z;

                if (d1 > deltaZ)
                {
                    deltaZ = d1;
                }
            }
        }

        return new Vector3(deltaX, deltaY, deltaZ);
    }


测试代码

using UnityEngine;
using System.Collections;
using NPhysX;
public class BoxBoxSweepTester : MonoBehaviour {

    public Vector3 direction;
    public float speed;
    public GameObject box;
    public GameObject box1;
    Box _box;
    Box _box1;
    // Use this for initialization
    void Start()
    {
        _box = new Box();
        _box1 = new Box();
        direction = Vector3.one;
    }

	
	// Update is called once per frame
	void Update () {
        Vector3 moveVector = speed * direction;
        Vector3 realMove = NSweepTests.SweepTest(box.GetComponent<BoxCollider>().bounds, box1.GetComponent<BoxCollider>().bounds, moveVector);
        box.transform.position += realMove;
    }
}


测试结果


参考

Swept AABB Collision Detection and Response - http://www.gamedev.net/page/resources/_/technical/game-programming/swept-aabb-collision-detection-and-response-r3084



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的基于obb的碰撞检测算法代码示例: ```cpp bool obbCollision(const OBB& obb1, const OBB& obb2) { // 计算两个obb的中心点距离 Vector3d t = obb2.center - obb1.center; // 对obb1的三个轴进行遍历 for (int i = 0; i < 3; i++) { // 计算obb1在当前轴上的投影长度 float r1 = obb1.axis[i].dot(obb1.half_size); // 计算obb2在当前轴上的投影长度 float r2 = obb2.axis[i].dot(obb2.half_size); // 两个obb在当前轴上的投影长度之和 float r = r1 + r2; // 中心点距离在当前轴上的投影长度 float t_len = abs(t.dot(obb1.axis[i])); // 如果中心点距离在当前轴上的投影长度大于两个obb在当前轴上的投影长度之和,则没有碰撞 if (t_len > r) { return false; } } // 对obb2的三个轴进行遍历 for (int i = 0; i < 3; i++) { // 计算obb1在当前轴上的投影长度 float r1 = obb1.axis[i].dot(obb1.half_size); // 计算obb2在当前轴上的投影长度 float r2 = obb2.axis[i].dot(obb2.half_size); // 两个obb在当前轴上的投影长度之和 float r = r1 + r2; // 中心点距离在当前轴上的投影长度 float t_len = abs(t.dot(obb2.axis[i])); // 如果中心点距离在当前轴上的投影长度大于两个obb在当前轴上的投影长度之和,则没有碰撞 if (t_len > r) { return false; } } // 对obb1和obb2的叉积轴进行遍历 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { // 叉积轴 Vector3d axis = obb1.axis[i].cross(obb2.axis[j]); // 计算obb1和obb2在当前轴上的投影长度之和 float r = obb1.axis[i].dot(obb1.half_size) + obb2.axis[j].dot(obb2.half_size); // 中心点距离在当前轴上的投影长度 float t_len = abs(t.dot(axis)); // 如果中心点距离在当前轴上的投影长度大于两个obb在当前轴上的投影长度之和,则没有碰撞 if (t_len > r) { return false; } } } // 所有轴都没有发现碰撞,则认为两个obb发生了碰撞 return true; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值