您的位置:首页 > 其它

基于投影方法的碰撞检测以及一个测试DEMO【C + SDL】

2010-06-30 18:30 519 查看
理论知识是网路上的《2D多边形碰撞检测和反馈》,具体到四条边平行于X,Y轴的矩形的碰撞检测情况就简单了不少。原理如下:

将两个矩形分别投影到X和Y轴。

在X轴上,如果两个矩形的投影(即一条线上的两个部分)有重叠区域,则X轴上重叠。

同样判断Y轴。如果X和Y轴上的投影都重叠,那么这两个矩形一定重叠。

由于判断投影不重叠更容易,所以我们用反证法。函数如下:

//投影直线重合判定: 1重合; 0不重合
int line_touch(int ax1, int ax2, int bx1, int bx2)
{
if(ax2 <= bx1) return 0;
if(ax1 >= bx2) return 0;
return 1;
}

//判定触碰
int touch(struct object pusher, struct object box)
{
int p_x1 = pusher.xpos;
int p_x2 = pusher.xpos + pusher.width;
int p_y1 = pusher.ypos;
int p_y2 = pusher.ypos + pusher.height;

int b_x1 = box.xpos;
int b_x2 = box.xpos + box.width;
int b_y1 = box.ypos;
int b_y2 = box.ypos + box.height;

if(line_touch(p_x1,p_x2,b_x1,b_x2) == 0) return 0;
if(line_touch(p_y1,p_y2,b_y1,b_y2) == 0) return 0;
return 1;
}

以下是一个测试程序,大体是一个类似于推箱子的踢足球小游戏。

想学习SDL的朋友可以在里面找到绝大多数基础的函数的使用实例。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"

#define X_LENGTH 640
#define Y_LENGTH 480
#define SCREEN_BPP 32

#define ROLE_W 20
#define ROLE_H 30

#define FTB_W 16
#define FTB_H 16

#define MOVE_LTH 10

struct point{
int xpos;
int ypos;
};

//全局变量
SDL_Surface *back = NULL;
SDL_Surface *image = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *message = NULL;
SDL_Surface *football = NULL;

struct point stand[4] = {{10,100}, {110,100}, {210,100}, {310,100} };
struct point walk[4][2] = {
{{10,250},{10,300}},
{{110,250},{110,300}},
{{210,250},{210,300}},
{{310,250},{310,300}}
};

struct point ball[2] = {{0,0}, {0,16}};

void Slock(SDL_Surface *screen)
{
if ( SDL_MUSTLOCK(screen) )
{
if ( SDL_LockSurface(screen) < 0 )
{
return;
}
}
}

void Sulock(SDL_Surface *screen)
{
if ( SDL_MUSTLOCK(screen) )
{
SDL_UnlockSurface(screen);
}
}

//object
struct object{
//坐标
int xpos;
int ypos;
//宽高
unsigned int height;
unsigned int width;
//方向(1左,2右,3上,4下)
unsigned int direction;
//源表面
SDL_Surface *src_s;
//源图块坐标
int xs;
int ys;
//上一个状态: 1 站;2 走
unsigned int state;
//移动序列
unsigned int seq;
};

//投影直线重合判定: 1重合; 0不重合
int line_touch(int ax1, int ax2, int bx1, int bx2)
{
if(ax2 <= bx1) return 0;
if(ax1 >= bx2) return 0;
return 1;
}

//判定触碰
int touch(struct object pusher, struct object box)
{
int p_x1 = pusher.xpos;
int p_x2 = pusher.xpos + pusher.width;
int p_y1 = pusher.ypos;
int p_y2 = pusher.ypos + pusher.height;

int b_x1 = box.xpos;
int b_x2 = box.xpos + box.width;
int b_y1 = box.ypos;
int b_y2 = box.ypos + box.height;

if(line_touch(p_x1,p_x2,b_x1,b_x2) == 0) return 0;
if(line_touch(p_y1,p_y2,b_y1,b_y2) == 0) return 0;
return 1;
}

//边界判定 1111 分别是左右上下
int border(struct object const obj)
{
int o_left = obj.xpos;
int o_right = obj.xpos + obj.width;
int o_top = obj.ypos;
int o_bottom = obj.ypos + obj.height;

int result = 0;

if(o_left < 0) result += 1;
if(o_right > X_LENGTH ) result += 10;
if(o_top < 0) result +=100;
if(o_bottom > Y_LENGTH ) result += 1000;
return result;
}

