前言

上一篇博文说道,射线与场景中模型上的所有三角形求交时,会大幅度影响效率且花费比较多的时间,因此会采取使用包围盒的形式,进行一个加速求交。在此文中介绍OBB碰撞模型的碰撞算法

OBB的碰撞模型

有没有想过为什么需要用到OBB模型呢,假设一个场景内两个人物相撞了,你怎么判断它们是否相撞呢,大概就是它们的碰撞体接触在了一起就相撞了。那怎么算碰撞在一起呢(此处只讨论2D规则的包围盒模型)?

方向包围盒OBB"("Oriented Bounding Box)是目前比较流行的一种包围盒,OBB最大的特点是其方向的任意性,这使得可以根据被包围的对象的形状特点尽可能紧密地包围对象,Unity中的BoxCollider的其实就是OBB模型,它不是轴对称模型,而是有方向的

OBB模型

与之相反的是AABB模型,是轴对称模型,即它的边一定与坐标轴平行,算法简单,但使用的局限性比较大,更多还是使用OBB模型

碰撞算法分析

想要判断两个OBB模型碰撞,也就是两个矩形相交,我们分为几个步骤,首先先转换问题,什么时候两个矩形不相交,两个矩形相离可看成有多个直线可将它们之间分开。

当逐渐移动某个矩形,使得某个时刻,两个矩形只有一个交点,交点属于矩形的某条边上,此时为临界状态,当且仅当只有一条直线将他们两个分开,此时这条直线必定与某条边平行。



所以我们只需找两个矩形的四条边分别作为轴,两个矩形的xVt、yVt分别进行投影,看投影后的两个线段是否相离,如果相离则在这个轴上可以将这两个进行分开,故此时两个矩形不相交,反之若相交,则继续接着其他轴进行判断,若所有轴都不能分开,则这两个矩形相交

我们观察 AB proj 与 boxA、boxB 的 xVt proj 、yVt proj 之间的关系,可以得出结论:

AB proj > sum(Vt proj) ,则矩形相离
AB proj = sum(Vt proj) ,则矩形相切
AB proj < sum(Vt proj) ,则矩形相交

参考网上某大佬的代码

参考博文:https://www.cnblogs.com/hont/p/9501169.html



分别取两个矩形的两个边,总共进行四次投影对称,作为对称轴

        axis1 = (P1 - P0).normalized;
axis2 = (P3 - P0).normalized; axis3 = (other.P1 - other.P0).normalized;
axis4 = (other.P3 - other.P0).normalized; mDebugInternalAxisIndex = 0; bool isNotIntersect = false;
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis1);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis2);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis3);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis4);

这里是取带符号的长度,用来比较投影后的线段是否相交

        float x_p0 = xProject_P0.magnitude * Mathf.Sign(Vector3.Dot(xProject_P0, axis));
float x_p1 = xProject_P1.magnitude * Mathf.Sign(Vector3.Dot(xProject_P1, axis));
float x_p2 = xProject_P2.magnitude * Mathf.Sign(Vector3.Dot(xProject_P2, axis));
float x_p3 = xProject_P3.magnitude * Mathf.Sign(Vector3.Dot(xProject_P3, axis));

相交判断:

        if (yMin >= xMin && yMin <= xMax) return false;
if (yMax >= xMin && yMax <= xMax) return false;

