初学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储存了lprcSrc1和lprcSrc2重叠的矩形区域,但是我们并不关心这个,只关心此函数的返回值。若存在重叠区域,返回值为非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

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))
则可以判定俩物体发生了碰撞 ,据此实现的判定函数如下:
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;
初学Direct X(8) ——碰撞检测的更多相关文章
- 初学Direct X(4)
初学Direct X(4) 本文学着做出一个如下的小游戏 游戏方式是使用键盘控制红色的Bucket收集蓝色的炸弹 1.酝酿一下 现在我已经掌握: 将位图文件加载到内存 绘制位图到buckbuffer ...
- 初学Direct X(7) ——位图的旋转,缩放以及平移
初学Direct X(7) --位图的旋转,缩放以及平移 本文旨在实现通过D3DXMatrixTransformation2D函数实现位图的旋转,缩放以及平移操作,但是具体的原理部分会在后面进一步的探 ...
- 初学Direct X(10)—— D3D基础预备知识
初学Direct X(10) -- D3D基础预备知识 1. 像素格式 D3DFMT_X8R8G8B8(F) X:未加使用 8:8位用于显示 B:用于显示蓝色 F:浮点像素类型 以下三个较为常用,使用 ...
- 初学Direct X(9) ——文字的显示
初学Direct X(9) --文字的显示 本次学习如何使用ID3DXFont创建字体,使得我们可以在任何安装了Windows系统中TrueType字体来打印文字,不过最好使用标准字体,这样文字在每一 ...
- 初学Direct X(6)
初学Direct X(6) 这一文本应和上一篇放在一起的,但是上一章写着写着发现对Draw绘制透明位图的方式有感觉了,决定就单写一篇,留作笔记了. 那这一篇是记录如何使用位图表来绘制动画帧,想象一下, ...
- 初学Direct X(5)
初学Direct X(5) 前面学习了使用表面绘制屏幕,但这种方法与另一种比较起来,有着绘图速度颇慢以及缺乏对任何透明类型的支持,这就是前面的篮框以及炸弹会有黑色背景的原因,这种方法就是纹理.他可以绘 ...
- 初学Direct X(3)
初学Direct X(3) 1.获取外设输入--键盘以及鼠标 无论是获取鼠标还是键盘的设备,首先得初始化DirectInput,不过先把必要的环境先配置好: 所要用到的头文件以及库文件是(相比于前两次 ...
- 初学Direct X (2)
初学Direct X (2) 这一次要学习如何现实位图,尽管看过对双缓冲机制还有很多疑问,但是这并不阻碍我对他的入门了解 Direct3D提供了一个双重/后台缓冲区,在调用CreateDevice之时 ...
- 初学DirectX(1)
初学Direct X (1) Direct3D设备用于访问视频卡的帧缓冲区,以及后台缓冲区.由于IDE是vs2013,默认安装了direct 9,只需要在使用头文件(1)并像使用库文件(2)即可 #i ...
随机推荐
- 图片验证码——base64编码的使用
一.介绍: 1.base64编码简介: Base64就是一种编码格式.Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成 ...
- ON_COMMAND ON_MESSAGE ON_NOTIFY区别与联系
ON_COMMAND是菜单和工具栏项处理消息的宏 ON_MESSAGE是处理自定义消息的宏 ON_NOTIFY 是控件向其父窗口发送消息处理的宏 对这几个消息的理解要先了解一下Window消息的背景. ...
- 如何解决使用JMeter时遇到的问题
Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域. 它可以用于测试静态和动态资源例如静态文件. ...
- Linux 文件压缩与解压相关
tar [-cxtzjvfpPN] 文件与目录 .... 参数:-c :建立一个压缩文件的参数指令-x :解开一个压缩文件的参数指令 -t :查看压缩文件里面的文件 特别注意: c/x/t 同时只能存 ...
- 课时8.HTML作用(掌握)
什么是HTML? HTML其实是HyperText Markup Language的缩写,超文本标记语言 如何重命名文件? 点击右键重命名 点击F2 首先利用记事本保存了一个标题和两段描述,然后修改纯 ...
- 大数据框架-Hbase
大规模结构化集群存储数据库.Table中的所有行都按照row key的字典序排列. 主键:row Key.访问行只能通过rowKey访问(范围或者准确值),或者全表扫描: 列族:cloumn fami ...
- Oracle->oracle单实例Shell脚本[20180122]
脚本主要用于redhat平台安装11g和12c软件 依赖包检查与安装 用户.组检查与安装 系统内核.用户限制 防火墙.selinux关闭 注意,linux组脚本 ...
- MySQL---事务、函数
事务 事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性. delimiter \\ create PROCEDURE p1( OUT ...
- Java线程状态图
嘤,先盗图一张,后面再补充描述!
- python学习——面向对象的三大特性
一.继承 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类. 1.python中类的继承分为:单继承和多继承 class P ...