//封装后的贴图函数【子函数】
void DrawIMG(SDL_Surface *src, int w, int h, int x, int y, SDL_Surface *aim, int x2, int y2)
{
SDL_Rect a;
a.x = x;
a.y = y;
a.w = w;
a.h = h;

SDL_Rect b;
b.x = x2;
b.y = y2;

SDL_BlitSurface(src, &a, aim, &b);
}

//贴背景
void DrawBG(SDL_Surface *bg)
{
SDL_Rect dest;
dest.x = 0;
dest.y = 0;

Slock(screen);
SDL_BlitSurface(bg,NULL,screen,&dest);
Sulock(screen);
}

//贴字符
void Drawmsg(SDL_Surface *message, int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
Slock(screen);
SDL_BlitSurface(message,NULL,screen,&dest);
Sulock(screen);
}

//贴动态图像
void DrawScene(struct object const obj, SDL_Surface *background)
{
switch(obj.direction)
{
case 1 : //左
Slock(screen);
//补充背景
DrawIMG(background, obj.width + MOVE_LTH, obj.height, obj.xpos, obj.ypos, screen, obj.xpos, obj.ypos);
//画移动物体
DrawIMG(obj.src_s, obj.width, obj.height, obj.xs, obj.ys, screen, obj.xpos, obj.ypos);
Sulock(screen);
break;
case 2 : //右
Slock(screen);
DrawIMG(background, obj.width + MOVE_LTH, obj.height, obj.xpos - MOVE_LTH, obj.ypos, screen, obj.xpos - MOVE_LTH, obj.ypos);
DrawIMG(obj.src_s, obj.width, obj.height, obj.xs, obj.ys, screen, obj.xpos, obj.ypos);
Sulock(screen);
break;
case 3 : //上
Slock(screen);
DrawIMG(background, obj.width, obj.height + MOVE_LTH, obj.xpos, obj.ypos, screen, obj.xpos, obj.ypos);
DrawIMG(obj.src_s, obj.width, obj.height, obj.xs, obj.ys, screen, obj.xpos, obj.ypos);
Sulock(screen);
break;
case 4 : //下
Slock(screen);
DrawIMG(background, obj.width, obj.height + MOVE_LTH, obj.xpos, obj.ypos - MOVE_LTH, screen, obj.xpos, obj.ypos - MOVE_LTH);
DrawIMG(obj.src_s, obj.width, obj.height, obj.xs, obj.ys, screen, obj.xpos, obj.ypos);
Sulock(screen);
break;
}
}

//站
void stand_f(struct object *obj)
{
switch(obj->direction)
{
case 4:
obj->xs = stand[0].xpos;
obj->ys = stand[0].ypos;
break;
case 1:
obj->xs = stand[1].xpos;
obj->ys = stand[1].ypos;
break;
case 3:
obj->xs = stand[2].xpos;
obj->ys = stand[2].ypos;
break;
case 2:
obj->xs = stand[3].xpos;
obj->ys = stand[3].ypos;
break;
}
}

//走
void walk_f(struct object *obj)
{
switch(obj->direction)
{
case 4:
if(obj->state == 2)//以前就是走的状态
{
if(obj->seq == 1) obj->seq = 0;
else if(obj->seq == 0) obj->seq = 1;
}
else obj->seq = 0;
obj->xs = walk[0][obj->seq].xpos;
obj->ys = walk[0][obj->seq].ypos;
break;
case 1:
if(obj->state == 2)//以前就是走的状态
{
if(obj->seq == 1) obj->seq = 0;
else if(obj->seq == 0) obj->seq = 1;
}
else obj->seq = 0;
obj->xs = walk[1][obj->seq].xpos;
obj->ys = walk[1][obj->seq].ypos;
break;
case 3:
if(obj->state == 2)//以前就是走的状态
{
if(obj->seq == 1) obj->seq = 0;
else if(obj->seq == 0) obj->seq = 1;
}
else obj->seq = 0;
obj->xs = walk[2][obj->seq].xpos;
obj->ys = walk[2][obj->seq].ypos;
break;
case 2:
if(obj->state == 2)//以前就是走的状态
{
if(obj->seq == 1) obj->seq = 0;
else if(obj->seq == 0) obj->seq = 1;
}
else obj->seq = 0;
obj->xs = walk[3][obj->seq].xpos;
obj->ys = walk[3][obj->seq].ypos;
break;
}
}

//动画坐标转换函数
void cartoon(struct object *obj)
{
switch(obj->state)
{
case 1:
stand_f(obj);
break;
case 2:
walk_f(obj);
break;
}
}

