今天给大家分享使用C语言配合easy-x图形库开发的一款控制台桌面小游戏。(IDE:请使用vs2010或者vs2019)
资源下载链接:
百度网盘
链接:https://pan.baidu.com/s/1KYtZDYwbR_X62k_Swl3anQ
提取码:8qa4
或者点击这里!!!
相信不管是80后还是90后,更或者是00后,应该都玩过“09坦克大战”这款经典的FP游戏吧,现在,给大家分享这款游戏的运行代码。(个人观点)
程序还有许多地方需要去优化的,有兴趣的小伙伴可以下载回去自己琢磨琢磨,修改完善。
当然,地图也是只有一份,有兴趣的小伙伴,可以自行下载资源回去,对地图进行增加。
由于代码量有点大,在这里就不一一讲解了。
当然本款小游戏也是在腾讯课堂骑牛学院martin老师的指点下完成的,有兴趣学习C/C++语言的小伙伴可以去腾讯课堂官网搜索骑牛学院!
废话不多说,先上游戏截图:
好了,游戏的效果图已经展现给大家看了,那么下面请看代码:
#include <graphics.h>
#include <conio.h>
#include <Windows.h>
#include <time.h>
#include <MMSystem.h>
#pragma comment (lib, "winmm.lib") // 导入声音库
#define ENEMY_NUM 10
enum DIRECTION {
UP,
DOWN,
LEFT,
RIGHT
};
// 坦克结构体
struct tank_s {
int x; // 坦克在地图数组中所在的列
int y; // 坦克在地图数组中所在的行
DIRECTION direction; // 坦克的方向,上、下、左、右
int live; // 坦克是否生存 1 -- 活着 0 -- 死了
};
// 子弹结构体
struct bullet_s {
int pos_x; // 子弹再窗口上的横坐标
int pos_y; // 子弹再窗口上的纵坐标
DIRECTION direction; // 子弹方向
int status; // 子弹的状态
};
// 定义地图数组
int map[26][26] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1 },
{ 2, 2, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
void set_prop_map(int x, int y, int value) {
map[y][x] = value;
map[y][x + 1] = value;
map[y + 1][x] = value;
map[y + 1][x + 1] = value;
}
void menu() {
// 显示logo
IMAGE logo_img;
loadimage(&logo_img, _T("logo.bmp"), 433, 174); // 将图片加载进logo_img变量中
putimage(110, 20, &logo_img);
// 实现导航按钮
setlinecolor(WHITE);
setfillcolor(BLACK);
fillrectangle(230, 200, 310, 240);
settextstyle(25, 0, _T("宋体"));
outtextxy(240, 210, _T("说 明"));
fillrectangle(350, 200, 430, 240);
outtextxy(360, 210, _T("开 始"));
MOUSEMSG mouse; // 定义鼠标
IMAGE illustrate_img;
loadimage(&illustrate_img, _T("illustrate.jpg"), 300, 300);
while (1) {
mouse = GetMouseMsg(); // 获取鼠标消息
switch (mouse.uMsg) {
case WM_MOUSEMOVE: // 鼠标移动事件
if ((mouse.x > 230 && mouse.x < 310) && (mouse.y > 200 && mouse.y < 240)) {
putimage(150, 250, &illustrate_img);
}
else {
solidrectangle(150, 250, 450, 550); // 绘制一个无边框的矩形
}
break;
case WM_LBUTTONDOWN: // 鼠标左键点击事件
if ((mouse.x > 350 && mouse.x < 430) && (mouse.y > 200 && mouse.y < 240)) {
cleardevice();
return;
}
}
}
}
// 初始化地图
// 可消除的墙1;不可消除的墙2;碉堡(3,4)
void initMap(int *map, int rows, int cols) {
IMAGE img_home, img_wall_1, img_wall_2;
loadimage(&img_home, _T("home.jpg"), 50, 50); // 碉堡
loadimage(&img_wall_1, _T("wall1.jpg"), 25, 25); // 不可消除的墙
loadimage(&img_wall_2, _T("wall2.jpg"), 25, 25); // 可消除的墙
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (*(map + cols * i + j) == 1) {
putimage(25 * j, 25 * i, &img_wall_2);
} else if (*(map + cols * i + j) == 2) {
putimage(25 * j, 25 * i, &img_wall_1);
} else if (*(map + cols * i + j) == 3) {
putimage(25 * j, 25 * i, &img_home);
set_prop_map(j, i, 4);
}
}
}
}
// 控制坦克按相应的位置前进一步
// 成功返回1;失败返回0
int do_tank_walk(tank_s *tank, DIRECTION direction, IMAGE *img, int step) {
int new_x = tank->x;
int new_y = tank->y;
int old_prop = map[tank->y][tank->x];
if (step) {
if (direction == UP) {
new_y -= 1;
} else if (direction == DOWN) {
new_y += 1;
} else if (direction == LEFT) {
new_x -= 1;
} else if (direction == RIGHT) {
new_x += 1;
} else {
return 0; // 无效方向
}
set_prop_map(tank->x, tank->y, 0);
}
setfillcolor(BLACK);
solidrectangle(tank->x * 25, tank->y * 25, tank->x * 25 + 50, tank->y * 25 + 50);
if (step) {
set_prop_map(new_x, new_y, old_prop);
tank->x = new_x;
tank->y = new_y;
}
putimage(tank->x * 25, tank->y * 25, img);
return 1;
}
// 实现子弹运行和碰撞检测,并反馈游戏结果
// 返回:1 - 表示游戏失败 0 - 继续游戏
int bullet_ation(bullet_s *bullet, tank_s *enemy_tank) {
int x, y, x1, y1; // 子弹目前所在的二维数组的坐标
x = bullet->pos_x / 25;
y = bullet->pos_y / 25;
// 1.擦除上一次绘制的子弹
setfillcolor(BLACK);
solidrectangle(bullet->pos_x, bullet->pos_y, bullet->pos_x + 3, bullet->pos_y + 3);
// 2.根据方向计算子弹在地图上的坐标
if (bullet->direction == UP) {
bullet->pos_y -= 2;
x1 = x + 1;
y1 = y;
} else if (bullet->direction == DOWN) {
bullet->pos_y += 2;
x1 = x + 1;
y1 = y;
} else if (bullet->direction == LEFT) {
bullet->pos_x -= 2;
x1 = x;
y1 = y + 1;
} else if (bullet->direction == RIGHT) {
bullet->pos_x += 2;
x1 = x;
y1 = y + 1;
} else {
return 0;
}
if (bullet->pos_x < 0 || bullet->pos_x > 650 || bullet->pos_y < 0 || bullet->pos_y > 650) {
bullet->status = 0;
return 0;
}
// 碰撞检查
if (map[y][x] == 4 || map[y1][x1] == 4) { // 子弹击中碉堡
//bullet->status = 0;
return 1;
}
if (map[y][x] == 200 || map[y1][x1] == 200) { // 击中我方坦克
return 1;
}
if ((map[y][x] >= 100 && map[y][x] <= 109) || (map[y1][x1] >= 100 && map[y1][x1] <= 109)) {
tank_s *tank = NULL;
bullet->status = 0;
if (map[y][x] >= 100 && map[y][x] <= 109) {
tank = enemy_tank + (map[y][x] - 100);
} else {
tank = enemy_tank + (map[y1][x1] - 100);
}
tank->live = 0;
set_prop_map(tank->x, tank->y, 0);
setfillcolor(BLACK);
solidrectangle(tank->x * 25, tank->y * 25, tank->x * 25 + 50, tank->y * 25 + 50);
}
if (map[y][x] == 1) { // 子弹击中可消除的墙
map[y][x] = 0;
bullet->status = 0;
setfillcolor(BLACK);
solidrectangle(x * 25, y * 25, x * 25 + 25, y * 25 + 25);
} else if (map[y][x] == 2) { // 不可消除的墙
//PlaySound(_T("hit.wav"), NULL, SND_FILENAME | SND_ASYNC);
bullet->status = 0;
}
if (map[y1][x1] == 1) { // 子弹击中可消除的墙
map[y1][x1] = 0;
bullet->status = 0;
setfillcolor(BLACK);
solidrectangle(x1 * 25, y1 * 25, x1 * 25 + 25, y1 * 25 + 25);
} else if (map[y1][x1] == 2) { // 不可消除的墙
bullet->status = 0;
}
// 3.重新绘制子弹
if (bullet->status == 1) {
setfillcolor(WHITE);
solidrectangle(bullet->pos_x, bullet->pos_y, bullet->pos_x + 3, bullet->pos_y + 3);
}
return 0;
}
// 坦克前进一步
void tank_walk(tank_s *tank, DIRECTION direction, IMAGE *img) {
switch (direction) {
case UP: // 上
if (tank->direction == UP && (tank->y - 1) >= 0 && map[tank->y - 1][tank->x] == 0 && map[tank->y - 1][tank->x + 1] == 0) {
do_tank_walk(tank, UP, img, 1);
}
else if (tank->direction != UP) {
tank->direction = UP;
do_tank_walk(tank, UP, img, 0);
}
break;
case DOWN: // 下
if (tank->direction == DOWN && (tank->y + 2) <= 25 && map[tank->y + 2][tank->x] == 0 && map[tank->y + 2][tank->x + 1] == 0) {
do_tank_walk(tank, DOWN, img, 1);
}
else if (tank->direction != DOWN) {
tank->direction = DOWN;
do_tank_walk(tank, DOWN, img, 0);
}
break;
case LEFT: // 左
if (tank->direction == LEFT && (tank->x - 1) >= 0 && map[tank->y][tank->x - 1] == 0 && map[tank->y + 1][tank->x - 1] == 0) {
do_tank_walk(tank, LEFT, img, 1);
}
else if (tank->direction != LEFT) {
tank->direction = LEFT;
do_tank_walk(tank, LEFT, img, 0);
}
break;
case RIGHT: // 右
if (tank->direction == RIGHT && (tank->x + 2) <= 25 && map[tank->y][tank->x + 2] == 0 && map[tank->y + 1][tank->x + 2] == 0) {
do_tank_walk(tank, RIGHT, img, 1);
}
else if (tank->direction != RIGHT) {
tank->direction = RIGHT;
do_tank_walk(tank, RIGHT, img, 0);
}
break;
default: // 其他键无需操作
break;
}
}
// 根据目标位置,调整敌方坦克的方向
DIRECTION enemy_direction(tank_s *tank, int x, int y) {
int r = rand() % 100;
if (tank->x > x) { // 目标再左边
if (tank->y > y) { // 目标在左上方
if (r <= 50) return UP;
return LEFT;
} else { // 目标在左下方
if (r <= 50) return DOWN;
return LEFT;
}
} else { // 目标再右边
if (tank->y > y) { // 目标在右上方
if (r <= 50) return UP;
return RIGHT;
} else { // 目标在右下方
if (r <= 50) return DOWN;
return RIGHT;
}
}
}
// 坦克开火
void tank_fire(tank_s *tank, bullet_s *bullet, int need_sound) {
if (bullet->status == 0) {
if (need_sound) {
PlaySound(_T("fire.wav"), NULL, SND_FILENAME | SND_ASYNC);
}
if (tank->direction == UP) {
bullet->pos_x = tank->x * 25 + 23;
bullet->pos_y = tank->y * 25 - 3;
}
else if (tank->direction == DOWN) {
bullet->pos_x = tank->x * 25 + 23;
bullet->pos_y = tank->y * 25 + 53;
}
else if (tank->direction == LEFT) {
bullet->pos_x = tank->x * 25 - 3;
bullet->pos_y = tank->y * 25 + 23;
}
else if (tank->direction == RIGHT) {
bullet->pos_x = tank->x * 25 + 53;
bullet->pos_y = tank->y * 25 + 23;
}
bullet->direction = tank->direction;
bullet->status = 1;
}
}
/****************************************
* 实现游戏场景
*****************************************/
int play() {
tank_s my_tank; // 我方坦克
bullet_s my_bullet; // 我方坦克发射的子弹
tank_s enemy_tank[ENEMY_NUM]; // 敌方坦克
bullet_s enemy_bullet[ENEMY_NUM]; // 敌方坦克发射的子弹
IMAGE my_tank_img[4]; // 我方坦克的显示图片
IMAGE enemy_tank_img[4];// 地方坦克的显示图片
int key;
int times = 0; // 记录当前程序的休眠次数,每次10ms
int enemy_total = 0;
// 初始化随机种子
srand(time(NULL));
// 游戏背景音乐
mciSendString(_T("play background.wav"), 0, 0, 0);
// 加载我方坦克的图片
loadimage(&my_tank_img[UP], _T("tank_up.jpg"), 50, 50);
loadimage(&my_tank_img[DOWN], _T("tank_down.jpg"), 50, 50);
loadimage(&my_tank_img[LEFT], _T("tank_left.jpg"), 50, 50);
loadimage(&my_tank_img[RIGHT], _T("tank_right.jpg"), 50, 50);
// 加载地方坦克的图片
loadimage(&enemy_tank_img[UP], _T("enemy_tank_up.jpg"), 50, 50);
loadimage(&enemy_tank_img[DOWN], _T("enemy_tank_down.jpg"), 50, 50);
loadimage(&enemy_tank_img[LEFT], _T("enemy_tank_left.jpg"), 50, 50);
loadimage(&enemy_tank_img[RIGHT], _T("enemy_tank_right.jpg"), 50, 50);
// 1 :子弹存在 0 : 子弹不存在
my_bullet.status = 0;
// 我方坦克出现的位置
my_tank.x = 8;
my_tank.y = 24;
my_tank.live = 1;
my_tank.direction = UP;
// 敌方坦克出现的位置
for (int i = 0; i < ENEMY_NUM; i++) {
if (i % 3 == 0) {
enemy_tank[i].x = 0;
}
else if (i % 3 == 1) {
enemy_tank[i].x = 12;
}
else if (i % 3 == 2) {
enemy_tank[i].x = 24;
}
enemy_tank[i].direction = DOWN;
enemy_tank[i].y = 0;
enemy_tank[i].live = 1;
//set_prop_map(enemy_tank[i].x, enemy_tank[i].y, 100 + i);
enemy_bullet[i].status = 0;
}
// 前三辆坦克出现
do_tank_walk(&enemy_tank[0], DOWN, &enemy_tank_img[DOWN], 0);
set_prop_map(enemy_tank[0].x, enemy_tank[0].y, 100);
do_tank_walk(&enemy_tank[1], DOWN, &enemy_tank_img[DOWN], 0);
set_prop_map(enemy_tank[1].x, enemy_tank[1].y, 101);
do_tank_walk(&enemy_tank[2], DOWN, &enemy_tank_img[DOWN], 0);
set_prop_map(enemy_tank[2].x, enemy_tank[2].y, 102);
enemy_total = 3;
set_prop_map(my_tank.x, my_tank.y, 200);
// 显示我方坦克
putimage(25 * my_tank.x, 25 * my_tank.y, &my_tank_img[my_tank.direction]);
while (1) {
if (times > 0 && times % 1000 == 0 && enemy_total < ENEMY_NUM) {
set_prop_map(enemy_tank[enemy_total].x, enemy_tank[enemy_total].y, 100 + enemy_total);
enemy_total++;
}
if (times % 100 == 0) {
for (int i = 0; i < enemy_total; i++) {
if (enemy_tank[i].live == 0) continue;
if (i % 2 == 0) { // 双数攻击我方老鹰
DIRECTION d = enemy_direction(&enemy_tank[i], 12, 24);
tank_walk(&enemy_tank[i], d, &enemy_tank_img[d]);
} else { // 单数攻击我方坦克
DIRECTION d = enemy_direction(&enemy_tank[i], my_tank.x, my_tank.y);
tank_walk(&enemy_tank[i], d, &enemy_tank_img[d]);
}
tank_fire(&enemy_tank[i], &enemy_bullet[i], 0);
}
} else if (times % 100 == 0) { // 敌方坦克还生存
for (int i = 0; i < enemy_total; i++) {
if (enemy_tank[i].live) {
tank_walk(&enemy_tank[i], enemy_tank[i].direction, &enemy_tank_img[enemy_tank->direction]);
}
}
}
if (_kbhit()) {
key = _getch();
switch (key) {
case 'w': // 上
tank_walk(&my_tank, UP, &my_tank_img[UP]);
break;
case 's': // 下
tank_walk(&my_tank, DOWN, &my_tank_img[DOWN]);
break;
case 'a': // 左
tank_walk(&my_tank, LEFT, &my_tank_img[LEFT]);
break;
case 'd': // 右
tank_walk(&my_tank, RIGHT, &my_tank_img[RIGHT]);
break;
case 'j': // 开火
tank_fire(&my_tank, &my_bullet, 1);
break;
case 'p': // 暂停
system("pause");
break;
default: // 其他键无需操作
break;
}
}
if (my_bullet.status == 1) {
if (bullet_ation(&my_bullet, enemy_tank)) {
return 1;
}
}
for (int i = 0; i < ENEMY_NUM; i++) {
if (enemy_bullet[i].status == 1) {
if (bullet_ation(&enemy_bullet[i], enemy_tank)) {
return 1;
}
}
}
// 判断敌方坦克是否全部被消灭
int isWin = 1;
for (int i = 0; i < ENEMY_NUM; i++) {
if (enemy_tank[i].live == 1) {
isWin = 0;
break;
}
}
if (isWin) return 0;
Sleep(5);
times++;
}
}
void game_voer(int result) {
IMAGE img;
if (result == 1) { // 等于1表示失败
loadimage(&img, _T("failure.jpg"), 500, 250);
putimage(80, 200, &img);
} else if (result == 0) { // 等于0表示成功
loadimage(&img, _T("success.jpg"), 500, 250);
putimage(80, 200, &img);
}
_getch();
}
int main(void) {
int result = 0;
initgraph(650, 650);
// 开始场景,显示菜单
menu();
// 初始化地图
initMap(*map, 26, 26);
result = play();
// 显示游戏结果
game_voer(result);
system("pause");
closegraph();
return 0;
}
最后祝csdn的各位,五一快乐!