c语言编程笔录

首页 >   > 笔记大全

笔记大全

c贪吃蛇代码是什么

更新时间:2023-10-24

第一段:需求分析和游戏框架搭建

在编写贪吃蛇游戏的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或其他图形库实现游戏界面的绘制和交互。在编写代码时,需要注意变量和函数的命名规范、代码风格的一致性等问题,以便编写出易于阅读和维护的代码。