Gun Mayhem—混乱大枪战游戏设计
一、项目说明
设计目的:
1.学习编程基础:编写一款游戏需要熟悉C语言的基础语法和编程逻辑,因此,编写游戏是体现C语言学习成果的一种很好的方式,可以帮初学者,更好的把握各知识点的关系,增强自己的实践编程能力,使自己能够根据实际需求,在将来的项目开发中做出更好的编程策略与逻辑判断。
2.开发游戏编程能力:本游戏需要我设计和实现游戏逻辑和算法,包括实现玩家的移动、游戏画面的渲染、游戏间物体的碰撞检测、角色发射功能的实现、不同类的功能、各类逻辑关系等内容,这些能力对于我开发其他类型的游戏,以及实现其他功能都是有帮助的。
3.提高代码规范和模块化思维:通过代码模块化的方式来提高编写代码的效率,和代码可维护性。同时培养良好的代码规范、注释、命名等习惯,来提高代码的可读性和可复用性。
设计意义:
通过这种形式可以深化对本学期学习的C语言相关知识的理解,例如结构体、数组、枚举等等,同时也可以增加C语言相关知识应用时的熟练度,提高对C语言深层知识的掌握程度,培养一个良好的编程习惯,加强自己的编程能力和相关思维能力,并积累用C语言解决生活中问题的经验。
另外本次学习中用到conio.h、time.h、graphics.h等函数库,编程过程中我对时间计算,键盘响应,图像绘制在C语言中的实现有了更加深入的理解。
模块组成:
项目模块:
项目使用函数:不包括类的成员函数,类成员函数在类的介绍中有详细说明
void Get img(double kx,double ky)//加载用到的图片
void delete img()//释放图片避免空指针导致内存泄露
void initgame()//初始化游戏
int startgame()//开始界面
void showgame()/更新游戏画面
void update noinput()//更新游戏数据
void update input()//玩家输入系统
int CheckCollision(void *obj1,int datetype1,void *obj2,int datetype2)//碰撞检测利用函数
void CollisionActor(actor *player)//角色与物体碰撞后的逻辑
void Collisionobject()//物体间碰撞后的逻辑
void Collisionsystem()//碰撞检测系统
4、各模块功能
(1)、游戏开始选择
开始界面:
游戏教程页面:
游戏界面:
实现代码:
int startgame(double kx, double ky)
{
int choice = 0;
int a[3][4] = {
{2619, 977},
{2610, 1297},
{2619, 1618}};
while (1)
{
putimage(0, 0, getwidth(), getheight(), img_start, 0, 0, 4250, 2000);
setfillcolor(EGERGB(241, 120, 111));
bar(a[choice][0] * kx, a[choice][1] * ky, a[choice][0] * kx + 50, a[choice][1] * ky + 50);
if ((GetAsyncKeyState(0x57) & 0x8000))
{
choice--;
if (choice < 0)
choice = 2;
}
else if ((GetAsyncKeyState(0x53) & 0x8000))
{
choice++;
if (choice > 2)
choice = 0;
}
else if ((GetAsyncKeyState(VK_RETURN) & 0x8000))
{
break;
}
Sleep(100);
}
printf("over%d\n", choice);
return choice;
}
以及主函数中控制函数:
while (1)
{
// 选择选项
int choice;
choice = startgame(Game_kx, Game_ky);
printf("%d", choice);
if (choice == 0)
{
initgame();
GAME();
}
else if (choice == 1)
{
cleardevice(NULL);
putimage(0, 0, getwidth(), getheight(), img_help, 0, 0, 4250, 2000);
Sleep(1000);
getch();
}
else
exit(0);
}
(2)、项目中创建的类:各类创建在class.h中
两个基础建筑类:用于辅助实现传送和承载人物和物体的功能,功能将在各类中展示:
block—障碍物类: door—传送门类:
功能实现代码:
//-----------------------------障碍物类------------------------------------------//
struct block
{
int x;
int y;
int width;
int hight;
int datetype; // 数据类型
void init(int a[4], double kx, double ky)
{
datetype = BLOCK;
x = a[0] * kx;
y = a[1] * ky;
width = a[2] * kx;
hight = a[3] * ky;
printf("%d,%d,%d,%d\n", x, y, width, hight);
}
void show()
{
setfillcolor(WHITE);
bar(x, y, x + width, y + hight);
}
};
//--------------------------------------传送门对类--------------------------------//
struct door
{
int x;
int y;
int dir;
int x2;
int y2;
int hight;
int width;
int datetype; // 数据类型
void init(int a[7], double kx, double ky)
{
datetype = DOOR;
x = a[0] * kx;
y = a[1] * ky;
dir = a[2];
x2 = a[3] * kx;
y2 = a[4] * ky;
width = a[5] * kx;
hight = a[6] * ky;
}
void show()
{
setfillcolor(BROWN);
bar(x, y, x + width, y + hight);
}
};
actor—角色类:
功能展示:
不同的枪的渲染:
发射子弹: 跳跃:
捡箱子前: 捡箱子后:实现了枪的切换
通过传送门传送:
实现代码:
struct actor
{
int x, y; // 角色位置
int width, hight; // 角色宽高
int actor_isonblock; // 用于判断是否在物体上(0在空中,1在物体上)
int actor_isjp, actor_jplimit, actor_jphight, actor_haslimit; // jp跳跃次数,jplimit最大跳跃高度,jphight跳跃高度,haslimit判断是否达到最大高度;
int dir, lastdir; // 角色朝向,0正面 1右 2下 -1左
int speed_move, speed_jp; // 角色移动跳跃速度
PIMAGE img_actor_left, img_actor_right; // 角色贴图
int life;
int guntime; // 生命值
int guntype; // 记录捡到什么枪
int isfire; // 记录是否开火如果按下攻击键isfire置为1,开枪后刷新为0
int isdancing;
int datetype;
int dance_tietu, run_tietu, withgun_tietu;
clock_t dance_start, dance_end, die_start, die_end;
void init(int Game_width, double kx, double ky)
{
// 加载人物站立图像及其掩图
x = Game_width / 2; // 初始x位置 // 角色构造函数人物初始化从空中下落
y = -100; // 初始y
img_actor_left = newimage();
getimage_pngfile(img_actor_left, "E:\\VSCODE\\game\\painting\\man_left.png");
datetype = ACTOR; // 数据类型为ACTOR
actor_isonblock = 0; // 初始角色在空中
dir = FRONT;
speed_move = ACTOR_SPEED_MOVE; // 移动速度
speed_jp = ACTOR_SPEED_JUMP; // 跳跃速度
width = Actor_width * kx; // 角色宽度
hight = Actor_hight * ky; // 角色高度
actor_jplimit = ACTOR_JUMP_LIMIT * ky; // 跳跃极限
actor_jphight = 0; // 当前跳跃高度
actor_isjp = 0; // 是否处于弹跳状态1为跳跃,0为不跳跃
life = ACTOR_LIFE; // 开局生命值为10
guntype = lanqiu; // 开局默认是手枪
isfire = 0; // 开局不开枪
isdancing = 0; // 开局不跳舞
guntime = gun[guntype][2];
dance_start = 0;
dance_end = 0;
die_start = 0;
die_end = 0;
dance_tietu = 0;
run_tietu = 0;
withgun_tietu = 0;
}
//jump跳跃函数
void jump()
{
if (actor_isjp == 0 && actor_isonblock == 0 && y < 720)
{
y = y + G;
}
if (actor_isjp == 1)
{
y -= speed_jp;
actor_jphight += speed_jp;
if (actor_jphight > actor_jplimit)
{
actor_isjp = 0;
actor_jphight = 0;
}
}
}
// 由子弹类调用hurt()
void hurt(int type, int damage)
{
switch (type)
{
case jinqiang:
/*之后加上回血特效*/
life = life - damage;
break;
case lanqiu:
isdancing = 1; // 让角色跳舞
default:
life = life - damage;
break;
}
printf("life:%d", life);
}
// 更新函数用于更新位置及贴图信息
void update()
{
if (life > 0)
{
if (isdancing == 0)
{
dance_start = time(NULL);
// 刷新左右移动
if (dir == -1 || dir == 1)
{
x = x + (speed_move * dir); // 更新移动
}
}
dance_end = time(NULL);
int diff = difftime(dance_end, dance_start);
if (diff > 3)
{
isdancing = 0;
dance_start = 0;
dance_end = 0;
dance_tietu = 0;
}
jump();
// 记录上一个方向
if (dir == RIGHT || dir == LEFT)
{
lastdir = dir;
}
}
// 死亡重新复活
if (die_start != 0)
{
die_end = time(NULL);
}
if (life <= 0 && die_start == 0)
{
die_start = time(NULL);
}
int diff_die = difftime(die_end, die_start);
if (diff_die > 5)
{
life = ACTOR_LIFE; // 生命值为10
guntype = shouqiang; // 默认是手枪s
actor_isonblock = 0; // 初始角色在空中
dir = FRONT;
x = rand() % 1200; // 初始x位置
y = -100; // 初始y
die_start = 0;
die_end = 0;
diff_die = 0;
}
}
void show(double kx, double ky)
{
if (life > 0)
{
if (isdancing == 1)
{
int i = (dance_tietu / 4) % 4;
suofang_img(img_chongfeng, x, y, img_man[dance][i][0], img_man[dance][i][1], width, hight, kx, ky);
dance_tietu++;
}
else
{
// 朝左运动
if (dir == LEFT)
{
int i = (run_tietu / 10) % 4;
switch (guntype)
{
case chongfeng:
{
suofang_img(img_chongfeng, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case jujiqiang:
{
suofang_img(img_juji, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case jinqiang:
{
suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case shouqiang:
{
suofang_img(img_shouqiang, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case lanqiu:
{
suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
default:
break;
}
}
// 朝右运动
if (dir == RIGHT)
{
int i = (run_tietu / 10) % 4;
switch (guntype)
{
case chongfeng:
{
suofang_img(img_chongfeng, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case jujiqiang:
{
suofang_img(img_juji, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case jinqiang:
{
suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case shouqiang:
{
suofang_img(img_shouqiang, x, y, img_man[run][i][0], img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
case lanqiu:
{
suofang_img(img_jinqiang, x, y, img_man[run][i][0]+190, img_man[run][i][1], width, hight, kx, ky);
run_tietu++;
if (i > 4)
{
run_tietu = 0;
}
}
break;
default:
break;
}
}
// 不动展示正面
if (dir == 0)
{
suofang_img(img_chongfeng, x, y, 0, 0, width, hight, kx, ky);
}
}
}
dir = FRONT;
}
};
box—箱子类:
功能展示:
箱子渲染及其与地面的碰撞检测:
实现代码:
//-------------------箱子类----------------------------//
struct box
{
int box_type;
int box_speed_x, box_speed_y;
int y, x;
int randvalue;
int width; // 盒子的边长
int is_onblock; // 在物体上
int valid; // 判断箱子是否被捡
int dir; // 箱子下落过程中水平运动方向
int datetype; // 数据类型
void init(int Game_width)
{
datetype = BOX;
randvalue = rand();
width = BOX_WIDTH; // 盒子边长
y = 0; // 盒子从窗口顶端释放
x = randvalue % Game_width; // 随机生成盒子x位置
dir = (randvalue % 2) * (-1); // 随机方向
box_type = randvalue % 5; // 随机生成盒子中的枪类型(0-4之间)
box_speed_x = dir * (randvalue % 10); // 随机盒子下抛x,y速度
box_speed_y = randvalue % 10;
valid = 1;
is_onblock = 0; // 盒子在空中
}
// 让盒子沿着抛物线运动
void box_run(int Game_width, double ky)
{
if (is_onblock == 0)
{
// 当盒子靠近窗口边缘就反弹
if (x > 0 && x + width < Game_width)
x += box_speed_x;
else
{
box_speed_x = -box_speed_x;
x += box_speed_x;
}
if (y < 1780 * ky)
y += box_speed_y;
box_speed_y += box_speed_y / 5;
}
}
// 画出盒子
void box_show(double kx, double ky)
{
suofang_img(img_xiangzi, x, y, 0, 0, width, width, kx, ky);
}
void getbox(actor *temp)
{
temp->guntype = box_type;
valid = 0;
}
};
bullet—子弹类:
功能展示:
不同枪对应不同子弹:
及其隐藏枪械篮球:击中敌人会让敌人跳舞并且无法移动
子弹传送:
实现代码:
//----------————————-子弹类---————————————------------//
struct Bullet
{
int x; // 子弹横坐标
int y; // 子弹纵坐标
int dir; // 子弹朝向,1右 -1左
int hight, width; // 子弹宽高
bool valid; // 子弹是否还在有效范围内有效范围外为0
int type; // 子弹类型
int damage; // 子弹伤害
double speed; // 子弹速度
int fire_speed; // 连发速度
int num; // 子弹数量
int houzuoli; // 后坐力
int fjxg; // 附加效果-1为让敌人跳舞 -2为扣自己的血给敌人加血
int datetype; // 数据类型
// 当角色开火时射出子弹
// 根据角色捡到什么类型的箱子,匹配子弹伤害子弹速度
void init(actor *player)
{
// 初始化子弹属性
datetype = BULLET;
dir = player->lastdir;
type = player->guntype;
speed = gun[type][0] * 0.5;
damage = gun[type][1];
fire_speed = gun[type][2];
houzuoli = gun[type][3];
fjxg = gun[type][4];
width = 10;
hight = 5;
if (dir == LEFT)
{
x = player->x - 15;
y = player->y + player->hight * 0.3;
}
else
{
x = player->x + player->width + 15;
y = player->y + player->hight * 0.3; // 修正子弹在枪口
}
valid = 1; // 子弹射出
}
void show(double kx, double ky)
{
if (valid == 1)
{
switch (type)
{
case shouqiang:
setfillcolor(WHITE);
bar(x, y, x + 10, y + 5);
break;
case jujiqiang:
setfillcolor(RED);
bar(x, y, x + 15, y + 5);
break;
case jinqiang:
setfillcolor(GREEN);
bar(x, y, x + 10, y + 5);
break;
case chongfeng:
setfillcolor(WHITE);
line(x, y, x + 300, y);
line(x, y, x + 300, y + 3);
line(x, y, x + 300, y - 3);
line(x, y, x + 300, y + 2);
line(x, y, x + 300, y - 2);
break;
case lanqiu:
suofang_img(img_lanqiu, x, y, 0, 0, 110, 110, kx, ky);
break;
default:
break;
}
}
}
void run()
{
if (valid == 1)
x = x + dir * speed;
}
void isvalid(int Game_width)
{
if (x < 0 || x > Game_width)
valid = 0;
}
void hit(actor *mubiao)
{
// 调用目标的hurt()函数实现
mubiao->hurt(type, damage);
valid = 0;
}
};
(3)游戏初始化:
游戏数据更新依赖数据初始化:
Class.h中预处理:
Game.c中:
调用initgame()函数初始化游戏数据更新依赖数据:
initgame()中调用了Get_img(double kx, double ky)函数载入图片:
角色初始化由角色成员函数actor::init(int Game_width, double kx, double ky)完成:
实现代码:
// 初始化游戏
void initgame()
{
// 初始化时间刷新变量
startTime = time(NULL);
fire1_start = 0;
fire1_end = 0;
fire2_start = 0;
fire2_end = 0;
num_box = 0; // 开始时没有盒子
num_block = 16;
num_door = 3;
escape = 0; // 退出键设置为0
printf("%d,%d\n", Game_hight, Game_width);
Game_kx = Game_width * 1.0 / BK_WIDTH;
Game_ky = Game_hight * 1.0 / BK_HEIGHT;
printf("%lf,%lf\n", Game_kx, Game_ky);
Get_img(Game_kx, Game_ky);
// door初始化
int door_date[3][7] = {
{1959, 608, RIGHT, 647, 110, 422, 422},
{0, 998, LEFT, 1847, 805, 265, 655},
{4125, 855, RIGHT, 320, 1348, 120, 425}};
for (int i = 0; i < num_door; i++)
{
doors[i].init(door_date[i], Game_kx, Game_ky);
}
int block_date[16][4] = {
{0, 1660, 1117, 20},
{0, 566, 130, 20},
{0, 860, 550, 20},
{567, 282, 425, 20},
{1000, 930, 170, 20},
{1130, 1790, 1980, 20},
{1300, 930, 150, 20},
{1543, 1411, 1239, 20},
{1600, 360, 400, 20},
{1977, 1100, 800, 20},
{2400, 380, 400, 20},
{3060, 430, 250, 20},
{3110, 1510, 1130, 20},
{3500, 820, 740, 20},
{3730, 260, 530, 20},
{3730, 1300, 530, 20},
};
// block初始化
for (int i = 0; i < num_block; i++)
{
blocks[i].init(block_date[i], Game_kx, Game_ky);
}
img_bk = newimage();
getimage(img_bk, "E:\\VSCODE\\game\\painting\\gunman_bk.png");
player1.init(Game_width, Game_kx, Game_ky); // 角色1初始化
player2.init(Game_width, Game_kx, Game_ky); // 角色2初始化
}
(4)玩家输入update_input()函数
实现代码:
// 玩家输入系统
void update_input()
{
///////////////////////////////////////////////player1///////////////////////////////////////////
if ((GetAsyncKeyState(0x41) & 0x8000)) // player1输入a时左移 //GetAsyncKeyState()实现同步输入
{
if (player1.x <= 1124 * Game_kx &&
player1.y + player1.hight >= 1683 * Game_ky + 5 ||
player1.x <= 123 * Game_kx &&
player1.y + player1.hight >= 582 * Game_ky)
;
else
{
if (player1.x > 0)
player1.dir = LEFT;
}
}
if ((GetAsyncKeyState(0x44) & 0x8000)) // player1输入d时左移
{
if (player1.x + player1.width >= 3105 * Game_kx &&
player1.y + player1.width >= 1504 * Game_ky ||
player1.x + player1.width >= 3730 * Game_kx &&
player1.y + player1.width >= 1300 * Game_ky)
;
else
{
if (player1.x + player1.width < Game_width)
player1.dir = RIGHT;
}
}
if ((GetAsyncKeyState(0x53) & 0x8000)) // player1输入s时跳下block
{
if (player1.y + player1.hight >= 1510 * Game_ky ||
player1.x >= 3722 * Game_kx &&
player1.y + player1.hight >= 1292 * Game_ky ||
player1.x <= 266)
;
else
{
if (player1.actor_isonblock == 1)
player1.actor_isonblock = 0;
}
}
if ((GetAsyncKeyState(0x4B) & 0x8000)) // player1输入k时跳跃,
{
if (player1.actor_isonblock == 1) // 当达到最大高度时不可以输入k,未达到时可以输入k
{
player1.actor_isjp = 1; // 设置跳跃标志位
}
}
if ((GetAsyncKeyState(0x4A) & 0x8000)) // 当按下j时发出子弹
{
if (fire1_start == 0)
{
player1.isfire = 1;
fire1_start = time(NULL);
}
fire1_end = time(NULL);
double diff = difftime(fire1_end, fire1_start);
if (diff >= player1.guntime)
{
player1.isfire = 1;
fire1_start = fire1_end;
}
}
if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000)) // 设置退出按键
{
escape = 1; // 设置退出按键
}
///////////////////////////////////player2///////////////////////////////////////////////////////
if ((GetAsyncKeyState(VK_LEFT) & 0x8000)) // player2输入左键时左移
{
if (player2.x <= 1124 * Game_kx &&
player2.y + player2.hight >= 1683 * Game_ky + 5 ||
player2.x <= 123 * Game_kx &&
player2.y + player2.hight >= 582 * Game_ky)
;
else
{
if (player2.x > 0)
player2.dir = LEFT;
}
}
if ((GetAsyncKeyState(VK_RIGHT) & 0x8000)) // player2输入右键时左移
{
if (player2.x + player2.width >= 3105 * Game_kx &&
player2.y + player2.width >= 1504 * Game_ky ||
player2.x + player2.width >= 3730 * Game_kx &&
player2.y + player2.width >= 1300 * Game_ky)
;
else
{
if (player2.x + player2.width < Game_width)
player2.dir = RIGHT;
}
}
if ((GetAsyncKeyState(VK_DOWN) & 0x8000)) // player2输入下键时跳下block
{
if (player2.y + player2.hight >= 1510 * Game_ky ||
player2.x >= 3722 * Game_kx &&
player2.y + player2.hight >= 1292 * Game_ky ||
player2.x <= 266)
;
else
{
if (player2.actor_isonblock == 1)
player2.actor_isonblock = 0;
}
}
if ((GetAsyncKeyState(VK_NUMPAD1) & 0x8000)) // 当按下1时发出子弹
{
if (fire2_start == 0)
{
player2.isfire = 1;
fire2_start = time(NULL);
}
fire2_end = time(NULL);
double diff = difftime(fire2_end, fire2_start);
if (diff >= player2.guntime)
{
player2.isfire = 1;
fire2_start = fire2_end;
}
}
if ((GetAsyncKeyState(VK_NUMPAD2) & 0x8000)) // player1输入k时跳跃,
{
if (player2.actor_isonblock == 1) // 当达到最大高度时不可以输入k,未达到时可以输入k
{
player2.actor_isjp = 1; // 设置跳跃标志位
}
}
}
(5)游戏数据更新update_noinput()函数
实现代码:
// 更新游戏变化
void update_noinput()
{
player1.update();
player2.update();
// 遍历子弹,对子弹数据进行更新,并检测子弹有效性无效则释放内存
if (num_bullet > 0)
{
for (int i = 0; i < num_bullet; i++)
{
bullets[i]->run();
bullets[i]->isvalid(Game_width);
if (bullets[i]->valid == 0)
{
free(bullets[i]);
for (int j = i; j < num_bullet; j++)
{ // 移动数组中的元素
bullets[j] = bullets[j + 1];
}
num_bullet--;
}
}
}
// 检测开火
// 角色1
if (player1.isfire == 1 && num_bullet < MAX_BULLETS && player1.life > 0)
{
bullets[num_bullet] = Creat_Bullet(&player1);
player1.x = player1.x - player1.lastdir * bullets[num_bullet]->houzuoli;
if (player1.guntype == jinqiang)
player1.life -= 2;
num_bullet++;
player1.isfire = 0;
}
// 角色2
if (player2.isfire == 1 && num_bullet < MAX_BULLETS && player2.life > 0)
{
bullets[num_bullet] = Creat_Bullet(&player2);
player2.x = player2.x - player2.dir * bullets[num_bullet]->houzuoli;
if (player2.guntype == jinqiang)
player2.life -= 2;
num_bullet++;
player2.isfire = 0;
}
// 遍历箱子,对箱子数据进行更新,并检测箱子有效性无效则释放内存
if (num_box > 0)
{
for (int i = 0; i < num_box; i++)
{
box1[i]->box_run(Game_width, Game_ky);
if (box1[i]->valid == 0)
{
free(box1[i]);
printf("boxfree:%d\n", i);
for (int j = i; j < num_box; j++)
{
// 移动数组中的元素
box1[i] = box1[i + 1];
box1[num_box] = {0};
}
num_box--;
}
}
}
// 每三十秒生成一个箱子定时生成
endTime = time(NULL);
double diff = difftime(endTime, startTime);
if (diff >= 30 && num_box <= MAX_BOXES) //&& num_box < MAX_BOXES
{
box1[num_box] = (box *)malloc(sizeof(box));
box1[num_box]->init(Game_width);
num_box++;
printf("num_box:%d\n", num_box);
startTime = endTime;
}
}
(6)碰撞检测系统CollisionSystem():
CollisionSystem()由物体碰撞检测CollisionObject()函数和人物碰撞检测函数CollisionActor(actor *player)构成他们都是基于碰撞检测函数CheckCollision(void *obj1, int datetype1, void *obj2, int datetype2)实现:
1.CheckCollision()函数用于判断单独两个对象直接的碰撞;
2.CollisionActor(actor *player)函数和CollisionObject()函数用于实现,检测碰撞前的逻辑和碰撞 后的数据更改;
实现代码:
// 碰撞检测利用函数
int CheckCollision(void *obj1, int datetype1, void *obj2, int datetype2)
{
int x1, y1, hight1, width1, x2, y2, hight2, width2;
// 将void类型的指针转换成对应的数据类型的指针
// 使用空指针
switch (datetype1)
{
case ACTOR:
{
actor *obj1_temp = (actor *)obj1;
x1 = obj1_temp->x;
y1 = obj1_temp->y;
hight1 = obj1_temp->hight;
width1 = obj1_temp->width;
break;
}
case BOX:
{
box *obj1_temp = (box *)obj1;
x1 = obj1_temp->x;
y1 = obj1_temp->y;
hight1 = obj1_temp->width;
width1 = obj1_temp->width;
break;
}
case BULLET:
{
Bullet *obj1_temp = (Bullet *)obj1;
x1 = obj1_temp->x;
y1 = obj1_temp->y;
hight1 = obj1_temp->hight;
width1 = obj1_temp->width;
break;
}
case DOOR:
{
door *obj1_temp = (door *)obj1;
x1 = obj1_temp->x;
y1 = obj1_temp->y;
hight1 = obj1_temp->hight;
width1 = obj1_temp->width;
break;
}
case BLOCK:
{
block *obj1_temp = (block *)obj1;
x1 = obj1_temp->x;
y1 = obj1_temp->y;
hight1 = obj1_temp->hight;
width1 = obj1_temp->width;
break;
}
default:
break;
}
// 使用空指针
switch (datetype2)
{
case ACTOR:
{
actor *obj2_temp = (actor *)obj2;
x2 = obj2_temp->x;
y2 = obj2_temp->y;
hight2 = obj2_temp->hight;
width2 = obj2_temp->width;
break;
}
case BOX:
{
box *obj2_temp = (box *)obj2;
x2 = obj2_temp->x;
y2 = obj2_temp->y;
hight2 = obj2_temp->width;
width2 = obj2_temp->width;
break;
}
case BULLET:
{
Bullet *obj2_temp = (Bullet *)obj2;
x2 = obj2_temp->x;
y2 = obj2_temp->y;
hight2 = obj2_temp->hight;
width2 = obj2_temp->width;
break;
}
case DOOR:
{
door *obj2_temp = (door *)obj2;
x2 = obj2_temp->x;
y2 = obj2_temp->y;
hight2 = obj2_temp->hight;
width2 = obj2_temp->width;
break;
}
case BLOCK:
{
block *obj2_temp = (block *)obj2;
x2 = obj2_temp->x;
y2 = obj2_temp->y;
hight2 = obj2_temp->hight;
width2 = obj2_temp->width;
break;
}
default:
break;
}
// 如果两个矩形不重叠,返回0,否则返回1
if (datetype1 == ACTOR && datetype2 == BLOCK)
{
y1 = y1 + hight1 - 5;
hight1 = 5;
}
if (x1 + width1 < x2 || x1 > x2 + width2 ||
y1 + hight1 < y2 || y1 > y2 + hight2)
{
return 0;
}
else
{
return 1;
}
}
// 角色与物体碰撞后的逻辑
void CollisionActor(actor *player)
{
// 检测箱子与角色的碰撞
for (int i = 0; i < num_box; i++)
{
if (CheckCollision(player, player->datetype, box1[i], box1[i]->datetype))
{
box1[i]->getbox(player);
printf("boxget:%d", i);
box1[i]->valid = 0;
};
}
// 检测子弹与角色的碰撞
for (int i = 0; i < num_bullet; i++)
{
if (CheckCollision(player, player->datetype, bullets[i], bullets[i]->datetype))
{
player->x = player->x + bullets[i]->houzuoli * bullets[i]->dir;
bullets[i]->hit(player);
};
}
// 检测block和人物碰撞
for (int i = 0; i < num_block; i++)
{
if (CheckCollision(player, player->datetype, &blocks[i], blocks[i].datetype) == 1)
{
player->actor_isonblock = 1;
// printf("%d.checkcollision:%d,%d;player:%d,%d\n", i, blocks[i].x, blocks[i].y, player->x, player->y);
break;
}
else
{
player->actor_isonblock = 0;
}
}
// 检测传送门和人
for (int i = 0; i < num_door; i++)
{
if (CheckCollision(player, player->datetype, &doors[i], doors[i].datetype) == 1)
{
player->x = doors[i].x2;
player->y = doors[i].y2;
player->dir = doors[i].dir;
}
}
}
// 物体间碰撞后的逻辑
void CollisionObject()
{
// 检测箱子和block和传送门碰撞
for (int i = 0; i < num_box; i++)
{
for (int k = 0; k < num_block; k++)
{
if (CheckCollision(box1[i], box1[i]->datetype, &blocks[k], blocks[k].datetype))
{
box1[i]->is_onblock = 1;
}
}
for (int l = 0; l < num_door; l++)
{
if (CheckCollision(box1[i], box1[i]->datetype, &doors[l], doors[l].datetype))
{
box1[i]->x = doors[l].x2;
box1[i]->y = doors[l].y2;
box1[i]->dir = doors[l].dir;
}
}
}
// 检测子弹传送门
for (int i = 0; i < num_bullet; i++)
{
for (int l = 0; l < num_door; l++)
{
if (CheckCollision(bullets[i], bullets[i]->datetype, &doors[l], doors[l].datetype))
{
bullets[i]->x = doors[l].x2;
bullets[i]->y = doors[l].y2;
bullets[i]->dir = doors[l].dir;
}}}}
// 碰撞检测系统
void CollisionSystem()
{
CollisionActor(&player1); // player1碰撞检测
CollisionActor(&player2); // player2碰撞检测
CollisionObject(); // 物体碰撞检测
}
(7)游戏画面渲染
功能展示:
实现代码:
// 更新游戏画面
void showgame()
{
putimage(0, 0, getwidth(), getheight(), img_bk, 0, 0, 4250, 2000);
// 渲染block
for (int i = 0; i < num_block; i++)
{
blocks[i].show();
}
// 渲染传送门
for (int i = 0; i <= num_door; i++)
{
doors[i].show();
}
player1.show(Game_kx, Game_ky);
player2.show(Game_kx, Game_ky);
// 渲染子弹
for (int i = 0; i < num_bullet; i++)
{
bullets[i]->show(Game_kx, Game_ky);
}
// 渲染箱子
for (int i = 0; i < num_box; i++)
{
box1[i]->box_show(Game_kx, Game_ky);
}
// 渲染生命条
if (player1.life > 0)
{
setfillcolor(EGERGB(241, 83, 83));
bar(player1.x - 5, player1.y - 15, player1.x + player1.life * 5, player1.y - 10);
}
if (player2.life > 0)
{
setfillcolor(EGERGB(241, 83, 83));
bar(player2.x - 5, player2.y - 15, player2.x + player2.life * 5, player2.y - 10);
}
}
欢迎下载体验又部分懒得改的bug