提瓦特幸存者4

时间:2024-11-09 08:50:52
#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