//// 主函数 /////
//// /////

int main(int argc, char *argv[])
{
//初始化
if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
{
printf("Unable to init SDL: %s/n", SDL_GetError());
exit(1);
}

//
atexit(SDL_Quit);

//设置视频(显示)
screen=SDL_SetVideoMode(X_LENGTH,Y_LENGTH,SCREEN_BPP,SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( screen == NULL )
{
printf("Unable to set 640x480 video: %s/n", SDL_GetError());
exit(1);
}

//设置窗口的名称
SDL_WM_SetCaption( "Carton", NULL );

/////////////设置字体//////////////
//
TTF_Font *font;
SDL_Color textColor = { 0, 0, 0 };
TTF_Init();
font = TTF_OpenFont("C://Windows//Fonts//BRITANIC.TTF",28);
if(font == NULL) return 1;
message = TTF_RenderText_Solid( font, "hello,the world!", textColor );
if(message == NULL) return 1;

/////////////////////////////
back = IMG_Load("c://back.bmp");
image = IMG_Load("c://abc.png");
football = IMG_Load("c://football.bmp");
SDL_SetColorKey(football, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(football->format, 255, 0, 255));
struct object role = {0,0,ROLE_H,ROLE_W,2,image,10,100,1,0};
struct object ftb = {150,150,FTB_H,FTB_W,2,football,0,0,1,0};
/////////////////////////////
Uint8* keys;
int done=0;

DrawBG(back);
DrawScene(role,back);
///////////字体//////
Slock(screen);
Drawmsg(message, 100, 100);
Sulock(screen);

while(done == 0)
{
SDL_Event event;

while ( SDL_PollEvent(&event) )
{
if ( event.type == SDL_QUIT ) { done = 1; }

if ( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
}
}
keys = SDL_GetKeyState(NULL);

if ( keys[SDLK_UP] )
{ role.ypos -= MOVE_LTH;
role.direction = 3;
role.state = 2;
cartoon(&role);}
else if ( keys[SDLK_DOWN] )
{ role.ypos += MOVE_LTH;
role.direction = 4;
role.state = 2;
cartoon(&role);}
else if ( keys[SDLK_LEFT] )
{ role.xpos -= MOVE_LTH;
role.direction = 1;
role.state = 2;
cartoon(&role);}
else if ( keys[SDLK_RIGHT] )
{ role.xpos += MOVE_LTH;
role.direction = 2;
role.state = 2;
cartoon(&role);}

int test_val = border(role);
if(test_val%10 == 1) role.xpos =0;
if(test_val/10%10 == 1) role.xpos = X_LENGTH - role.width;
if(test_val/100%10 == 1) role.ypos = 0;
if(test_val/1000 == 1) role.ypos = Y_LENGTH - role.height;

if(touch(role,ftb))
{
switch(role.direction)
{
case 1:
ftb.xpos = role.xpos - ftb.width;
ftb.direction = 1;
break;
case 2:
ftb.xpos = role.xpos + role.width;
ftb.direction = 2;
break;
case 3:
ftb.ypos = role.ypos - ftb.height;
ftb.direction = 3;
break;
case 4:
ftb.ypos = role.ypos + role.height;;
ftb.direction = 4;
break;
}

int test_val = border(ftb);
if(test_val%10 == 1){
ftb.xpos = 0;
if ( keys[SDLK_UP] ) role.ypos += MOVE_LTH;
if ( keys[SDLK_DOWN] ) role.ypos -= MOVE_LTH;
if ( keys[SDLK_LEFT] ) role.xpos += MOVE_LTH;
}
if(test_val/10%10 == 1){
ftb.xpos = X_LENGTH - ftb.width;
if ( keys[SDLK_UP] ) role.ypos += MOVE_LTH;
if ( keys[SDLK_DOWN] ) role.ypos -= MOVE_LTH;
if ( keys[SDLK_RIGHT] ) role.xpos -= MOVE_LTH;
}
if(test_val/100%10 == 1){
ftb.ypos = 0;
role.ypos += MOVE_LTH;
}
if(test_val/1000 == 1){
ftb.ypos = Y_LENGTH - ftb.height;
role.ypos -= MOVE_LTH;
}

if(ftb.ys == 0) ftb.ys = 16;
else ftb.ys = 0;
}
DrawScene(ftb,back);
DrawScene(role,back);
SDL_Flip(screen);
SDL_Delay(100);
}

//释放字体文件

TTF_CloseFont( font );
//释放TTF

TTF_Quit();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