初学Direct X(8)

初学Direct X(8)

——碰撞检测

真正让一个游戏鹤立鸡群的是程序对碰撞的响应有多好,这里介绍两种检测的方法:
1) 基于边框的碰撞检测
2) 基于距离的碰撞检测

1. 基于边框的碰撞检测

1.1 实现的基础

我们将要检测的两个物体(位图)视为两个矩形,在此基础之上,检测两矩形是否有重叠区域。若存在重叠区域,则发生了碰撞;反之则没有。检测两矩形是否有重叠到了Windows API,即IntersectRect,它的定义如下:

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

lprcDst储存了lprcSrc1lprcSrc2重叠的矩形区域,但是我们并不关心这个,只关心此函数的返回值。若存在重叠区域,返回值为非0;反之为0。
据此可以实现碰撞检测的函数如下:(关于SPRITE的定义参见这儿

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 实现

我们实现一个宇宙飞船在小行星中飞行的场景,其中小行星是不断运动的。居然用到了物体检测,而且显然场景中不止一个物体了,此时我们需要用一个结构体(或者类)来表达一个物体,就像下面这样:
 

//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);
    }
};

在声明完一个物体结构体时,我们需要加载位图:

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);
}

下面是运行结果:


白线表示的是小行星运行的范围

2. 2. 基于距离的碰撞检测

2.1 实现的基础

将俩物体视为两个圆,圆心分别为(X0,Y0),(X1,Y1),半径分别为R0,R1,若满足:

dist < R0+R1
(dist 为俩圆心的距离:sqrt((X0 - X1)^2 + (Y0 - Y1)^2))

扫描二维码关注公众号,回复: 70018 查看本文章

则可以判定俩物体发生了碰撞 ,据此实现的判定函数如下:

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 实现

对照2.1中的实现,只需要将原来的碰撞检测函数替换为如下即可:

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

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

猜你喜欢

转载自www.cnblogs.com/leihui/p/8951178.html