??[導(dǎo)讀]本系列博文內(nèi)容鏈接如下:
【C++】做一個飛機空戰(zhàn)小游戲(一)——使用getch()函數(shù)獲得鍵盤碼值
【C++】做一個飛機空戰(zhàn)小游戲(二)——利用getch()函數(shù)實現(xiàn)鍵盤控制單個字符移動
【C++】做一個飛機空戰(zhàn)小游戲(三)——getch()函數(shù)控制任意造型飛機圖標移動【C++】做一個飛機空戰(zhàn)小游戲(四)——給游戲添加背景音樂(多線程技巧應(yīng)用)
【C++】做一個飛機空戰(zhàn)小游戲(五)——getch()控制兩個飛機圖標移動(控制光標位置)
【C++】做一個飛機空戰(zhàn)小游戲(六)——給兩架飛機設(shè)置不同顏色(cout輸出彩色字符、結(jié)構(gòu)體使用技巧)
【C++】做一個飛機空戰(zhàn)小游戲(七)——兩組按鍵同時檢測平滑移動(GetAsyncKeyState()函數(shù)應(yīng)用)
【C++】做一個飛機空戰(zhàn)小游戲(八)——生成敵方炮彈(rand()函數(shù)應(yīng)用)
【C++】做一個飛機空戰(zhàn)小游戲(九)——發(fā)射子彈的編程技巧
【C++】做一個飛機空戰(zhàn)小游戲(十)——子彈擊落炮彈、炮彈與飛機相撞?
在第五節(jié)【C++】做一個飛機空戰(zhàn)小游戲(五)——getch()控制兩個飛機圖標移動(控制光標位置)中實現(xiàn)了通過鍵盤控制兩架飛機的效果,但是兩架飛機顏色造型一樣,很難區(qū)分,所以需要給兩架飛機選用不同的顏色、不同造型,這樣就好辨別了。
本節(jié)之前飛機只有位置、造型兩個屬性,所以可以用獨立的變量來聲明即可。隨著程序設(shè)計的深入,飛機的屬性越來越多,就需要構(gòu)造飛機的結(jié)構(gòu)體,把各項屬性都放到飛機結(jié)構(gòu)體中。這樣,可以減少聲明變量的個數(shù),減少自定義函數(shù)的形參。使程序的邏輯性更清晰,代碼量更少。
目錄
一、結(jié)構(gòu)體
(一)定義
(二)使用結(jié)構(gòu)體需要注意幾點
1、結(jié)構(gòu)體是一種數(shù)據(jù)類型
2、結(jié)構(gòu)體需要先構(gòu)造后使用
3、結(jié)構(gòu)體的成員也可以是結(jié)構(gòu)體類型數(shù)據(jù)
4、結(jié)構(gòu)體是可以擴展的
(三)參考博文
二、本文用到的結(jié)構(gòu)體
(一)位置坐標結(jié)構(gòu)體
1、構(gòu)造形式
2、功能
3、用途
(二)飛機結(jié)構(gòu)體
1、構(gòu)造形式
2、功能
3、用途
三、優(yōu)化程序
(一)聲明數(shù)據(jù)類型
1、聲明全局結(jié)構(gòu)體數(shù)組
2、優(yōu)化按鍵指令存儲枚舉變量
(二)已有自定義函數(shù)的優(yōu)化
1、形參數(shù)量減少
2、返回型函數(shù)類型減少
(三)新增自定義函數(shù)
1、新增獲取按鍵鍵值線程函數(shù)
2、新增彩色字符輸出函數(shù)
3、新增隱藏光標函數(shù)
(四)優(yōu)化后的程序代碼
1、主函數(shù)
2、頭文件control_plane.h
3、庫函數(shù)control_plane.cpp
五、運行效果
一、結(jié)構(gòu)體
(一)定義
結(jié)構(gòu)體就是一個可以包含不同數(shù)據(jù)類型的一個結(jié)構(gòu),它是一種可以自己定義的數(shù)據(jù)類型,它的特點和數(shù)組主要有兩點不同,首先結(jié)構(gòu)體可以在一個結(jié)構(gòu)中聲明不同的數(shù)據(jù)類型。第二,相同結(jié)構(gòu)的結(jié)構(gòu)體變量是可以相互賦值的,而數(shù)組是做不到的,因為數(shù)組是單一數(shù)據(jù)類型的數(shù)據(jù)集合,它本身不是數(shù)據(jù)類型(而結(jié)構(gòu)體是),數(shù)組名稱是常量指針,所以不可以做為左值進行運算,所以數(shù)組之間就不能通過數(shù)組名稱相互復(fù)制了,即使數(shù)據(jù)類型和數(shù)組大小完全相同。
(二)使用結(jié)構(gòu)體需要注意幾點
1、結(jié)構(gòu)體是一種數(shù)據(jù)類型
結(jié)構(gòu)體就是把若干種數(shù)據(jù)類型的放到一起,構(gòu)造了一個新的數(shù)據(jù)類型。所以關(guān)于結(jié)構(gòu)體變量的操作和其他的數(shù)據(jù)類型有好多相似的地方。
聲明變量的過程實際就是到內(nèi)存中劃分一片存儲空間給這個變量使用,變量名就是這片空間的標記符號,不同的數(shù)據(jù)類型所占空間不同,所以聲明變量的時候需要明確數(shù)據(jù)類型。結(jié)構(gòu)體變量的聲明后,其獲得的空間就是結(jié)構(gòu)體內(nèi)所有成員所需的空間。
2、結(jié)構(gòu)體需要先構(gòu)造后使用
結(jié)構(gòu)體是用戶自己根據(jù)需求定制的一種數(shù)據(jù)類型,不是系統(tǒng)就有的數(shù)據(jù)類型,所以必須先構(gòu)造一個結(jié)構(gòu)體數(shù)據(jù)類型,然后再用這個結(jié)構(gòu)體數(shù)據(jù)類型聲明變量,再對變量進行其他的操作。
3、結(jié)構(gòu)體的成員也可以是結(jié)構(gòu)體類型數(shù)據(jù)
也就是結(jié)構(gòu)體是可以嵌套的,還可以多層嵌套。
4、結(jié)構(gòu)體是可以擴展的
結(jié)構(gòu)體構(gòu)造完成后,還可以進行增加、減少或者改變成員。
(三)參考博文
關(guān)于結(jié)構(gòu)體基礎(chǔ)知識介紹的博文有很多,本文不在詳細介紹,具體內(nèi)容可以參考以下博文:
c語言基礎(chǔ)語法六——結(jié)構(gòu)體(完結(jié))_賣酒的小碼農(nóng)的博客-CSDN博客
二、本文用到的結(jié)構(gòu)體
(一)位置坐標結(jié)構(gòu)體
1、構(gòu)造形式
//定義坐標結(jié)構(gòu)體
typedef struct{
int x;
int y;
} Location;
2、功能
這個結(jié)構(gòu)體數(shù)據(jù)類型的功能是用來存放屏幕上點的坐標。Location為結(jié)構(gòu)體類型名稱,其有兩個整形的成員:x,y,分別代表x坐標和y坐標。
3、用途
可以用來存儲飛機、炮彈、和其他字符信息要顯示的位置。
(二)飛機結(jié)構(gòu)體
1、構(gòu)造形式
//定義飛機結(jié)構(gòu)體
typedef struct{
Location location;
int color;
int icon;
direction_cmd keycmd;
}Plane;
2、功能
這個結(jié)構(gòu)體數(shù)據(jù)類型的功能是用來存儲飛機屬性信息的。Plane為結(jié)構(gòu)體類型名稱,其有1個Location類型的成員location,用來存儲飛機坐標;1個int型的成員color,用來存儲飛機的顏色;1個int型的成員icon,用來存儲飛機的造型;1個direction_cmd形的成員keycmd,用來存儲飛機接收到的鍵盤指令。
注意:direction_cmd是本例定義的一個枚舉型的數(shù)據(jù)類型,其內(nèi)部暫時存儲了飛機所能接受的"上、下、左、右"和“無”4個鍵盤指令名稱。
typedef enum {none_cmd,up_cmd,down_cmd,left_cmd,right_cmd} direction_cmd;
3、用途
這個結(jié)構(gòu)體的用途就是聲明飛機,聲明后可以通過改變其成員(屬性)的值,定制出風(fēng)格不同的飛機。還可以在程序里根據(jù)屬性值的不同做出不同的反應(yīng),呈現(xiàn)不同的狀態(tài),具有不同的信息,做出不同的動作。
三、優(yōu)化程序
聲明了飛機結(jié)構(gòu)體之后,位置信息、按鍵指令信息、飛機圖標信息、顏色信息都在結(jié)構(gòu)體之內(nèi)了,以上4個信息都不用單獨聲明了,聲明一個結(jié)構(gòu)體就都包含了。另外,兩個飛機就可以用一個結(jié)構(gòu)體數(shù)組來表示,飛機的序號可以用數(shù)組的角標區(qū)分。
(一)聲明數(shù)據(jù)類型
1、聲明全局結(jié)構(gòu)體數(shù)組
聲明全局Plane plane[num_plane],用于存放和傳遞各種信息。采用數(shù)組方式,給賦值帶了很大便利,減少代碼量。
#define num_plane 2 //飛機架數(shù)
extern Plane plane[num_plane];
2、優(yōu)化按鍵指令存儲枚舉變量
typedef enum {none_cmd,up_cmd,down_cmd,left_cmd,right_cmd} direction_cmd;
枚舉變量里增加“none_cmd”選項,方向指令只保留一組,不再區(qū)分是哪個飛機的指令,使指令更加通用。
(二)已有自定義函數(shù)的優(yōu)化
1、形參數(shù)量減少
目前形參只有Plane一個結(jié)構(gòu)體類型了。
//聲明刷新飛機位置函數(shù)
void show_plane(Plane plane);
//獲取鍵盤指令
void key(void);
//更新每架飛機的坐標
void plane_location_update(void);
//初始化函數(shù)(設(shè)置飛機的顏色、形狀)
void init(void);
2、返回型函數(shù)類型減少
因為函數(shù)運行后的結(jié)果都存在全局Plane結(jié)構(gòu)體中了,所以函數(shù)不需要各種返回結(jié)果。
(三)新增自定義函數(shù)
1、新增獲取按鍵鍵值線程函數(shù)
之前的按鍵獲取函數(shù)在主線程中,按鍵按下后再執(zhí)行位置更新和畫面更新,如果沒有按鍵按下,主程序就一直處于等待按鍵按下的狀態(tài),后邊的程序都執(zhí)行不了。游戲后邊需要增加炮彈的功能,需要一直更新炮彈的位置,所以需要把按鍵程序單放到一個線程里。主線程里只保留位置更新和畫面更新功能。
//定義獲取按鍵線程函數(shù)
void* thread_key(void* arg);
void getkey();
2、新增彩色字符輸出函數(shù)
//輸出有顏色字符函數(shù)
template<typename T> //T表示任何可以被cout輸出的類型
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0);
3、新增隱藏光標函數(shù)
//隱藏光標函數(shù)
HANDLE han = GetStdHandle(-11);
void hide(){
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = 0;
cursor.dwSize = 1;
SetConsoleCursorInfo(han,&cursor);
}
隱藏光標函數(shù),可以不再界面上不顯示光標所在位置,更新畫面時不會出現(xiàn)光標,減少閃爍感。
(四)優(yōu)化后的程序代碼
1、主函數(shù)
#include "control_plane.h"
using namespace std;
Plane plane[num_plane];
int main(int argc, char** argv) {
init(); //初始化
bgmusic();//播放背景音樂
getkey();
while(1) //循環(huán)等待鍵盤指令
{
for(int i=0;i<num_plane;i++)
{
show_plane(plane[i]); //刷新飛機1圖標
}
if(plane[0].keycmd!=none_cmd ||plane[1].keycmd!=none_cmd)
{
system("cls");
}
plane_location_update();
}
return 0;
}
2、頭文件control_plane.h
#ifndef CONTROL_PLANE_H
#define CONTROL_PLANE
#include <iostream>
#include <string>
#include<stdlib.h>
#include<windows.h>
#include <pthread.h>//導(dǎo)入線程頭文件庫
#include <mmsystem.h> //導(dǎo)入聲音頭文件庫
#pragma comment(lib,"winmm.lib")//導(dǎo)入聲音的鏈接庫
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
#define t_b 0 //圖形顯示區(qū)域上側(cè)邊界
#define l_b 0 //圖形顯示區(qū)域左側(cè)邊界
#define r_b 100 //圖形顯示區(qū)域右側(cè)邊界
#define b_b 20 //圖形顯示區(qū)域下側(cè)邊界
#define num_plane 2 //飛機架數(shù)
//定義飛機造型
const string icon_plane1[]={" ■","■ ■ ■","■■■■■","■ ■ ■"," ■"," ■■■"};
const string icon_plane2[]={" ■","■ ■ ■","■■■■■"," ■"," ■■■","■■■■■"};
//定義坐標結(jié)構(gòu)體
typedef struct{
int x;
int y;
} Location;
//定義移動方向命令枚舉類型
typedef enum {none_cmd,up_cmd,down_cmd,left_cmd,right_cmd} direction_cmd;
//定義飛機結(jié)構(gòu)體
typedef struct{
Location location;
int color;
int icon;
direction_cmd keycmd;
}Plane;
extern Plane plane[num_plane];
//聲明刷新飛機位置函數(shù)
void show_plane(Plane plane);
//獲取鍵盤指令
void key(void);
//更新所有飛機坐標
void plane_location_update(void);
//初始化函數(shù)
void init(void);
//播放背景音樂線程
void* thread_bgmusic(void* arg);
void play_bgmusic();
void bgmusic();
//獲取按鍵指令線程
void* thread_key(void* arg);
void getkey();
//輸出彩色字符函數(shù)
template<typename T> //T表示任何可以被cout輸出的類型
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0);
#endif
3、庫函數(shù)control_plane.cpp
#include <iostream>
#include "conio.h"
#include <string>
#include "control_plane.h"
#include<windows.h>
using namespace std;
//彩色輸出函數(shù)
template<typename T> //T表示任何可以被cout輸出的類型
void ColorCout(T t, const int ForeColor = 7, const int BackColor = 0)
{
// 0 = 黑色 1 = 藍色 2 = 綠色 3 = 淺綠色 4 = 紅色 5 = 紫色 6 = 黃色 7 = 白色
// 8 = 灰色 9 = 淡藍色 10 = 淡綠色 11 = 淡淺綠色 12 = 淡紅色 13 = 淡紫色 14 = 淡黃色 15 = 亮白色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), ForeColor + BackColor * 0x10);
cout << t;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
//隱藏光標函數(shù)
HANDLE han = GetStdHandle(-11);
void hide(){
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible = 0;
cursor.dwSize = 1;
SetConsoleCursorInfo(han,&cursor);
}
//初始化函數(shù)
void init(void)
{
plane[0].location={2*r_b/3,b_b};
plane[1].location={r_b/3,b_b};
plane[0].color=1;
plane[1].color=2;
plane[0].icon=1;
plane[1].icon=2;
for(int i=0;i<num_plane;i++)
{
system("cls");
show_plane(plane[i]); //刷新飛機圖標
plane[i].keycmd=none_cmd;
}
hide();//隱藏光標
}
//********************************************************************************
//以下三個函數(shù)為獲得按鍵指令線程函數(shù)
//********************************************************************************
void* thread_key(void* arg)
{
while(1)
{
key(); //獲取按鍵指令
}
}
void getkey()
{
pthread_t tid;
pthread_create(&tid, NULL, thread_key, NULL);
}
//獲取鍵盤指令函數(shù)
void key(void)
{
int key_value1,key_value2; //聲明兩個變量,存放鍵值
key_value1=getch(); //先獲取第一個碼值
if(key_value1==224) //如果第一個碼值為224,則進行第二個碼值的判斷
{
key_value2=getch(); //先獲取第二個碼值
switch(key_value2)
{
case 72: //向上方向鍵
plane[0].keycmd=up_cmd;
break;
case 80: //向下方向鍵
plane[0].keycmd=down_cmd;
break;
case 75: //向左方向鍵
plane[0].keycmd=left_cmd;
break;
case 77: //向右方向鍵
plane[0].keycmd=right_cmd;
break;
}
}
else
{
switch(key_value1)
{
case 119: //向上方向鍵
plane[1].keycmd=up_cmd;
break;
case 115: //向下方向鍵
plane[1].keycmd=down_cmd;
break;
case 97: //向左方向鍵
plane[1].keycmd=left_cmd;
break;
case 100: //向右方向鍵
plane[1].keycmd=right_cmd;
break;
}
}
}
//********************************************************************************
//以下三個函數(shù)為播放背景音樂功能
//********************************************************************************
//播放一遍背景音樂
void play_bgmusic() {
mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);
mciSendString(TEXT("play s1"),NULL,0,NULL);
Sleep(153*1000);//153*1000意思是153秒,是整首音樂的時長
mciSendString(TEXT("close S1"),NULL,0,NULL);
}
//循環(huán)播放音樂線程函數(shù)
void* thread_bgmusic(void* arg) //
{
while(1)
{
play_bgmusic();
}
}
//創(chuàng)建音樂播放線程,開始循環(huán)播放音樂
void bgmusic()
{
pthread_t tid;
pthread_create(&tid, NULL, thread_bgmusic, NULL);
}
void gotoxy(int x, int y) {
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//獲取標準輸出設(shè)備句柄
SetConsoleCursorPosition(hOut, pos);//兩個參數(shù)分別指定哪個窗口,具體位置
}
//飛機圖標刷新函數(shù)
void show_plane(Plane plane) //預(yù)先定義字符定位顯示函數(shù),x是列坐標,y是行坐標,原點(x=0,y=0)位于屏幕左上角
{
int x,y;
int i,j;
int rows;
x=plane.location.x;
y=plane.location.y;
switch(plane.icon)
{
case 1://第一種造型
rows=sizeof(icon_plane1)/sizeof(icon_plane1[0]);
for(i=0;i<rows;i++)
{
gotoxy(x,y+i);
ColorCout(icon_plane1[i],plane.color);
}
break;
case 2://第二種造型
rows=sizeof(icon_plane2)/sizeof(icon_plane2[0]);
for(i=0;i<rows;i++)
{
gotoxy(x,y+i);
ColorCout(icon_plane2[i],plane.color);
}
break;
}
}
//更新兩個飛機的坐標
void plane_location_update(void)
{
for(int i=0;i<2;i++)
{
if(plane[i].keycmd!=none_cmd)
{
int x,y;
x=plane[i].location.x;
y=plane[i].location.y;
switch(plane[i].keycmd)
{
case up_cmd:
y--; //字符上移一行,行值y減1
if(y<t_b) //限定y值最小值為0
{
y=t_b;
}
break;
case down_cmd:
y++; //字符下移一行,行值y加1
if(y>b_b) //限定y高度
{
y=b_b;
}
break;
case left_cmd:
x--; //字符左移一列,列值x減1
if(x<l_b)
{
x=l_b; //限定x最小值為0;
}
break;
case right_cmd:
x++; //字符右移一列,列值x加1
if(x>r_b)
{
x=r_b; //限定x寬度
}
break;
}
plane[i].location.x=x;
plane[i].location.y=y;
plane[i].keycmd=none_cmd;
}
}
}
五、運行效果
?文章來源:http://www.zghlxwxcb.cn/news/detail-633091.html
(未完待續(xù))?文章來源地址http://www.zghlxwxcb.cn/news/detail-633091.html
到了這里,關(guān)于【C++】做一個飛機空戰(zhàn)小游戲(六)——給兩架飛機設(shè)置不同顏色(cout輸出彩色字符、結(jié)構(gòu)體使用技巧)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!