Merge pull request 'sdl2_version' (#2) from sdl2_version into main
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
+18
@@ -432,3 +432,21 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
# ---> CMake
|
||||
build/
|
||||
cmake-build-*/
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
CMakeScripts/
|
||||
*.cmake
|
||||
!CMakeLists.txt
|
||||
Makefile
|
||||
*.make
|
||||
|
||||
session-*.md
|
||||
.hermes/
|
||||
|
||||
# CMake 构建产物
|
||||
build_Debug/
|
||||
build_Release/
|
||||
build_*/
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(tank_battles_on_the_scrap_paper
|
||||
VERSION 1.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SDL2_image REQUIRED)
|
||||
find_package(SDL2_mixer REQUIRED)
|
||||
find_package(SDL2_ttf REQUIRED)
|
||||
|
||||
set(SOURCES
|
||||
tank_battles_on_the_scrap_paper/main.cpp
|
||||
tank_battles_on_the_scrap_paper/src/AssetManager.cpp
|
||||
tank_battles_on_the_scrap_paper/src/Renderer.cpp
|
||||
tank_battles_on_the_scrap_paper/src/Map.cpp
|
||||
tank_battles_on_the_scrap_paper/src/Bullet.cpp
|
||||
tank_battles_on_the_scrap_paper/src/Tank.cpp
|
||||
tank_battles_on_the_scrap_paper/src/Game.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
tank_battles_on_the_scrap_paper/data_config.h
|
||||
tank_battles_on_the_scrap_paper/src/AssetManager.h
|
||||
tank_battles_on_the_scrap_paper/src/Renderer.h
|
||||
tank_battles_on_the_scrap_paper/src/Map.h
|
||||
tank_battles_on_the_scrap_paper/src/Bullet.h
|
||||
tank_battles_on_the_scrap_paper/src/Tank.h
|
||||
tank_battles_on_the_scrap_paper/src/Game.h
|
||||
tank_battles_on_the_scrap_paper/src/UI.h
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
SDL2::SDL2
|
||||
SDL2_image::SDL2_image
|
||||
SDL2_mixer::SDL2_mixer
|
||||
SDL2_ttf::SDL2_ttf
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
tank_battles_on_the_scrap_paper
|
||||
)
|
||||
|
||||
file(COPY
|
||||
tank_battles_on_the_scrap_paper/images/
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images/
|
||||
)
|
||||
|
||||
file(COPY
|
||||
tank_battles_on_the_scrap_paper/music/
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/music/
|
||||
)
|
||||
@@ -3,3 +3,5 @@
|
||||
v16: 添加了全局背景音效
|
||||
|
||||
v17: 修改了一点bug,在返回菜单后调用InitMap(),避免再次进入游戏界面时有脏数据导致显示出错
|
||||
|
||||
v18: 从 Windows/EasyX 移植到 SDL2,支持 Linux;修复启动黑屏问题(自动设置工作目录为可执行文件所在目录)
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33530.505
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tank_battles_on_the_scrap_paper", "tank_battles_on_the_scrap_paper\tank_battles_on_the_scrap_paper.vcxproj", "{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Debug|x64.Build.0 = Debug|x64
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Debug|x86.Build.0 = Debug|Win32
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Release|x64.ActiveCfg = Release|x64
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Release|x64.Build.0 = Release|x64
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Release|x86.ActiveCfg = Release|Win32
|
||||
{AC8C4A04-1650-473C-8E9A-3B7CDE2AD5CC}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4E9B74CF-EE0B-430C-8380-34BE94639065}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,24 +0,0 @@
|
||||
// 子弹定义
|
||||
#ifndef BULLET_H
|
||||
#define BULLET_H
|
||||
|
||||
/*
|
||||
子弹:
|
||||
子弹坐标:x,y
|
||||
攻击力damage:表示当前子弹造成的伤害
|
||||
存活标志:is_alive
|
||||
方向dir:
|
||||
UP 上
|
||||
DOWN 下
|
||||
LEFT 左
|
||||
RIGHT 右
|
||||
*/
|
||||
struct bullet {
|
||||
int x, y;
|
||||
int damage; // 子弹攻击力
|
||||
int dir;
|
||||
bool is_live; // 是否存活
|
||||
//tank shooter; //值为enemies[i]或player
|
||||
};
|
||||
|
||||
#endif // !BULLET_H
|
||||
@@ -1,52 +1,36 @@
|
||||
// 其他配置定义
|
||||
// 项目配置常量
|
||||
#ifndef DATA_CONFIG_H
|
||||
#define DATA_CONFIG_H
|
||||
// 导入 tank 和 bullet 定义头文件
|
||||
#include "tank.h"
|
||||
#include "bullet.h"
|
||||
|
||||
// 常量定义
|
||||
const int CELL_SIZE = 30; // 每个单元格(坦克、墙、空地等)的大小(例如:像素)
|
||||
const int MAP_CELL_NUM = 20; // 地图大小为MAP_CELL_NUM^2
|
||||
const int ENEMIES_NUM = 10; // 地图上最多的敌人数量
|
||||
const int BULLET_NUM = 20; // 地图上最多的子弹数量
|
||||
const int SINGLE_BULLET = 3; // 每个坦克同时存在的最多子弹数量
|
||||
const int White = 0; // 灰度图-白
|
||||
const int Black = 1; // 灰度图-黑
|
||||
// 网格
|
||||
constexpr int CELL_SIZE = 30;
|
||||
constexpr int MAP_CELL_NUM = 20;
|
||||
|
||||
#define PIEXL1 30 //像素常量
|
||||
#define PIEXL2 4
|
||||
#define X_OFFSET 50 //以左上第一块墙为基准的偏移量
|
||||
#define Y_OFFSET 45 //以左上第一块墙为基准的偏移量
|
||||
/*
|
||||
地图:
|
||||
使用数组存储地图信息
|
||||
map[x][y] 存储当前坦克信息
|
||||
BLANK 空地
|
||||
RED_TANK 红方坦克
|
||||
RED_DEAD_TANK 红方阵亡坦克
|
||||
BLUE_TANK, 蓝方坦克
|
||||
BLUE_DEAD_TANK 蓝方阵亡坦克
|
||||
WALL 墙
|
||||
BULLET 子弹
|
||||
*/
|
||||
// 偏移
|
||||
constexpr int X_OFFSET = 50;
|
||||
constexpr int Y_OFFSET = 45;
|
||||
|
||||
// 窗口
|
||||
constexpr int WINDOW_W = 700;
|
||||
constexpr int WINDOW_H = 700;
|
||||
|
||||
// 实体数量
|
||||
constexpr int ENEMIES_NUM = 10;
|
||||
constexpr int MAX_PLAYER_BULLETS = 3;
|
||||
constexpr int MAX_ENEMY_BULLETS = ENEMIES_NUM;
|
||||
|
||||
// 方向
|
||||
enum Direction { UP, DOWN, LEFT, RIGHT };
|
||||
|
||||
// 地图单元类型
|
||||
enum CELL_Type {
|
||||
BLANK, // 空地
|
||||
RED_TANK, // 红方坦克
|
||||
RED_DEAD_TANK, // 红方报废坦克
|
||||
BLUE_TANK, // 蓝方坦克
|
||||
BLUE_DEAD_TANK, // 蓝方报废坦克
|
||||
WALL, // 墙
|
||||
BULLET, // 子弹
|
||||
BLANK,
|
||||
RED_TANK,
|
||||
RED_DEAD_TANK,
|
||||
BLUE_TANK,
|
||||
BLUE_DEAD_TANK,
|
||||
WALL,
|
||||
BULLET
|
||||
};
|
||||
int map[MAP_CELL_NUM][MAP_CELL_NUM]; //在map.h预设好
|
||||
|
||||
int kill = 5; //击杀数
|
||||
tank player; //玩家
|
||||
tank enemies[ENEMIES_NUM]; //敌人
|
||||
bullet E_bullets[BULLET_NUM * ENEMIES_NUM]; //敌方坦克子弹数组
|
||||
bullet P_bullets[BULLET_NUM]; //玩家子弹数组
|
||||
|
||||
tank enemy; // 用于更改敌人的属性(难度设置用到)
|
||||
|
||||
#endif // !DATA_CONFIG_H
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
// map.cpp
|
||||
#include "map.h"
|
||||
|
||||
// 定义两个地图数组
|
||||
int map1[50][50];
|
||||
int map2[50][50];
|
||||
|
||||
void init_map1()
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
for (int j = 0; j < 50; j++)
|
||||
{
|
||||
// 四周为墙
|
||||
if (i == 0 || i == 49 || j == 0 || j == 49)
|
||||
{
|
||||
map1[i][j] = WALL;
|
||||
}
|
||||
// 构造中间的+形状
|
||||
else if (i == 24 || j == 24)
|
||||
{
|
||||
map1[i][j] = WALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
map1[i][j] = BLANK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_map2()
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
for (int j = 0; j < 50; j++)
|
||||
{
|
||||
// 四周为墙
|
||||
if (i == 0 || i == 49 || j == 0 || j == 49)
|
||||
{
|
||||
map2[i][j] = WALL;
|
||||
}
|
||||
// 构造中间的X形状
|
||||
else if (i == j || i + j == 49)
|
||||
{
|
||||
map2[i][j] = WALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
map2[i][j] = BLANK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 预制地图定义
|
||||
// map.h
|
||||
#ifndef MAP_H
|
||||
#define MAP_H
|
||||
|
||||
#include "data_config.h"
|
||||
|
||||
// 声明两个地图数组
|
||||
extern int map1[50][50];
|
||||
extern int map2[50][50];
|
||||
|
||||
// 声明地图初始化函数
|
||||
void init_map1();
|
||||
void init_map2();
|
||||
|
||||
#endif // !MAP_H
|
||||
@@ -1,16 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ 生成的包含文件。
|
||||
// 供 resource.rc 使用
|
||||
//
|
||||
#define IDR_WAVE1 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
Binary file not shown.
@@ -0,0 +1,52 @@
|
||||
#include "AssetManager.h"
|
||||
#include <cstdio>
|
||||
|
||||
AssetManager::AssetManager(SDL_Renderer* r) : renderer(r) {}
|
||||
|
||||
AssetManager::~AssetManager() {
|
||||
for (auto& [_, tex] : textures)
|
||||
SDL_DestroyTexture(tex);
|
||||
if (font) TTF_CloseFont(font);
|
||||
if (bgm) { Mix_HaltMusic(); Mix_FreeMusic(bgm); }
|
||||
}
|
||||
|
||||
SDL_Texture* AssetManager::getTexture(const std::string& path) {
|
||||
auto it = textures.find(path);
|
||||
if (it != textures.end())
|
||||
return it->second;
|
||||
|
||||
SDL_Surface* surf = IMG_Load(path.c_str());
|
||||
if (!surf) {
|
||||
fprintf(stderr, "IMG_Load error: %s - %s\n", path.c_str(), IMG_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
|
||||
SDL_FreeSurface(surf);
|
||||
if (tex) {
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
} else {
|
||||
fprintf(stderr, "CreateTexture error: %s - %s\n", path.c_str(), SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
textures[path] = tex;
|
||||
return tex;
|
||||
}
|
||||
|
||||
bool AssetManager::loadFont(const char* path, int size) {
|
||||
font = TTF_OpenFont(path, size);
|
||||
if (font) {
|
||||
printf("Loaded font: %s\n", path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetManager::loadBGM(const char* path) {
|
||||
bgm = Mix_LoadMUS(path);
|
||||
if (bgm) {
|
||||
Mix_PlayMusic(bgm, -1);
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr, "BGM load failed: %s\n", Mix_GetError());
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef ASSET_MANAGER_H
|
||||
#define ASSET_MANAGER_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class AssetManager {
|
||||
public:
|
||||
AssetManager(SDL_Renderer* renderer);
|
||||
~AssetManager();
|
||||
|
||||
SDL_Texture* getTexture(const std::string& path);
|
||||
TTF_Font* getFont() const { return font; }
|
||||
Mix_Music* getBGM() const { return bgm; }
|
||||
|
||||
bool loadFont(const char* path, int size);
|
||||
bool loadBGM(const char* path);
|
||||
|
||||
private:
|
||||
SDL_Renderer* renderer;
|
||||
std::unordered_map<std::string, SDL_Texture*> textures;
|
||||
TTF_Font* font = nullptr;
|
||||
Mix_Music* bgm = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "Bullet.h"
|
||||
#include "Tank.h"
|
||||
|
||||
void Bullet::fire(int bx, int by, Direction d, int dmg) {
|
||||
x = bx; y = by; dir = d; damage = dmg; isLive = true;
|
||||
}
|
||||
|
||||
CELL_Type Bullet::peekAt(int map[MAP_CELL_NUM][MAP_CELL_NUM]) const {
|
||||
return static_cast<CELL_Type>(map[x + dx(dir)][y + dy(dir)]);
|
||||
}
|
||||
|
||||
void Bullet::step() {
|
||||
x += dx(dir);
|
||||
y += dy(dir);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef BULLET_H
|
||||
#define BULLET_H
|
||||
|
||||
#include "../data_config.h"
|
||||
|
||||
class Bullet {
|
||||
public:
|
||||
int x = 0, y = 0;
|
||||
int damage = 0;
|
||||
Direction dir = UP;
|
||||
bool isLive = false;
|
||||
|
||||
void fire(int bx, int by, Direction d, int dmg);
|
||||
CELL_Type peekAt(int map[MAP_CELL_NUM][MAP_CELL_NUM]) const;
|
||||
void step();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,451 @@
|
||||
#include "Game.h"
|
||||
#include "Tank.h"
|
||||
#include "UI.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
#include <unistd.h>
|
||||
|
||||
Game::Game() {
|
||||
std::srand(static_cast<unsigned>(std::time(nullptr)));
|
||||
playerBullets.resize(MAX_PLAYER_BULLETS);
|
||||
enemyBullets.resize(MAX_ENEMY_BULLETS);
|
||||
enemies.resize(ENEMIES_NUM);
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
delete render;
|
||||
delete assets;
|
||||
if (sdlRenderer) SDL_DestroyRenderer(sdlRenderer);
|
||||
if (window) SDL_DestroyWindow(window);
|
||||
Mix_CloseAudio();
|
||||
TTF_Quit();
|
||||
IMG_Quit();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
bool Game::initSDL() {
|
||||
char* base = SDL_GetBasePath();
|
||||
if (base) { chdir(base); SDL_free(base); }
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||
fprintf(stderr, "SDL init failed: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
if (!(IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) & (IMG_INIT_PNG | IMG_INIT_JPG))) {
|
||||
fprintf(stderr, "SDL_image init failed: %s\n", IMG_GetError());
|
||||
return false;
|
||||
}
|
||||
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
|
||||
fprintf(stderr, "SDL_mixer init failed: %s\n", Mix_GetError());
|
||||
return false;
|
||||
}
|
||||
if (TTF_Init() < 0) {
|
||||
fprintf(stderr, "SDL_ttf init failed: %s\n", TTF_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("纸牌坦克", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
WINDOW_W, WINDOW_H, SDL_WINDOW_SHOWN);
|
||||
if (!window) { fprintf(stderr, "Window failed: %s\n", SDL_GetError()); return false; }
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!sdlRenderer) { fprintf(stderr, "Renderer failed: %s\n", SDL_GetError()); return false; }
|
||||
|
||||
assets = new AssetManager(sdlRenderer);
|
||||
render = new Renderer(sdlRenderer, assets);
|
||||
|
||||
const char* fontPaths[] = {
|
||||
"/usr/share/fonts/wqy-microhei-fonts/wqy-microhei.ttc",
|
||||
"/usr/share/fonts/wqy-zenhei-fonts/wqy-zenhei.ttc",
|
||||
"/usr/share/fonts/google-noto-sans-cjk-vf-fonts/NotoSansCJK-VF.ttc",
|
||||
"/usr/share/fonts/google-noto-serif-cjk-vf-fonts/NotoSerifCJK-VF.ttc",
|
||||
nullptr
|
||||
};
|
||||
bool fontOk = false;
|
||||
for (int i = 0; fontPaths[i]; i++) {
|
||||
if (assets->loadFont(fontPaths[i], 30)) { fontOk = true; break; }
|
||||
}
|
||||
if (!fontOk) fprintf(stderr, "Warning: no Chinese font found\n");
|
||||
|
||||
assets->loadBGM("music/bgm.wav");
|
||||
|
||||
lastPlayerMove = lastPlayerShoot = lastEnemyMove = lastEnemyShoot = 0;
|
||||
lastBulletMove = lastEnemyBulletMove = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Game::run() {
|
||||
if (!initSDL()) return -1;
|
||||
|
||||
scene = Scene::MENU;
|
||||
SDL_Event e;
|
||||
while (running) {
|
||||
while (SDL_PollEvent(&e)) handleEvents(e);
|
||||
update();
|
||||
renderScene();
|
||||
SDL_Delay(16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------- event -------------------
|
||||
|
||||
void Game::handleEvents(SDL_Event& e) {
|
||||
if (e.type == SDL_QUIT) { running = false; return; }
|
||||
|
||||
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) {
|
||||
switch (scene) {
|
||||
case Scene::LEVEL:
|
||||
case Scene::MEMBER:
|
||||
case Scene::GAME:
|
||||
case Scene::WIN:
|
||||
case Scene::LOSE:
|
||||
scene = Scene::MENU;
|
||||
map.reset();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.type != SDL_MOUSEBUTTONDOWN) return;
|
||||
int mx = e.button.x, my = e.button.y;
|
||||
|
||||
switch (scene) {
|
||||
case Scene::MENU:
|
||||
if (UI::hit(UI::BTN_START, mx, my)) { initGame(); scene = Scene::GAME; }
|
||||
if (UI::hit(UI::BTN_LEVEL, mx, my)) { scene = Scene::LEVEL; }
|
||||
if (UI::hit(UI::BTN_MEMBER, mx, my)) { scene = Scene::MEMBER; }
|
||||
if (UI::hit(UI::BTN_QUIT, mx, my)) { running = false; }
|
||||
break;
|
||||
case Scene::LEVEL:
|
||||
if (UI::hit(UI::BTN_EASY, mx, my)) {
|
||||
enemyCfg.hp = 5; enemyCfg.attack = 1; scene = Scene::MENU;
|
||||
}
|
||||
if (UI::hit(UI::BTN_MEDIUM, mx, my)) {
|
||||
enemyCfg.hp = 10; enemyCfg.attack = 2; scene = Scene::MENU;
|
||||
}
|
||||
if (UI::hit(UI::BTN_HARD, mx, my)) {
|
||||
enemyCfg.hp = 15; enemyCfg.attack = 3; scene = Scene::MENU;
|
||||
}
|
||||
break;
|
||||
case Scene::MEMBER:
|
||||
if (UI::hit(UI::BTN_BACK, mx, my)) scene = Scene::MENU;
|
||||
break;
|
||||
case Scene::WIN:
|
||||
case Scene::LOSE:
|
||||
if (UI::hit(UI::BTN_BACK, mx, my)) scene = Scene::MENU;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- update -------------------
|
||||
|
||||
void Game::update() {
|
||||
if (scene != Scene::GAME) return;
|
||||
updateGame();
|
||||
}
|
||||
|
||||
void Game::initGame() {
|
||||
map.reset();
|
||||
killCount = 0;
|
||||
|
||||
player = Tank(5, 5);
|
||||
player.x = MAP_CELL_NUM / 2;
|
||||
player.y = MAP_CELL_NUM / 2;
|
||||
player.dir = UP;
|
||||
|
||||
std::default_random_engine gen(std::random_device{}());
|
||||
std::uniform_int_distribution<int> dist(1, MAP_CELL_NUM - 2);
|
||||
|
||||
for (auto& e : enemies) {
|
||||
do {
|
||||
e.x = dist(gen);
|
||||
e.y = dist(gen);
|
||||
} while (map.get(e.x, e.y) != BLANK);
|
||||
e.hp = enemyCfg.hp;
|
||||
e.attack = enemyCfg.attack;
|
||||
e.dir = LEFT;
|
||||
e.isTaken = false;
|
||||
map.set(e.x, e.y, BLUE_TANK);
|
||||
}
|
||||
|
||||
for (auto& b : playerBullets) b.isLive = false;
|
||||
for (auto& b : enemyBullets) b.isLive = false;
|
||||
}
|
||||
|
||||
bool Game::isWin() const {
|
||||
for (auto& e : enemies)
|
||||
if (e.hp > 0) return false;
|
||||
return player.hp > 0;
|
||||
}
|
||||
|
||||
void Game::updateGame() {
|
||||
if (player.hp <= 0) { scene = Scene::LOSE; return; }
|
||||
if (isWin()) { scene = Scene::WIN; return; }
|
||||
|
||||
Uint32 now = SDL_GetTicks();
|
||||
|
||||
// --- 玩家移动 ---
|
||||
const Uint8* keys = SDL_GetKeyboardState(nullptr);
|
||||
bool moved = false;
|
||||
if (keys[SDL_SCANCODE_W] || keys[SDL_SCANCODE_UP]) { player.dir = UP; moved = true; }
|
||||
else if (keys[SDL_SCANCODE_S] || keys[SDL_SCANCODE_DOWN]) { player.dir = DOWN; moved = true; }
|
||||
else if (keys[SDL_SCANCODE_A] || keys[SDL_SCANCODE_LEFT]) { player.dir = LEFT; moved = true; }
|
||||
else if (keys[SDL_SCANCODE_D] || keys[SDL_SCANCODE_RIGHT]){ player.dir = RIGHT; moved = true; }
|
||||
|
||||
if (moved && now - lastPlayerMove >= 100) {
|
||||
lastPlayerMove = now;
|
||||
int tx = player.x + dx(player.dir);
|
||||
int ty = player.y + dy(player.dir);
|
||||
CELL_Type c = map.get(tx, ty);
|
||||
|
||||
if (c == BLANK) {
|
||||
player.x = tx; player.y = ty;
|
||||
} else if (c == BULLET) {
|
||||
player.x = tx; player.y = ty;
|
||||
player.hp--;
|
||||
for (auto& b : enemyBullets) {
|
||||
if (b.isLive && b.x == tx && b.y == ty) { b.isLive = false; break; }
|
||||
}
|
||||
} else if (c == BLUE_DEAD_TANK) {
|
||||
player.x = tx; player.y = ty;
|
||||
player.attack++;
|
||||
if (player.hp < 5) player.hp++;
|
||||
for (auto& e : enemies) {
|
||||
if (e.hp <= 0 && !e.isTaken && e.x == tx && e.y == ty) {
|
||||
e.isTaken = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 玩家射击 ---
|
||||
if (keys[SDL_SCANCODE_SPACE] && now - lastPlayerShoot >= 500) {
|
||||
lastPlayerShoot = now;
|
||||
for (auto& b : playerBullets) {
|
||||
if (!b.isLive) {
|
||||
b.fire(player.x, player.y, player.dir, player.attack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 敌人移动 ---
|
||||
if (now - lastEnemyMove >= 1000) {
|
||||
lastEnemyMove = now;
|
||||
for (auto& e : enemies) {
|
||||
if (e.hp <= 0) continue;
|
||||
Direction d = static_cast<Direction>(rand() % 4);
|
||||
e.dir = d;
|
||||
int tx = e.x + dx(d);
|
||||
int ty = e.y + dy(d);
|
||||
CELL_Type c = map.get(tx, ty);
|
||||
|
||||
if (c == BLANK) {
|
||||
e.x = tx; e.y = ty;
|
||||
} else if (c == BULLET) {
|
||||
e.x = tx; e.y = ty;
|
||||
e.hp--;
|
||||
for (auto& pb : playerBullets) {
|
||||
if (pb.isLive && pb.x == tx && pb.y == ty) { pb.isLive = false; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 敌人射击 ---
|
||||
if (now - lastEnemyShoot >= 3000) {
|
||||
lastEnemyShoot = now;
|
||||
for (auto& e : enemies) {
|
||||
if (e.hp <= 0) continue;
|
||||
for (auto& b : enemyBullets) {
|
||||
if (!b.isLive) {
|
||||
b.fire(e.x, e.y, e.dir, enemyCfg.attack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 玩家子弹移动 ---
|
||||
if (now - lastBulletMove >= 100) {
|
||||
lastBulletMove = now;
|
||||
for (auto& b : playerBullets) {
|
||||
if (!b.isLive) continue;
|
||||
CELL_Type c = b.peekAt(map.grid);
|
||||
|
||||
if (c == BLANK) {
|
||||
b.step();
|
||||
} else if (c == WALL) {
|
||||
b.isLive = false;
|
||||
} else if (c == BLUE_TANK) {
|
||||
b.isLive = false;
|
||||
int tx = b.x + dx(b.dir), ty = b.y + dy(b.dir);
|
||||
for (auto& e : enemies) {
|
||||
if (e.hp > 0 && e.x == tx && e.y == ty) { e.hp -= b.damage; break; }
|
||||
}
|
||||
} else {
|
||||
b.isLive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 敌人子弹移动 ---
|
||||
if (now - lastEnemyBulletMove >= 500) {
|
||||
lastEnemyBulletMove = now;
|
||||
for (auto& eb : enemyBullets) {
|
||||
if (!eb.isLive) continue;
|
||||
CELL_Type c = eb.peekAt(map.grid);
|
||||
|
||||
if (c == BLANK) {
|
||||
eb.step();
|
||||
} else if (c == WALL) {
|
||||
eb.isLive = false;
|
||||
} else if (c == RED_TANK) {
|
||||
eb.isLive = false;
|
||||
player.hp -= eb.damage;
|
||||
} else {
|
||||
eb.step();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 同步地图 ---
|
||||
map.reset();
|
||||
map.set(player.x, player.y, RED_TANK);
|
||||
for (auto& e : enemies) {
|
||||
if (e.hp > 0)
|
||||
map.set(e.x, e.y, BLUE_TANK);
|
||||
else if (!e.isTaken)
|
||||
map.set(e.x, e.y, BLUE_DEAD_TANK);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- render -------------------
|
||||
|
||||
void Game::renderScene() {
|
||||
render->clear();
|
||||
switch (scene) {
|
||||
case Scene::MENU: renderMenu(); break;
|
||||
case Scene::LEVEL: renderLevel(); break;
|
||||
case Scene::MEMBER: renderMember(); break;
|
||||
case Scene::GAME: renderGameView(); break;
|
||||
case Scene::WIN: renderWin(); break;
|
||||
case Scene::LOSE: renderLose(); break;
|
||||
default: break;
|
||||
}
|
||||
render->present();
|
||||
}
|
||||
|
||||
static SDL_Color GRAY = {157, 157, 157, 255};
|
||||
|
||||
void Game::renderMenu() {
|
||||
render->drawImage("images/菜单界面.jpg", 0, 0, WINDOW_W, WINDOW_H);
|
||||
render->drawImage("images/开始游戏按键.jpg", UI::BTN_START.x, UI::BTN_START.y, UI::BTN_START.w, UI::BTN_START.h);
|
||||
render->drawImage("images/难度设置按键.jpg", UI::BTN_LEVEL.x, UI::BTN_LEVEL.y, UI::BTN_LEVEL.w, UI::BTN_LEVEL.h);
|
||||
render->drawImage("images/相关人员按键.jpg", UI::BTN_MEMBER.x, UI::BTN_MEMBER.y, UI::BTN_MEMBER.w, UI::BTN_MEMBER.h);
|
||||
render->drawImage("images/退出游戏按键.jpg", UI::BTN_QUIT.x, UI::BTN_QUIT.y, UI::BTN_QUIT.w, UI::BTN_QUIT.h);
|
||||
}
|
||||
|
||||
void Game::renderLevel() {
|
||||
render->drawImage("images/游戏主界面背景.jpg", 0, 0, WINDOW_W, WINDOW_H);
|
||||
render->drawImage("images/简单难度.jpg", UI::BTN_EASY.x, UI::BTN_EASY.y, UI::BTN_EASY.w, UI::BTN_EASY.h);
|
||||
render->drawImage("images/中等难度.jpg", UI::BTN_MEDIUM.x, UI::BTN_MEDIUM.y, UI::BTN_MEDIUM.w, UI::BTN_MEDIUM.h);
|
||||
render->drawImage("images/困难难度.jpg", UI::BTN_HARD.x, UI::BTN_HARD.y, UI::BTN_HARD.w, UI::BTN_HARD.h);
|
||||
}
|
||||
|
||||
void Game::renderMember() {
|
||||
render->drawImage("images/开发人员界面.jpg", 0, 0, WINDOW_W, WINDOW_H);
|
||||
render->drawImage("images/返回按钮.jpg", UI::BTN_BACK.x, UI::BTN_BACK.y, UI::BTN_BACK.w, UI::BTN_BACK.h);
|
||||
}
|
||||
|
||||
void Game::renderGameView() {
|
||||
render->drawImage("images/游戏主界面背景.jpg", 0, 0, WINDOW_W, WINDOW_H);
|
||||
|
||||
SDL_Texture* hpTex = assets->getTexture("images/red_hp_bar.jpg");
|
||||
for (int i = 0; i < player.hp; i++)
|
||||
render->drawTexture(hpTex, UI::HP_BAR_X + i * CELL_SIZE, UI::HP_BAR_Y, CELL_SIZE, CELL_SIZE);
|
||||
|
||||
int dieNum = 0;
|
||||
for (auto& e : enemies) if (e.hp <= 0) dieNum++;
|
||||
render->drawText("杀敌:", 110, 660, GRAY);
|
||||
render->drawInt(dieNum, 185, 660, GRAY);
|
||||
render->drawText("/10", 210, 660, GRAY);
|
||||
std::string atk = "当前攻击力" + std::to_string(player.attack);
|
||||
render->drawText(atk, 325, 660, GRAY);
|
||||
|
||||
SDL_Texture* wallTex = assets->getTexture("images/wall.jpg");
|
||||
for (int i = 0; i < MAP_CELL_NUM; i++)
|
||||
for (int j = 0; j < MAP_CELL_NUM; j++)
|
||||
if (map.grid[i][j] == WALL)
|
||||
render->drawTexture(wallTex, j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE);
|
||||
|
||||
// 子弹
|
||||
auto drawBullet = [&](Bullet& b) {
|
||||
if (!b.isLive) return;
|
||||
int sx = b.x * CELL_SIZE + X_OFFSET;
|
||||
int sy = b.y * CELL_SIZE + Y_OFFSET;
|
||||
bool horiz = (b.dir == LEFT || b.dir == RIGHT);
|
||||
int tx = horiz ? sx : sx + 10;
|
||||
int ty = horiz ? sy + 10 : sy;
|
||||
int bw = horiz ? CELL_SIZE / 2 : CELL_SIZE / 4;
|
||||
int bh = horiz ? CELL_SIZE / 4 : CELL_SIZE / 2;
|
||||
const char* img = nullptr;
|
||||
switch (b.dir) {
|
||||
case UP: img = "images/B_UP_b.png"; break;
|
||||
case DOWN: img = "images/B_DOWN_b.png"; break;
|
||||
case LEFT: img = "images/B_LEFT_b.png"; break;
|
||||
case RIGHT: img = "images/B_RIGHT_b.png"; break;
|
||||
}
|
||||
SDL_Texture* tex = assets->getTexture(img);
|
||||
render->drawTexture(tex, tx, ty, bw, bh);
|
||||
};
|
||||
|
||||
for (auto& b : playerBullets) drawBullet(b);
|
||||
for (auto& b : enemyBullets) drawBullet(b);
|
||||
|
||||
// 坦克
|
||||
auto drawTank = [&](Tank& t, bool isPlayer) {
|
||||
int px = t.x * CELL_SIZE + X_OFFSET;
|
||||
int py = t.y * CELL_SIZE + Y_OFFSET;
|
||||
const char* img = nullptr;
|
||||
if (isPlayer) {
|
||||
switch (t.dir) {
|
||||
case UP: img = "images/red_up_b.png"; break;
|
||||
case DOWN: img = "images/red_down_b.png"; break;
|
||||
case LEFT: img = "images/red_left_b.png"; break;
|
||||
case RIGHT: img = "images/red_right_b.png"; break;
|
||||
}
|
||||
} else {
|
||||
switch (t.dir) {
|
||||
case UP: img = "images/blue_up_b.png"; break;
|
||||
case DOWN: img = "images/blue_down_b.png"; break;
|
||||
case LEFT: img = "images/blue_left_b.png"; break;
|
||||
case RIGHT: img = "images/blue_right_b.png"; break;
|
||||
}
|
||||
}
|
||||
render->drawTexture(assets->getTexture(img), px, py, CELL_SIZE, CELL_SIZE);
|
||||
};
|
||||
|
||||
drawTank(player, true);
|
||||
for (auto& e : enemies) {
|
||||
if (e.isTaken) continue;
|
||||
if (e.hp > 0) drawTank(e, false);
|
||||
else render->drawTexture(assets->getTexture("images/dead_tank_b.png"),
|
||||
e.x * CELL_SIZE + X_OFFSET, e.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::renderWin() {
|
||||
render->drawImage("images/胜利.png", 0, 0, WINDOW_W, WINDOW_H);
|
||||
render->drawImage("images/返回按钮.jpg", UI::BTN_BACK.x, UI::BTN_BACK.y, UI::BTN_BACK.w, UI::BTN_BACK.h);
|
||||
}
|
||||
|
||||
void Game::renderLose() {
|
||||
render->drawImage("images/失败.png", 0, 0, WINDOW_W, WINDOW_H);
|
||||
render->drawImage("images/返回按钮.jpg", UI::BTN_BACK.x, UI::BTN_BACK.y, UI::BTN_BACK.w, UI::BTN_BACK.h);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
#include "../data_config.h"
|
||||
#include "Map.h"
|
||||
#include "Tank.h"
|
||||
#include "Bullet.h"
|
||||
#include "AssetManager.h"
|
||||
#include "Renderer.h"
|
||||
|
||||
enum class Scene { MENU, LEVEL, MEMBER, GAME, WIN, LOSE, QUIT };
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
int run();
|
||||
|
||||
private:
|
||||
SDL_Window* window = nullptr;
|
||||
SDL_Renderer* sdlRenderer = nullptr;
|
||||
AssetManager* assets = nullptr;
|
||||
Renderer* render = nullptr;
|
||||
|
||||
Scene scene = Scene::MENU;
|
||||
bool running = true;
|
||||
|
||||
Map map;
|
||||
Tank player;
|
||||
std::vector<Tank> enemies;
|
||||
std::vector<Bullet> playerBullets;
|
||||
std::vector<Bullet> enemyBullets;
|
||||
Tank enemyCfg;
|
||||
int killCount = 0;
|
||||
|
||||
// 计时
|
||||
Uint32 lastPlayerMove = 0, lastPlayerShoot = 0;
|
||||
Uint32 lastEnemyMove = 0, lastEnemyShoot = 0;
|
||||
Uint32 lastBulletMove = 0, lastEnemyBulletMove = 0;
|
||||
|
||||
bool initSDL();
|
||||
bool initFont();
|
||||
void handleEvents(SDL_Event& e);
|
||||
void update();
|
||||
void renderScene();
|
||||
|
||||
void initGame();
|
||||
void updateGame();
|
||||
|
||||
void renderMenu();
|
||||
void renderLevel();
|
||||
void renderMember();
|
||||
void renderGameView();
|
||||
void renderWin();
|
||||
void renderLose();
|
||||
|
||||
bool isWin() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "Map.h"
|
||||
|
||||
Map::Map() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Map::reset() {
|
||||
for (int i = 0; i < MAP_CELL_NUM; i++) {
|
||||
for (int j = 0; j < MAP_CELL_NUM; j++) {
|
||||
if (i == 0 || i == MAP_CELL_NUM - 1 || j == 0 || j == MAP_CELL_NUM - 1)
|
||||
grid[i][j] = WALL;
|
||||
else
|
||||
grid[i][j] = BLANK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CELL_Type Map::get(int x, int y) const {
|
||||
return static_cast<CELL_Type>(grid[x][y]);
|
||||
}
|
||||
|
||||
void Map::set(int x, int y, CELL_Type type) {
|
||||
grid[x][y] = type;
|
||||
}
|
||||
|
||||
CELL_Type Map::peek(int x, int y, Direction dir) const {
|
||||
switch (dir) {
|
||||
case UP: return static_cast<CELL_Type>(grid[x][y - 1]);
|
||||
case DOWN: return static_cast<CELL_Type>(grid[x][y + 1]);
|
||||
case LEFT: return static_cast<CELL_Type>(grid[x - 1][y]);
|
||||
case RIGHT: return static_cast<CELL_Type>(grid[x + 1][y]);
|
||||
}
|
||||
return BLANK;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef MAP_H
|
||||
#define MAP_H
|
||||
|
||||
#include "../data_config.h"
|
||||
|
||||
class Map {
|
||||
public:
|
||||
int grid[MAP_CELL_NUM][MAP_CELL_NUM];
|
||||
|
||||
Map();
|
||||
void reset();
|
||||
CELL_Type get(int x, int y) const;
|
||||
void set(int x, int y, CELL_Type type);
|
||||
CELL_Type peek(int x, int y, Direction dir) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "Renderer.h"
|
||||
|
||||
Renderer::Renderer(SDL_Renderer* r, AssetManager* a) : renderer(r), assets(a) {}
|
||||
|
||||
void Renderer::clear() {
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
}
|
||||
|
||||
void Renderer::present() {
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
void Renderer::drawTexture(SDL_Texture* tex, int x, int y, int w, int h) {
|
||||
if (!tex) return;
|
||||
SDL_Rect dst = {x, y, w, h};
|
||||
SDL_RenderCopy(renderer, tex, nullptr, &dst);
|
||||
}
|
||||
|
||||
void Renderer::drawImage(const std::string& path, int x, int y, int w, int h) {
|
||||
drawTexture(assets->getTexture(path), x, y, w, h);
|
||||
}
|
||||
|
||||
void Renderer::drawText(const std::string& text, int x, int y, SDL_Color color) {
|
||||
TTF_Font* font = assets->getFont();
|
||||
if (!font || text.empty()) return;
|
||||
SDL_Surface* surf = TTF_RenderUTF8_Blended(font, text.c_str(), color);
|
||||
if (!surf) return;
|
||||
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
|
||||
if (tex) {
|
||||
SDL_Rect dst = {x, y, surf->w, surf->h};
|
||||
SDL_RenderCopy(renderer, tex, nullptr, &dst);
|
||||
SDL_DestroyTexture(tex);
|
||||
}
|
||||
SDL_FreeSurface(surf);
|
||||
}
|
||||
|
||||
void Renderer::drawInt(int value, int x, int y, SDL_Color color) {
|
||||
drawText(std::to_string(value), x, y, color);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <string>
|
||||
#include "AssetManager.h"
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(SDL_Renderer* r, AssetManager* a);
|
||||
|
||||
void clear();
|
||||
void present();
|
||||
void drawTexture(SDL_Texture* tex, int x, int y, int w, int h);
|
||||
void drawImage(const std::string& path, int x, int y, int w, int h);
|
||||
void drawText(const std::string& text, int x, int y, SDL_Color color);
|
||||
void drawInt(int value, int x, int y, SDL_Color color);
|
||||
|
||||
private:
|
||||
SDL_Renderer* renderer;
|
||||
AssetManager* assets;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "Tank.h"
|
||||
|
||||
Tank::Tank(int h, int a) : hp(h), attack(a) {}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef TANK_H
|
||||
#define TANK_H
|
||||
|
||||
#include "../data_config.h"
|
||||
|
||||
class Tank {
|
||||
public:
|
||||
int x = 0, y = 0;
|
||||
int hp = 5, attack = 1;
|
||||
Direction dir = UP;
|
||||
bool isTaken = false;
|
||||
|
||||
Tank(int h = 5, int a = 1);
|
||||
};
|
||||
|
||||
inline int dx(Direction d) { return d == LEFT ? -1 : (d == RIGHT ? 1 : 0); }
|
||||
inline int dy(Direction d) { return d == UP ? -1 : (d == DOWN ? 1 : 0); }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef UI_H
|
||||
#define UI_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
namespace UI {
|
||||
|
||||
inline bool hit(const SDL_Rect& r, int mx, int my) {
|
||||
return mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h;
|
||||
}
|
||||
|
||||
// 菜单按钮
|
||||
constexpr SDL_Rect BTN_START = {50, 250, 150, 50};
|
||||
constexpr SDL_Rect BTN_LEVEL = {50, 330, 150, 50};
|
||||
constexpr SDL_Rect BTN_MEMBER = {50, 410, 150, 50};
|
||||
constexpr SDL_Rect BTN_QUIT = {50, 490, 150, 50};
|
||||
|
||||
// 难度按钮
|
||||
constexpr SDL_Rect BTN_EASY = {275, 150, 150, 100};
|
||||
constexpr SDL_Rect BTN_MEDIUM = {275, 300, 150, 100};
|
||||
constexpr SDL_Rect BTN_HARD = {275, 450, 150, 100};
|
||||
|
||||
// 返回按钮
|
||||
constexpr SDL_Rect BTN_BACK = {30, 550, 150, 50};
|
||||
|
||||
// HP 条
|
||||
constexpr int HP_BAR_X = 100;
|
||||
constexpr int HP_BAR_Y = 10;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
// 坦克定义
|
||||
#ifndef TANK_H
|
||||
#define TANK_H
|
||||
|
||||
/*
|
||||
坦克:
|
||||
坐标:x,y
|
||||
血量hp:表示当前坦克血量
|
||||
攻击力attack:表示当前坦克攻击力
|
||||
方向dir:
|
||||
UP 上
|
||||
DOWN 下
|
||||
LEFT 左
|
||||
RIGHT 右
|
||||
*/
|
||||
enum Direction {
|
||||
UP, // 上
|
||||
DOWN, // 下
|
||||
LEFT, // 左
|
||||
RIGHT, // 右
|
||||
};
|
||||
|
||||
struct tank {
|
||||
int x, y;//坐标
|
||||
int hp; // 血量
|
||||
int attack; // 攻击力
|
||||
int dir; // 方向
|
||||
bool is_taken = false;//报销坦克是否已经被玩家捡取
|
||||
|
||||
tank(): attack(1), hp(5){}
|
||||
};
|
||||
|
||||
#endif // !TANK_H
|
||||
@@ -1,147 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{ac8c4a04-1650-473c-8e9a-3b7cde2ad5cc}</ProjectGuid>
|
||||
<RootNamespace>tankbattlesonthescrappaper</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="bullet.h" />
|
||||
<ClInclude Include="data_config.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="tank.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="resource.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Media Include="music\bgm.wav" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="tank.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bullet.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="data_config.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="resource.rc">
|
||||
<Filter>资源文件</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Media Include="music\bgm.wav">
|
||||
<Filter>资源文件</Filter>
|
||||
</Media>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user