1.2 項目介紹
2.1. 項目需求
實現1款和經典的《90坦克大戰》一樣的遊戲,任務是消滅敵對坦克,保護己方領地。防止敵方打破你的老窩圍牆而把你的鷹打壞。
2.2. 學習目標
回顧經典,按照軟件開發的標準流程,零基礎開發第一個完整項目,快速提高編程能力和水平,真正理解軟件開發的概念和基本流程!
2.3. 授課方式
手把手方式教大家完整的實現項目所有源碼並進行講解,不懂之處,隨時可以諮詢Martin老師答疑!QQ: 2684436901
2.項目準備
2.1 環境安裝
請參考《項目經理帶你-零基礎學習C++》 第6節 C++開發環境 安裝好開發工具vc2010 ,課程鏈接:https://ke.qq.com/course/377567?tuin=cc02ada
2.2圖形庫安裝
1.下載easyx 圖形庫 網址:https://www.easyx.cn/
- 安裝
2.3開發環境測試
#include <graphics.h>
void main()
{
initgraph(666, 666); //定義畫布大小 666*666
system("pause");
}
3.項目啓動
3.1模塊劃分
(作用:1.化繁爲簡 2.適合團隊協作 3.高質量代碼)
3.2源準備
美工素材請從奇牛C/C++學習交流羣文件夾下載
羣號:875300321
下載路徑: 羣文件夾 => 坦克大戰資源 => 坦克大戰_圖片&音樂.rar
4.項目實現
4.1模塊1 - 開始場景
“搭臺唱戲”要素:
-
“戲臺” - 繪圖環境
- Logo - 美工圖片,遊戲標誌
- 按鈕 - 實現“說明”和“開始”導航
- 說明 - 美工圖片,操作說明
4.1.1戲臺實現
#include <graphics.h>
void main()
{
initgraph(650, 650); //定義畫布大小 650*650
system("pause");
}
項目精講 - 像素
像素是整個圖像中不可分割的單位或者是元素,每個像素近似一個小方塊,這些小方塊都有一個明確的位置和被分配的色彩數值(顯示不同的顏色)
項目精講 - 分辨率
分辨率(屏幕分辨率)是屏幕圖像的精密度,是指顯示器所能顯示的像素有多少,分辨率越大,單位面積內分佈的像素點就越多,畫面就越精細
如: 14英寸筆記本屏幕分辨率 1280 x 960 表示的意義是屏幕是由 1280 乘以 960 = 1228800 個像素點組成,其中寬佔1280 個像素,高佔960 像素
4.1.2顯示LOGO
//顯示 logo
IMAGE logo_img;
loadimage(&logo_img, _T("logo.bmp"), 433, 147);
putimage(110, 20, &logo_img);
void menu(){
//顯示 logo
IMAGE logo_img;
loadimage(&logo_img, _T("logo.bmp"), 433, 147);
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==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 ;
}
}
}
}
4.1.3導航按鈕實現
使用矩形繪製函數和文字輸出函數實現按鈕顯示效果
附:EasyX 幫助文檔
羣號:875300321
下載路徑: 羣文件夾 => 坦克大戰資源 => EasyX_Help.chm
- 使用鼠標事件實現導航效果
1.當鼠標移到 “說明” 按鈕時,顯示操作說明,當鼠標離開隱藏
2.當鼠標點擊“開始”按鈕時,就進入 遊戲場景
4.2模塊2 - 遊戲場景
地圖初始化
名稱 | 王 | 後 | 車 | 象 | 馬 | 兵 | |
---|---|---|---|---|---|---|---|
角色 | 國王 | 皇后 | 戰車 | 主教 | 騎士 | 禁衛軍 |
地圖表示:
使用二維數組
- 遊戲道具顯示(牆、老鷹、我方坦克、敵方坦克、子彈)
- 便於程序控制敵方坦克前進,控制子彈移動和判斷子彈擊中目標等
道具表示:
可消除牆爲1,不可消除牆爲2,老鷹(3,4),敵方坦克 100 - 109,我方坦克200
代碼實現
//定義地圖數組
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 menu();
void init_map();
void init_map_2(int *map, int rows, int cols);
int main(void){
//搭建舞臺
initgraph(650,650);
//開始場景,顯示菜單
menu();
//初始化地圖
init_map_2(&map[0][0], 26, 26);
system("pause");
}
/*
初始化地圖,可消除牆爲1,不可消除牆爲 2,老鷹 (3 ,4)
*/
void init_map(){
int i, j;
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(i=0; i<26; i++){
for(j=0; j<26; j++){
if(map[i][j] == 1){
putimage(25*j, 25*i, &img_wall_2);
}else if(map[i][j] == 2){
putimage(25*j, 25*i, &img_wall_1);
}else if(map[i][j] == 3){
putimage(25*j, 25*i, &img_home);
map[i][j] = 4;
map[i][j+1] = 4;
map[i+1][j] = 4;
map[i+1][j+1] = 4;
}
}
}
}
//逼格更高、兼容性更好的初始化地圖函數
void init_map_2(int *map, int rows, int cols){
int i, j;
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(i=0; i<rows; i++){
for(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);
//以下代碼需要考慮數組越界,該如何處理?
*(map+cols*i+j) = 4;
*(map+cols*i+(j+1)) = 4;
*(map+cols*(i+1)+j) = 4;
*(map+cols*(i+1)+(j+1)) = 4;
}
}
}
}
4.2.2我方坦克實現
坦克結構體定義
enum DIRECTION{
UP,
DOWN,
LEFT,
RIGHT
};
//坦克結構體
struct tank_s{
int x; //坦克在地圖數組中所在列
int y; //坦克在地圖數組中所在的行
DIRECTION direction; //坦克的方向,上、下、左、右
int live; //是否生存 1-活着 0-掛了
};
我方坦克顯示
tank_s my_tank;
IMAGE my_tank_img[4];
//加載我方坦克的圖片
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);
//設定我方坦克的出場的位置
my_tank.x = 8;
my_tank.y = 24;
my_tank.live = 1;
my_tank.direction = UP;
map[my_tank.y][my_tank.x] =200;
map[my_tank.y][my_tank.x+1] =200;
map[my_tank.y+1][my_tank.x] =200;
map[my_tank.y+1][my_tank.x+1] =200;
putimage(my_tank.x * 25, my_tank.y * 25, &my_tank_img[my_tank.direction]);
熱鍵控制(aswd )
/*****************************
*實現遊戲場景
******************************/
void play(){
tank_s my_tank;
IMAGE my_tank_img[4];
int key;
//加載我方坦克的圖片
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);
//設定我方坦克的出場的位置
my_tank.x = 8;
my_tank.y = 24;
my_tank.live = 1;
my_tank.direction = UP;
set_prop_map(my_tank.x, my_tank.y, 200);
putimage(my_tank.x * 25, my_tank.y * 25, &my_tank_img[my_tank.direction]);
while(1){
if(_kbhit()){
key = _getch();
switch(key){
case 'a': //左
if((my_tank.x-1)>=0 && map[my_tank.y][my_tank.x-1] ==0 &&map[my_tank.y+1][my_tank.x-1] ==0){//左邊是空地
my_tank.direction = LEFT;
tank_walk(&my_tank, LEFT, &my_tank_img[my_tank.direction]);
}
break;
case 'w': //上
if((my_tank.y-1)>=0 && map[my_tank.y-1][my_tank.x] ==0 &&map[my_tank.y-1][my_tank.x+1] ==0){//上邊是空地
my_tank.direction = UP;
tank_walk(&my_tank, UP, &my_tank_img[my_tank.direction]);
}
break;
case 's': //下
if((my_tank.y+2)<=25 && map[my_tank.y+2][my_tank.x] ==0 &&map[my_tank.y+2][my_tank.x+1] ==0){//下邊是空地
my_tank.direction = DOWN;
tank_walk(&my_tank, DOWN, &my_tank_img[my_tank.direction]);
}
break;
case 'd': //右
if((my_tank.x+2)<=25 && map[my_tank.y][my_tank.x+2] ==0 &&map[my_tank.y+1][my_tank.x+2] ==0){//右邊是空地
my_tank.direction = RIGHT;
tank_walk(&my_tank, RIGHT,&my_tank_img[my_tank.direction]);
}
break;
case 'j': //開火
break;
case 'p': //暫停
system("pause");
break;
default: //其他鍵盤輸入無須處理
break;
}
}
Sleep(10);
}
}
坦克移動
void set_prop_map(int x, int y, int val){
map[y][x] = val;
map[y][x+1] = val;
map[y+1][x] = val;
map[y+1][x+1] = val;
}
/*********************************
*控制坦克按相應的方向前進一步
*返回值:失敗 - 0 成功 -1
*********************************/
int tank_walk(tank_s *tank, DIRECTION direction, IMAGE *img){
int new_x = tank->x;
int new_y = tank->y;
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);
set_prop_map(new_x, new_y, 200);
tank->x = new_x;
tank->y = new_y;
putimage(tank->x * 25, tank->y * 25, img);
return 1;
}
4.2.3子彈飛行控制實現
子彈結構體定義
//子彈結構體
struct bullet_s{
int pos_x; //子彈在“戲臺”上的橫座標
int pos_y; //子彈在“戲臺”上的縱座標
DIRECTION direction; //子彈方向
int status; //子彈是否存在
};
子彈熱鍵控制 (j)
if(my_bullet.status == 0){
if(my_tank.direction == UP){
my_bullet.pos_x = my_tank.x * 25 + 23;
my_bullet.pos_y = my_tank.y * 25 -3;
}else if(my_tank.direction == LEFT){
my_bullet.pos_x = my_tank.x * 25 -3;
my_bullet.pos_y = my_tank.y * 25 +23;
}else if(my_tank.direction == DOWN){
my_bullet.pos_x = my_tank.x * 25 + 23;
my_bullet.pos_y = my_tank.y * 25 + 50;
}else if(my_tank.direction == RIGHT){
my_bullet.pos_x = my_tank.x * 25 + 50;
my_bullet.pos_y = my_tank.y * 25 + 23;
}
my_bullet.direction = my_tank.direction;
my_bullet.status = 1;
}
子彈運行和碰撞檢測
void bullet_action(bullet_s *bullet){
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;
}
if(bullet->pos_x<0 || bullet->pos_x>650 || bullet->pos_y<0 || bullet->pos_y>650){
bullet->status = 0;
return;
}
//碰撞檢測
if(map[y][x] == 4 || map[y1][x1] == 4){
return ;
}
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){
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);
}
}
4.2.4敵方坦克實現
坦克出場
1.首先出場3臺坦克,然後每隔小段時間出場一輛坦克
tank_s enemy_tank[ENEMY_NUM]; //敵方坦克
bullet_s enemy_bullet[ENEMY_NUM];//敵方坦克發射的子彈
IMAGE enemy_tank_img[4];
//加載敵方坦克的圖片
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);
//設置敵方坦克出場的位置
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;
}
//前3輛坦克閃亮登場
tank_walk(&enemy_tank[0], DOWN, &enemy_tank_img[DOWN], 0);
tank_walk(&enemy_tank[1], DOWN, &enemy_tank_img[DOWN], 0);
tank_walk(&enemy_tank[2], DOWN, &enemy_tank_img[DOWN], 0);
………………………………………………………………………………………………………………………………………………………………
需要更多,歡迎進羣!