简约的示例图
using UnityEngine;
// OBB.cs
public class OBB : MonoBehaviour
{
public bool enableDebug;
public int debug_axisIndex;
int mDebugInternalAxisIndex; public Vector2 size; public Color gizmosColor = Color.white; Vector2 P0 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(-size * 0.5f); } }
Vector2 P1 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(size.x * 0.5f, -size.y * 0.5f, 0)); } }
Vector2 P2 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(size * 0.5f); } }
Vector2 P3 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(-size.x * 0.5f, size.y * 0.5f, 0)); } } Vector2 axis1, axis2, axis3, axis4; // 较参考博文添加以下变量,用来缓存向量减少gc
Vector3 xProject_P0;
Vector3 xProject_P1;
Vector3 xProject_P2;
Vector3 xProject_P3; Vector3 yProject_P0;
Vector3 yProject_P1;
Vector3 yProject_P2;
Vector3 yProject_P3; public bool Intersects(OBB other)
{
axis1 = (P1 - P0).normalized;
axis2 = (P3 - P0).normalized; axis3 = (other.P1 - other.P0).normalized;
axis4 = (other.P3 - other.P0).normalized; mDebugInternalAxisIndex = 0; bool isNotIntersect = false;
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis1);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis2);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis3);
isNotIntersect |= ProjectionIsNotIntersect(this, other, axis4); return isNotIntersect ? false : true;
} bool ProjectionIsNotIntersect(OBB x, OBB y, Vector2 axis)
{
xProject_P0 = Vector3.Project(x.P0, axis);
xProject_P1 = Vector3.Project(x.P1, axis);
xProject_P2 = Vector3.Project(x.P2, axis);
xProject_P3 = Vector3.Project(x.P3, axis); float x_p0 = xProject_P0.magnitude * Mathf.Sign(Vector3.Dot(xProject_P0, axis));
float x_p1 = xProject_P1.magnitude * Mathf.Sign(Vector3.Dot(xProject_P1, axis));
float x_p2 = xProject_P2.magnitude * Mathf.Sign(Vector3.Dot(xProject_P2, axis));
float x_p3 = xProject_P3.magnitude * Mathf.Sign(Vector3.Dot(xProject_P3, axis)); yProject_P0 = Vector3.Project(y.P0, axis);
yProject_P1 = Vector3.Project(y.P1, axis);
yProject_P2 = Vector3.Project(y.P2, axis);
yProject_P3 = Vector3.Project(y.P3, axis); float y_p0 = yProject_P0.magnitude * Mathf.Sign(Vector3.Dot(yProject_P0, axis));
float y_p1 = yProject_P1.magnitude * Mathf.Sign(Vector3.Dot(yProject_P1, axis));
float y_p2 = yProject_P2.magnitude * Mathf.Sign(Vector3.Dot(yProject_P2, axis));
float y_p3 = yProject_P3.magnitude * Mathf.Sign(Vector3.Dot(yProject_P3, axis)); float xMin = Mathf.Min(x_p0, x_p1, x_p2, x_p3);
float xMax = Mathf.Max(x_p0, x_p1, x_p2, x_p3);
float yMin = Mathf.Min(y_p0, y_p1, y_p2, y_p3);
float yMax = Mathf.Max(y_p0, y_p1, y_p2, y_p3); if (enableDebug)
{
if (debug_axisIndex == mDebugInternalAxisIndex)
{
Debug.DrawRay(Vector3.Project(x.P0, axis), Vector3.one * 0.1f);
Debug.DrawRay(Vector3.Project(x.P2, axis), Vector3.one * 0.1f); Debug.DrawRay(Vector3.Project(y.P0, axis), Vector3.one * 0.1f, Color.white * 0.9f);
Debug.DrawRay(Vector3.Project(y.P2, axis), Vector3.one * 0.1f, Color.white * 0.9f); Debug.DrawRay(Vector3.zero, Vector3.one * 0.1f, Color.black);
Debug.DrawRay(Vector3.zero, axis, Color.yellow);
Debug.DrawRay(xMin * Vector3.right, Vector3.one * 0.1f, Color.blue);
Debug.DrawRay(xMax * Vector3.right, Vector3.one * 0.1f, Color.cyan);
Debug.DrawRay(yMin * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f);
Debug.DrawRay(yMax * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f); Debug.Log("(yMin >= xMin && yMin <= xMax): " + (yMin >= xMin && yMin <= xMax) + " frame count: " + Time.frameCount);
Debug.Log("(yMax >= xMin && yMax <= xMax): " + (yMax >= xMin && yMax <= xMax) + " frame count: " + Time.frameCount);
Debug.Log("(xMin >= yMin && xMin <= yMax): " + (xMin >= yMin && xMin <= yMax) + " frame count: " + Time.frameCount);
Debug.Log("(xMax >= yMin && xMax <= yMax): " + (xMax >= yMin && xMax <= yMax) + " frame count: " + Time.frameCount);
}
mDebugInternalAxisIndex++;
} if (yMin >= xMin && yMin <= xMax) return false;
if (yMax >= xMin && yMax <= xMax) return false;
// 此处只需做两次判断即可,参考博文做了四次判断
// if (xMin >= yMin && xMin <= yMax) return false;
// if (xMax >= yMin && xMax <= yMax) return false; return true;
} void OnDrawGizmos()
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = gizmosColor;
Gizmos.DrawWireCube(Vector3.zero, new Vector3(size.x, size.y, 1f));
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// OBBTest.cs
public class OBBTest : MonoBehaviour
{
public OBB a;
public OBB b; void Update()
{
var isIntersects = a.Intersects(b);
if (isIntersects)
{
a.gizmosColor = Color.red;
b.gizmosColor = Color.red;
}
else
{
a.gizmosColor = Color.white;
b.gizmosColor = Color.white;
}
}
}

