1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 12:45:29 +08:00

finish translation

This commit is contained in:
aillieo
2017-07-03 13:37:30 +08:00
parent ff28c1cf4e
commit e6667c0094
2 changed files with 89 additions and 98 deletions

View File

@@ -6,20 +6,20 @@
| 翻译 | [aillieo](https://github.com/aillieo) |
| 校对 | 暂无 |
当试图判断两个物体之间是否有碰撞发生时,我们通常不使用物体本身的数据,因为这些物体常常会很复杂,这将导致碰撞检测变得很复杂。正因这一点,使用**重叠**在物体上的更简单的外形(通常有较简单明确的数学定义)来进行碰撞检测成为常用的方法。我们基于这些简单的外形来检测碰撞,这样代码会变得更简单且节省了很多性能。这些碰撞外形例如圆、球体、长方形和立方体等,与拥有上百个三角形的网格相比简单了很多。
当试图判断两个物体之间是否有碰撞发生时,我们通常不使用物体本身的数据,因为这些物体常常会很复杂,这将导致碰撞检测变得很复杂。正因这一点,使用**重叠**在物体上的更简单的外形(通常有较简单明确的数学定义)来进行碰撞检测成为常用的方法。我们基于这些简单的外形来检测碰撞,这样代码会变得更简单且节省了很多性能。这些<def>碰撞外形</def>例如圆、球体、长方形和立方体等,与拥有上百个三角形的网格相比简单了很多。
虽然它们确实提供了更简单更高效的碰撞检测算法,但这些简单的碰撞外形拥有一个共同的缺点,这些外形通常无法完全包裹物体。产生的影响就是当检测到碰撞时,实际的物体并没有真正的碰撞。必须记住的是这些外形仅仅是真实外形的近似。
## AABB - AABB 碰撞
AABB代表的是与坐标轴对齐的边界框(bounding box)边界框是指与场景基础坐标轴2D中的是x和y轴对齐的长方形的碰撞外形。与坐标轴对齐意味着这个长方形没有经过旋转并且它的边线和场景中基础坐标轴平行例如左右边线和y轴平行。这些边界框总是和场景的坐标轴平行这使得所有的计算都变得更简单。下边是我们用一个AABB包裹一个球对象物体
AABB代表的是<def>与坐标轴对齐的边界框(bounding box)</def>边界框是指与场景基础坐标轴2D中的是x和y轴对齐的长方形的碰撞外形。与坐标轴对齐意味着这个长方形没有经过旋转并且它的边线和场景中基础坐标轴平行例如左右边线和y轴平行。这些边界框总是和场景的坐标轴平行这使得所有的计算都变得更简单。下边是我们用一个AABB包裹一个球对象物体
![](../../../img/06/Breakout/05/02/collisions_ball_aabb.png)
Breakout中几乎所有的物体都是基于长方形的物体因此很理所应当地使用与坐标系对齐的边界框来进行碰撞检测。这就是我们接下来要做的。
有多种方式来定义与坐标轴对齐的边界框。其中一种定义AABB的方式是获取左上角点和右下角点的位置。我们定义的GameObject类已经包含了一个左上角点位置它的Position vector并且我们可以通过把左上角点的矢量加上它的尺寸Position` +`Size很容易地计算出右下角点。每个GameObject都包含一个AABB我们可以高效地使用它们碰撞。
有多种方式来定义与坐标轴对齐的边界框。其中一种定义AABB的方式是获取左上角点和右下角点的位置。我们定义的<fun>GameObject</fun>类已经包含了一个左上角点位置它的Position vector并且我们可以通过把左上角点的矢量加上它的尺寸<fun>Position</fun> + <fun>Size</fun>)很容易地计算出右下角点。每个<fun>GameObject</fun>都包含一个AABB我们可以高效地使用它们碰撞。
那么我们如何判断碰撞呢当两个碰撞外形进入对方的区域时就会发生碰撞例如定义了第一个物体的碰撞外形以某种形式进入了第二个物体的碰撞外形。对于AABB来说很容易判断因为它们是与坐标轴对齐的对于每个轴我们要检测两个物体的边界在此轴向是否有重叠。因此我们只是简单地检查两个物体的水平边界是否重合以及垂直边界是否重合。如果水平边界**和**垂直边界都有重叠那么我们就检测到一次碰撞。
@@ -28,26 +28,26 @@ Breakout中几乎所有的物体都是基于长方形的物体因此很理所
将这一概念转化为代码也是很直白的。我们对两个轴都检测是否重叠,如果都重叠就返回碰撞:
```
```c++
GLboolean CheckCollision(GameObject &one, GameObject &two) // AABB - AABB collision
{
// Collision x-axis?
// x轴方向碰撞
bool collisionX = one.Position.x + one.Size.x >= two.Position.x &&
two.Position.x + two.Size.x >= one.Position.x;
// Collision y-axis?
// y轴方向碰撞
bool collisionY = one.Position.y + one.Size.y >= two.Position.y &&
two.Position.y + two.Size.y >= one.Position.y;
// Collision only if on both axes
// 只有两个轴向都有碰撞时才碰撞
return collisionX && collisionY;
}
```
我们检查第一个物体的最右侧是否大于第二个物体的最左侧**并且**第二个物体的最右侧是否大于第一个物体的最左侧;垂直的轴向与此相似。如果您无法顺利地将这一过程可视化,可以尝试在纸上画边界线/长方形来自行判断。
为更好地组织碰撞的代码,我们在Game类中加入一个额外的函数:
为更好地组织碰撞的代码,我们在<fun>Game</fun>类中加入一个额外的函数:
```
```c++
class Game
{
public:
@@ -56,11 +56,9 @@ class Game
};
```
我们可以使用DoCollisions来检查球与关卡中的砖块是否发生碰撞。如果检测到碰撞就将砖块的Destroyed属性设为`true`,此举会停止关卡中对此砖块的渲染。
我们可以使用<fun>DoCollisions</fun>来检查球与关卡中的砖块是否发生碰撞。如果检测到碰撞,就将砖块的<fun>Destroyed</fun>属性设为<var>true</var>,此举会停止关卡中对此砖块的渲染。
```
```c++
void Game::DoCollisions()
{
for (GameObject &box : this->Levels[this->Level].Bricks)
@@ -77,15 +75,15 @@ void Game::DoCollisions()
}
```
接下来我们需要更新Game的Update函数:
接下来我们需要更新<fun>Game</fun>的<fun>Update</fun>函数:
```
```c++
void Game::Update(GLfloat dt)
{
// Update objects
// 更新对象
Ball->Move(dt, this->Width);
// Check for collisions
// 检测碰撞
this->DoCollisions();
}
```
@@ -102,7 +100,7 @@ void Game::Update(GLfloat dt)
![](../../../img/06/Breakout/05/02/collisions_ball_aabb_touch.png)
使用圆形碰撞外形而不是AABB来代表球会更合乎常理。因此我们在球对象中包含了Radius变量为了定义圆形碰撞外形我们需要的是一个位置矢量和一个半径。
使用圆形碰撞外形而不是AABB来代表球会更合乎常理。因此我们在球对象中包含了<var>Radius</var>变量,为了定义圆形碰撞外形,我们需要的是一个位置矢量和一个半径。
![](../../../img/06/Breakout/05/02/collisions_circle.png)
@@ -114,18 +112,23 @@ void Game::Update(GLfloat dt)
![](../../../img/06/Breakout/05/02/collisions_aabb_circle.png)
首先我们要获取球心\(\bar{C}\)与AABB中心\(\bar{B}\)的矢量差\(\bar{D}\)。接下来用AABB的半边长(half-extents)\(w\)和\(\bar{h}\)来限制(clamp)矢量\(\bar{D}\)。长方形的半边长是指长方形的中心到它的边的距离简单的说就是它的尺寸除以2。这一过程返回的是一个总是位于AABB的边上的位置矢量除非圆心在AABB内部
首先我们要获取球心\(\bar{C}\)与AABB中心\(\bar{B}\)的矢量差\(\bar{D}\)。接下来用AABB的半边长(half-extents)\(w\)和\(\bar{h}\)来<def>限制(clamp)</def>矢量\(\bar{D}\)。长方形的半边长是指长方形的中心到它的边的距离简单的说就是它的尺寸除以2。这一过程返回的是一个总是位于AABB的边上的位置矢量除非圆心在AABB内部
限制运算把一个值**限制**在给定范围内,并返回限制后的值。通常可以表示为:
`
float clamp(float value, float min, float max) {
return std::max(min, std::min(max, value));
}
`
!!! Important
限制运算把一个值**限制**在给定范围内,并返回限制后的值。通常可以表示为:
```
float clamp(float value, float min, float max) {
return std::max(min, std::min(max, value));
}
```
例如,值<var>42.0f</var>被限制到<var>6.0f</var>和<var>3.0f</var>之间会得到<var>6.0f</var>;而<var>4.20f</var>会被限制为<var>4.20f</var>。
限制一个2D的矢量表示将其<var>x</var>和<var>y</var>分量都限制在给定的范围内。
例如,值`42.0f`被限制到`6.0f``3.0f`之间会得到`6.0f`;而`4.20f`会被限制为`4.20f`
限制一个2D的矢量表示将其`x``y`分量都限制在给定的范围内。
这个限制后矢量\(\bar{P}\)就是AABB上距离圆最近的点。接下来我们需要做的就是计算一个新的差矢量\(\bar{D'}\),它是圆心\(\bar{C}\)和\(\bar{P}\)的差矢量。
@@ -137,34 +140,34 @@ float clamp(float value, float min, float max) {
这一过程通过下边的代码来表示:
```
```c++
GLboolean CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
{
// Get center point circle first
// 获取圆的中心
glm::vec2 center(one.Position + one.Radius);
// Calculate AABB info (center, half-extents)
// 计算AABB的信息中心、半边长
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(
two.Position.x + aabb_half_extents.x,
two.Position.y + aabb_half_extents.y
);
// Get difference vector between both centers
// 获取两个中心的差矢量
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// Add clamped value to AABB_center and we get the value of box closest to circle
// AABB_center加上clamped这样就得到了边界框上距离圆最近的点closest
glm::vec2 closest = aabb_center + clamped;
// Retrieve vector between center circle and closest point AABB and check if length <= radius
// 获得圆心center和最近点closest的矢量并判断是否 length <= radius
difference = closest - center;
return glm::length(difference) < one.Radius;
}
```
我们创建了CheckCollision的一个重载函数用于专门处理一个BallObject和一个GameObject的情况。因为我们并没有在对象中保存碰撞外形的信息因此我们必须为其计算首先计算球心然后是AABB的半边长及中心。
我们创建了<fun>CheckCollision</fun>的一个重载函数用于专门处理一个<fun>BallObject</fun>和一个<fun>GameObject</fun>的情况。因为我们并没有在对象中保存碰撞外形的信息因此我们必须为其计算首先计算球心然后是AABB的半边长及中心。
使用这些碰撞外形的参数,我们计算出矢量差\(\bar{D}\)然后得到限制后的值并与AABB中心相加得到最近的点\(\bar{P}\)。然后计算出圆心和最近点的矢量差\(\bar{D'}\)并返回两个外形是否碰撞。
使用这些碰撞外形的参数,我们计算出<var>difference</var>\(\bar{D}\)然后得到限制后的值<var>clamped</var>并与AABB中心相加得到<var>closest</var>\(\bar{P}\)。然后计算出<var>center</var><var>closest</var>的矢量差\(\bar{D'}\)并返回两个外形是否碰撞。
之前我们调用CheckCollision时将球对象作为其第一个参数因此现在CheckCollision的重载变量会自动生效我们无需修改任何代码。现在的结果会比之前的碰撞检测算法更准确。
之前我们调用<fun>CheckCollision</fun>时将球对象作为其第一个参数,因此现在<fun>CheckCollision</fun>的重载变量会自动生效,我们无需修改任何代码。现在的结果会比之前的碰撞检测算法更准确。
<video src="../../../../img/06/Breakout/05/02/collisions_circle.mp4" controls="controls"></video>