Beginners Direct X (8)

Beginners Direct X (8)

--Impact checking

What really makes a game stand out is how well the program responds to collisions. Here are two detection methods:
1) Border-based collision detection
2) Distance-based collision detection

1. Border-based collision detection

1.1 The basis of implementation

We regard the two objects (bitmaps) to be detected as two rectangles, and on this basis, detect whether the two rectangles have overlapping areas. If there is an overlapping area, a collision occurs; otherwise, there is no. Detect whether two rectangles overlap to the Windows API, namely IntersectRect , which is defined as follows:

BOOL IntersectRect(
  _Out_       LPRECT lprcDst,
  _In_  const RECT   *lprcSrc1,
  _In_  const RECT   *lprcSrc2
);

lprcDst stores the rectangular area where lprcSrc1 and lprcSrc2 overlap, but we don't care about this, only the return value of this function. If there is an overlapping area, the return value is non-0; otherwise, it is 0.
Based on this, the function that can achieve collision detection is as follows: ( For the definition of SPRITE, see here )

bool Collision(SPRITE sprite1, SPRITE sprite2){
    RECT rect1;
    rect1.left = (long)sprite1.x;
    rect1.top = (long)sprite1.y;
    rect1.right = (long)sprite1.x + sprite1.width * sprite1.scaling;
    rect1.bottom = (long)sprite1.y + sprite1.height * sprite1.scaling;

    RECT rect2;
    rect2.left = (long)sprite2.x;
    rect2.top = (long)sprite2.y;
    rect2.right = (long)sprite2.x + sprite2.width * sprite2.scaling;
    rect2.bottom = (long)sprite2.y + sprite2.height * sprite2.scaling;

    RECT dest; //ignored
    return IntersectRect(&dest, &rect1, &rect2);
}

1.2 Implementation

We implement a scenario where a spacecraft is flying in an asteroid, where the asteroid is in constant motion. Object detection is actually used, and obviously there is more than one object in the scene. At this time, we need to use a structure (or class) to express an object, like the following:
 

//sprite structure
struct SPRITE
{
    float x, y;                 // 在窗口中显示的位置
    int frame, columns;         // 在位图表中的位置信息
    int width, height;          // 同上,单位位图的尺寸
    float scaling, rotation;    // 放缩比,旋转角度
    int startframe, endframe;   // 在位图表中的位置信息
    int starttime, delay;       // 用于帧率显示
    int direction;              // 帧数前进单位
    float velx, vely;           // 位移单位
    D3DCOLOR color;

    SPRITE()
    {
        frame = 0;
        columns = 1;
        width = height = 0;
        scaling = 1.0f;
        rotation = 0.0f;
        startframe = endframe = 0;
        direction = 1;
        starttime = delay = 0;
        velx = vely = 0.0f;
        color = D3DCOLOR_XRGB(255, 255, 255);
    }
};

After declaring an object structure, we need to load the bitmap:

fatship.tga
fatship.tga

asteroid.tga
asteroid.tga

// 全局变量
SPRITE ship, asteroid1, asteroid2;
LPDIRECT3DTEXTURE9 imgShip;
LPDIRECT3DTEXTURE9 imgAsteroid;

--------------------------------
--------------------------------

// Game_Init()
// 初始化物体,包括运动状态以及在窗口中显示位置
imgShip = LoadTexture("fatship.tga");
imgAsteroid = LoadTexture("asteroid.tga");
//set properties for sprites
ship.x = 450;
ship.y = 300;
ship.width = ship.height = 128;

asteroid1.x = 50;
asteroid1.y = 200;
asteroid1.width = asteroid1.height = 60;
asteroid1.columns = 8;
asteroid1.startframe = 0;
asteroid1.endframe = 63;
asteroid1.velx = -2.0f;

asteroid2.x = 900;
asteroid2.y = 500;
asteroid2.width = asteroid2.height = 60;
asteroid2.columns = 8;
asteroid2.startframe = 0;
asteroid2.endframe = 63;
asteroid2.velx = 2.0f;

--------------------------------
--------------------------------

