提瓦特幸存者4
#include <iostream>
#include <windows.h>
#include <string>
#include <graphics.h>
#include <vector>
#pragma comment(lib, "MSIMG32.LIB")
#pragma comment(lib, "Winmm.lib")
class Button {
private:
enum State { idle, hovered, pushed };
private:
IMAGE imgIdle, imgHovered, imgPushed; //资源
RECT region; //位置
State state = idle; //状态
public:
Button(const RECT& region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed);
virtual ~Button() = 0; //纯虚函数 依然可以有函数体
public:
void processMessage(const ExMessage& msg);
void draw();
private:
//鼠标是否命中区域
bool checkCursorHit(int x, int y) {
return x >= region.left && x <= region.right && y >= region.top && y <= region.bottom;
}
virtual void onClick() = 0;
};
class StartButton :public Button {
public:
StartButton(RECT region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed)
:Button(region, pathIdle, pathHovered, pathPushed) {};
virtual ~StartButton();
private:
virtual void onClick();
};
class QuitButton :public Button {
public:
QuitButton(RECT region, LPCTSTR pathIdle, LPCTSTR pathHovered, LPCTSTR pathPushed)
:Button(region, pathIdle, pathHovered, pathPushed) {};
virtual ~QuitButton();
private:
virtual void onClick();
};
class Atlas {
public:
std::vector<IMAGE*> images;
public:
Atlas(LPCTSTR path, int num);
~Atlas();
};
class Animation {
private:
int imgIndex = 0; //帧索引
int timer = 0; //计时器: 本张动画已经播放的时间
int frameInterval; //帧间隔ms: 两帧动画间的时间
Atlas* atlas;
public:
Animation(Atlas* atlas, int frameInterval); //LPCSTR 更通用的常字符指针
public:
//播放一张动画
void draw(int x, int y, int playTime); //playTime :本张动画播放的时间
};
class Bullet {
private:
const int radius = 10;
public:
POINT pos{ 0,0 };
public:
void draw() const {
//橙红色填充圆
setlinecolor(RGB(255, 155, 50));
setfillcolor(RGB(200, 75, 10));
fillcircle(pos.x, pos.y, radius);
}
};
class Player {
private:
POINT playerPos{ 500,500 }; //玩家位置
const int playerSpeed = 6; //移动速度
bool isLeft = false, isRight = false, //移动方向
isUp = false, isDown = false;
bool isFacingLeft = false; //面部朝向
const int shadowWidth = 32; //玩家阴影高度
public:
const int width = 80; //玩家高度
const int height = 80;
private:
IMAGE playerShadow;
Animation* animationPlayerLeft; //玩家动画
Animation* animationPlayerRight;
public:
Player();
~Player();
public:
void processMessage(const ExMessage& msg);
void move();
void draw(int frameInterval);
public:
int x() const { return playerPos.x; }
int y() const { return playerPos.y; }
};
class Enemy {
private:
POINT pos{ 0,0 }; //敌人位置
const int width = 80; //敌人高度
const int height = 80;
const int shadowWidth = 48; //敌人阴影高度
const int enemySpeed = 2; //移动速度
bool isLeft = false, isRight = false, //移动方向
isUp = false, isDown = false;
bool isFacingLeft = false; //面部朝向
bool isAlive = true; //怪物存活
private:
IMAGE enemyShadow;
Animation* animationEnemyLeft; //敌人动画
Animation* animationEnemyRight;
public:
Enemy();
~Enemy();
public:
void move(const Player& player);
bool checkBulletCollision(const Bullet& bullet) const;
bool checkPlayerCollision(const Player& player) const;
void draw(int frameInterval);
public:
void hurt() { isAlive = false; }
bool checkAlive() const { return isAlive; }
};
void putImageAlpha(int x, int y, IMAGE* img); //图像绘制(透明度)
void generateEnemy(std::vector<Enemy*>& enemys); //生成敌人
void updateBullets(std::vector<Bullet>& bullets, const Player& player); //更新子弹
void drawScore(const int& score);
Atlas* playerLeftAtlas;
Atlas* playerRightAtlas;
Atlas* enemyLeftAtlas;
Atlas* enemyRightAtlas;
const int windowWidth = 1280;
const int windowHeight = 720;
const int buttonWidth = 192;
const int buttonHeight = 75;
const int frameInterval = 1000 / 120;
bool running = true;
bool isStarted = false;
int main() {
initgraph(windowWidth, windowHeight);
//加载地图
IMAGE background;
loadimage(&background, _T("resources/img/background.png"));
//加载菜单
IMAGE menu;
loadimage(&menu, _T("resources/img/menu.png"));
//加载按钮
RECT startRegion, quitRegion;
startRegion.left = (windowWidth - buttonWidth) / 2;
startRegion.right = startRegion.left + buttonWidth;
startRegion.top = 430;
startRegion.bottom = startRegion.top + buttonHeight;
quitRegion.left = (windowWidth - buttonWidth) / 2;
quitRegion.right = quitRegion.left + buttonWidth;
quitRegion.top = 550;
quitRegion.bottom = quitRegion.top + buttonHeight;
StartButton startButton{ startRegion , _T("resources/img/ui_start_idle.png"),
_T("resources/img/ui_start_hovered.png"),_T("resources/img/ui_start_pushed.png") };
QuitButton quitButton{ quitRegion , _T("resources/img/ui_quit_idle.png"),
_T("resources/img/ui_quit_hovered.png"),_T("resources/img/ui_quit_pushed.png") };
//初始化资产
playerLeftAtlas = new Atlas(_T("resources/img/player_left_%d.png"), 6);
playerRightAtlas = new Atlas(_T("resources/img/player_right_%d.png"), 6);
enemyLeftAtlas = new Atlas(_T("resources/img/enemy_left_%d.png"), 6);
enemyRightAtlas = new Atlas(_T("resources/img/enemy_right_%d.png"), 6);
//加载mp3
//取 alias 为 bgm
mciSendString(_T("open resources/mus/bgm.mp3 alias bgm"), nullptr, 0, nullptr);
mciSendString(_T("open resources/mus/hit.wav alias hit"), nullptr, 0, nullptr);
//重复播放bgm 从0开始
mciSendString(_T("play bgm repeat from 0"), nullptr, 0, nullptr);
Player player;
std::vector<Bullet> bullets(3);
std::vector<Enemy*> enemys;
unsigned int score = 0;
ExMessage message;
BeginBatchDraw();
while (running) {
ULONGLONG startTime = GetTickCount64();
//读数据
peekmessage(&message);
//数据处理
//事件处理
if(isStarted)
player.processMessage(message);
else {
quitButton.processMessage(message);
startButton.processMessage(message);
}
//数据处理
if (isStarted) {
//更新玩家
player.move();
//更新子弹
updateBullets(bullets, player);
generateEnemy(enemys);
for (auto& enemy : enemys)
enemy->move(player);
//怪物和子弹碰撞检测
for (auto& enemy : enemys)
for (const auto& bullet : bullets)
if (enemy->checkBulletCollision(bullet)) {
score++;
enemy->hurt();
mciSendString(_T("play hit from 0"), nullptr, 0, nullptr);
}
//怪物和玩家碰撞检测
for (auto& enemy : enemys) {
if (enemy->checkPlayerCollision(player)) {
TCHAR text[64];
_stprintf_s(text, _T("最终得分: %d"), score);
MessageBox(GetHWnd(), text, _T("游戏结束"), MB_OK);
running = false;
break;
}
}
//移除以消失的怪物
for (auto& enemy : enemys) {
for (const auto& bullet : bullets) {
if (!enemy->checkAlive()) {
std::swap(enemy, enemys.back());
delete enemys.back();
enemys.pop_back();
}
}
}
}
//渲染
cleardevice();
if (isStarted) {
putimage(0, 0, &background);
player.draw(frameInterval);
for (const auto& bullet : bullets)
bullet.draw();
for (const auto& enemy : enemys)
enemy->draw(frameInterval);
drawScore(score);
}
else {
putimage(0, 0, &menu);
startButton.draw();
quitButton.draw();
}
FlushBatchDraw();
//120刷新
ULONGLONG executionTime = GetTickCount64() - startTime;
if (executionTime < frameInterval)
Sleep(frameInterval - executionTime);
}
EndBatchDraw();
//释放资产
delete playerLeftAtlas;
delete playerRightAtlas;
delete enemyLeftAtlas;
delete enemyRightAtlas;
}
Player::Player(){
loadimage(&playerShadow, _T("resources/img/shadow_player.png"));
animationPlayerLeft = new Animation(playerLeftAtlas, 45);
animationPlayerRight = new Animation(playerRightAtlas, 45);
}
Player::~Player(){
delete animationPlayerLeft;
delete animationPlayerRight;
}
void Player::processMessage(const ExMessage& msg){
//判断移动方向
if (msg.message == WM_KEYDOWN) {
switch (msg.vkcode) {
case VK_UP:
isUp = true;
break;
case VK_DOWN:
isDown = true;
break;
case VK_LEFT:
isLeft = true;
break;
case VK_RIGHT:
isRight = true;
break;
default:
break;
}
}
else if (msg.message == WM_KEYUP) {
switch (msg.vkcode) {
case VK_UP:
isUp = false;
break;
case VK_DOWN:
isDown = false;
break;
case VK_LEFT:
isLeft = false;
break;
case VK_RIGHT:
isRight = false;
break;
default:
break;
}
}
}
//计算移动信息
void Player::move(){
// x,y 代表 向量
int x = isRight - isLeft;
int y = isDown - isUp;
double modulus = sqrt(x * x + y * y); //向量的模
if (modulus) {
double vectorX = x / modulus;
double vectorY = y / modulus;
playerPos.x += int(playerSpeed * vectorX);
playerPos.y += int(playerSpeed * vectorY);
}
//校准
if (playerPos.x < 0) playerPos.x = 0;
if (playerPos.y < 0) playerPos.y = 0;
if (playerPos.x + width > windowWidth) playerPos.x = windowWidth - width;
if (playerPos.y + height > windowHeight) playerPos.y = windowHeight - height;
//修改面部朝向
//等于0时,指向原先面部朝向
if (x > 0