C++基于EasyX框架實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲
正式使用Easyx之前,你需要先安裝他?。?/p>
EasyX 2022 版 (2022-9-1 更新) - EasyX
選擇合適的版本安裝

安裝結(jié)束后就可以開(kāi)始敲代碼啦!
這里作者使用的是Visual Studio 2022所以安裝EasyX_20220901版本
啟動(dòng)Visual Studio 2022,新建一個(gè)空項(xiàng)目

這是工程目錄:

首先來(lái)看看Bullet(子彈)類(lèi)
頭文件:
#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;
}
}
}這里簡(jiǎn)單地實(shí)現(xiàn)了子彈的移動(dòng)和檢測(cè)超出邊界,不難理解
Enemy類(lèi)頭文件:
#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;
}
}這里的類(lèi)成員變量type表示敵機(jī)大小,3最大,同時(shí)血量最多,也實(shí)現(xiàn)了移動(dòng)和檢測(cè)超出邊界功能
有的人會(huì)發(fā)現(xiàn)random方法以及WIDTH常量等,這里是因?yàn)槲覀儗⑦@些常量寫(xiě)在一個(gè)頭文件下:
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/";
接下來(lái)是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;
}
}代碼都很短,也實(shí)現(xiàn)了移動(dòng)和限制活動(dòng)區(qū)域(checkBound)操作,不難理解
接下來(lái)是作者自己寫(xiě)了一個(gè)實(shí)用的頭文件,用于判斷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分別為第一個(gè)物體的左邊x坐標(biāo)、右邊x坐標(biāo)、上邊y坐標(biāo)、下邊y坐標(biāo),el、er、et、ed是第二個(gè)物體的,然后進(jìn)行判斷,返回bool值,這個(gè)待會(huì)在main.cpp會(huì)用到
接下來(lái)也是一個(gè)常用的頭文件,因?yàn)閑asyx渲染透明圖片很麻煩,所以這個(gè)方法通過(guò)計(jì)算來(lái)繪制,這個(gè)是借用了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ū)域 顯存里像素的角標(biāo)
int srcX = 0; // 在 image 顯存里像素的角標(biāo)
// 實(shí)現(xiàn)透明貼圖 公式: Cp=αp*FP+(1-αp)*BP , 貝葉斯定理來(lái)進(jìn)行點(diǎn)顏色的概率計(jì)算
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)
{
// 獲取像素角標(biāo)
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è)置對(duì)應(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
}
}
}
}接下來(lái)這一個(gè)是作者自己寫(xiě)的一個(gè)常用的頭文件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;
}上面實(shí)現(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;
}實(shí)現(xiàn)了程序的主流程
到此這篇關(guān)于C++基于EasyX框架實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲的文章就介紹到這了,更多相關(guān)C++ EasyX飛機(jī)大戰(zhàn)游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++類(lèi)繼承時(shí)的構(gòu)造函數(shù)
這篇文章主要介紹了C++類(lèi)繼承時(shí)的構(gòu)造函數(shù),C++中,子類(lèi)繼承父類(lèi)除去構(gòu)造函數(shù)和析構(gòu)函數(shù)以外的所有成員。因此,子類(lèi)需要編寫(xiě)自己的構(gòu)造函數(shù)和析構(gòu)函數(shù)。更多相關(guān)詳情需要的小伙伴可以參考下面文章介紹2022-03-03
C語(yǔ)言中的時(shí)間函數(shù)clock()和time()你都了解嗎
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中的時(shí)間函數(shù)clock()和time(),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02
一篇文章讓你輕松理解C++中vector和list區(qū)別
對(duì)于學(xué)c語(yǔ)言的同學(xué)來(lái)說(shuō),vector和list這兩個(gè)東西經(jīng)常會(huì)搞錯(cuò),下面這篇文章主要給大家介紹了關(guān)于C++中vector和list區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-01-01
C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)程序的示例代碼
數(shù)獨(dú)是源自瑞士的一種數(shù)學(xué)游戲。是一種運(yùn)用紙、筆進(jìn)行演算的邏輯游戲。本文將利用C語(yǔ)言實(shí)現(xiàn)數(shù)獨(dú)程序,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03
一篇文章帶你入門(mén)C語(yǔ)言數(shù)據(jù)結(jié)構(gòu):緒論
這篇文章主要介紹了C語(yǔ)言的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對(duì)廣大的程序愛(ài)好者有所幫助,同時(shí)祝大家有一個(gè)好成績(jī),需要的朋友可以參考下,希望能給你帶來(lái)幫助2021-08-08

