第一段:需求分析和游戏框架搭建
在编写贪吃蛇游戏的C++代码之前,我们需要先进行需求分析,并建立整个游戏的框架。
首先我们需要定义蛇、果子等游戏元素,以及蛇的移动、果子的生成等游戏逻辑。这里我们可以定义一个结构体Snake来表示蛇的一些属性,如位置、方向、长度等:
struct Snake {
int x, y; // 蛇头位置
int direction; // 移动方向
int length; // 蛇的长度
// ...
};
接下来,我们需要使用Windows API或其他图形库来实现游戏的基本框架。这里我们以Windows API为例,创建一个窗口并在上面绘制游戏场景:
HWND hwnd; // 窗口句柄
HDC hdc; // 设备环境句柄
// ...
void init() {
// 创建窗口
hwnd = CreateWindow(/*...*/);
// 获取设备环境句柄
hdc = GetDC(hwnd);
}
// 绘制游戏场景
void render() {
// 绘制背景
// 绘制蛇
// 绘制果子
// ...
// 通过Windows API在窗口中绘制场景
StretchBlt(hdc, 0, 0, GAME_WIDTH, GAME_HEIGHT, backdc, 0, 0, GAME_WIDTH, GAME_HEIGHT, SRCCOPY);
}
第二段:实现蛇和果子的生成和移动
在游戏框架搭建完毕后,我们需要开始实现具体的游戏逻辑代码,包括蛇的生成、移动和吃到果子等动作。
首先我们可以编写一个函数createSnake()来生成一条初始长度为3的蛇:
Snake snake;
// ...
void createSnake() {
snake.x = GAME_WIDTH / 2;
snake.y = GAME_HEIGHT / 2;
snake.direction = DIRECTION_RIGHT;
snake.length = 3;
// 初始化蛇的身体
for (int i = 0; i < snake.length; i++) {
body[i].x = snake.x - i;
body[i].y = snake.y;
}
}
接下来,我们需要编写一个函数moveSnake()来实现蛇的移动。由于蛇的身体是由多个方块组成的,因此在移动时需要从后往前移动,保证蛇的每个身体部位都能正确地前进。
void moveSnake() {
// 移动身体
for (int i = snake.length - 1; i > 0; i--) {
body[i].x = body[i - 1].x;
body[i].y = body[i - 1].y;
}
// 移动蛇头
switch (snake.direction) {
case DIRECTION_UP:
snake.y--;
break;
case DIRECTION_DOWN:
snake.y++;
break;
case DIRECTION_LEFT:
snake.x--;
break;
case DIRECTION_RIGHT:
snake.x++;
break;
}
}
最后,我们要编写函数checkCollision()来检测蛇是否和墙壁或自己相撞,以及蛇是否吃到了果子:
bool checkCollision() {
if (snake.x < 0 || snake.x >= GAME_WIDTH || snake.y < 0 || snake.y >= GAME_HEIGHT) {
// 蛇撞墙
return true;
}
for (int i = 1; i < snake.length; i++) {
if (snake.x == body[i].x && snake.y == body[i].y) {
// 蛇撞到自己
return true;
}
}
if (snake.x == fruit.x && snake.y == fruit.y) {
// 蛇吃到果子
return true;
}
return false;
}
第三段:实现控制和游戏循环
在游戏的核心逻辑代码实现完毕后,我们需要编写一个游戏循环来不断地更新游戏状态并重绘游戏场景。
首先,我们需要通过处理Windows消息来实现键盘控制蛇的移动方向:
void handleInput() {
if (GetKeyState(VK_UP) & 0x8000) {
snake.direction = DIRECTION_UP;
}
if (GetKeyState(VK_DOWN) & 0x8000) {
snake.direction = DIRECTION_DOWN;
}
if (GetKeyState(VK_LEFT) & 0x8000) {
snake.direction = DIRECTION_LEFT;
}
if (GetKeyState(VK_RIGHT) & 0x8000) {
snake.direction = DIRECTION_RIGHT;
}
}
接下来,我们可以编写游戏循环,不断地调用moveSnake()、checkCollision()等函数,并重绘游戏场景:
void gameLoop() {
while (true) {
// 处理输入
handleInput();
// 移动蛇
moveSnake();
// 检测碰撞
if (checkCollision()) {
// 处理游戏结束
// ...
}
// 重绘场景
render();
// 延时等待
Sleep(100);
}
}
第四段:完整代码示例和总结
综合以上几个部分的代码,我们可以得到一个完整的C++贪吃蛇游戏代码示例:
#include
#include
// 游戏场景大小
const int GAME_WIDTH = 640;
const int GAME_HEIGHT = 480;
// 方向常量
const int DIRECTION_UP = 1;
const int DIRECTION_DOWN = 2;
const int DIRECTION_LEFT = 3;
const int DIRECTION_RIGHT = 4;
// 蛇的属性和身体部位
struct Snake {
int x, y; // 蛇头位置
int direction; // 移动方向
int length; // 蛇的长度
// ...
} snake;
struct Body {
int x, y;
} body[100]; // 蛇的最大长度为100
// 果子的位置
struct Fruit {
int x, y;
} fruit;
HWND hwnd; // 窗口句柄
HDC hdc; // 设备环境句柄
HDC backdc; // 后备设备环境句柄,用于双缓冲绘制
HBITMAP backbmp; // 后备位图句柄
// 创建窗口
void createWindow() {
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASS wc = { 0 };
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = &WndProc;
wc.lpszClassName = L"GameWindow";
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
hwnd = CreateWindow(L"GameWindow", L"贪吃蛇", WS_OVERLAPPEDWINDOW, 0, 0, GAME_WIDTH, GAME_HEIGHT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
}
// 初始化游戏
void init() {
// 初始化窗口
createWindow();
// 获取设备环境句柄
hdc = GetDC(hwnd);
backdc = CreateCompatibleDC(hdc);
backbmp = CreateCompatibleBitmap(hdc, GAME_WIDTH, GAME_HEIGHT);
SelectObject(backdc, backbmp);
}
// 绘制游戏场景
void render() {
// 绘制背景
FillRect(backdc, &RECT{ 0, 0, GAME_WIDTH, GAME_HEIGHT }, (HBRUSH)GetStockObject(WHITE_BRUSH));
// 绘制蛇
for (int i = 0; i < snake.length; i++) {
RECT rect = { body[i].x * 10, body[i].y * 10, body[i].x * 10 + 10, body[i].y * 10 + 10 };
FillRect(backdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
}
// 绘制果子
RECT rect = { fruit.x * 10, fruit.y * 10, fruit.x * 10 + 10, fruit.y * 10 + 10 };
FillRect(backdc, &rect, (HBRUSH)GetStockObject(RED_BRUSH));
// 通过Windows API在窗口中绘制场景
StretchBlt(hdc, 0, 0, GAME_WIDTH, GAME_HEIGHT, backdc, 0, 0, GAME_WIDTH, GAME_HEIGHT, SRCCOPY);
}
// 判断蛇是否撞到墙壁或自身
bool checkCollision() {
if (snake.x < 0 || snake.x >= GAME_WIDTH / 10 || snake.y < 0 || snake.y >= GAME_HEIGHT / 10) {
// 蛇撞墙
return true;
}
for (int i = 1; i < snake.length; i++) {
if (snake.x == body[i].x && snake.y == body[i].y) {
// 蛇撞到自己
return true;
}
}
if (snake.x == fruit.x && snake.y == fruit.y) {
// 蛇吃到果子
snake.length++;
if (snake.length > 100) {
snake.length = 100;
}
int x, y;
// 随机生成果子位置
do {
x = rand() % (GAME_WIDTH / 10);
y = rand() % (GAME_HEIGHT / 10);
} while (x == snake.x && y == snake.y);
fruit.x = x;
fruit.y = y;
}
return false;
}
// 移动蛇和身体
void moveSnake() {
// 移动身体
for (int i = snake.length - 1; i > 0; i--) {
body[i].x = body[i - 1].x;
body[i].y = body[i - 1].y;
}
// 移动蛇头
switch (snake.direction) {
case DIRECTION_UP:
snake.y--;
break;
case DIRECTION_DOWN:
snake.y++;
break;
case DIRECTION_LEFT:
snake.x--;
break;
case DIRECTION_RIGHT:
snake.x++;
break;
}
}
// 处理键盘输入
void handleInput() {
if (GetKeyState(VK_UP) & 0x8000) {
snake.direction = DIRECTION_UP;
}
if (GetKeyState(VK_DOWN) & 0x8000) {
snake.direction = DIRECTION_DOWN;
}
if (GetKeyState(VK_LEFT) & 0x8000) {
snake.direction = DIRECTION_LEFT;
}
if (GetKeyState(VK_RIGHT) & 0x8000) {
snake.direction = DIRECTION_RIGHT;
}
}
// 游戏循环
void gameLoop() {
while (true) {
// 处理输入
handleInput();
// 移动蛇
moveSnake();
// 检测碰撞
if (checkCollision()) {
// 处理游戏结束
MessageBox(hwnd, L"你挂了!", L"游戏结束", MB_OK);
break;
}
// 重绘场景
render();
// 延时等待
Sleep(100);
}
}
// Win32消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
// 处理窗口销毁消息
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// Win32程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 随机数种子
srand((unsigned int)(time(NULL)));
// 初始化游戏
init();
// 创建蛇和初始果子
snake.x = GAME_WIDTH / 20;
snake.y = GAME_HEIGHT / 20;
snake.direction = DIRECTION_RIGHT;
snake.length = 3;
for (int i = 0; i < snake.length; i++) {
body[i].x = snake.x - i;
body[i].y = snake.y;
}
fruit.x = rand() % (GAME_WIDTH / 10);
fruit.y = rand() % (GAME_HEIGHT / 10);
// 开始游戏循环
gameLoop();
// 释放资源
DeleteObject(backbmp);
DeleteDC(backdc);
ReleaseDC(hwnd, hdc);
return 0;
}
综上所述,编写C++贪吃蛇游戏代码需要先进行需求分析,并建立游戏的框架。然后可以分别实现蛇和果子的生成和移动、键盘控制、游戏循环等内容,最后通过Windows API或其他图形库实现游戏界面的绘制和交互。在编写代码时,需要注意变量和函数的命名规范、代码风格的一致性等问题,以便编写出易于阅读和维护的代码。