ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用C++从零写一个可操控游戏引擎:Dev-C++编译通过,含方向键移动与“-”发射

用C++从零写一个可操控游戏引擎:Dev-C++编译通过,含方向键移动与“-”发射 1. 控制台里也能跑出“游戏引擎”先把最小骨架搭起来你可能觉得“游戏引擎”这四个字离初学者很远Unity、Unreal 那种动辄几个 G 的工程才配叫引擎。其实引擎的本质就三件事输入、状态更新、画面重绘。只要把这三件事拆开、循环跑起来哪怕画面只是控制台里的一个和一个-它也已经是一个可操控的最小引擎骨架了。这篇面向 C 初学者和 Dev-C 用户目标很明确在控制台窗口里用方向键控制角色上下左右移动按空格发射-子弹并且保证在 Dev-C 里能直接编译通过。我会给出完整源码结构、编译参数、运行验证步骤最后再说说后续想接 AI 辅助开发时怎么用 TaoToken 统一管理 Key 和 API 通道省得每换一个工具就重新配一遍。先说清楚适合谁如果你刚学完for、if、函数想找个能立刻看到反馈的小项目或者你一直在用 Dev-C想搞明白gotoxy、getch、system(cls)这些老派但好用的东西怎么组合成一个循环——那这篇就是给你写的。不适合已经想上图形库、想搞物理碰撞的人那是下一步的事。核心检索词先摆出来C 游戏引擎、Dev-C 编译通过、方向键移动、空格发射子弹、控制台游戏循环。下面所有代码都围绕这几个点展开你可以直接复制粘贴。2. 前置准备Dev-C 工程与 TaoToken 通道2.1 Dev-C 这边要确认的三件事第一确认你装的是带 MinGW 的 Dev-C大多数安装包默认带。菜单工具 - 编译选项 - 编译器里能看到g.exe路径就对了。第二新建工程时选Console Application语言选C。第三源文件编码建议用 ANSI 或 UTF-8 无 BOM避免中文注释在某些版本里乱码。这里有个坑我踩过Dev-C 默认可能把windows.h相关的链接库漏掉。如果你编译时报undefined reference to GetStdHandle之类去工程 - 工程属性 - 参数 - 链接器里加上-lgdi32 -luser32一般就好了。2.2 为什么这里要提 TaoToken写这个小引擎本身不需要联网。但接下来你大概率会想让 AI 帮我补个敌人生成、帮我解释这段循环、或者把代码改成面向对象结构。这时候你会同时用到好几个工具——网页对话、命令行 coding agent、IDE 插件——每个都要单独填 Key、单独配地址很烦。TaoToken 的作用就是把这些统一到一个入口一个 Key、一个 API 地址模型对话、Coding Plan、控制台、API Keys 都在同一套体系里。它的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM。你先把账号和 Key 准备好后面第五节会讲具体怎么在 AI 辅助开发时用上。注意TaoToken 是帮你统一管理模型调用通道的工具不是让你绕过任何本地编译环境。Dev-C 的编译、链接、运行全部还是在你本机完成。3. 可复制配置完整源码结构与 ggoot.h3.1 文件结构工程里放两个文件就够了GameEngine/ ├── main.cpp // 主循环、输入、状态、渲染 └── ggoot.h // 光标控制、清屏等底层封装ggoot.h这个名字是沿用你熟悉的写法内容其实就是把 Windows 控制台 API 包一层让主逻辑干净一点。3.2 ggoot.h 完整内容#ifndef GGOOT_H #define GGOOT_H #include iostream #include windows.h // 把光标移动到控制台的 (x, y) 坐标 void gotoxy(int x, int y) { COORD pos { (SHORT)x, (SHORT)y }; HANDLE hOut GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hOut, pos); } // 隐藏控制台光标避免闪烁干扰画面 void HideTheCursor() { CONSOLE_CURSOR_INFO cciCursor; HANDLE hStdOut GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleCursorInfo(hStdOut, cciCursor)) { cciCursor.bVisible FALSE; SetConsoleCursorInfo(hStdOut, cciCursor); } } // 清屏等价于 system(cls)但更快、无闪烁 void ClearScreen() { HANDLE hOut GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD count; DWORD cellCount; COORD homeCoords { 0, 0 }; if (hOut INVALID_HANDLE_VALUE) return; if (!GetConsoleScreenBufferInfo(hOut, csbi)) return; cellCount csbi.dwSize.X * csbi.dwSize.Y; FillConsoleOutputCharacter(hOut, (TCHAR) , cellCount, homeCoords, count); FillConsoleOutputAttribute(hOut, csbi.wAttributes, cellCount, homeCoords, count); SetConsoleCursorPosition(hOut, homeCoords); } #endif这里我把system(cls)换成了ClearScreen()。原因很实际system(cls)每次都会新起一个进程循环里频繁调用会闪、会慢。用FillConsoleOutputCharacter直接刷缓冲区画面稳很多。这是从“能跑”到“像个引擎”的第一个小升级。3.3 main.cpp输入、状态、渲染三段式#include ggoot.h #include conio.h #include vector #include cstdlib #include ctime using namespace std; const int WIDTH 40; // 逻辑宽度 const int HEIGHT 20; // 逻辑高度 struct Bullet { int x, y; bool alive; }; int playerX WIDTH / 2; int playerY HEIGHT / 2; vectorBullet bullets; // 处理一次按键更新游戏状态 void handleInput(char key) { switch (key) { case w: case W: if (playerY 0) playerY--; break; case s: case S: if (playerY HEIGHT - 1) playerY; break; case a: case A: if (playerX 0) playerX--; break; case d: case D: if (playerX WIDTH - 1) playerX; break; case : { Bullet b; b.x playerX 1; b.y playerY; b.alive true; bullets.push_back(b); break; } default: break; } } // 更新子弹位置 void updateBullets() { for (size_t i 0; i bullets.size(); i) { if (!bullets[i].alive) continue; bullets[i].x; if (bullets[i].x WIDTH) bullets[i].alive false; } } // 渲染一帧 void render() { ClearScreen(); // 画边框 for (int x 0; x WIDTH; x) { gotoxy(x, 0); cout #; gotoxy(x, HEIGHT - 1); cout #; } for (int y 0; y HEIGHT; y) { gotoxy(0, y); cout #; gotoxy(WIDTH - 1, y); cout #; } // 画子弹 for (size_t i 0; i bullets.size(); i) { if (bullets[i].alive) { gotoxy(bullets[i].x, bullets[i].y); cout -; } } // 画玩家 gotoxy(playerX, playerY); cout ; // 状态栏 gotoxy(0, HEIGHT); cout WASD move | Space fire | bullets: bullets.size() ; } int main() { HideTheCursor(); srand((unsigned)time(0)); while (true) { if (_kbhit()) { char key _getch(); if (key 27) break; // ESC 退出 handleInput(key); } updateBullets(); render(); Sleep(30); // 约 33 FPS控制台够用 } gotoxy(0, HEIGHT 2); cout Game loop exited. endl; return 0; }3.4 关键参数对照参数作用建议值WIDTH / HEIGHT逻辑画布大小40 x 20窗口别太小Sleep(30)每帧间隔毫秒20–50太小会闪_kbhit()非阻塞检测按键必须配合_getch()bullets[i].x子弹速度每帧 1想快就 2ESC退出循环别用CtrlC会留残影提示_kbhit和_getch来自conio.h是 Windows 下 Dev-C 自带的不需要额外装库。如果你在别的编译器上报错检查是不是用了getch而不是_getch。4. 验证请求编译、运行、看到结果4.1 Dev-C 编译步骤把ggoot.h和main.cpp放进同一个工程目录。在 Dev-C 里文件 - 新建 - 工程 - Console Application然后把这两个文件加进去。按F11编译运行。如果报错先看这几条undefined reference to SetConsoleCursorPosition链接器加-luser32。_kbhit was not declared确认#include conio.h在且没写错成conio。中文乱码源文件另存为 ANSI 编码或把中文注释删掉测试。4.2 运行后你应该看到窗口里出现一个#围成的边框中间一个。按W/A/S/D会上下左右移动碰到边框停住。按空格从右侧飞出一串-一直飞到右边界消失。底部状态栏实时显示当前子弹数量。按ESC退出循环结束。这个过程里handleInput负责“输入”updateBullets负责“状态更新”render负责“重绘”。三者被while(true)串起来这就是引擎最朴素的骨架。你后面加敌人、加碰撞、加分数都是往这三个函数里塞逻辑结构不用动。4.3 想接 AI 辅助时怎么验证通道当你准备让 AI 帮你扩展这个引擎时先在 TaoToken 控制台创建一个 API Key然后到模型对话页面发一条测试请求比如“解释一下上面 render 函数里为什么先画边框再画子弹”。能正常返回说明 Key 和通道是通的。这一步的入口是模型对话地址在 https://taotoken.net/api 对应的控制台里能找到。5. 本篇常见错排查5.1 画面闪烁严重多半是还在用system(cls)。换成第 3.2 节的ClearScreen()闪烁会明显减少。另外Sleep别小于 15 毫秒否则刷新太快人眼反而觉得抖。5.2 按键没反应或要按好几次getch()是阻塞的_kbhit()是非阻塞的两者必须配对。如果你只写char c getch();放在循环开头程序会卡在等按键子弹就不动了。正确写法是if (_kbhit()) { char key _getch(); ... }。5.3 子弹越飞越卡bullets这个 vector 只增不减飞出去的子弹虽然alivefalse但还占着内存。长时间玩会越来越慢。解决办法是在updateBullets末尾加一句清理for (int i (int)bullets.size() - 1; i 0; --i) { if (!bullets[i].alive) bullets.erase(bullets.begin() i); }5.4 Dev-C 里gotoxy报重定义如果你同时包含了windows.h和某个也定义了gotoxy的头会冲突。把ggoot.h里的函数名改成GotoXY之类或者确认只包含一次。头文件里的#ifndef GGOOT_H就是干这个的别删。5.5 窗口太小边框画到外面控制台默认缓冲区可能只有 80x25逻辑坐标 40x20 一般够。如果还是错位在main开头加一句调整窗口大小system(mode con cols60 lines30);放在HideTheCursor()之前即可。6. 后续怎么把这个骨架用起来到这里你已经有了一个能跑、能操控、能发射子弹的最小引擎。它不炫但结构是干净的输入、更新、渲染分离。接下来想加敌人就在updateBullets旁边加updateEnemies想加碰撞就在更新阶段判断playerX/Y和敌人坐标是否重合想加分数加个全局变量在渲染时画出来。如果你打算让 AI 帮你做这些扩展建议把 Key 和通道统一到 TaoToken 管理。长期写代码、跑 agent 的话用 Coding Plan 更省心入口在 https://taotoken.net/api 对应的控制台里只是偶尔问模型问题用模型对话就够了。API Keys 页面负责创建和轮换 Key接入文档里有各语言的最小示例。这样你换工具时不用重复配置专注在引擎逻辑本身。最后留一个可以立刻动手的小任务把Sleep(30)改成Sleep(15)再把子弹速度从x改成x 2感受一下帧率和速度对操控手感的影响。改完你就明白所谓引擎调参调的其实就是这几个数字。
返回列表