C/C++ 手寫“90坦克大戰遊戲”

今天給大家分享使用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的各位,五一快樂!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章