C++基于EasyX框架實現(xiàn)飛機大戰(zhàn)小游戲
正式使用Easyx之前,你需要先安裝他??!
EasyX 2022 版 (2022-9-1 更新) - EasyX
選擇合適的版本安裝
安裝結(jié)束后就可以開始敲代碼啦!
這里作者使用的是Visual Studio 2022所以安裝EasyX_20220901版本
啟動Visual Studio 2022,新建一個空項目
這是工程目錄:
首先來看看Bullet(子彈)類
頭文件:
#pragma once class Bullet { public: Bullet(int x, int y, int owner); int owner; // 0 means player, 1 means enemy int x; int y; int speed; int pic_w = 5; int pic_h = 11; bool dead = false; void move(); void checkBound(); };
源文件:
#include "Bullet.h" #include "constants.h" Bullet::Bullet(int x, int y, int owner) { this->x = x; this->y = y; this->owner = owner; speed = 12; } void Bullet::move() { if (owner == 0) { y -= speed; } else { y += speed; } } void Bullet::checkBound() { if (owner == 0) { if (y + pic_h <= 0) { dead = true; } } else { if (y >= HEIGHT) { dead = true; } } }
這里簡單地實現(xiàn)了子彈的移動和檢測超出邊界,不難理解
Enemy類頭文件:
#pragma once class Enemy { public: Enemy(int type); int x; int y; int speed; int pic_w; int pic_h; int type; int health; int static_health; bool dead = false; void move(); void checkBound(); };
源文件:
#include "Enemy.h" #include "constants.h" Enemy::Enemy(int type) { this->type = type; switch (type) { case 1: pic_w = 60; pic_h = 44; health = random(1, 2); break; case 2: pic_w = 70; pic_h = 100; health = random(3, 4); break; case 3: pic_w = 160; pic_h = 250; health = random(5, 6); break; default: pic_w = 60; pic_h = 44; health = 1; break; } x = random(0, WIDTH - pic_w); y = 0 - pic_h; speed = random(5,8); static_health = health; } void Enemy::move() { y += speed; } void Enemy::checkBound() { if (y >= HEIGHT || health <= 0) { dead = true; } }
這里的類成員變量type表示敵機大小,3最大,同時血量最多,也實現(xiàn)了移動和檢測超出邊界功能
有的人會發(fā)現(xiàn)random方法以及WIDTH常量等,這里是因為我們將這些常量寫在一個頭文件下:
constants.h:
#pragma once #ifndef WIDTH #define WIDTH 800 #endif #ifndef HEIGHT #define HEIGHT 1000 #endif #include <cstdlib> #include <string> using namespace std; #ifndef random(a,x) #define random(a,x) a+rand()%x #endif const string PATH = "./resources/";
接下來是Player.h
#pragma once #include <string> #include "constants.h" using namespace std; class Player { public: Player(); int speed; int y; int x; int pic_w; int pic_h; void move(int a); void checkBound(); };
Player.cpp:
#include "Player.h" #include "constants.h" Player::Player() { speed = 12; y = HEIGHT - 170; x = 300; pic_w = 100; pic_h = 126; } void Player::move(int a) { if (a == 0) { // left x -= speed; } else { // right x += speed; } } void Player::checkBound() { if (x < 0) { x = 0; } else if (x + pic_w > WIDTH) { x = WIDTH - pic_w; } }
代碼都很短,也實現(xiàn)了移動和限制活動區(qū)域(checkBound)操作,不難理解
接下來是作者自己寫了一個實用的頭文件,用于判斷2者是否碰撞,CheckCollide.h:
#pragma once bool collide( int l, int r, int t, int d, int el, int er, int et, int ed) { if ((l <= er && t <= ed && l >= el && t >= et) || (r >= el && t <= ed && r <= er && t >= et) || (l <= er && d >= et && l >= el && d <= ed) || (r >= el && d >= et && r <= er && d <= ed)) { return true; } return false; }
其中,l、r、t、d分別為第一個物體的左邊x坐標、右邊x坐標、上邊y坐標、下邊y坐標,el、er、et、ed是第二個物體的,然后進行判斷,返回bool值,這個待會在main.cpp會用到
接下來也是一個常用的頭文件,因為easyx渲染透明圖片很麻煩,所以這個方法通過計算來繪制,這個是借用了EasyX 繪制透明背景圖這篇文章的代碼,PhotoTransparent.h
#pragma once #include <graphics.h> #include "constants.h" void drawAlpha(IMAGE* image, int x, int y, int width, int height, int pic_x = 0, int pic_y = 0, double AA = 1) { // 變量初始化 DWORD* dst = GetImageBuffer(); // GetImageBuffer() 函數(shù),用于獲取繪圖設(shè)備的顯存指針, EasyX 自帶 DWORD* draw = GetImageBuffer(); DWORD* src = GetImageBuffer(image); // 獲取 picture 的顯存指針 int imageWidth = image->getwidth(); // 獲取圖片寬度 int imageHeight = image->getheight(); // 獲取圖片寬度 int dstX = 0; // 在 繪圖區(qū)域 顯存里像素的角標 int srcX = 0; // 在 image 顯存里像素的角標 // 實現(xiàn)透明貼圖 公式: Cp=αp*FP+(1-αp)*BP , 貝葉斯定理來進行點顏色的概率計算 for (int iy = 0; iy < height; iy++) { for (int ix = 0; ix < width; ix++) { // 防止越界 if (ix + pic_x >= 0 && ix + pic_x < imageWidth && iy + pic_y >= 0 && iy + pic_y < imageHeight && ix + x >= 0 && ix + x < WIDTH && iy + y >= 0 && iy + y < HEIGHT) { // 獲取像素角標 int srcX = (ix + pic_x) + (iy + pic_y) * imageWidth; dstX = (ix + x) + (iy + y) * WIDTH; int sa = ((src[srcX] & 0xff000000) >> 24) * AA; // 0xAArrggbb; AA 是透明度 int sr = ((src[srcX] & 0xff0000) >> 16); // 獲取 RGB 里的 R int sg = ((src[srcX] & 0xff00) >> 8); // G int sb = src[srcX] & 0xff; // B // 設(shè)置對應(yīng)的繪圖區(qū)域像素信息 int dr = ((dst[dstX] & 0xff0000) >> 16); int dg = ((dst[dstX] & 0xff00) >> 8); int db = dst[dstX] & 0xff; draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) //公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr | ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg | (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db } } } }
接下來這一個是作者自己寫的一個常用的頭文件StringCharExchange.h:
#pragma once #include <graphics.h> #define BUFFERSIZE 1024 TCHAR* Transform(char c[BUFFERSIZE]) { TCHAR result[BUFFERSIZE]; MultiByteToWideChar(CP_ACP, 0, c, -1, result, BUFFERSIZE); return result; } TCHAR* Transform(string s) { TCHAR result[BUFFERSIZE]; char c[BUFFERSIZE]; strcpy_s(c, s.c_str()); MultiByteToWideChar(CP_ACP, 0, c, -1, result, BUFFERSIZE); return result; }
上面實現(xiàn)了char*轉(zhuǎn)TCHAR*以及string轉(zhuǎn)TCHAR*
最后是main.cpp
#include <graphics.h> #include "constants.h" #include "Player.h" #include "PhotoTransparent.h" #include <time.h> #include <vector> #include "Bullet.h" #include "Enemy.h" #include "CheckCollide.h" #include "StringCharExchange.h" Player* player = new Player(); vector<Bullet>* bullets = new vector<Bullet>; vector<Enemy>* enemies = new vector<Enemy>; int startTime[5] = { 0 }; int durations[5] = { 200,1000,0,0,0 }; int health = 5; int score = 0; bool lose = false; void DrawBackGroundImage() { IMAGE img; loadimage(&img, Transform(PATH+"background2.png"), WIDTH, HEIGHT); putimage(0, 0, &img); } void DrawPlayer() { IMAGE img; loadimage(&img, Transform(PATH + "myplane1.png"), player->pic_w, player->pic_h); drawAlpha(&img, player->x, player->y, player->pic_w, player->pic_h); } void DrawBullets() { for (int i = 0;i < bullets->size();i++) { Bullet* bullet = &(bullets->at(i)); IMAGE img; loadimage(&img, Transform(PATH + "bullet1.png")); drawAlpha(&img, bullet->x, bullet->y, bullet->pic_w, bullet->pic_h); } } void DrawEnemies() { for (int i = 0;i < enemies->size();i++) { Enemy* enemy = &(enemies->at(i)); IMAGE img; switch (enemy->type) { case 1: loadimage(&img, Transform(PATH + "small_enemy.png")); break; case 2: loadimage(&img, Transform(PATH + "mid_enemy.png")); break; case 3: loadimage(&img, Transform(PATH + "big_enemy.png")); break; default: break; } drawAlpha(&img, enemy->x, enemy->y, enemy->pic_w, enemy->pic_h); } } void UpdateBullets() { for (int i = 0;i < bullets->size();i++) { Bullet* bullet = &(bullets->at(i)); bullet->move(); bullet->checkBound(); if (bullet->dead) { swap(bullets->at(i), bullets->at(bullets->size() - 1)); bullets->pop_back(); i--; } } } void UpdateEnemies() { for (int i = 0;i < enemies->size();i++) { Enemy* enemy = &(enemies->at(i)); enemy->move(); enemy->checkBound(); if (enemy->dead) { swap(enemies->at(i), enemies->at(enemies->size() - 1)); enemies->pop_back(); i--; } } } void CheckPlayerHit() { for (int i = 0;i < enemies->size();i++) { Enemy* enemy = &(enemies->at(i)); int l, r, t, d; int el, er, et, ed; l = player->x; r = player->x + player->pic_w; t = player->y; d = player->y + player->pic_h; el = enemy->x; er = enemy->x + enemy->pic_w; et = enemy->y; ed = enemy->y + enemy->pic_h; if (collide(l, r, t, d, el, er, et, ed)) { health--; swap(enemies->at(i), enemies->at(enemies->size() - 1)); enemies->pop_back(); i--; } } } void CheckBulletHit() { for (int i = 0;i < bullets->size();i++) { Bullet* bullet = &(bullets->at(i)); int l, r, t, d; l = bullet->x; r = bullet->x + bullet->pic_w; t = bullet->y; d = bullet->y + bullet->pic_h; for (int j = 0;j < enemies->size();j++) { Enemy* enemy = &(enemies->at(j)); int el, er, et, ed; el = enemy->x; er = enemy->x + enemy->pic_w; et = enemy->y; ed = enemy->y + enemy->pic_h; if (collide(l, r, t, d, el, er, et, ed)) { enemy->health--; if (enemy->health <= 0 || enemy->dead) { score += enemy->static_health; swap(enemies->at(j), enemies->at(enemies->size() - 1)); enemies->pop_back(); } j--; swap(bullets->at(i), bullets->at(bullets->size() - 1)); bullets->pop_back(); i--; break; } } } } void _DrawText() { settextcolor(RGB(0, 0, 255)); settextstyle(26, 0, _T("simhei")); char c[BUFFERSIZE]; snprintf(c, 64, "Health: %d", health); TCHAR* c2 = Transform(c); outtextxy(10, 10, c2); settextcolor(RGB(255, 0, 0)); settextstyle(26, 0, _T("simhei")); char c3[BUFFERSIZE]; snprintf(c3, 64, "Score: %d", score); TCHAR* c4 = Transform(c3); outtextxy(10, 44, c4); } void CheckLose() { if (health <= 0) { lose = true; } } void Draw() { CheckLose(); DrawBackGroundImage(); _DrawText(); if (!lose) { UpdateBullets(); UpdateEnemies(); CheckPlayerHit(); CheckBulletHit(); DrawPlayer(); DrawEnemies(); DrawBullets(); } } void Timer() { int endTime = clock(); if (endTime - startTime[0] >= durations[0]) { // Create bullet event bullets->push_back(Bullet(player->x + player->pic_w / 2, player->y, 0)); startTime[0] = endTime; } if (endTime - startTime[1] >= durations[1]) { // Create enemy event enemies->push_back(Enemy(random(1,3))); startTime[1] = endTime; } } int Listen() { if (GetAsyncKeyState(VK_ESCAPE)) { return 1; } if (GetAsyncKeyState(VK_LEFT)) { player->move(0); player->checkBound(); } if (GetAsyncKeyState(VK_RIGHT)) { player->move(1); player->checkBound(); } return 0; } int main() { initgraph(WIDTH, HEIGHT); setbkmode(TRANSPARENT); BeginBatchDraw(); while (true) { Draw(); if (Listen()) { break; } Timer(); FlushBatchDraw(); } EndBatchDraw(); closegraph(); return 0; }
實現(xiàn)了程序的主流程
到此這篇關(guān)于C++基于EasyX框架實現(xiàn)飛機大戰(zhàn)小游戲的文章就介紹到這了,更多相關(guān)C++ EasyX飛機大戰(zhàn)游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言中的時間函數(shù)clock()和time()你都了解嗎
這篇文章主要為大家詳細介紹了C語言中的時間函數(shù)clock()和time(),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02一篇文章讓你輕松理解C++中vector和list區(qū)別
對于學(xué)c語言的同學(xué)來說,vector和list這兩個東西經(jīng)常會搞錯,下面這篇文章主要給大家介紹了關(guān)于C++中vector和list區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-01-01一篇文章帶你入門C語言數(shù)據(jù)結(jié)構(gòu):緒論
這篇文章主要介紹了C語言的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對廣大的程序愛好者有所幫助,同時祝大家有一個好成績,需要的朋友可以參考下,希望能給你帶來幫助2021-08-08