// Game_Run()
// 检测外设的输入
if (Key_Down(DIK_W))
{
    ship.y -= 1.0f;
    if (ship.y < 0) 
        ship.y = 0;
}
if (Key_Down(DIK_S))
{
    ship.y += 1.0f;
    if (ship.y > SCREENH - ship.height)
        ship.y = SCREENH - ship.height;
}
//小行星和飞船的移动
asteroid1.x += asteroid1.velx;
// 到达边界,返回
if (asteroid1.x < 0 || asteroid1.x > SCREENW - asteroid1.width)
    asteroid1.velx *= -1;
Sprite_Animate(asteroid1.frame, asteroid1.startframe, asteroid1.endframe, asteroid1.direction, asteroid1.starttime, asteroid1.delay);

asteroid2.x += asteroid2.velx;
// 到达边界,返回
if (asteroid2.x < 0 || asteroid2.x > SCREENW - asteroid2.width)
    asteroid2.velx *= -1;
Sprite_Animate(asteroid2.frame, asteroid2.startframe, asteroid2.endframe, asteroid2.direction, asteroid2.starttime, asteroid2.delay);
// 碰撞检测,若发生了碰撞,则会使得小行星反方向返回
if (Collision(ship, asteroid1))
        asteroid1.velx *= -1;
if (Collision(ship, asteroid2))
        asteroid2.velx *= -1;

// 将各个物体按照计算出来的位置绘制到屏幕上
if (d3ddev->BeginScene()){

    spriteobj->Begin(D3DXSPRITE_ALPHABLEND);

    Sprite_Transform_Draw(imgShip, ship.x, ship.y, ship.width, ship.height, ship.frame, ship.columns);

    Sprite_Transform_Draw(imgAsteroid, asteroid1.x, asteroid1.y, asteroid1.width, asteroid1.height, asteroid1.frame, asteroid1.columns);

    Sprite_Transform_Draw(imgAsteroid, asteroid2.x, asteroid2.y, asteroid2.width, asteroid2.height, asteroid2.frame, asteroid2.columns);

    spriteobj->End();

    d3ddev->EndScene();
    d3ddev->Present(NULL,NULL,NULL,NULL);
}

The following is the running result:


The white line shows the range of the asteroid's orbit

2. 2. Distance-based collision detection

2.1 The basis of implementation

Consider the two objects as two circles, the centers are (X 0 , Y 0 ), (X 1 , Y 1 ), and the radii are R 0 , R 1 , respectively , if they satisfy:

dist < R 0 +R 1
(dist is the distance between the centers of the two circles: sqrt((X 0 - X 1 )^2 + (Y 0 - Y 1 )^2))

Then it can be determined that the two objects have collided, and the determination function implemented accordingly is as follows:

bool CollisionD(SPRITE sprite1, SPRITE sprite2)
{
    double radius1, radius2;
    
    // 将宽和高中较长的一条边做为半径,以确保包围住物体
    //calculate radius 1
    if (sprite1.width > sprite1.height)
        radius1 = (sprite1.width * sprite1.scaling) / 2.0;
    else
        radius1 = (sprite1.height * sprite1.scaling) / 2.0;

    //center point 1
    double x1 = sprite1.x + radius1;
    double y1 = sprite1.y + radius1;
    D3DXVECTOR2 vector1(x1, y1);

    //calculate radius 2
    if (sprite2.width > sprite2.height)
        radius2 = (sprite2.width * sprite2.scaling) / 2.0;
    else
        radius2 = (sprite2.height * sprite2.scaling) / 2.0;

    //center point 2
    double x2 = sprite2.x + radius2;
    double y2 = sprite2.y + radius2;
    D3DXVECTOR2 vector2(x2, y2);

    //calculate distance
    double deltax = vector1.x - vector2.x;
    double deltay = vector2.y - vector1.y;
    double dist = sqrt((deltax * deltax) + (deltay * deltay));

    //return distance comparison
    return (dist < radius1 + radius2);
}

2.2 Implementation

Compared with the implementation in 2.1, just replace the original collision detection function with the following:

if (CollisionD(ship, asteroid1))
    asteroid1.velx *= -1;

if (CollisionD(ship, asteroid2))
    asteroid2.velx *= -1;

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324880844&siteId=291194637