效果



游戏中的2D OBB碰撞模型的碰撞算法介绍和实践的更多相关文章

  1. 游戏中的AOI(Area of Interest)算法

    游戏中的AOI(Area of Interest)算法 游戏的AOI算法应该算作游戏的基础核心了,许多逻辑都是因为AOI进出事件驱动的,许多网络同步数据也是因为AOI进出事件产生的.因此,良好的AOI ...

  2. 游戏中的 2D 可见性

    转自:http://www.gameres.com/469173.html 拖动圆点转一圈,看看玩家都能看到些什么: 这个算法也能计算出给定光源所照亮的区域.对每条光线,我们可以构建出被照亮区域的光线 ...

  3. 如何在Cocos2D游戏中实现A*寻路算法(六)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  4. Unity 2D游戏开发教程之游戏中精灵的跳跃状态

    Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...

  5. 地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了

    地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了 四叉树对于区域查询,效率比较高. 原理图

  6. Unity3D 2D游戏中寻径算法的一些解决思路

    需求 unity3d的3d开发环境中,原生自带了Navigation的组件,可以很便捷快速的实现寻路功能.但是在原生的2d中并没有相同的功能. 现在国内很多手机游戏都有自动寻路的功能,或者游戏中存在一 ...

  7. 《MFC游戏开发》笔记九 游戏中的碰撞判定初步&怪物运动简单AI

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9374935 作者:七十一雾央 新浪微博:http:// ...

  8. Unity3D系列教程--使用免费工具在Unity3D中开发2D游戏 第一节

    声明:   本博客文章翻译类别的均为个人翻译,版权全部.出处: http://blog.csdn.net/ml3947,个人博客:http://www.wjfxgame.com. 译者说明:这是一个系 ...

  9. 《MFC游戏开发》笔记十 游戏中的碰撞检测进阶:地图类型&障碍物判定

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9394465 作者:七十一雾央 新浪微博:http:// ...

随机推荐

  1. C指针与二维数组

    先贴上完整的代码: #include<stdio.h> int main(int argc, char *argv[]){ int a[3] [5]={1,2,3,4,5,6,7,8,9, ...

  2. AmazonS3 使用AWS SDK for Java实现跨源资源共享 (CORS)

    CORS 配置 创建 CORS 配置并对存储桶设置该配置 通过添加规则来检索并修改配置 向存储桶添加修改过的配置 删除配置 import com.amazonaws.AmazonServiceExce ...

  3. Java并发编程之锁机制

    锁分类 悲观锁与乐观锁 悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改.因此对于同一个数据的并发操作,悲观锁采取加锁的形式.悲观的认为,不加锁的并发操作一定会出问题 ...

  4. PTA 求链式表的表长

    6-1 求链式表的表长 (10 分)   本题要求实现一个函数,求链式表的表长. 函数接口定义: int Length( List L ); 其中List结构定义如下: typedef struct ...

  5. [set]JZOJ 5821 手机信号

    Description

  6. 「HTML+CSS」--自定义按钮样式【004】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  7. UnboundLocalError: local variable 'foo' referenced before assignment Python常见错误

    在定义局部变量前在函数中使用局部变量(此时有与局部变量同名的全局变量存在) 在函数中使用局部变来那个而同时又存在同名全局变量时是很复杂的, 使用规则:如果在函数中定义了任何东西,如果它只是在函数中使用 ...

  8. 关于搭建FTP服务器

    首先我们创建一个用户账户用于登录FTP进行操作.右键点击桌面的我的点击选择管理选项,进入管理界面打开本地用户和组选项,我们可以看到列表中的用户选项 2 然后右键用户选项,在下拉菜单中选择新用户,开始建 ...

  9. 熟知Mysql基本操作

    本文是学习 Mysql必知必会 后的笔记 学习之前需要创建一个数据库,然后导入下面两个mysql脚本 create database db1 charset utf8; ############### ...

  10. OAuth 2.0 了解了,OAuth 2.1 呢?

    OAuth 2.0 OAuth 2.0 是工业级标准授权协议. OAuth 2.0 聚焦于客户端开发者便利性,为网页应用程序.桌面客户端.手机.客厅设备提供特定的授权流程. RFC6749 OAuth ...