項目經理帶你 - 零基礎手寫坦克大戰

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/

  1. 安裝

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);

………………………………………………………………………………………………………………………………………………………………
需要更多,歡迎進羣!

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