From 9b9cb9e86c3fe14f3d23b1d018c822290de15784 Mon Sep 17 00:00:00 2001 From: flykhan Date: Sun, 3 May 2026 10:58:06 +0800 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=E5=B0=86=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E4=BB=8E=20.sln/.vcxproj=20=E8=BD=AC=E6=8D=A2=E4=B8=BA=20CMake?= =?UTF-8?q?=20=E6=9E=84=E5=BB=BA=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 11 ++++++++ CMakeLists.txt | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 CMakeLists.txt diff --git a/.gitignore b/.gitignore index cbef543..42f2da8 100644 --- a/.gitignore +++ b/.gitignore @@ -432,3 +432,14 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +# ---> CMake +build/ +cmake-build-*/ +CMakeCache.txt +CMakeFiles/ +CMakeScripts/ +*.cmake +!CMakeLists.txt +Makefile +*.make + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6c1470a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,69 @@ +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) + +# ── 源文件 ── +set(SOURCES + tank_battles_on_the_scrap_paper/main.cpp + tank_battles_on_the_scrap_paper/map.cpp +) + +set(HEADERS + tank_battles_on_the_scrap_paper/bullet.h + tank_battles_on_the_scrap_paper/tank.h + tank_battles_on_the_scrap_paper/map.h + tank_battles_on_the_scrap_paper/data_config.h + tank_battles_on_the_scrap_paper/resource.h +) + +# ── 可执行文件 ── +add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} + tank_battles_on_the_scrap_paper/resource.rc +) + +# ── Windows 特定配置 ── +if(WIN32) + # EasyX 图形库 + target_include_directories(${PROJECT_NAME} PRIVATE + ${EASYX_INCLUDE_DIR} + ) + + # Windows 多媒体库 + target_link_libraries(${PROJECT_NAME} PRIVATE + winmm.lib + gdi32.lib + ) + + # 控制台子系统 + set_target_properties(${PROJECT_NAME} PROPERTIES + WIN32_EXECUTABLE FALSE + ) + + target_compile_definitions(${PROJECT_NAME} PRIVATE + _CONSOLE + _UNICODE + UNICODE + ) +else() + # 非 Windows 平台给出提示 + message(WARNING + "${PROJECT_NAME} 依赖 EasyX (Windows-only 图形库)," + "当前平台不支持编译。仅生成项目结构参考。" + ) +endif() + +# ── 复制资源文件到构建目录 ── +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/ +) From 897abcf0686ce01b836596ead4eb503822f0ee53 Mon Sep 17 00:00:00 2001 From: flykhan Date: Sun, 3 May 2026 11:29:39 +0800 Subject: [PATCH 2/4] fix: SDL2 Linux port - fix black screen by setting CWD to binary path --- CMakeLists.txt | 47 +- tank_battles_on_the_scrap_paper.sln | 31 - tank_battles_on_the_scrap_paper/data_config.h | 73 +- tank_battles_on_the_scrap_paper/main.cpp | 1231 +++++++++-------- tank_battles_on_the_scrap_paper/resource.h | 16 - tank_battles_on_the_scrap_paper/resource.rc | Bin 3018 -> 0 bytes .../tank_battles_on_the_scrap_paper.vcxproj | 147 -- ...battles_on_the_scrap_paper.vcxproj.filters | 46 - 8 files changed, 670 insertions(+), 921 deletions(-) delete mode 100644 tank_battles_on_the_scrap_paper.sln delete mode 100644 tank_battles_on_the_scrap_paper/resource.h delete mode 100644 tank_battles_on_the_scrap_paper/resource.rc delete mode 100644 tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj delete mode 100644 tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj.filters diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c1470a..186acd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,12 @@ project(tank_battles_on_the_scrap_paper set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# ── SDL2 依赖 ── +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 @@ -18,45 +24,18 @@ set(HEADERS tank_battles_on_the_scrap_paper/tank.h tank_battles_on_the_scrap_paper/map.h tank_battles_on_the_scrap_paper/data_config.h - tank_battles_on_the_scrap_paper/resource.h ) # ── 可执行文件 ── -add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} - tank_battles_on_the_scrap_paper/resource.rc +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 ) -# ── Windows 特定配置 ── -if(WIN32) - # EasyX 图形库 - target_include_directories(${PROJECT_NAME} PRIVATE - ${EASYX_INCLUDE_DIR} - ) - - # Windows 多媒体库 - target_link_libraries(${PROJECT_NAME} PRIVATE - winmm.lib - gdi32.lib - ) - - # 控制台子系统 - set_target_properties(${PROJECT_NAME} PROPERTIES - WIN32_EXECUTABLE FALSE - ) - - target_compile_definitions(${PROJECT_NAME} PRIVATE - _CONSOLE - _UNICODE - UNICODE - ) -else() - # 非 Windows 平台给出提示 - message(WARNING - "${PROJECT_NAME} 依赖 EasyX (Windows-only 图形库)," - "当前平台不支持编译。仅生成项目结构参考。" - ) -endif() - # ── 复制资源文件到构建目录 ── file(COPY tank_battles_on_the_scrap_paper/images/ diff --git a/tank_battles_on_the_scrap_paper.sln b/tank_battles_on_the_scrap_paper.sln deleted file mode 100644 index aa28c4a..0000000 --- a/tank_battles_on_the_scrap_paper.sln +++ /dev/null @@ -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 diff --git a/tank_battles_on_the_scrap_paper/data_config.h b/tank_battles_on_the_scrap_paper/data_config.h index 60ee9ec..e170b05 100644 --- a/tank_battles_on_the_scrap_paper/data_config.h +++ b/tank_battles_on_the_scrap_paper/data_config.h @@ -1,52 +1,51 @@ -// ö +// �������ö��� #ifndef DATA_CONFIG_H #define DATA_CONFIG_H -// tank bullet ͷļ +// ���� 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; // Ҷͼ- +// �������� +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; // �Ҷ�ͼ-�� -#define PIEXL1 30 //س +#define PIEXL1 30 //���س��� #define PIEXL2 4 -#define X_OFFSET 50 //ϵһǽΪ׼ƫ -#define Y_OFFSET 45 //ϵһǽΪ׼ƫ +#define X_OFFSET 50 //�����ϵ�һ��ǽΪ��׼��ƫ���� +#define Y_OFFSET 45 //�����ϵ�һ��ǽΪ��׼��ƫ���� /* - ͼ - ʹ洢ͼϢ - map[x][y] 洢ǰ̹Ϣ - BLANK յ - RED_TANK 췽̹ - RED_DEAD_TANK 췽̹ - BLUE_TANK, ̹ - BLUE_DEAD_TANK ̹ + ��ͼ�� + ʹ������洢��ͼ��Ϣ + map[x][y] �洢��ǰ̹����Ϣ + BLANK �յ� + RED_TANK �췽̹�� + RED_DEAD_TANK �췽����̹�� + BLUE_TANK, ����̹�� + BLUE_DEAD_TANK ��������̹�� WALL ǽ - BULLET ӵ + BULLET �ӵ� */ enum CELL_Type { - BLANK, // յ - RED_TANK, // 췽̹ - RED_DEAD_TANK, // 췽̹ - BLUE_TANK, // ̹ - BLUE_DEAD_TANK, // ̹ + BLANK, // �յ� + RED_TANK, // �췽̹�� + RED_DEAD_TANK, // �췽����̹�� + BLUE_TANK, // ����̹�� + BLUE_DEAD_TANK, // ��������̹�� WALL, // ǽ - BULLET, // ӵ + 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; // ڸĵ˵ԣѶõ +// 全局变量声明(extern),定义在 main.cpp 中 +extern int map[MAP_CELL_NUM][MAP_CELL_NUM]; +extern int kill; +extern tank player; +extern tank enemies[ENEMIES_NUM]; +extern bullet E_bullets[BULLET_NUM * ENEMIES_NUM]; +extern bullet P_bullets[BULLET_NUM]; +extern tank enemy; #endif // !DATA_CONFIG_H \ No newline at end of file diff --git a/tank_battles_on_the_scrap_paper/main.cpp b/tank_battles_on_the_scrap_paper/main.cpp index 864fd83..631d17c 100644 --- a/tank_battles_on_the_scrap_paper/main.cpp +++ b/tank_battles_on_the_scrap_paper/main.cpp @@ -1,24 +1,138 @@ -// Ŀֽϵ̹˴ս -#include -#include -#include -#include -#include +// 项目:纸牌上的坦克大战 - SDL2 Linux Port +#include +#include #include #include -// data_config ͷļ -#include "data_config.h" -// #include "map.h" // Ԥͼͷļ -// #include "main.h" // 뺯ͷļ +#include +#include +#include +#include +#include +#include -// -------- Ч -------- -#include"resource.h"//Դ֮һresource.hͷļ ǵü -#include -#include -#pragma comment(lib,"winmm.lib") +// 引入 data_config 等头文件 +#include "data_config.h" +// #include "map.h" // 引入预设地图头文件 +// #include "main.h" // 引入函数声明头文件 using namespace std; +// -------- 全局变量定义 --------- +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; // 用于修改敌人的属性,难度设置时用到 + +// -------- SDL2 全局变量 -------- +SDL_Window* g_window = NULL; +SDL_Renderer* g_renderer = NULL; +TTF_Font* g_font = NULL; +Mix_Music* g_bgm = NULL; + +// -------- 辅助函数 -------- + +// 加载纹理(带缩放) +SDL_Texture* load_texture(const char* path) { + static unordered_map tex_cache; + string key(path); + auto it = tex_cache.find(key); + if (it != tex_cache.end()) { + return it->second; + } + SDL_Surface* surf = IMG_Load(path); + if (!surf) { + fprintf(stderr, "IMG_Load error: %s - %s\n", path, IMG_GetError()); + return NULL; + } + SDL_Texture* tex = SDL_CreateTextureFromSurface(g_renderer, surf); + SDL_FreeSurface(surf); + if (tex) { + SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); + } else { + fprintf(stderr, "CreateTexture error: %s - %s\n", path, SDL_GetError()); + } + tex_cache[key] = tex; + return tex; +} + +// 渲染纹理到指定位置和大小 +void render_texture(SDL_Texture* tex, int x, int y, int w, int h) { + if (!tex) return; + SDL_Rect dst = {x, y, w, h}; + SDL_RenderCopy(g_renderer, tex, NULL, &dst); +} + +// 渲染纹理到指定位置(不缩放,使用纹理原始大小) +void render_texture_raw(SDL_Texture* tex, int x, int y) { + if (!tex) return; + int w, h; + SDL_QueryTexture(tex, NULL, NULL, &w, &h); + SDL_Rect dst = {x, y, w, h}; + SDL_RenderCopy(g_renderer, tex, NULL, &dst); +} + +// 带缩放加载并渲染纹理(一次性操作) +void draw_image(const char* path, int x, int y, int w, int h) { + SDL_Texture* tex = load_texture(path); + render_texture(tex, x, y, w, h); +} + +// 渲染文本 +void render_text(const char* text, int x, int y, SDL_Color color) { + if (!g_font || !text) return; + SDL_Surface* surf = TTF_RenderUTF8_Blended(g_font, text, color); + if (!surf) return; + SDL_Texture* tex = SDL_CreateTextureFromSurface(g_renderer, surf); + if (tex) { + SDL_Rect dst = {x, y, surf->w, surf->h}; + SDL_RenderCopy(g_renderer, tex, NULL, &dst); + SDL_DestroyTexture(tex); + } + SDL_FreeSurface(surf); +} + +// 渲染int数值为文本 +void render_int(int value, int x, int y, SDL_Color color) { + string s = to_string(value); + render_text(s.c_str(), x, y, color); +} + +// 清屏 +void clear_screen() { + SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255); + SDL_RenderClear(g_renderer); +} + +// 更新画面 +void present_screen() { + SDL_RenderPresent(g_renderer); +} + +// 事件处理辅助:等待并处理键盘和鼠标事件,返回是否退出 / 转到菜单 +// 返回: 0=继续循环, -1=退出/返回菜单 +int handle_menu_events(bool& goto_menu) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + exit(0); + } + if (e.type == SDL_KEYDOWN) { + if (e.key.keysym.sym == SDLK_ESCAPE) { + goto_menu = true; + return -1; + } + } + if (e.type == SDL_MOUSEBUTTONDOWN) { + return 1; // 鼠标点击发生,调用者需检查坐标 + } + } + return 0; +} + +// -------- 原函数声明(保持不变) -------- void EnemyBulletMove(); void PlayerBulletMove(); int BulletMoveCheck(int dir, bullet bul); @@ -45,106 +159,123 @@ void PlayerShoot(); int TankMoveCheck(int dir, tank tank); void Update(); -// git - +// git 提交 /* - ˵棺 - չʾϷѶȣ˳ѡ - ûͨwsѡӦİťس + 菜单界面: + 展示开始游戏,难度设置,退出等选项 + 用户通过w/s来选择对应的按钮,回车进行选择 - Ϸ - ѡϷͼʼͼ̹ + 开始游戏: + 当选中开始游戏时,跳转游戏主函数,直接进入游戏。 - Ѷȣ - öӦѶȽϷ + 难度设置: + 当选中难度设置时,跳转难度选择函数,进行对敌方坦克血量以及攻击力进行变化等操作。 + 调整完毕之后需要esc返回菜单然后才可以进入游戏。 - ڿԱ - ҵҳ棬չʾԱ + 退出游戏: + 当选中退出时,直接退出程序。 - ˳Ϸ - ѡ˳Ϸ + 关于开发人员 + 跳转相关信息页面,展示开发人员信息 - Ϸ - չʾͼ̹ˣҶ̹ԶƶͿ - wsadҷƶ - ¿ոԼǰڵ - УѪ - һ̹ˣϷ,ص˵ + // 游戏进行中的开始页面 - ͣ - չʾϷ˳ϷťͬʱϷͣ +游戏过程 + 展示地图和坦克,玩家控制己方坦克进行自动移动和开火 + 操作wsad控制己方坦克进行上下左右移动 + 按下空格控制己方向前发射子弹 + 撞到墙,掉血 + 当杀死所有敌方坦克时,游戏胜利,返回主菜单 + + 暂停界面 + 展示继续游戏、退出游戏按钮,同时游戏暂停 */ /* - - 1˵ - 2˵Ϸ - 3Ϸͣ - 4ͣò˵ + 功能模块 + 1.菜单界面 + 2.菜单界面转到游戏界面 + 3.游戏界面转到暂停界面 + 4.暂停界面转到菜单界面 */ //-----------------view------------------------ /* - ˣ - ܣչʾϷѶȣ˳ѡ, - Ϸ - ѡϷʱϷ溯ֱӽϷ - Ѷȣ - ѡѶʱҳ溯з̹ѪԼ仯ĺݡ - ֮Ҫesc˵ȻɽϷ - ˳Ϸ - ѡ˳ʱ˳ֱӽϷ - ǣ - չʾŶϢ - // Ϸеʼҳ + 功能描述: + 功能:展示开始游戏,难度设置,退出等选项, 关于我们 + 开始游戏: + 当选中开始游戏时,跳转游戏主函数,直接进入游戏。 + 难度设置: + 当选中难度设置时,跳转难度选择函数,进行对敌方坦克血量以及攻击力进行变化等操作。 + 调整完毕之后需要esc返回菜单然后才可以进入游戏。 + 退出游戏: + 当选中退出时,直接退出程序。 + 关于我们: + 展示团队信息 + // 游戏进行中的开始页面 */ void MenuView(); /* -* Ϸ - ˣunder a bushel - ܣ +* 游戏界面 + 功能描述: */ -// ÿһΣͼͼÿһ״̬ -// һµͼϽӣ̹˺ӵ꣺λúͷ -void GameView() //Ϸͼ +// 每一帧,地图和坦克每一帧状态渲染 +// 在一个新的地图上进行操作,包括坦克和子弹的坐标:位置和方向 +void GameView() //游戏主界面视图 { - InitMap(); //ʼͼ - // InitMap(map1); //ʼͼ - EnemyInit(); //ʼз̹˵λ - PlayerInit(); //ʼ̹˵λ + InitMap(); //初始化地图 + // InitMap(map1); //初始化地图 + EnemyInit(); //初始化敌方坦克的位置 + PlayerInit(); //初始化己方坦克的位置 - GameView_ShowMap(); //ʾͼ - GameView_ShowTank(); //ʾ̹ + // 初始渲染 + clear_screen(); + GameView_ShowMap(); //显示地图 + GameView_ShowTank(); //显示坦克 + present_screen(); - while (!IsWin()) // Ϸûʤ + while (!IsWin()) // 游戏循环没有胜利 { - if (player.hp <= 0) //жҵֵǷСڻ 0 + if (player.hp <= 0) //判断玩家的生命值是否小于或等于 0 { - GameView_ShowLose(); // failView ʾʧͼ - /*MenuView();*/ //ص˵ + GameView_ShowLose(); //调用 failView 函数显示失败视图 + /*MenuView();*/ //返回主菜单界面 break; } - if (GetAsyncKeyState(VK_ESCAPE)) //ûļ¼ǷΪ ESC - { - MenuView(); // menuView ص˵ + // 处理ESC和窗口事件 + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + exit(0); + } + if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { + MenuView(); //调用菜单 menuView 函数返回主菜单界面 + } } - Move(); // MoveShoot Update ЩҪʵ̹˺ӵƶ״̬¡ - // - if(GetAsyncKeyState(VK_SPACE)) + Move(); // 调用 Move、Shoot 和 Update 函数:这些函数需要实现坦克和子弹的移动、射击和状态更新。 + //射击 + const Uint8* keys = SDL_GetKeyboardState(NULL); + if (keys[SDL_SCANCODE_SPACE]) PlayerShoot(); EnemyShoot(); // Update(); Update(); - GameView_ShowMap(); //ʾͼ״̬ - GameView_ShowBullet(); //ʾӵ״̬ - GameView_ShowTank(); //ʾ̹˵״̬ + + // 渲染帧 + clear_screen(); + GameView_ShowMap(); //显示地图的最新状态 + GameView_ShowBullet(); //再进行子弹显示更新状态 + GameView_ShowTank(); //显示坦克的最新状态 + present_screen(); + + SDL_Delay(16); // ~60 FPS,控制帧率 } - if (IsWin()) //Ϸʤ winView ʾʤͼص˵档 + if (IsWin()) //如果游戏胜利,调用 winView 函数显示胜利界面,并返回主菜单界面。 { GameView_ShowWIN(); /*MenuView();*/ @@ -152,10 +283,10 @@ void GameView() // } /* - :Ӻˮ - - жӵǷﵽޣ0-3ֱӱ0-3ҵһis_live == false,break - ģӵʼ + 创建者:桃仁和毛血旺 + 功能 + 用来判断玩家子弹是否到达上限,0-3跳过直接返回0-3并找到第一个is_live == false,break + 射击模块:子弹初始化发射 */ void PlayerShoot() { @@ -167,7 +298,7 @@ void PlayerShoot() { if (P_bullets[i].is_live == false) { - //ʼӵ + //初始化子弹 P_bullets[i].x = player.x; P_bullets[i].y = player.y; P_bullets[i].dir = player.dir; @@ -179,11 +310,11 @@ void PlayerShoot() } /* - :Ӻˮ - 룺˽ṹ - ˷ӵԶ,̹ - жǷҪ䣬%2,01 - if Ҫ ӵ + 创建者:桃仁和毛血旺 + 输入:敌人结构体数组 + 敌人发射子弹自动射击,由敌方坦克 + 先通过随机数判断是否要发射,随机数%2,返回0或1 + if 要发射 则循环子弹数组 */ void EnemyShoot() { @@ -191,12 +322,12 @@ void EnemyShoot() if ((clock() - start) < 3000) return; start = clock(); - for (int i = 0; i < ENEMIES_NUM; i++) //е̹ + for (int i = 0; i < ENEMIES_NUM; i++) //遍历所有敌方坦克 { if (enemies[i].hp > 0) { for (auto& j : E_bullets) { - if (j.is_live == false) //ֻҪһӵڣ䣬ŶѾڣ򲻷ӵ + if (j.is_live == false) //只要一个子弹不存活,发射,否则若都已经存在,则不发射子弹 { j.x = enemies[i].x; j.y = enemies[i].y; @@ -211,81 +342,70 @@ void EnemyShoot() } /* - :Ӻˮ - ܣ - ƶӵƶӵĿĴײ + 创建者:桃仁和毛血旺 + 功能: + 调用移动函数,子弹移动函数和碰撞检测等 */ void Move() { bool move = false; - if (GetAsyncKeyState('W') || GetAsyncKeyState(VK_UP)) + const Uint8* keys = SDL_GetKeyboardState(NULL); + if (keys[SDL_SCANCODE_W] || keys[SDL_SCANCODE_UP]) { player.dir = UP; move = true; } - else if (GetAsyncKeyState('S') || GetAsyncKeyState(VK_DOWN)) + else if (keys[SDL_SCANCODE_S] || keys[SDL_SCANCODE_DOWN]) { player.dir = DOWN; move = true; } - else if (GetAsyncKeyState('A') || GetAsyncKeyState(VK_LEFT)) + else if (keys[SDL_SCANCODE_A] || keys[SDL_SCANCODE_LEFT]) { player.dir = LEFT; move = true; } - else if (GetAsyncKeyState('D') || GetAsyncKeyState(VK_RIGHT)) + else if (keys[SDL_SCANCODE_D] || keys[SDL_SCANCODE_RIGHT]) { player.dir = RIGHT; move = true; - };//ķ + };//设置方向 if (move) { - PlayerMove(); //player.dir ȫֱô + PlayerMove(); //player.dir 是全局变量所以能调用 } - EnemiesMove(); //ƶ + EnemiesMove(); //敌人移动函数 PlayerBulletMove(); EnemyBulletMove(); } ///* -//* ҷǰһǿյ +//* 判断我方坦克前方是否为空地 //*/ //bool PlayerCollision(); /* - ˣС yang - ܣ - mapɵͼ + 创建者:小 yang + 功能: + 根据map数组生成地图 */ -void GameView_ShowMap() // ̹ȫֵģҪ +void GameView_ShowMap() // 坦克是全局的,不需要传参 //void GameView_ShowMap(tank player) { - // ˫ - setbkmode(TRANSPARENT); // ñ͸ - BeginBatchDraw(); // ʼͼ + //绘制背景 + draw_image("images/游戏主界面背景.jpg", 0, 0, 700, 700); - //ȡͼƬ - //·ȷȷΨһ ------ - IMAGE image_WALL, image_back, image_RED_HP; - // IMAGE image_WALL, image_back, image_RED_HP, image_BLUE_HP; - loadimage(&image_WALL, ".//images//wall.jpg", CELL_SIZE, CELL_SIZE); - loadimage(&image_back, ".//images//Ϸ汳.jpg", 700, 700); - loadimage(&image_RED_HP, ".//images//red_hp_bar.jpg", CELL_SIZE, CELL_SIZE); - // loadimage(&image_BLUE_HP, ".//images//blue_hp.jpg", 30, 30); // version1汾ĿǰзѪ - - //Ⱦ - putimage(0, 0, &image_back); - - //췽̹HPȾ + //红色方HP渲染 + SDL_Texture* hp_tex = load_texture("images/red_hp_bar.jpg"); for (int i = 0; i < player.hp; i++) { - putimage(100 + i * 30 , 10, &image_RED_HP); + render_texture(hp_tex, 100 + i * 30, 10, CELL_SIZE, CELL_SIZE); } - ////̹HPȾ + ////蓝色方HP渲染 //for (int j = 0; j < player.hp; j++) //{ // putimage(570 - j * 30, 120, &image_RED_HP); @@ -295,25 +415,27 @@ void GameView_ShowMap() // ̹ for (auto& i : enemies) { if (i.hp <= 0) dieNum++; } - //ʾʵʱȾ - settextcolor((157, 157, 157)); - settextstyle(30, 0, ""); - outtextxy(110, 660, "ܣ"); - outtextxy(185, 660, to_string(dieNum).data()); - outtextxy(210, 660, "/"); - outtextxy(225, 660, "10"); + + //文本渲染杀敌数 + SDL_Color text_color = {157, 157, 157, 255}; + render_text("杀敌:", 110, 660, text_color); + render_int(dieNum, 185, 660, text_color); + render_text("/", 210, 660, text_color); + render_text("10", 225, 660, text_color); - outtextxy(325, 660, string("" + to_string(player.attack)).data()); + //当前攻击力 + string atk_str = "当前攻击力" + to_string(player.attack); + render_text(atk_str.c_str(), 325, 660, text_color); - - //ǽȾ + //墙壁纹理渲染 + SDL_Texture* wall_tex = load_texture("images/wall.jpg"); for (int i = 0; i < MAP_CELL_NUM; i++) { for (int j = 0; j < MAP_CELL_NUM; j++) { if (map[i][j] == WALL) { - putimage(j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, &image_WALL); + render_texture(wall_tex, j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } } } @@ -321,110 +443,63 @@ void GameView_ShowMap() // ̹ /* - ˣ - ܣڵͼ + 创建者: + 功能:渲染子弹到地图 */ // void GameView_ShowBullet(); // void GameView_ShowBullet(bullet b); /* - ˣС yang - ܣ̹ͼ + 创建者:小 yang + 功能:渲染坦克到地图 */ void GameView_ShowTank() // void GameView_ShowTank(tank player, tank enemies[]) { - // ˫ - setbkmode(TRANSPARENT); // ñ͸ - BeginBatchDraw(); // ʼͼ + //我方坦克纹理 + SDL_Texture* red_up = load_texture("images/red_up_b.png"); + SDL_Texture* red_down = load_texture("images/red_down_b.png"); + SDL_Texture* red_left = load_texture("images/red_left_b.png"); + SDL_Texture* red_right = load_texture("images/red_right_b.png"); - //̹ͼƬ - IMAGE image_REDtank_UP[2], image_REDtank_DOWN[2], image_REDtank_LEFT[2], image_REDtank_RIGHT[2], + //敌方坦克纹理 + SDL_Texture* blue_up = load_texture("images/blue_up_b.png"); + SDL_Texture* blue_down = load_texture("images/blue_down_b.png"); + SDL_Texture* blue_left = load_texture("images/blue_left_b.png"); + SDL_Texture* blue_right = load_texture("images/blue_right_b.png"); - image_BLUEtank_UP[2], image_BLUEtank_DOWN[2], image_BLUEtank_LEFT[2], image_BLUEtank_RIGHT[2], + //死亡坦克纹理 + SDL_Texture* dead_tank = load_texture("images/dead_tank_b.png"); - image_BLUE_DEADtank[2]; - - //صͼ - //·ȷȷΨһ ------ - - loadimage(&image_REDtank_DOWN[White], ".//images//red_down_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_REDtank_DOWN[Black], ".//images//red_down_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_REDtank_UP[White], ".//images//red_up_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_REDtank_UP[Black], ".//images//red_up_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_REDtank_LEFT[White], ".//images//red_left_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_REDtank_LEFT[Black], ".//images//red_left_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_REDtank_RIGHT[White], ".//images//red_right_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_REDtank_RIGHT[Black], ".//images//red_right_b.png", CELL_SIZE, CELL_SIZE); - - - loadimage(&image_BLUEtank_UP[White], ".//images//blue_up_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_BLUEtank_UP[Black], ".//images//blue_up_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_BLUEtank_DOWN[White], ".//images//blue_down_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_BLUEtank_DOWN[Black], ".//images//blue_down_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_BLUEtank_LEFT[White], ".//images//blue_left_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_BLUEtank_LEFT[Black], ".//images//blue_left_b.png", CELL_SIZE, CELL_SIZE); - - loadimage(&image_BLUEtank_RIGHT[White], ".//images//blue_right_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_BLUEtank_RIGHT[Black], ".//images//blue_right_b.png", CELL_SIZE, CELL_SIZE); - - // loadimage(&image_RED_DEADtank, "./̹.png", CELL_SIZE, CELL_SIZE); // ̹˱ϺζϷֱӵϷʧҳ棬˲ҪʾԼı״̬ - loadimage(&image_BLUE_DEADtank[White], ".//images//dead_tank_w.png", CELL_SIZE, CELL_SIZE); - loadimage(&image_BLUE_DEADtank[Black], ".//images//dead_tank_b.png", CELL_SIZE, CELL_SIZE); - //loadimage(&image_BLANK, ".//images//blank.jpg", CELL_SIZE, CELL_SIZE); - - - - - //ȾͼԪ + //渲染地图元素 for (int i = 0; i < MAP_CELL_NUM; i++) { for (int j = 0; j < MAP_CELL_NUM; j++) { - //if (map[i][j] == BLANK) - //{ - // putimage(j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, &image_BLANK); // ͼ - //} - - if ((map[i][j] == RED_TANK && player.dir == LEFT)) + if (map[i][j] == RED_TANK && player.dir == LEFT) { - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_LEFT[White], SRCAND); - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_LEFT[Black], SRCPAINT); + render_texture(red_left, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } if (map[i][j] == RED_TANK && player.dir == RIGHT) { - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_RIGHT[White], SRCAND); - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_RIGHT[Black], SRCPAINT); + render_texture(red_right, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } if (map[i][j] == RED_TANK && player.dir == DOWN) { - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_DOWN[White], SRCAND); - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_DOWN[Black], SRCPAINT); + render_texture(red_down, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } if (map[i][j] == RED_TANK && player.dir == UP) { - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_UP[White], SRCAND); - putimage(player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, &image_REDtank_UP[Black], SRCPAINT); + render_texture(red_up, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } - - //if (map[i][j] == RED_DEAD_TANK) - //{ - - // putimage(j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, &image_RED_DEADtank); - //} } } - //Ⱦ̹ + //渲染敌方坦克 for (int i = 0; i < ENEMIES_NUM; i++) { if (enemies[i].is_taken) @@ -433,213 +508,134 @@ void GameView_ShowTank() switch (enemies[i].dir) { case UP: - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_UP[White], SRCAND); - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_UP[Black], SRCPAINT); + render_texture(blue_up, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); break; case DOWN: - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_DOWN[White], SRCAND); - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_DOWN[Black], SRCPAINT); + render_texture(blue_down, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); break; case LEFT: - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_LEFT[White], SRCAND); - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_LEFT[Black], SRCPAINT); + render_texture(blue_left, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); break; case RIGHT: - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_RIGHT[White], SRCAND); - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUEtank_RIGHT[Black], SRCPAINT); + render_texture(blue_right, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); break; default: break; } } else { - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUE_DEADtank[White], SRCAND); - putimage(enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, &image_BLUE_DEADtank[Black], SRCPAINT); + render_texture(dead_tank, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); } } - - // ͼʾͼ - EndBatchDraw(); // ͼ } - -IMAGE image_BULLET[4][2]; +// 子弹纹理缓存(仅加载一次) +SDL_Texture* g_bullet_tex[4] = {NULL, NULL, NULL, NULL}; void GameView_ShowBullet() { - bool b = true; - if (b) { - b = false; - loadimage(&image_BULLET[UP][White], ".//images//B_UP_w.png", CELL_SIZE / 2 / 2, CELL_SIZE / 2); - loadimage(&image_BULLET[UP][Black], ".//images//B_UP_b.png", CELL_SIZE / 2 / 2, CELL_SIZE / 2); - loadimage(&image_BULLET[DOWN][White], ".//images//B_DOWN_w.png", CELL_SIZE / 2 / 2, CELL_SIZE / 2); - loadimage(&image_BULLET[DOWN][Black], ".//images//B_DOWN_b.png", CELL_SIZE / 2 / 2, CELL_SIZE / 2); - loadimage(&image_BULLET[LEFT][White], ".//images//B_LEFT_w.png", CELL_SIZE / 2, CELL_SIZE / 2 / 2); - loadimage(&image_BULLET[LEFT][Black], ".//images//B_LEFT_b.png", CELL_SIZE / 2, CELL_SIZE / 2 / 2); - loadimage(&image_BULLET[RIGHT][White], ".//images//B_RIGHT_w.png", CELL_SIZE / 2, CELL_SIZE / 2 / 2); - loadimage(&image_BULLET[RIGHT][Black], ".//images//B_RIGHT_b.png", CELL_SIZE / 2, CELL_SIZE / 2 / 2); + // 首次使用时加载子弹纹理 + if (!g_bullet_tex[UP]) { + g_bullet_tex[UP] = load_texture("images/B_UP_b.png"); + g_bullet_tex[DOWN] = load_texture("images/B_DOWN_b.png"); + g_bullet_tex[LEFT] = load_texture("images/B_LEFT_b.png"); + g_bullet_tex[RIGHT] = load_texture("images/B_RIGHT_b.png"); } - for (int i = 0; i < BULLET_NUM; i++) { //ӵ + + for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹 if (P_bullets[i].is_live) { int x = P_bullets[i].x * CELL_SIZE + X_OFFSET; int y = P_bullets[i].y * CELL_SIZE + Y_OFFSET; - putimage((P_bullets[i].dir > DOWN ? x : x + 10.5), (P_bullets[i].dir > DOWN ? y + 10.5 : y), &image_BULLET[P_bullets[i].dir][White], SRCAND); - putimage((P_bullets[i].dir > DOWN ? x : x + 10.5), (P_bullets[i].dir > DOWN ? y + 10.5 : y), &image_BULLET[P_bullets[i].dir][Black], SRCPAINT); + int tex_x = (P_bullets[i].dir > DOWN ? x : x + 10); + int tex_y = (P_bullets[i].dir > DOWN ? y + 10 : y); + int bw = (P_bullets[i].dir > DOWN ? CELL_SIZE / 2 : CELL_SIZE / 2 / 2); + int bh = (P_bullets[i].dir > DOWN ? CELL_SIZE / 2 / 2 : CELL_SIZE / 2); + render_texture(g_bullet_tex[P_bullets[i].dir], tex_x, tex_y, bw, bh); } } - for (int i = 0; i < BULLET_NUM * ENEMIES_NUM; i++) { //Ƶзӵ + for (int i = 0; i < BULLET_NUM * ENEMIES_NUM; i++) { //绘制敌方子弹 if (E_bullets[i].is_live) { int x = E_bullets[i].x * CELL_SIZE + X_OFFSET; int y = E_bullets[i].y * CELL_SIZE + Y_OFFSET; - putimage((E_bullets[i].dir > DOWN ? x : x + 10.5), (E_bullets[i].dir > DOWN ? y + 10.5 : y), &image_BULLET[E_bullets[i].dir][White], SRCAND); - putimage((E_bullets[i].dir > DOWN ? x : x + 10.5), (E_bullets[i].dir > DOWN ? y + 10.5 : y), &image_BULLET[E_bullets[i].dir][Black], SRCPAINT); + int tex_x = (E_bullets[i].dir > DOWN ? x : x + 10); + int tex_y = (E_bullets[i].dir > DOWN ? y + 10 : y); + int bw = (E_bullets[i].dir > DOWN ? CELL_SIZE / 2 : CELL_SIZE / 2 / 2); + int bh = (E_bullets[i].dir > DOWN ? CELL_SIZE / 2 / 2 : CELL_SIZE / 2); + render_texture(g_bullet_tex[E_bullets[i].dir], tex_x, tex_y, bw, bh); } } - - /*for (int i = 0; i < 50; i++) - { - for (int j = 0; j < 50; j++) - { - if (map[i][j] == BULLET && bullets[i].dir == UP) - { - putimage(j * PIEXL1 + X + (PIEXL1 - PIEXL2) / 2, i * PIEXL1 + (PIEXL1 - PIEXL2) / 2, &image_BULLET_UP); - } - if (map[i][j] == BULLET && BULLET.dir == DOWN) - { - putimage(j * PIEXL1 + X + (PIEXL1 - PIEXL2) / 2, i * PIEXL1 + (PIEXL1 - PIEXL2) / 2, &image_BULLET_DOWN); - } - if (map[i][j] == BULLET && BULLET.dir == RIGHT) - { - putimage(j * PIEXL1 + X + (PIEXL1 - PIEXL2) / 2, i * PIEXL1 + (PIEXL1 - PIEXL2) / 2, &image_BULLET_RIGHT); - } - if (map[i][j] == BULLET && BULLET.dir == LEFT) - { - putimage(j * PIEXL1 + X + (PIEXL1 - PIEXL2) / 2, i * PIEXL1 + (PIEXL1 - PIEXL2) / 2, &image_BULLET_LEFT); - } - } - }*/ } /* - ˣ - ܣʤ + 创建者:凌零 + 功能:显示胜利界面 */ void GameView_ShowWIN() { - cleardevice(); - setbkmode(TRANSPARENT); - IMAGE img; - loadimage(&img, ".//images//ʤ.png", 700, 700); - putimage(0, 0, &img); - IMAGE img_back; - loadimage(&img_back, ".//images//ذť.jpg", 150, 50); - putimage(30, 550, &img_back); - // - ExMessage msg; + clear_screen(); + draw_image("images/胜利.png", 0, 0, 700, 700); + draw_image("images/返回按钮.jpg", 30, 550, 150, 50); + present_screen(); + + bool goto_menu = false; while (true) { - //̲ - if (_kbhit()) - { - char key = _getch();//һֱ - //printf("%d,%c\n", key, key); - switch (key) - { - case 27://ϼ - //ز˵ - MenuView(); - //printf("ز˵/n"); - return; - break; - } + int ev = handle_menu_events(goto_menu); + if (goto_menu) { + MenuView(); + return; } - - // - if (peekmessage(&msg, WH_MOUSE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - - //ص˵ - if (msg.x >= 30 && msg.x <= 180 && msg.y >= 550 && msg.y <= 600) + if (ev == 1) { + //鼠标点击 - 判断返回按钮区域 + // 用SDL_GetMouseState获取点击坐标 + int mx, my; + Uint32 buttons = SDL_GetMouseState(&mx, &my); + if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) { + if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) { MenuView(); return; - //printf("ص˵\n"); - //תѶȽ } - break; - default: - break; } } + SDL_Delay(16); } - //char s[] = "˵"; - //outtextxy(250, 400, s); - //_getch(); } /* - ˣ - ܣʧܽ + 创建者:凌零 + 功能:显示失败界面 */ void GameView_ShowLose() { - cleardevice(); - setbkmode(TRANSPARENT); - IMAGE img; - loadimage(&img, ".//images//ʧ.png", 700, 700); - putimage(0, 0, &img); - IMAGE img_back; - loadimage(&img_back, ".//images//ذť.jpg", 150, 50); - putimage(30, 550, &img_back); - // - ExMessage msg; + clear_screen(); + draw_image("images/失败.png", 0, 0, 700, 700); + draw_image("images/返回按钮.jpg", 30, 550, 150, 50); + present_screen(); + + bool goto_menu = false; while (true) { - //̲ - if (_kbhit()) - { - char key = _getch();//һֱ - //printf("%d,%c\n", key, key); - switch (key) - { - case 27://ϼ - //ز˵ - MenuView(); - //printf("ز˵/n"); - return; - break; - } + int ev = handle_menu_events(goto_menu); + if (goto_menu) { + MenuView(); + return; } - - // - if (peekmessage(&msg, WH_MOUSE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - - //ص˵ - if (msg.x >= 30 && msg.x <= 180 && msg.y >= 550 && msg.y <= 600) + if (ev == 1) { + int mx, my; + Uint32 buttons = SDL_GetMouseState(&mx, &my); + if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) { + if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) { MenuView(); return; - //printf("ص˵\n"); - //תѶȽ } - break; - default: - break; } } + SDL_Delay(16); } - //char s[] = "˵"; - //outtextxy(250, 400, s); - //_getch(); } //------------------view------------------- @@ -647,13 +643,13 @@ void GameView_ShowLose() //-------------------service--------------- /* -* ˢµͼ +* 刷新地图 */ void Update() { - InitMap(); //ԭеĵͼݸһmap + InitMap(); //把原有的地图数据再赋值一次map - map[player.x][player.y] = RED_TANK; //ҡˡӵ飬λϢд뵽ͼ + map[player.x][player.y] = RED_TANK; //把玩家、敌人、子弹数组,将位置信息写入到地图 for (int i = 0; i < ENEMIES_NUM; i++) { if (enemies[i].hp > 0) { @@ -664,13 +660,13 @@ void GameView_ShowLose() } } - //for (int j = 0; j < BULLET_NUM; j++) { //ӵ + //for (int j = 0; j < BULLET_NUM; j++) { //玩家子弹 // if (P_bullets[j].is_live == true) { // map[enemies[j].x][enemies[j].y] = BULLET; // } //} - //for (int k = 0; k < BULLET_NUM * ENEMIES_NUM; k++) { //зӵ + //for (int k = 0; k < BULLET_NUM * ENEMIES_NUM; k++) { //敌方子弹 // if (E_bullets[k].is_live == true) { // map[enemies[k].x][enemies[k].y] = BULLET; // } @@ -678,19 +674,19 @@ void GameView_ShowLose() } /* - ˣ - ܣʼΪǽĿհ׵ͼ + 创建者:凌零 + 功能:初始化为周围是墙壁的空白地图 */ void InitMap() { - // ʼͼ + // 初始化地图 for (int i = 0; i < MAP_CELL_NUM; i++) { for (int j = 0; j < MAP_CELL_NUM; j++) { - // ܵλã趨Ϊ WALL + // 如果是边缘的位置,设定为 WALL if (i == 0 || i == MAP_CELL_NUM - 1 || j == 0 || j == MAP_CELL_NUM - 1) { map[i][j] = WALL; } - // λ趨Ϊ BLANK + // 其他位置设定为 BLANK else { map[i][j] = BLANK; } @@ -699,8 +695,8 @@ void InitMap() } /* - ˣ - ܣչĵͼʼmap.hļͼ + 创建者:凌零 + 功能:扩展功能的地图初始化(根据map.h文件预设地图数据) */ //void InitMap(const int preset_map[MAP_CELL_NUM][MAP_CELL_NUM]) { // for (int i = 0; i < MAP_CELL_NUM; i++) { @@ -711,10 +707,10 @@ void InitMap() //} /* - ˣɥɥ - ܣ - շж̹ƶĿ - ʵ̹ƶ + 创建者:丧丧的咸鱼 + 功能: + 根据传入的方向参数判断坦克移动的可行性 + 实现坦克移动 */ void PlayerMove() { static clock_t start = clock() - 100; @@ -722,14 +718,14 @@ void PlayerMove() { return; start = clock(); if (player.dir == UP) { - switch (TankMoveCheck(player.dir, player)) { //յ,з̹,ӵ,з̹,ǽ + switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 case BLANK: player.y--; break; case BULLET: player.y--; player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //ҳĿӵ䱨 + for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { E_bullets[i].is_live = false; } @@ -739,8 +735,8 @@ void PlayerMove() { player.y--; player.attack++; if(player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //ѭ̹ - if (player.x == enemies[i].x && player.y == enemies[i].y) //Ҹλ̹ + for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 + if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 enemies[i].is_taken = true; } break; @@ -751,14 +747,14 @@ void PlayerMove() { } else if (player.dir == DOWN) { - switch (TankMoveCheck(player.dir, player)) { //յ,з̹,ӵ,з̹,ǽ + switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 case BLANK: player.y++; break; case BULLET: player.y++; player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //ҳĿӵ䱨 + for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { E_bullets[i].is_live = false; } @@ -768,8 +764,8 @@ void PlayerMove() { player.y++; player.attack++; if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //ѭ̹ - if (player.x == enemies[i].x && player.y == enemies[i].y) //Ҹλ̹ + for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 + if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 enemies[i].is_taken = true; } break; @@ -778,14 +774,14 @@ void PlayerMove() { } } else if (player.dir == LEFT) { - switch (TankMoveCheck(player.dir, player)) { //յ,з̹,ӵ,з̹,ǽ + switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 case BLANK: player.x--; break; case BULLET: player.x--; player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //ҳĿӵ䱨 + for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { E_bullets[i].is_live = false; } @@ -795,8 +791,8 @@ void PlayerMove() { player.x--; player.attack++; if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //ѭ̹ - if (player.x == enemies[i].x && player.y == enemies[i].y) //Ҹλ̹ + for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 + if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 enemies[i].is_taken = true; } break; @@ -805,14 +801,14 @@ void PlayerMove() { } } else if (player.dir == RIGHT) { - switch (TankMoveCheck(player.dir, player)) { //յ,з̹,ӵ,з̹,ǽ + switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 case BLANK: player.x++; break; case BULLET: player.x++; player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //ҳĿӵ䱨 + for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { E_bullets[i].is_live = false; } @@ -822,8 +818,8 @@ void PlayerMove() { player.x++; player.attack++; if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //ѭ̹ - if (player.x == enemies[i].x && player.y == enemies[i].y) //Ҹλ̹ + for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 + if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 enemies[i].is_taken = true; } break; @@ -831,17 +827,17 @@ void PlayerMove() { break; } } -}//ײǽ͵з̹̹˲ƶ޸ +}//撞墙和敌方坦克坦克不移动将不会修改 /* - ˣɥɥ - ܣ̹ײ⣺Ƿǽз̹ˡз̹ˡյ - ԲͬвͬĴ - 0->յء1->ʾǽ2->̹ˡ3->̹ˡ4->̹ - һλõ꣨player.dir㣩ʲô + 创建者:丧丧的咸鱼 + 功能:坦克碰撞检测:检测是否为墙壁、敌方死亡坦克、敌方坦克、空地 + 对不同类型进行不同的处理 + 0->空地、1->表示墙、2->己方坦克、3->敌方坦克、4->死亡坦克 + 返回值为下一个位置的坐标(根据player.dir计算),并返回该位置存储的枚举值 */ -int TankMoveCheck(int dir, tank tank) { //̹ܷƶ,ؼλô洢öֵ - int ret = 0; // ĬϷֵΪ 0 +int TankMoveCheck(int dir, tank tank) { //坦克能否移动检查,返回检测位置存储的枚举值 + int ret = 0; // 默认返回值为 0 switch (dir) { case UP: return map[tank.x][tank.y - 1]; @@ -855,25 +851,25 @@ int TankMoveCheck(int dir, tank tank) { //̹ case RIGHT: return map[tank.x + 1][tank.y]; break; - default: // ĬϷ֧ + default: // 添加默认分支 break; } return ret; } /* - ˣ - ܣӵ飬ÿһӵд - ȿ϶üĩ״̬λã12-1512131415 - ӵײ⣺Ƿǽз̹ˡз̹ˡӵзӵյء̹ˡ + 创建者:凌零 + 功能:遍历子弹数组,对每一个子弹进行处理 + 首先肯定要用个外循环遍历所有子弹,分1,2,......1,5. 12,13,14,15,16... + 子弹碰撞检测:检测是否为墙壁、敌方死亡坦克、敌方坦克、玩家子弹、敌方子弹、空地、己方坦克。 */ /* - ˣ + 创建者:凌零 */ -int BulletMoveCheck(int dir, bullet bul) //صǰĿmapֵ +int BulletMoveCheck(int dir, bullet bul) //返回当前目标位置的map枚举值 { - int ret = 0; // ĬϷֵΪ 0 + int ret = 0; // 默认返回值为 0 switch (dir) { case UP: return map[bul.x][bul.y - 1]; @@ -887,37 +883,37 @@ int BulletMoveCheck(int dir, bullet bul) // case RIGHT: return map[bul.x + 1][bul.y]; break; - default: // ĬϷ֧ + default: // 添加默认分支 return ret; break; } } /* - ˣɥɥ - ܣ - ƶwasd4ȡģ0123 - ֮-жһλõ״̬ȻӦ - ʱƶı䷽жϵƶĿʵֵƶ + 创建者:丧丧的咸鱼 + 功能: + 通过随机移动(wasd)和方向取余4取模0123 + 生成方向之后-调用检查判断下一个位置的状态然后再进行相应的操作 + 同时移动时改变方向判断下一步移动的可行性实现随机移动 */ -void EnemiesMove() { //ؿ +void EnemiesMove() { //敌人控制函数 static clock_t start = clock() - 1000; if((clock() - start) < 1000) return; start = clock(); - for (int i = 0; i < ENEMIES_NUM; ++i) { //nз̹ƶ + for (int i = 0; i < ENEMIES_NUM; ++i) { //n个敌方坦克移动 if (enemies[i].hp <= 0) continue; - int dir = rand() % 4; //ɵз̹˵ƶ - if (dir == UP) { //0 - enemies[i].dir = UP; // ǰ enemies[i] ķ - switch (TankMoveCheck(dir, enemies[i])) { //յ,з̹,ӵ,ǽ + int dir = rand() % 4; //随机生成敌方坦克的移动方向 + if (dir == UP) { //0向上 + enemies[i].dir = UP; // 更新当前 enemies[i] 的方向为向上 + switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 case BLANK: enemies[i].y--; break; case BULLET: enemies[i].y--; - for (int i = 0; i < BULLET_NUM; i++) { //ӵ + for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { P_bullets[i].is_live = false; enemies[i].hp--; @@ -926,15 +922,15 @@ void EnemiesMove() { // break; } } - else if (dir == DOWN) { //1 + else if (dir == DOWN) { //1向下 enemies[i].dir = DOWN; - switch (TankMoveCheck(dir, enemies[i])) { //յ,з̹,ӵ,ǽ + switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 case BLANK: enemies[i].y++; break; case BULLET: enemies[i].y++; - for (int i = 0; i < BULLET_NUM; i++) { //ӵ + for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { P_bullets[i].is_live = false; enemies[i].hp--; @@ -943,15 +939,15 @@ void EnemiesMove() { // break; } } - else if (dir == LEFT) { //2 + else if (dir == LEFT) { //2向左 enemies[i].dir = LEFT; - switch (TankMoveCheck(dir, enemies[i])) { //յ,з̹,ӵǽ + switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 case BLANK: enemies[i].x--; break; case BULLET: enemies[i].x--; - for (int i = 0; i < BULLET_NUM; i++) { //ӵ + for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { P_bullets[i].is_live = false; enemies[i].hp--; @@ -960,15 +956,15 @@ void EnemiesMove() { // break; } } - else if (dir == RIGHT) { //3 + else if (dir == RIGHT) { //3向右 enemies[i].dir = RIGHT; - switch (TankMoveCheck(dir, enemies[i])) { //յ,з̹,ӵ,ǽ + switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 case BLANK: enemies[i].x++; break; case BULLET: enemies[i].x++; - for (int i = 0; i < BULLET_NUM; i++) { //ӵ + for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { P_bullets[i].is_live = false; enemies[i].hp--; @@ -981,11 +977,11 @@ void EnemiesMove() { // } /* - ˣ - ܣ - շBulletMoveCheck() -ķֵжӵƶ - ʵӵƶ + 创建者:凌零 + 功能: + 根据传入的方向参数调用BulletMoveCheck() + 返回的枚举值判断子弹移动结果 + 实现子弹移动 */ void PlayerBulletMove() { @@ -1000,7 +996,7 @@ void PlayerBulletMove() { {-1, 0}, {1, 0}, }; - for (int i = 0; i < BULLET_NUM; i++) // ӵ + for (int i = 0; i < BULLET_NUM; i++) // 遍历子弹 { if (P_bullets[i].is_live == false) { @@ -1008,10 +1004,10 @@ void PlayerBulletMove() { } else { - // ӵײ⣺Ƿյءǽ̹ˡз̹ˡз̹ˡӵзӵ - // 0->յء1->ʾǽ2->̹ˡ3->̹ˡ4->̹ˡ5->ҷӵ6->зӵ - // 0 յ 1 ǽ 3 з̹ 6 зӵ - int type = BulletMoveCheck(P_bullets[i].dir, P_bullets[i]); + // 子弹碰撞检测:检测是否碰到空地、墙壁、己方坦克、敌方坦克、敌方死亡坦克、玩家子弹、敌方子弹 + // 0->空地、1->表示墙、2->己方坦克、3->敌方坦克、4->死亡坦克、5->我方子弹、6->敌方子弹 + // 0 空地 1 墙 3 敌方坦克 6 敌方子弹 + int type = BulletMoveCheck(P_bullets[i].dir, P_bullets[i]); if (type == BLANK) { P_bullets[i].x += arr[P_bullets[i].dir][0]; @@ -1021,37 +1017,37 @@ void PlayerBulletMove() { { P_bullets[i].is_live = false; } - else if (type == BLUE_TANK) //з̹ + else if (type == BLUE_TANK) //敌方坦克 { - P_bullets[i].is_live = false; //ӵ - for (int k = 0; k < ENEMIES_NUM; k++) //̹˱ + P_bullets[i].is_live = false; //子弹消失 + for (int k = 0; k < ENEMIES_NUM; k++) //坦克爆炸 { switch (P_bullets[i].dir) { case UP: - if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y - 1) //Ϸз̹ + if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y - 1) //位于子弹上方敌方坦克受到伤害 { enemies[k].hp -= P_bullets[i].damage; } break; case DOWN: - if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y + 1) //Ϸз̹ + if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y + 1) //位于子弹下方敌方坦克受到伤害 { enemies[k].hp -= P_bullets[i].damage; } break; case LEFT: - if (enemies[k].x == P_bullets[i].x - 1 && enemies[k].y == P_bullets[i].y) //Ϸз̹ + if (enemies[k].x == P_bullets[i].x - 1 && enemies[k].y == P_bullets[i].y) //位于子弹左方敌方坦克受到伤害 { enemies[k].hp -= P_bullets[i].damage; } break; case RIGHT: - if (enemies[k].x == P_bullets[i].x + 1 && enemies[k].y == P_bullets[i].y) //Ϸз̹ + if (enemies[k].x == P_bullets[i].x + 1 && enemies[k].y == P_bullets[i].y) //位于子弹右方敌方坦克受到伤害 { enemies[k].hp -= P_bullets[i].damage; } break; - default: // ĬϷ֧ + default: // 添加默认分支 break; } @@ -1152,54 +1148,54 @@ void EnemyBulletMove() { } } /* - ˣ - ܣ̹ + 创建者:凌零 + 功能:初始化玩家坦克 */ void PlayerInit() { - player.x = MAP_CELL_NUM / 2; // ʼ + player.x = MAP_CELL_NUM / 2; // 玩家初始位置 player.y = MAP_CELL_NUM / 2; - player.hp = 5; // Ѫʼ - player.dir = UP; // ʼҷ - player.attack = 5; // ʼҹΪ 5 - kill = 0; // ҳʼɱΪ 0 - map[player.x][player.y] = RED_TANK; // ڵͼϱ̹˵λ + player.hp = 5; // 玩家血量初始化 + player.dir = UP; // 初始化玩家方向 + player.attack = 5; // 初始化玩家攻击力为 5 + kill = 0; // 玩家击杀数初始化为 0 + map[player.x][player.y] = RED_TANK; // 在地图上标记坦克的位置 } /* - ˣ - ܣɵз̹ + 创建者:凌零 + 功能:生成敌方坦克 */ void EnemyInit() { - srand((unsigned int)time(NULL)); // - // ʹ ⴴ + srand((unsigned int)time(NULL)); //初始化随机数种子 + // 使用 库创建随机数生成器 //default_random_engine generator; - //uniform_int_distribution distribution(0, 49); // һȷֲɵΧ1-48 + //uniform_int_distribution distribution(0, 49); // 创建一个均匀分布,生成的随机数范围在1-48 - // ʼ̹ + // 初始化敌方坦克 for (int i = 0; i < ENEMIES_NUM; i++) { - enemies[i].x = rand() % (MAP_CELL_NUM - 2) + 1 ; // ʹ + enemies[i].x = rand() % (MAP_CELL_NUM - 2) + 1 ; // 使用随机数生成器生成坐标 enemies[i].y = rand() % (MAP_CELL_NUM - 2) + 1; - // ɵλѾ̹˻ǽô + // 如果随机到的位置已经被坦克或者墙壁占据,那么重新生成 while (map[enemies[i].x][enemies[i].y] != BLANK) { enemies[i].x = rand() % (MAP_CELL_NUM - 2) + 1; enemies[i].y = rand() % (MAP_CELL_NUM - 2) + 1; } - enemies[i].hp = enemy.hp; // ˳ʼѪΪ5 - enemies[i].attack = enemy.attack; // ˳ʼΪ1 - enemies[i].dir = LEFT; // ˳ʼ泯 - map[enemies[i].x][enemies[i].y] = BLUE_TANK; // ڵͼϱǵλ + enemies[i].hp = enemy.hp; // 设置敌人初始血量为5 + enemies[i].attack = enemy.attack; // 设置敌人初始攻击力为1 + enemies[i].dir = LEFT; // 设置敌人初始面朝左 + map[enemies[i].x][enemies[i].y] = BLUE_TANK; // 在地图上标记敌人位置 } } /* - ˣ - ܣ - ֵΪ:˵ʧܣ̹hp<=0߻û棺̹hp>0,з̹>0 - ֵΪ棺˵Ӯ ̹hp==0,з̹==0 + 创建者:凌零 + 功能: + 返回值为假:说明失败,己方坦克hp<=0,或者还没有胜利条件:己方坦克hp>0,敌方坦克数量>0 + 返回值为真:说明赢了 己方坦克hp==0,敌方坦克数量==0 */ bool IsWin() { bool all_die = true; @@ -1208,250 +1204,265 @@ bool IsWin() { all_die = false; } if (player.hp > 0 && all_die) { - return true; // ʤ + return true; // 胜利 } - else return false; // ûʤʧܡ ϷС + else return false; // 没胜利或失败。 还在游戏进行中。 } -// +// 难度选择 void Level() { - cleardevice(); - IMAGE img_memberview; - loadimage(&img_memberview, ".//images//Ϸ汳.jpg", 700, 700); - putimage(0, 0, &img_memberview); + clear_screen(); + draw_image("images/游戏主界面背景.jpg", 0, 0, 700, 700); - IMAGE img_easy; - loadimage(&img_easy, ".//images//Ѷ.jpg", 150, 100); - putimage(275, 150, &img_easy); + draw_image("images/简单难度.jpg", 275, 150, 150, 100); + draw_image("images/中等难度.jpg", 275, 300, 150, 100); + draw_image("images/困难难度.jpg", 275, 450, 150, 100); + present_screen(); - IMAGE img_middle; - loadimage(&img_middle, ".//images//еѶ.jpg", 150, 100); - putimage(275, 300, &img_middle); - - IMAGE img_difficult; - loadimage(&img_difficult, ".//images//Ѷ.jpg", 150, 100); - putimage(275, 450, &img_difficult); - - //̲ - ExMessage msg; while (true) { - //̲ - if (_kbhit()) - { - char key = _getch();//һֱ - //printf("%d,%c\n", key, key); - switch (key) - { - case 27://ϼ - //ز˵ + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) exit(0); + if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { MenuView(); - //printf("ز˵/n"); return; - break; } - } + if (e.type == SDL_MOUSEBUTTONDOWN) { + int mx = e.button.x; + int my = e.button.y; - // - if (peekmessage(&msg, WH_MOUSE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - - //ѡѶ - if (msg.x >= 275 && msg.x <= 425 && msg.y >= 150 && msg.y <= 250) + //选择简单难度 + if (mx >= 275 && mx <= 425 && my >= 150 && my <= 250) { enemy.hp = 5; enemy.attack = 1; - //printf("Ѷ\n"); MenuView(); - //printf("ز˵/n"); return; } - //ѡеѶ - if (msg.x >= 275 && msg.x <= 425 && msg.y >= 300 && msg.y <= 400) + //选择中等难度 + if (mx >= 275 && mx <= 425 && my >= 300 && my <= 400) { enemy.hp = 10; enemy.attack = 2; - //printf("еѶ\n"); MenuView(); - //printf("ز˵/n"); return; } - //ѡѶ - if (msg.x >= 275 && msg.x <= 425 && msg.y >= 450 && msg.y <= 550) + //选择困难难度 + if (mx >= 275 && mx <= 425 && my >= 450 && my <= 550) { enemy.hp = 15; enemy.attack = 3; - //printf("Ѷ\n"); MenuView(); - //printf("ز˵/n"); return; } - break; - default: - break; } } + SDL_Delay(16); } } -// +// 开发人员 void MemberView() { - cleardevice(); - IMAGE img_memberview; - loadimage(&img_memberview, ".//images//Ա.jpg", 700, 700); - putimage(0, 0, &img_memberview); + clear_screen(); + draw_image("images/开发人员界面.jpg", 0, 0, 700, 700); + draw_image("images/返回按钮.jpg", 30, 550, 150, 50); + present_screen(); - IMAGE img_back; - loadimage(&img_back, ".//images//ذť.jpg", 150, 50); - putimage(30, 550, &img_back); - // - ExMessage msg; while (true) { - //̲ - if (_kbhit()) - { - char key = _getch();//һֱ - //printf("%d,%c\n", key, key); - switch (key) - { - case 27://ϼ - //ز˵ + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) exit(0); + if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { MenuView(); - //printf("ز˵/n"); return; - break; } - } + if (e.type == SDL_MOUSEBUTTONDOWN) { + int mx = e.button.x; + int my = e.button.y; - // - if (peekmessage(&msg, WH_MOUSE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - - //ص˵ - if (msg.x >= 30 && msg.x <= 180 && msg.y >= 550 && msg.y <= 600) + //返回主菜单界面 + if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) { MenuView(); return; - //printf("ص˵\n"); - //תѶȽ } - break; - default: - break; } } + SDL_Delay(16); } } -// +// 菜单 void MenuView() { InitMap(); - cleardevice(); - //ͼƬ - setbkmode(TRANSPARENT); - IMAGE img_menuview; - loadimage(&img_menuview, ".//images//˵.jpg", 700, 700); - putimage(0, 0, &img_menuview); + clear_screen(); + draw_image("images/菜单界面.jpg", 0, 0, 700, 700); + draw_image("images/开始游戏按键.jpg", 50, 250, 150, 50); + draw_image("images/难度设置按键.jpg", 50, 330, 150, 50); + draw_image("images/相关人员按键.jpg", 50, 410, 150, 50); + draw_image("images/退出游戏按键.jpg", 50, 490, 150, 50); + present_screen(); - IMAGE img_start; - loadimage(&img_start, ".//images//ʼϷ.jpg", 150, 50); - putimage(50, 250, &img_start); - - IMAGE img_level; - loadimage(&img_level, ".//images//Ѷð.jpg", 150, 50); - putimage(50, 330, &img_level); - - IMAGE img_member; - loadimage(&img_member, ".//images//Ա.jpg", 150, 50); - putimage(50, 410, &img_member); - - IMAGE img_quit; - loadimage(&img_quit, ".//images//˳Ϸ.jpg", 150, 50); - putimage(50, 490, &img_quit); - // - ExMessage msg; while (true) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) exit(0); + if (e.type == SDL_MOUSEBUTTONDOWN) { + int mx = e.button.x; + int my = e.button.y; - // - if (peekmessage(&msg, WH_MOUSE)) - { - switch (msg.message) - { - case WM_LBUTTONDOWN: - - //תϷ - if (msg.x >= 50 && msg.x <= 200 && msg.y >= 250 && msg.y <= 300) + //跳转游戏主页面 + if (mx >= 50 && mx <= 200 && my >= 250 && my <= 300) { GameView(); - //printf("ʼϷ\n"); } - //תѶȽ - if (msg.x >= 50 && msg.x <= 200 && msg.y >= 330 && msg.y <= 380) + //跳转至游戏难度选择 + if (mx >= 50 && mx <= 200 && my >= 330 && my <= 380) { Level(); return; - //printf("Ѷ\n"); } - //תԱ - if (msg.x >= 50 && msg.x <= 200 && msg.y >= 410 && msg.y <= 460) + //跳转至开发人员界面 + if (mx >= 50 && mx <= 200 && my >= 410 && my <= 460) { MemberView(); return; - //printf("Ա\n"); } - //Ϸ - if (msg.x >= 50 && msg.x <= 200 && msg.y >= 490 && msg.y <= 540) + //退出游戏 + if (mx >= 50 && mx <= 200 && my >= 490 && my <= 540) { exit(0); } - break; - default: - break; } } + SDL_Delay(16); } } void change() { - //ȡھ - HWND hnd = GetHWnd(); - //ôڱ - SetWindowText(hnd, "ֽ̹"); + SDL_SetWindowTitle(g_window, "纸牌坦克"); } //-------------------service--------------- /* - ˣɥɥ + 创建者:丧丧的咸鱼 */ int main() { - // bgm - //PlaySound(LPWSTR(IDR_WAVE1), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC | SND_LOOP); - PlaySound(MAKEINTRESOURCE(IDR_WAVE1), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC | SND_LOOP); + // 将工作目录设置为可执行文件所在目录,确保相对路径资源(images/、music/)能正确加载 + char* base_path = SDL_GetBasePath(); + if (base_path) { + chdir(base_path); + SDL_free(base_path); + } - // ---------------------- - // - initgraph(700, 700); + // 初始化SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "SDL初始化失败: %s\n", SDL_GetError()); + return -1; + } + + // 初始化SDL_image + int img_flags = IMG_INIT_PNG | IMG_INIT_JPG; + if (!(IMG_Init(img_flags) & img_flags)) { + fprintf(stderr, "SDL_image初始化失败: %s\n", IMG_GetError()); + SDL_Quit(); + return -1; + } + + // 初始化SDL_mixer + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { + fprintf(stderr, "SDL_mixer初始化失败: %s\n", Mix_GetError()); + IMG_Quit(); + SDL_Quit(); + return -1; + } + + // 初始化SDL_ttf + if (TTF_Init() < 0) { + fprintf(stderr, "SDL_ttf初始化失败: %s\n", TTF_GetError()); + Mix_CloseAudio(); + IMG_Quit(); + SDL_Quit(); + return -1; + } + + // 创建窗口和渲染器 + g_window = SDL_CreateWindow("纸牌坦克", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 700, 700, SDL_WINDOW_SHOWN); + if (!g_window) { + fprintf(stderr, "窗口创建失败: %s\n", SDL_GetError()); + TTF_Quit(); + Mix_CloseAudio(); + IMG_Quit(); + SDL_Quit(); + return -1; + } + + g_renderer = SDL_CreateRenderer(g_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!g_renderer) { + fprintf(stderr, "渲染器创建失败: %s\n", SDL_GetError()); + SDL_DestroyWindow(g_window); + TTF_Quit(); + Mix_CloseAudio(); + IMG_Quit(); + SDL_Quit(); + return -1; + } + + // 加载字体(尝试多个常用中文字体路径) + const char* font_paths[] = { + "/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", + NULL + }; + for (int i = 0; font_paths[i] != NULL; i++) { + g_font = TTF_OpenFont(font_paths[i], 30); + if (g_font) { + printf("已加载字体: %s\n", font_paths[i]); + break; + } + } + if (!g_font) { + fprintf(stderr, "警告: 未找到中文字体,文本将无法显示。请安装 wqy-microhei 或 noto-cjk 字体。\n"); + // 尝试使用任何可用TTF字体 + } + + // 加载BGM + g_bgm = Mix_LoadMUS("music/bgm.wav"); + if (!g_bgm) { + fprintf(stderr, "BGM加载失败: %s,将静音运行\n", Mix_GetError()); + } else { + Mix_PlayMusic(g_bgm, -1); // 循环播放 + } + + // 设置窗口标题 change(); + // 进入主菜单 MenuView(); - getchar(); - //رմ - closegraph(); + // 清理资源 + if (g_bgm) { + Mix_HaltMusic(); + Mix_FreeMusic(g_bgm); + } + if (g_font) TTF_CloseFont(g_font); + SDL_DestroyRenderer(g_renderer); + SDL_DestroyWindow(g_window); + Mix_CloseAudio(); + TTF_Quit(); + IMG_Quit(); + SDL_Quit(); return 0; -} \ No newline at end of file +} diff --git a/tank_battles_on_the_scrap_paper/resource.h b/tank_battles_on_the_scrap_paper/resource.h deleted file mode 100644 index 6964a71..0000000 --- a/tank_battles_on_the_scrap_paper/resource.h +++ /dev/null @@ -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 diff --git a/tank_battles_on_the_scrap_paper/resource.rc b/tank_battles_on_the_scrap_paper/resource.rc deleted file mode 100644 index ada8777c13963ed67efd20ba6a2b4d485d8bf88e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3018 zcmd6p+iuf95QhJ21n*#yn^w?sOhbkoiY#jvfZZojLe}P=pLuk9Ie8gv3id5#K~4N`iJ_Yijy|Ys*{VIp6&fY zC+exMftreRtg*TZ;Wn9f)YdyVJ>EyE!)Y`3=bH`X8{Rrk1GT_q6XO_^E_K2Em!M-f z^ttt0-VwKMa8JV{g#V0Z(<6WO!Ii@|(ObvAe296AlIU20l|@p_y?2WZkG$WvT+~|^ z=%DlcY){{=d&l>04mv-E`#GLor1EC!FnPF^KNIbN(n}&I$(tTIG9ZH*#Lk!qYBOx# zuZ*Bjv(Fw~;&hDOD>7`~o!W-mfjfYuFJD`*oxCJO#s|8er>n?%s0M!M=jqkzZ{` zdU9KFLy)_}*=oryXXI7+in2APq7k)fL_r60~s+s z&|_TCHC)kQd|B%Q?5~*Tk-x8tKU+5H()*Qbo{lNA?0v!Af7plx&5C8xG diff --git a/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj b/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj deleted file mode 100644 index 4157d31..0000000 --- a/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {ac8c4a04-1650-473c-8e9a-3b7cde2ad5cc} - tankbattlesonthescrappaper - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - MultiByte - - - Application - false - v143 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj.filters b/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj.filters deleted file mode 100644 index 97a5b30..0000000 --- a/tank_battles_on_the_scrap_paper/tank_battles_on_the_scrap_paper.vcxproj.filters +++ /dev/null @@ -1,46 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - 源文件 - - - - - 头文件 - - - 头文件 - - - 头文件 - - - 头文件 - - - - - 资源文件 - - - - - 资源文件 - - - \ No newline at end of file From 3294a6678726aeb5a715b883a34c43a2ccccf254 Mon Sep 17 00:00:00 2001 From: flykhan Date: Sun, 3 May 2026 11:33:26 +0800 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=97=A5=E5=BF=97=20v18=20-=20SDL2=20Linux=20?= =?UTF-8?q?=E7=A7=BB=E6=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d1df6a6..beaaf4f 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,6 @@ v16: 添加了全局背景音效 -v17: 修改了一点bug,在返回菜单后调用InitMap(),避免再次进入游戏界面时有脏数据导致显示出错 \ No newline at end of file +v17: 修改了一点bug,在返回菜单后调用InitMap(),避免再次进入游戏界面时有脏数据导致显示出错 + +v18: 从 Windows/EasyX 移植到 SDL2,支持 Linux;修复启动黑屏问题(自动设置工作目录为可执行文件所在目录) \ No newline at end of file From 3b6f6d39d276120dad27270a0678c8bb9e0e5c31 Mon Sep 17 00:00:00 2001 From: flykhan Date: Sun, 3 May 2026 11:53:29 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=E9=A1=B9=E7=9B=AE=E5=85=A8?= =?UTF-8?q?=E9=9D=A2=E9=87=8D=E6=9E=84=20=E2=80=94=20=E4=BB=8E=E5=8D=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=BF=87=E7=A8=8B=E5=BC=8F=E9=87=8D=E5=86=99?= =?UTF-8?q?=E4=B8=BA=E6=A8=A1=E5=9D=97=E5=8C=96=20OOP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **问题背景** 原项目全部逻辑集中在 main.cpp(1468 行),大量全局变量、4 方向逻辑 复制粘贴 4 遍、场景通过递归函数调用跳转(栈溢出隐患),难以维护扩展。 **重构内容** 文件架构(新增 src/ 目录): - src/Game.h/.cpp — 状态机驱动主循环,Scene 枚举消除递归跳转 - src/AssetManager.h/.cpp — 纹理/字体/BGM 加载与缓存管理 - src/Renderer.h/.cpp — SDL 渲染封装(清屏/纹理/文字/提交) - src/Map.h/.cpp — 地图数据 + 统一碰撞检测 - src/Tank.h/.cpp — 坦克类,dx()/dy() 方向向量消除 4x 复制粘贴 - src/Bullet.h/.cpp — 子弹类,peekAt()/step() 封装移动逻辑 - src/UI.h — 按钮区域常量 + 命中检测 核心改进: - 全局变量清零 → 全部归入 Game 类实例管理 - 场景递归跳转 → Scene 枚举状态机,主循环驱动 - 4 方向复制粘贴 → 方向向量 {dx,dy} 表统一处理 - 修复 EnemiesMove 中循环变量 i 遮蔽 bug - clock() → SDL_GetTicks(),跨平台时间精度 - 清理死代码 map1[50]、旧注释、未使用常量 - data_config.h 精简为纯常量 文件变更: - 新增 7 个源文件(src/下) - 重写 main.cpp(1468 → 6 行入口) - 重写 data_config.h、CMakeLists.txt - 删除 tank.h、bullet.h、map.h、map.cpp(被新架构替代) - 更新 .gitignore(build_Debug/、.hermes/ 等) --- .gitignore | 7 + CMakeLists.txt | 25 +- tank_battles_on_the_scrap_paper/bullet.h | 24 - tank_battles_on_the_scrap_paper/data_config.h | 73 +- tank_battles_on_the_scrap_paper/main.cpp | 1468 +---------------- tank_battles_on_the_scrap_paper/map.cpp | 54 - tank_battles_on_the_scrap_paper/map.h | 16 - .../src/AssetManager.cpp | 52 + .../src/AssetManager.h | 30 + .../src/Bullet.cpp | 15 + tank_battles_on_the_scrap_paper/src/Bullet.h | 18 + tank_battles_on_the_scrap_paper/src/Game.cpp | 451 +++++ tank_battles_on_the_scrap_paper/src/Game.h | 62 + tank_battles_on_the_scrap_paper/src/Map.cpp | 34 + tank_battles_on_the_scrap_paper/src/Map.h | 17 + .../src/Renderer.cpp | 40 + .../src/Renderer.h | 25 + tank_battles_on_the_scrap_paper/src/Tank.cpp | 3 + tank_battles_on_the_scrap_paper/src/Tank.h | 19 + tank_battles_on_the_scrap_paper/src/UI.h | 32 + tank_battles_on_the_scrap_paper/tank.h | 33 - 21 files changed, 854 insertions(+), 1644 deletions(-) delete mode 100644 tank_battles_on_the_scrap_paper/bullet.h delete mode 100644 tank_battles_on_the_scrap_paper/map.cpp delete mode 100644 tank_battles_on_the_scrap_paper/map.h create mode 100644 tank_battles_on_the_scrap_paper/src/AssetManager.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/AssetManager.h create mode 100644 tank_battles_on_the_scrap_paper/src/Bullet.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/Bullet.h create mode 100644 tank_battles_on_the_scrap_paper/src/Game.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/Game.h create mode 100644 tank_battles_on_the_scrap_paper/src/Map.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/Map.h create mode 100644 tank_battles_on_the_scrap_paper/src/Renderer.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/Renderer.h create mode 100644 tank_battles_on_the_scrap_paper/src/Tank.cpp create mode 100644 tank_battles_on_the_scrap_paper/src/Tank.h create mode 100644 tank_battles_on_the_scrap_paper/src/UI.h delete mode 100644 tank_battles_on_the_scrap_paper/tank.h diff --git a/.gitignore b/.gitignore index 42f2da8..7745194 100644 --- a/.gitignore +++ b/.gitignore @@ -443,3 +443,10 @@ CMakeScripts/ Makefile *.make +session-*.md +.hermes/ + +# CMake 构建产物 +build_Debug/ +build_Release/ +build_*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 186acd5..2031d20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,26 +7,32 @@ project(tank_battles_on_the_scrap_paper set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# ── SDL2 依赖 ── 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/map.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/bullet.h - tank_battles_on_the_scrap_paper/tank.h - tank_battles_on_the_scrap_paper/map.h 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 @@ -36,7 +42,10 @@ target_link_libraries(${PROJECT_NAME} PRIVATE 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/ diff --git a/tank_battles_on_the_scrap_paper/bullet.h b/tank_battles_on_the_scrap_paper/bullet.h deleted file mode 100644 index 653f5b3..0000000 --- a/tank_battles_on_the_scrap_paper/bullet.h +++ /dev/null @@ -1,24 +0,0 @@ -// ӵ -#ifndef BULLET_H -#define BULLET_H - -/* - ӵ - ӵ꣺xy - 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 \ No newline at end of file diff --git a/tank_battles_on_the_scrap_paper/data_config.h b/tank_battles_on_the_scrap_paper/data_config.h index e170b05..018626c 100644 --- a/tank_battles_on_the_scrap_paper/data_config.h +++ b/tank_battles_on_the_scrap_paper/data_config.h @@ -1,51 +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 }; -// 全局变量声明(extern),定义在 main.cpp 中 -extern int map[MAP_CELL_NUM][MAP_CELL_NUM]; -extern int kill; -extern tank player; -extern tank enemies[ENEMIES_NUM]; -extern bullet E_bullets[BULLET_NUM * ENEMIES_NUM]; -extern bullet P_bullets[BULLET_NUM]; -extern tank enemy; -#endif // !DATA_CONFIG_H \ No newline at end of file +#endif diff --git a/tank_battles_on_the_scrap_paper/main.cpp b/tank_battles_on_the_scrap_paper/main.cpp index 631d17c..22b696c 100644 --- a/tank_battles_on_the_scrap_paper/main.cpp +++ b/tank_battles_on_the_scrap_paper/main.cpp @@ -1,1468 +1,6 @@ -// 项目:纸牌上的坦克大战 - SDL2 Linux Port -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "src/Game.h" -// 引入 data_config 等头文件 -#include "data_config.h" -// #include "map.h" // 引入预设地图头文件 -// #include "main.h" // 引入函数声明头文件 - -using namespace std; - -// -------- 全局变量定义 --------- -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; // 用于修改敌人的属性,难度设置时用到 - -// -------- SDL2 全局变量 -------- -SDL_Window* g_window = NULL; -SDL_Renderer* g_renderer = NULL; -TTF_Font* g_font = NULL; -Mix_Music* g_bgm = NULL; - -// -------- 辅助函数 -------- - -// 加载纹理(带缩放) -SDL_Texture* load_texture(const char* path) { - static unordered_map tex_cache; - string key(path); - auto it = tex_cache.find(key); - if (it != tex_cache.end()) { - return it->second; - } - SDL_Surface* surf = IMG_Load(path); - if (!surf) { - fprintf(stderr, "IMG_Load error: %s - %s\n", path, IMG_GetError()); - return NULL; - } - SDL_Texture* tex = SDL_CreateTextureFromSurface(g_renderer, surf); - SDL_FreeSurface(surf); - if (tex) { - SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); - } else { - fprintf(stderr, "CreateTexture error: %s - %s\n", path, SDL_GetError()); - } - tex_cache[key] = tex; - return tex; -} - -// 渲染纹理到指定位置和大小 -void render_texture(SDL_Texture* tex, int x, int y, int w, int h) { - if (!tex) return; - SDL_Rect dst = {x, y, w, h}; - SDL_RenderCopy(g_renderer, tex, NULL, &dst); -} - -// 渲染纹理到指定位置(不缩放,使用纹理原始大小) -void render_texture_raw(SDL_Texture* tex, int x, int y) { - if (!tex) return; - int w, h; - SDL_QueryTexture(tex, NULL, NULL, &w, &h); - SDL_Rect dst = {x, y, w, h}; - SDL_RenderCopy(g_renderer, tex, NULL, &dst); -} - -// 带缩放加载并渲染纹理(一次性操作) -void draw_image(const char* path, int x, int y, int w, int h) { - SDL_Texture* tex = load_texture(path); - render_texture(tex, x, y, w, h); -} - -// 渲染文本 -void render_text(const char* text, int x, int y, SDL_Color color) { - if (!g_font || !text) return; - SDL_Surface* surf = TTF_RenderUTF8_Blended(g_font, text, color); - if (!surf) return; - SDL_Texture* tex = SDL_CreateTextureFromSurface(g_renderer, surf); - if (tex) { - SDL_Rect dst = {x, y, surf->w, surf->h}; - SDL_RenderCopy(g_renderer, tex, NULL, &dst); - SDL_DestroyTexture(tex); - } - SDL_FreeSurface(surf); -} - -// 渲染int数值为文本 -void render_int(int value, int x, int y, SDL_Color color) { - string s = to_string(value); - render_text(s.c_str(), x, y, color); -} - -// 清屏 -void clear_screen() { - SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255); - SDL_RenderClear(g_renderer); -} - -// 更新画面 -void present_screen() { - SDL_RenderPresent(g_renderer); -} - -// 事件处理辅助:等待并处理键盘和鼠标事件,返回是否退出 / 转到菜单 -// 返回: 0=继续循环, -1=退出/返回菜单 -int handle_menu_events(bool& goto_menu) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - exit(0); - } - if (e.type == SDL_KEYDOWN) { - if (e.key.keysym.sym == SDLK_ESCAPE) { - goto_menu = true; - return -1; - } - } - if (e.type == SDL_MOUSEBUTTONDOWN) { - return 1; // 鼠标点击发生,调用者需检查坐标 - } - } - return 0; -} - -// -------- 原函数声明(保持不变) -------- -void EnemyBulletMove(); -void PlayerBulletMove(); -int BulletMoveCheck(int dir, bullet bul); -void change(); -void EnemiesMove(); -void EnemyInit(); -void EnemyShoot(); -void GameView(); -void GameView_ShowBullet(); -void GameView_ShowLose(); -void GameView_ShowMap(); -void GameView_ShowTank(); -void GameView_ShowWIN(); -void InitMap(); -// void InitMap(const int preset_map[50][50]); -bool IsWin(); -void Level(); -void MemberView(); -void MenuView(); -void Move(); -void PlayerInit(); -void PlayerMove(); -void PlayerShoot(); -int TankMoveCheck(int dir, tank tank); -void Update(); - -// git 提交 - -/* - 菜单界面: - 展示开始游戏,难度设置,退出等选项 - 用户通过w/s来选择对应的按钮,回车进行选择 - - 开始游戏: - 当选中开始游戏时,跳转游戏主函数,直接进入游戏。 - - 难度设置: - 当选中难度设置时,跳转难度选择函数,进行对敌方坦克血量以及攻击力进行变化等操作。 - 调整完毕之后需要esc返回菜单然后才可以进入游戏。 - - 退出游戏: - 当选中退出时,直接退出程序。 - - 关于开发人员 - 跳转相关信息页面,展示开发人员信息 - - // 游戏进行中的开始页面 - -游戏过程 - 展示地图和坦克,玩家控制己方坦克进行自动移动和开火 - 操作wsad控制己方坦克进行上下左右移动 - 按下空格控制己方向前发射子弹 - 撞到墙,掉血 - 当杀死所有敌方坦克时,游戏胜利,返回主菜单 - - 暂停界面 - 展示继续游戏、退出游戏按钮,同时游戏暂停 -*/ - -/* - 功能模块 - 1.菜单界面 - 2.菜单界面转到游戏界面 - 3.游戏界面转到暂停界面 - 4.暂停界面转到菜单界面 -*/ - -//-----------------view------------------------ -/* - 功能描述: - 功能:展示开始游戏,难度设置,退出等选项, 关于我们 - 开始游戏: - 当选中开始游戏时,跳转游戏主函数,直接进入游戏。 - 难度设置: - 当选中难度设置时,跳转难度选择函数,进行对敌方坦克血量以及攻击力进行变化等操作。 - 调整完毕之后需要esc返回菜单然后才可以进入游戏。 - 退出游戏: - 当选中退出时,直接退出程序。 - 关于我们: - 展示团队信息 - // 游戏进行中的开始页面 -*/ -void MenuView(); - -/* -* 游戏界面 - 功能描述: -*/ -// 每一帧,地图和坦克每一帧状态渲染 -// 在一个新的地图上进行操作,包括坦克和子弹的坐标:位置和方向 -void GameView() //游戏主界面视图 -{ - InitMap(); //初始化地图 - // InitMap(map1); //初始化地图 - EnemyInit(); //初始化敌方坦克的位置 - PlayerInit(); //初始化己方坦克的位置 - - // 初始渲染 - clear_screen(); - GameView_ShowMap(); //显示地图 - GameView_ShowTank(); //显示坦克 - present_screen(); - - while (!IsWin()) // 游戏循环没有胜利 - { - if (player.hp <= 0) //判断玩家的生命值是否小于或等于 0 - { - GameView_ShowLose(); //调用 failView 函数显示失败视图 - /*MenuView();*/ //返回主菜单界面 - break; - } - - // 处理ESC和窗口事件 - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - exit(0); - } - if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { - MenuView(); //调用菜单 menuView 函数返回主菜单界面 - } - } - - Move(); // 调用 Move、Shoot 和 Update 函数:这些函数需要实现坦克和子弹的移动、射击和状态更新。 - //射击 - const Uint8* keys = SDL_GetKeyboardState(NULL); - if (keys[SDL_SCANCODE_SPACE]) - PlayerShoot(); - EnemyShoot(); - // Update(); - Update(); - - // 渲染帧 - clear_screen(); - GameView_ShowMap(); //显示地图的最新状态 - GameView_ShowBullet(); //再进行子弹显示更新状态 - GameView_ShowTank(); //显示坦克的最新状态 - present_screen(); - - SDL_Delay(16); // ~60 FPS,控制帧率 - } - - if (IsWin()) //如果游戏胜利,调用 winView 函数显示胜利界面,并返回主菜单界面。 - { - GameView_ShowWIN(); - /*MenuView();*/ - } -} - -/* - 创建者:桃仁和毛血旺 - 功能 - 用来判断玩家子弹是否到达上限,0-3跳过直接返回0-3并找到第一个is_live == false,break - 射击模块:子弹初始化发射 -*/ -void PlayerShoot() -{ - static clock_t start = clock() - 500; - if ((clock() - start) < 500) - return; - start = clock(); - for (int i = 0; i < SINGLE_BULLET; i++) - { - if (P_bullets[i].is_live == false) - { - //初始化子弹 - P_bullets[i].x = player.x; - P_bullets[i].y = player.y; - P_bullets[i].dir = player.dir; - P_bullets[i].damage = player.attack; - P_bullets[i].is_live = true; - break; - } - } -} - -/* - 创建者:桃仁和毛血旺 - 输入:敌人结构体数组 - 敌人发射子弹自动射击,由敌方坦克 - 先通过随机数判断是否要发射,随机数%2,返回0或1 - if 要发射 则循环子弹数组 -*/ -void EnemyShoot() -{ - static clock_t start = clock() - 3000; - if ((clock() - start) < 3000) - return; - start = clock(); - for (int i = 0; i < ENEMIES_NUM; i++) //遍历所有敌方坦克 - { - if (enemies[i].hp > 0) { - for (auto& j : E_bullets) - { - if (j.is_live == false) //只要一个子弹不存活,发射,否则若都已经存在,则不发射子弹 - { - j.x = enemies[i].x; - j.y = enemies[i].y; - j.dir = enemies[i].dir; - j.damage = enemy.attack; - j.is_live = true; - break; - } - } - } - } -} - -/* - 创建者:桃仁和毛血旺 - 功能: - 调用移动函数,子弹移动函数和碰撞检测等 -*/ -void Move() -{ - bool move = false; - const Uint8* keys = SDL_GetKeyboardState(NULL); - if (keys[SDL_SCANCODE_W] || keys[SDL_SCANCODE_UP]) - { - player.dir = UP; - move = true; - } - else if (keys[SDL_SCANCODE_S] || keys[SDL_SCANCODE_DOWN]) - { - player.dir = DOWN; - move = true; - } - else if (keys[SDL_SCANCODE_A] || keys[SDL_SCANCODE_LEFT]) - { - player.dir = LEFT; - move = true; - } - else if (keys[SDL_SCANCODE_D] || keys[SDL_SCANCODE_RIGHT]) - { - player.dir = RIGHT; - move = true; - };//设置方向 - - - - if (move) { - PlayerMove(); //player.dir 是全局变量所以能调用 - } - EnemiesMove(); //敌人移动函数 - PlayerBulletMove(); - EnemyBulletMove(); -} - -///* -//* 判断我方坦克前方是否为空地 -//*/ -//bool PlayerCollision(); - - -/* - 创建者:小 yang - 功能: - 根据map数组生成地图 -*/ -void GameView_ShowMap() // 坦克是全局的,不需要传参 -//void GameView_ShowMap(tank player) -{ - //绘制背景 - draw_image("images/游戏主界面背景.jpg", 0, 0, 700, 700); - - //红色方HP渲染 - SDL_Texture* hp_tex = load_texture("images/red_hp_bar.jpg"); - for (int i = 0; i < player.hp; i++) - { - render_texture(hp_tex, 100 + i * 30, 10, CELL_SIZE, CELL_SIZE); - } - - ////蓝色方HP渲染 - //for (int j = 0; j < player.hp; j++) - //{ - // putimage(570 - j * 30, 120, &image_RED_HP); - //} - - int dieNum = 0; - for (auto& i : enemies) { - if (i.hp <= 0) dieNum++; - } - - //文本渲染杀敌数 - SDL_Color text_color = {157, 157, 157, 255}; - render_text("杀敌:", 110, 660, text_color); - render_int(dieNum, 185, 660, text_color); - render_text("/", 210, 660, text_color); - render_text("10", 225, 660, text_color); - - //当前攻击力 - string atk_str = "当前攻击力" + to_string(player.attack); - render_text(atk_str.c_str(), 325, 660, text_color); - - //墙壁纹理渲染 - SDL_Texture* wall_tex = load_texture("images/wall.jpg"); - for (int i = 0; i < MAP_CELL_NUM; i++) - { - for (int j = 0; j < MAP_CELL_NUM; j++) - { - if (map[i][j] == WALL) - { - render_texture(wall_tex, j * CELL_SIZE + X_OFFSET, i * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - } - } -} - - -/* - 创建者: - 功能:渲染子弹到地图 -*/ -// void GameView_ShowBullet(); -// void GameView_ShowBullet(bullet b); - -/* - 创建者:小 yang - 功能:渲染坦克到地图 - -*/ -void GameView_ShowTank() -// void GameView_ShowTank(tank player, tank enemies[]) -{ - //我方坦克纹理 - SDL_Texture* red_up = load_texture("images/red_up_b.png"); - SDL_Texture* red_down = load_texture("images/red_down_b.png"); - SDL_Texture* red_left = load_texture("images/red_left_b.png"); - SDL_Texture* red_right = load_texture("images/red_right_b.png"); - - //敌方坦克纹理 - SDL_Texture* blue_up = load_texture("images/blue_up_b.png"); - SDL_Texture* blue_down = load_texture("images/blue_down_b.png"); - SDL_Texture* blue_left = load_texture("images/blue_left_b.png"); - SDL_Texture* blue_right = load_texture("images/blue_right_b.png"); - - //死亡坦克纹理 - SDL_Texture* dead_tank = load_texture("images/dead_tank_b.png"); - - //渲染地图元素 - for (int i = 0; i < MAP_CELL_NUM; i++) - { - for (int j = 0; j < MAP_CELL_NUM; j++) - { - if (map[i][j] == RED_TANK && player.dir == LEFT) - { - render_texture(red_left, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - - if (map[i][j] == RED_TANK && player.dir == RIGHT) - { - render_texture(red_right, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - - if (map[i][j] == RED_TANK && player.dir == DOWN) - { - render_texture(red_down, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - - if (map[i][j] == RED_TANK && player.dir == UP) - { - render_texture(red_up, player.x * CELL_SIZE + X_OFFSET, player.y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - } - } - - //渲染敌方坦克 - for (int i = 0; i < ENEMIES_NUM; i++) - { - if (enemies[i].is_taken) - continue; - if (enemies[i].hp > 0) { - switch (enemies[i].dir) - { - case UP: - render_texture(blue_up, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - break; - case DOWN: - render_texture(blue_down, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - break; - case LEFT: - render_texture(blue_left, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - break; - case RIGHT: - render_texture(blue_right, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - break; - default: - break; - } - } - else { - render_texture(dead_tank, enemies[i].x * CELL_SIZE + X_OFFSET, enemies[i].y * CELL_SIZE + Y_OFFSET, CELL_SIZE, CELL_SIZE); - } - } -} - -// 子弹纹理缓存(仅加载一次) -SDL_Texture* g_bullet_tex[4] = {NULL, NULL, NULL, NULL}; - -void GameView_ShowBullet() -{ - // 首次使用时加载子弹纹理 - if (!g_bullet_tex[UP]) { - g_bullet_tex[UP] = load_texture("images/B_UP_b.png"); - g_bullet_tex[DOWN] = load_texture("images/B_DOWN_b.png"); - g_bullet_tex[LEFT] = load_texture("images/B_LEFT_b.png"); - g_bullet_tex[RIGHT] = load_texture("images/B_RIGHT_b.png"); - } - - for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹 - if (P_bullets[i].is_live) { - int x = P_bullets[i].x * CELL_SIZE + X_OFFSET; - int y = P_bullets[i].y * CELL_SIZE + Y_OFFSET; - int tex_x = (P_bullets[i].dir > DOWN ? x : x + 10); - int tex_y = (P_bullets[i].dir > DOWN ? y + 10 : y); - int bw = (P_bullets[i].dir > DOWN ? CELL_SIZE / 2 : CELL_SIZE / 2 / 2); - int bh = (P_bullets[i].dir > DOWN ? CELL_SIZE / 2 / 2 : CELL_SIZE / 2); - render_texture(g_bullet_tex[P_bullets[i].dir], tex_x, tex_y, bw, bh); - } - } - for (int i = 0; i < BULLET_NUM * ENEMIES_NUM; i++) { //绘制敌方子弹 - if (E_bullets[i].is_live) { - int x = E_bullets[i].x * CELL_SIZE + X_OFFSET; - int y = E_bullets[i].y * CELL_SIZE + Y_OFFSET; - int tex_x = (E_bullets[i].dir > DOWN ? x : x + 10); - int tex_y = (E_bullets[i].dir > DOWN ? y + 10 : y); - int bw = (E_bullets[i].dir > DOWN ? CELL_SIZE / 2 : CELL_SIZE / 2 / 2); - int bh = (E_bullets[i].dir > DOWN ? CELL_SIZE / 2 / 2 : CELL_SIZE / 2); - render_texture(g_bullet_tex[E_bullets[i].dir], tex_x, tex_y, bw, bh); - } - } -} - -/* - 创建者:凌零 - 功能:显示胜利界面 -*/ -void GameView_ShowWIN() -{ - clear_screen(); - draw_image("images/胜利.png", 0, 0, 700, 700); - draw_image("images/返回按钮.jpg", 30, 550, 150, 50); - present_screen(); - - bool goto_menu = false; - while (true) - { - int ev = handle_menu_events(goto_menu); - if (goto_menu) { - MenuView(); - return; - } - if (ev == 1) { - //鼠标点击 - 判断返回按钮区域 - // 用SDL_GetMouseState获取点击坐标 - int mx, my; - Uint32 buttons = SDL_GetMouseState(&mx, &my); - if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) { - if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) - { - MenuView(); - return; - } - } - } - SDL_Delay(16); - } -} - -/* - 创建者:凌零 - 功能:显示失败界面 - -*/ - -void GameView_ShowLose() -{ - clear_screen(); - draw_image("images/失败.png", 0, 0, 700, 700); - draw_image("images/返回按钮.jpg", 30, 550, 150, 50); - present_screen(); - - bool goto_menu = false; - while (true) - { - int ev = handle_menu_events(goto_menu); - if (goto_menu) { - MenuView(); - return; - } - if (ev == 1) { - int mx, my; - Uint32 buttons = SDL_GetMouseState(&mx, &my); - if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) { - if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) - { - MenuView(); - return; - } - } - } - SDL_Delay(16); - } -} - -//------------------view------------------- - -//-------------------service--------------- - -/* -* 刷新地图 -*/ - void Update() -{ - InitMap(); //把原有的地图数据再赋值一次map - - map[player.x][player.y] = RED_TANK; //把玩家、敌人、子弹数组,将位置信息写入到地图 - - for (int i = 0; i < ENEMIES_NUM; i++) { - if (enemies[i].hp > 0) { - map[enemies[i].x][enemies[i].y] = BLUE_TANK; - } - else if (enemies[i].hp <= 0 && enemies[i].is_taken == false) { - map[enemies[i].x][enemies[i].y] = BLUE_DEAD_TANK; - } - } - - //for (int j = 0; j < BULLET_NUM; j++) { //玩家子弹 - // if (P_bullets[j].is_live == true) { - // map[enemies[j].x][enemies[j].y] = BULLET; - // } - //} - - //for (int k = 0; k < BULLET_NUM * ENEMIES_NUM; k++) { //敌方子弹 - // if (E_bullets[k].is_live == true) { - // map[enemies[k].x][enemies[k].y] = BULLET; - // } - //} -} - -/* - 创建者:凌零 - 功能:初始化为周围是墙壁的空白地图 -*/ -void InitMap() -{ - // 初始化地图 - for (int i = 0; i < MAP_CELL_NUM; i++) { - for (int j = 0; j < MAP_CELL_NUM; j++) { - // 如果是边缘的位置,设定为 WALL - if (i == 0 || i == MAP_CELL_NUM - 1 || j == 0 || j == MAP_CELL_NUM - 1) { - map[i][j] = WALL; - } - // 其他位置设定为 BLANK - else { - map[i][j] = BLANK; - } - } - } -} - -/* - 创建者:凌零 - 功能:扩展功能的地图初始化(根据map.h文件预设地图数据) -*/ -//void InitMap(const int preset_map[MAP_CELL_NUM][MAP_CELL_NUM]) { -// for (int i = 0; i < MAP_CELL_NUM; i++) { -// for (int j = 0; j < MAP_CELL_NUM; j++) { -// map[i][j] = preset_map[i][j]; -// } -// } -//} - -/* - 创建者:丧丧的咸鱼 - 功能: - 根据传入的方向参数判断坦克移动的可行性 - 实现坦克移动 -*/ -void PlayerMove() { - static clock_t start = clock() - 100; - if ((clock() - start) < 100) - return; - start = clock(); - if (player.dir == UP) { - switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 - case BLANK: - player.y--; - break; - case BULLET: - player.y--; - player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 - if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { - E_bullets[i].is_live = false; - } - } - break; - case BLUE_DEAD_TANK: - player.y--; - player.attack++; - if(player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 - if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 - enemies[i].is_taken = true; - } - break; - default: - break; - } - - - } - else if (player.dir == DOWN) { - switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 - case BLANK: - player.y++; - break; - case BULLET: - player.y++; - player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 - if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { - E_bullets[i].is_live = false; - } - } - break; - case BLUE_DEAD_TANK: - player.y++; - player.attack++; - if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 - if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 - enemies[i].is_taken = true; - } - break; - default: - break; - } - } - else if (player.dir == LEFT) { - switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 - case BLANK: - player.x--; - break; - case BULLET: - player.x--; - player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 - if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { - E_bullets[i].is_live = false; - } - } - break; - case BLUE_DEAD_TANK: - player.x--; - player.attack++; - if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 - if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 - enemies[i].is_taken = true; - } - break; - default: - break; - } - } - else if (player.dir == RIGHT) { - switch (TankMoveCheck(player.dir, player)) { //空地,敌方坦克,子弹,敌方死亡坦克,墙 - case BLANK: - player.x++; - break; - case BULLET: - player.x++; - player.hp--; - for (int i = 0; i <= BULLET_NUM * ENEMIES_NUM; i++) { //找出并干掉那颗子弹报销 - if (E_bullets[i].x == player.x && E_bullets[i].y == player.y) { - E_bullets[i].is_live = false; - } - } - break; - case BLUE_DEAD_TANK: - player.x++; - player.attack++; - if (player.hp < 5) player.hp++; - for (int i = 0; i < ENEMIES_NUM; i++) { //循环敌人坦克数组 - if (player.x == enemies[i].x && player.y == enemies[i].y) //找个位置下无敌坦克 - enemies[i].is_taken = true; - } - break; - default: - break; - } - } -}//撞墙和敌方坦克坦克不移动将不会修改 - -/* - 创建者:丧丧的咸鱼 - 功能:坦克碰撞检测:检测是否为墙壁、敌方死亡坦克、敌方坦克、空地 - 对不同类型进行不同的处理 - 0->空地、1->表示墙、2->己方坦克、3->敌方坦克、4->死亡坦克 - 返回值为下一个位置的坐标(根据player.dir计算),并返回该位置存储的枚举值 -*/ -int TankMoveCheck(int dir, tank tank) { //坦克能否移动检查,返回检测位置存储的枚举值 - int ret = 0; // 默认返回值为 0 - switch (dir) { - case UP: - return map[tank.x][tank.y - 1]; - break; - case DOWN: - return map[tank.x][tank.y + 1]; - break; - case LEFT: - return map[tank.x - 1][tank.y]; - break; - case RIGHT: - return map[tank.x + 1][tank.y]; - break; - default: // 添加默认分支 - break; - } - return ret; -} - -/* - 创建者:凌零 - 功能:遍历子弹数组,对每一个子弹进行处理 - 首先肯定要用个外循环遍历所有子弹,分1,2,......1,5. 12,13,14,15,16... - 子弹碰撞检测:检测是否为墙壁、敌方死亡坦克、敌方坦克、玩家子弹、敌方子弹、空地、己方坦克。 -*/ - -/* - 创建者:凌零 -*/ -int BulletMoveCheck(int dir, bullet bul) //返回当前目标位置的map枚举值 -{ - int ret = 0; // 默认返回值为 0 - switch (dir) { - case UP: - return map[bul.x][bul.y - 1]; - break; - case DOWN: - return map[bul.x][bul.y + 1]; - break; - case LEFT: - return map[bul.x - 1][bul.y]; - break; - case RIGHT: - return map[bul.x + 1][bul.y]; - break; - default: // 添加默认分支 - return ret; - break; - } -} - -/* - 创建者:丧丧的咸鱼 - 功能: - 通过随机移动(wasd)和方向取余4取模0123 - 生成方向之后-调用检查判断下一个位置的状态然后再进行相应的操作 - 同时移动时改变方向判断下一步移动的可行性实现随机移动 -*/ -void EnemiesMove() { //敌人控制函数 - static clock_t start = clock() - 1000; - if((clock() - start) < 1000) - return; - start = clock(); - for (int i = 0; i < ENEMIES_NUM; ++i) { //n个敌方坦克移动 - if (enemies[i].hp <= 0) - continue; - int dir = rand() % 4; //随机生成敌方坦克的移动方向 - if (dir == UP) { //0向上 - enemies[i].dir = UP; // 更新当前 enemies[i] 的方向为向上 - switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 - case BLANK: - enemies[i].y--; - break; - case BULLET: - enemies[i].y--; - for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 - if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { - P_bullets[i].is_live = false; - enemies[i].hp--; - } - } - break; - } - } - else if (dir == DOWN) { //1向下 - enemies[i].dir = DOWN; - switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 - case BLANK: - enemies[i].y++; - break; - case BULLET: - enemies[i].y++; - for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 - if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { - P_bullets[i].is_live = false; - enemies[i].hp--; - } - } - break; - } - } - else if (dir == LEFT) { //2向左 - enemies[i].dir = LEFT; - switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 - case BLANK: - enemies[i].x--; - break; - case BULLET: - enemies[i].x--; - for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 - if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { - P_bullets[i].is_live = false; - enemies[i].hp--; - } - } - break; - } - } - else if (dir == RIGHT) { //3向右 - enemies[i].dir = RIGHT; - switch (TankMoveCheck(dir, enemies[i])) { //空地,敌方坦克,子弹,墙 - case BLANK: - enemies[i].x++; - break; - case BULLET: - enemies[i].x++; - for (int i = 0; i < BULLET_NUM; i++) { //遍历玩家子弹数组 - if (P_bullets[i].x == enemies[i].x && P_bullets[i].y == enemies[i].y) { - P_bullets[i].is_live = false; - enemies[i].hp--; - } - } - break; - } - } - } -} - -/* - 创建者:凌零 - 功能: - 根据传入的方向参数调用BulletMoveCheck() - 返回的枚举值判断子弹移动结果 - 实现子弹移动 -*/ - -void PlayerBulletMove() { - static clock_t start = clock() - 100; - if ((clock() - start) < 100) - return; - start = clock(); - - int arr[4][2] = { - {0, -1}, - {0, 1}, - {-1, 0}, - {1, 0}, - }; - for (int i = 0; i < BULLET_NUM; i++) // 遍历子弹 - { - if (P_bullets[i].is_live == false) - { - continue; - } - else - { - // 子弹碰撞检测:检测是否碰到空地、墙壁、己方坦克、敌方坦克、敌方死亡坦克、玩家子弹、敌方子弹 - // 0->空地、1->表示墙、2->己方坦克、3->敌方坦克、4->死亡坦克、5->我方子弹、6->敌方子弹 - // 0 空地 1 墙 3 敌方坦克 6 敌方子弹 - int type = BulletMoveCheck(P_bullets[i].dir, P_bullets[i]); - if (type == BLANK) - { - P_bullets[i].x += arr[P_bullets[i].dir][0]; - P_bullets[i].y += arr[P_bullets[i].dir][1]; - } - else if (type == WALL) - { - P_bullets[i].is_live = false; - } - else if (type == BLUE_TANK) //敌方坦克 - { - P_bullets[i].is_live = false; //子弹消失 - for (int k = 0; k < ENEMIES_NUM; k++) //坦克爆炸 - { - switch (P_bullets[i].dir) { - case UP: - if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y - 1) //位于子弹上方敌方坦克受到伤害 - { - enemies[k].hp -= P_bullets[i].damage; - } - break; - case DOWN: - if (enemies[k].x == P_bullets[i].x && enemies[k].y == P_bullets[i].y + 1) //位于子弹下方敌方坦克受到伤害 - { - enemies[k].hp -= P_bullets[i].damage; - } - break; - case LEFT: - if (enemies[k].x == P_bullets[i].x - 1 && enemies[k].y == P_bullets[i].y) //位于子弹左方敌方坦克受到伤害 - { - enemies[k].hp -= P_bullets[i].damage; - } - break; - case RIGHT: - if (enemies[k].x == P_bullets[i].x + 1 && enemies[k].y == P_bullets[i].y) //位于子弹右方敌方坦克受到伤害 - { - enemies[k].hp -= P_bullets[i].damage; - } - break; - default: // 添加默认分支 - break; - } - - } - } - else - { - P_bullets[i].is_live = false; - if (BULLET_NUM == 2) - { - for (int k = 3; k <= 8; k++) - { - if (P_bullets[k].x == P_bullets[i].x && P_bullets[k].y == P_bullets[i].y) - { - P_bullets[k].is_live = false; - break; - } - } - } - else - { - for (int k = 3; k <= 12; k++) - { - if (P_bullets[k].x == P_bullets[i].x && P_bullets[k].y == P_bullets[i].y) - { - P_bullets[k].is_live = false; - break; - } - } - } - break; - } - } - } -} - -void EnemyBulletMove() { - static clock_t start = clock() - 500; - if ((clock() - start) < 500) - return; - start = clock(); - - int arr[4][2] = { - {0, -1}, - {0, 1}, - {-1, 0}, - {1, 0}, - }; - for (auto& i : E_bullets) { - if (i.is_live) { - int type = BulletMoveCheck(i.dir, i); - if (type == BLANK) - { - i.x += arr[i.dir][0]; - i.y += arr[i.dir][1]; - } - else if (type == WALL) - { - i.is_live = false; - } - else if (type == RED_TANK) - { - i.is_live = false; - player.hp -= i.damage; - } - else { - i.x += arr[i.dir][0]; - i.y += arr[i.dir][1]; - } - /*else - { - i.is_live = false; - if (BULLET_NUM == 2) - { - for (int k = 3; k <= 8; k++) - { - if (P_bullets[k].x == i.x && P_bullets[k].y == i.y) - { - P_bullets[k].is_live = false; - break; - } - } - } - else - { - for (int k = 3; k <= 12; k++) - { - if (P_bullets[k].x == i.x && P_bullets[k].y == i.y) - { - P_bullets[k].is_live = false; - break; - } - } - } - break; - }*/ - } - } -} -/* - 创建者:凌零 - 功能:初始化玩家坦克 -*/ -void PlayerInit() -{ - player.x = MAP_CELL_NUM / 2; // 玩家初始位置 - player.y = MAP_CELL_NUM / 2; - player.hp = 5; // 玩家血量初始化 - player.dir = UP; // 初始化玩家方向 - player.attack = 5; // 初始化玩家攻击力为 5 - kill = 0; // 玩家击杀数初始化为 0 - map[player.x][player.y] = RED_TANK; // 在地图上标记坦克的位置 -} - -/* - 创建者:凌零 - 功能:生成敌方坦克 -*/ -void EnemyInit() -{ - srand((unsigned int)time(NULL)); //初始化随机数种子 - // 使用 库创建随机数生成器 - //default_random_engine generator; - //uniform_int_distribution distribution(0, 49); // 创建一个均匀分布,生成的随机数范围在1-48 - - // 初始化敌方坦克 - for (int i = 0; i < ENEMIES_NUM; i++) { - enemies[i].x = rand() % (MAP_CELL_NUM - 2) + 1 ; // 使用随机数生成器生成坐标 - enemies[i].y = rand() % (MAP_CELL_NUM - 2) + 1; - - // 如果随机到的位置已经被坦克或者墙壁占据,那么重新生成 - while (map[enemies[i].x][enemies[i].y] != BLANK) { - enemies[i].x = rand() % (MAP_CELL_NUM - 2) + 1; - enemies[i].y = rand() % (MAP_CELL_NUM - 2) + 1; - } - - enemies[i].hp = enemy.hp; // 设置敌人初始血量为5 - enemies[i].attack = enemy.attack; // 设置敌人初始攻击力为1 - enemies[i].dir = LEFT; // 设置敌人初始面朝左 - map[enemies[i].x][enemies[i].y] = BLUE_TANK; // 在地图上标记敌人位置 - } -} - -/* - 创建者:凌零 - 功能: - 返回值为假:说明失败,己方坦克hp<=0,或者还没有胜利条件:己方坦克hp>0,敌方坦克数量>0 - 返回值为真:说明赢了 己方坦克hp==0,敌方坦克数量==0 -*/ -bool IsWin() { - bool all_die = true; - for (auto& i : enemies) { - if (i.hp > 0) - all_die = false; - } - if (player.hp > 0 && all_die) { - return true; // 胜利 - } - else return false; // 没胜利或失败。 还在游戏进行中。 -} - -// 难度选择 -void Level() -{ - clear_screen(); - draw_image("images/游戏主界面背景.jpg", 0, 0, 700, 700); - - draw_image("images/简单难度.jpg", 275, 150, 150, 100); - draw_image("images/中等难度.jpg", 275, 300, 150, 100); - draw_image("images/困难难度.jpg", 275, 450, 150, 100); - present_screen(); - - while (true) - { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) exit(0); - if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { - MenuView(); - return; - } - if (e.type == SDL_MOUSEBUTTONDOWN) { - int mx = e.button.x; - int my = e.button.y; - - //选择简单难度 - if (mx >= 275 && mx <= 425 && my >= 150 && my <= 250) - { - enemy.hp = 5; - enemy.attack = 1; - MenuView(); - return; - } - //选择中等难度 - if (mx >= 275 && mx <= 425 && my >= 300 && my <= 400) - { - enemy.hp = 10; - enemy.attack = 2; - MenuView(); - return; - } - //选择困难难度 - if (mx >= 275 && mx <= 425 && my >= 450 && my <= 550) - { - enemy.hp = 15; - enemy.attack = 3; - MenuView(); - return; - } - } - } - SDL_Delay(16); - } -} - -// 开发人员 -void MemberView() -{ - clear_screen(); - draw_image("images/开发人员界面.jpg", 0, 0, 700, 700); - draw_image("images/返回按钮.jpg", 30, 550, 150, 50); - present_screen(); - - while (true) - { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) exit(0); - if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { - MenuView(); - return; - } - if (e.type == SDL_MOUSEBUTTONDOWN) { - int mx = e.button.x; - int my = e.button.y; - - //返回主菜单界面 - if (mx >= 30 && mx <= 180 && my >= 550 && my <= 600) - { - MenuView(); - return; - } - } - } - SDL_Delay(16); - } -} - -// 菜单 -void MenuView() -{ - InitMap(); - clear_screen(); - draw_image("images/菜单界面.jpg", 0, 0, 700, 700); - draw_image("images/开始游戏按键.jpg", 50, 250, 150, 50); - draw_image("images/难度设置按键.jpg", 50, 330, 150, 50); - draw_image("images/相关人员按键.jpg", 50, 410, 150, 50); - draw_image("images/退出游戏按键.jpg", 50, 490, 150, 50); - present_screen(); - - while (true) - { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) exit(0); - if (e.type == SDL_MOUSEBUTTONDOWN) { - int mx = e.button.x; - int my = e.button.y; - - //跳转游戏主页面 - if (mx >= 50 && mx <= 200 && my >= 250 && my <= 300) - { - GameView(); - } - //跳转至游戏难度选择 - if (mx >= 50 && mx <= 200 && my >= 330 && my <= 380) - { - Level(); - return; - } - //跳转至开发人员界面 - if (mx >= 50 && mx <= 200 && my >= 410 && my <= 460) - { - MemberView(); - return; - } - //退出游戏 - if (mx >= 50 && mx <= 200 && my >= 490 && my <= 540) - { - exit(0); - } - } - } - SDL_Delay(16); - } -} - -void change() -{ - SDL_SetWindowTitle(g_window, "纸牌坦克"); -} - - -//-------------------service--------------- - -/* - 创建者:丧丧的咸鱼 -*/ int main() { - // 将工作目录设置为可执行文件所在目录,确保相对路径资源(images/、music/)能正确加载 - char* base_path = SDL_GetBasePath(); - if (base_path) { - chdir(base_path); - SDL_free(base_path); - } - - // 初始化SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { - fprintf(stderr, "SDL初始化失败: %s\n", SDL_GetError()); - return -1; - } - - // 初始化SDL_image - int img_flags = IMG_INIT_PNG | IMG_INIT_JPG; - if (!(IMG_Init(img_flags) & img_flags)) { - fprintf(stderr, "SDL_image初始化失败: %s\n", IMG_GetError()); - SDL_Quit(); - return -1; - } - - // 初始化SDL_mixer - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { - fprintf(stderr, "SDL_mixer初始化失败: %s\n", Mix_GetError()); - IMG_Quit(); - SDL_Quit(); - return -1; - } - - // 初始化SDL_ttf - if (TTF_Init() < 0) { - fprintf(stderr, "SDL_ttf初始化失败: %s\n", TTF_GetError()); - Mix_CloseAudio(); - IMG_Quit(); - SDL_Quit(); - return -1; - } - - // 创建窗口和渲染器 - g_window = SDL_CreateWindow("纸牌坦克", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 700, 700, SDL_WINDOW_SHOWN); - if (!g_window) { - fprintf(stderr, "窗口创建失败: %s\n", SDL_GetError()); - TTF_Quit(); - Mix_CloseAudio(); - IMG_Quit(); - SDL_Quit(); - return -1; - } - - g_renderer = SDL_CreateRenderer(g_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (!g_renderer) { - fprintf(stderr, "渲染器创建失败: %s\n", SDL_GetError()); - SDL_DestroyWindow(g_window); - TTF_Quit(); - Mix_CloseAudio(); - IMG_Quit(); - SDL_Quit(); - return -1; - } - - // 加载字体(尝试多个常用中文字体路径) - const char* font_paths[] = { - "/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", - NULL - }; - for (int i = 0; font_paths[i] != NULL; i++) { - g_font = TTF_OpenFont(font_paths[i], 30); - if (g_font) { - printf("已加载字体: %s\n", font_paths[i]); - break; - } - } - if (!g_font) { - fprintf(stderr, "警告: 未找到中文字体,文本将无法显示。请安装 wqy-microhei 或 noto-cjk 字体。\n"); - // 尝试使用任何可用TTF字体 - } - - // 加载BGM - g_bgm = Mix_LoadMUS("music/bgm.wav"); - if (!g_bgm) { - fprintf(stderr, "BGM加载失败: %s,将静音运行\n", Mix_GetError()); - } else { - Mix_PlayMusic(g_bgm, -1); // 循环播放 - } - - // 设置窗口标题 - change(); - - // 进入主菜单 - MenuView(); - - // 清理资源 - if (g_bgm) { - Mix_HaltMusic(); - Mix_FreeMusic(g_bgm); - } - if (g_font) TTF_CloseFont(g_font); - SDL_DestroyRenderer(g_renderer); - SDL_DestroyWindow(g_window); - Mix_CloseAudio(); - TTF_Quit(); - IMG_Quit(); - SDL_Quit(); - - return 0; + Game game; + return game.run(); } diff --git a/tank_battles_on_the_scrap_paper/map.cpp b/tank_battles_on_the_scrap_paper/map.cpp deleted file mode 100644 index bc78e00..0000000 --- a/tank_battles_on_the_scrap_paper/map.cpp +++ /dev/null @@ -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; - } - } - } -} diff --git a/tank_battles_on_the_scrap_paper/map.h b/tank_battles_on_the_scrap_paper/map.h deleted file mode 100644 index 580b357..0000000 --- a/tank_battles_on_the_scrap_paper/map.h +++ /dev/null @@ -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 diff --git a/tank_battles_on_the_scrap_paper/src/AssetManager.cpp b/tank_battles_on_the_scrap_paper/src/AssetManager.cpp new file mode 100644 index 0000000..2852b82 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/AssetManager.cpp @@ -0,0 +1,52 @@ +#include "AssetManager.h" +#include + +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; +} diff --git a/tank_battles_on_the_scrap_paper/src/AssetManager.h b/tank_battles_on_the_scrap_paper/src/AssetManager.h new file mode 100644 index 0000000..714d912 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/AssetManager.h @@ -0,0 +1,30 @@ +#ifndef ASSET_MANAGER_H +#define ASSET_MANAGER_H + +#include +#include +#include +#include +#include +#include + +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 textures; + TTF_Font* font = nullptr; + Mix_Music* bgm = nullptr; +}; + +#endif diff --git a/tank_battles_on_the_scrap_paper/src/Bullet.cpp b/tank_battles_on_the_scrap_paper/src/Bullet.cpp new file mode 100644 index 0000000..e3475b4 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Bullet.cpp @@ -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(map[x + dx(dir)][y + dy(dir)]); +} + +void Bullet::step() { + x += dx(dir); + y += dy(dir); +} diff --git a/tank_battles_on_the_scrap_paper/src/Bullet.h b/tank_battles_on_the_scrap_paper/src/Bullet.h new file mode 100644 index 0000000..1e4e6da --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Bullet.h @@ -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 diff --git a/tank_battles_on_the_scrap_paper/src/Game.cpp b/tank_battles_on_the_scrap_paper/src/Game.cpp new file mode 100644 index 0000000..6f8a35c --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Game.cpp @@ -0,0 +1,451 @@ +#include "Game.h" +#include "Tank.h" +#include "UI.h" +#include +#include +#include +#include +#include + +Game::Game() { + std::srand(static_cast(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 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(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); +} diff --git a/tank_battles_on_the_scrap_paper/src/Game.h b/tank_battles_on_the_scrap_paper/src/Game.h new file mode 100644 index 0000000..cf3f3c3 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Game.h @@ -0,0 +1,62 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include +#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 enemies; + std::vector playerBullets; + std::vector 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 diff --git a/tank_battles_on_the_scrap_paper/src/Map.cpp b/tank_battles_on_the_scrap_paper/src/Map.cpp new file mode 100644 index 0000000..138a07f --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Map.cpp @@ -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(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(grid[x][y - 1]); + case DOWN: return static_cast(grid[x][y + 1]); + case LEFT: return static_cast(grid[x - 1][y]); + case RIGHT: return static_cast(grid[x + 1][y]); + } + return BLANK; +} diff --git a/tank_battles_on_the_scrap_paper/src/Map.h b/tank_battles_on_the_scrap_paper/src/Map.h new file mode 100644 index 0000000..67dfd9f --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Map.h @@ -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 diff --git a/tank_battles_on_the_scrap_paper/src/Renderer.cpp b/tank_battles_on_the_scrap_paper/src/Renderer.cpp new file mode 100644 index 0000000..d86ec21 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Renderer.cpp @@ -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); +} diff --git a/tank_battles_on_the_scrap_paper/src/Renderer.h b/tank_battles_on_the_scrap_paper/src/Renderer.h new file mode 100644 index 0000000..cdb93e4 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Renderer.h @@ -0,0 +1,25 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include +#include +#include +#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 diff --git a/tank_battles_on_the_scrap_paper/src/Tank.cpp b/tank_battles_on_the_scrap_paper/src/Tank.cpp new file mode 100644 index 0000000..348a49c --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Tank.cpp @@ -0,0 +1,3 @@ +#include "Tank.h" + +Tank::Tank(int h, int a) : hp(h), attack(a) {} diff --git a/tank_battles_on_the_scrap_paper/src/Tank.h b/tank_battles_on_the_scrap_paper/src/Tank.h new file mode 100644 index 0000000..e85a167 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/Tank.h @@ -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 diff --git a/tank_battles_on_the_scrap_paper/src/UI.h b/tank_battles_on_the_scrap_paper/src/UI.h new file mode 100644 index 0000000..ce59453 --- /dev/null +++ b/tank_battles_on_the_scrap_paper/src/UI.h @@ -0,0 +1,32 @@ +#ifndef UI_H +#define UI_H + +#include + +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 diff --git a/tank_battles_on_the_scrap_paper/tank.h b/tank_battles_on_the_scrap_paper/tank.h deleted file mode 100644 index 9b020d1..0000000 --- a/tank_battles_on_the_scrap_paper/tank.h +++ /dev/null @@ -1,33 +0,0 @@ -// ̹˶ -#ifndef TANK_H -#define TANK_H - -/* - ̹ˣ - ꣺xy - Ѫ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