C++基于EasyX框架实现飞机大战小游戏
Leleprogrammer 人气:0正式使用Easyx之前,你需要先安装他!!
EasyX 2022 版 (2022-9-1 更新) - EasyX
选择合适的版本安装
安装结束后就可以开始敲代码啦!
这里作者使用的是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; } } }
这里简单地实现了子弹的移动和检测超出边界,不难理解
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最大,同时血量最多,也实现了移动和检测超出边界功能
有的人会发现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; } }
代码都很短,也实现了移动和限制活动区域(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() 函数,用于获取绘图设备的显存指针, EasyX 自带 DWORD* draw = GetImageBuffer(); DWORD* src = GetImageBuffer(image); // 获取 picture 的显存指针 int imageWidth = image->getwidth(); // 获取图片宽度 int imageHeight = image->getheight(); // 获取图片宽度 int dstX = 0; // 在 绘图区域 显存里像素的角标 int srcX = 0; // 在 image 显存里像素的角标 // 实现透明贴图 公式: 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 // 设置对应的绘图区域像素信息 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; }
上面实现了char*转TCHAR*以及string转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; }
实现了程序的主流程
加载全部内容