基于EasyX和Raylib的贪吃蛇

基于 EasyX

// 根据《C和C++游戏趣味编程》第七章 贪吃蛇 写出

#include <graphics.h>
#include <conio.h> // _kbhit()
#include <stdio.h>
#include <stdlib.h>

// 全局变量定义
#define HEIGHT 30       // 高度上一共30个小格子
#define WIDTH 40        // 宽度上一共40个小格子
#define BLOCK_SIZE 20    // 每个小格子的长宽大小

int blocks[HEIGHT][WIDTH] = { 0 }; // 记录所有的游戏数据
char moveDirection = 'd'; // move to right
bool fail = false; // 是否游戏失败
bool shouldExit = false;
int food_i;
int food_j;

// 初始化函数
void startup()
{
    blocks[HEIGHT / 2][WIDTH / 2] = 1; // 画面中间画蛇头,数字为1
    // 向左依次4个蛇身,数值依次为2, 3, 4, 5
    for (int i = 1; i <= 4; i++)
    {
        blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
    }

    food_i = rand() % (HEIGHT - 5) + 2;
    food_j = rand() % (WIDTH - 5) + 2;

    initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE, SHOWCONSOLE);
    SetWindowText(GetHWnd(), "Greedy Snake");
    setlinecolor(RGB(200, 200, 200));
    BeginBatchDraw();
}

void moveSnake()
{
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (blocks[i][j] > 0) // 蛇身
            {
                blocks[i][j]++;
            }
        }
    }

    int oldTail_i;
    int oldTail_j;
    int oldHead_i;
    int oldHead_j;
    int max = 0;
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (max < blocks[i][j])
            {
                max = blocks[i][j];
                oldTail_i = i;
                oldTail_j = j;
            }

            if (blocks[i][j] == 2) // 原来的蛇头
            {
                oldHead_i = i;
                oldHead_j = j;
            }
        }
    }

    int newHead_i = oldHead_i;
    int newHead_j = oldHead_j;
    
    if (moveDirection == 'w') // up
    {
        newHead_i = oldHead_i - 1;
    }
    if (moveDirection == 'd') // right
    {
        newHead_j = oldHead_j + 1;
    }
    if (moveDirection == 'a') // left
    {
        newHead_j = oldHead_j - 1;
    }
    if (moveDirection == 's') // down
    {
        newHead_i = oldHead_i + 1;
    }

    // 撞墙、撞到自身, 都算失败
    if ((newHead_j >= WIDTH || newHead_j < 0 || newHead_i >= HEIGHT || newHead_i < 0)
        || (blocks[newHead_i][newHead_j] > 0)
    )
    {
        fail = true;
        return;
    }

    blocks[newHead_i][newHead_j] = 1; // 生成新蛇头
    if (newHead_i == food_i && newHead_j == food_j) // 蛇头吃到食物
    {
        // 重新生成食物
        food_i = rand() % (HEIGHT - 5) + 2;
        food_j = rand() % (WIDTH - 5) + 2;
    }
    else
    {
        blocks[oldTail_i][oldTail_j] = 0; // 没吃到食物时,旧蛇尾变成空白,蛇长度不变
    }
}

// 绘制函数
void show()
{
    cleardevice();

    // 绘制每个方格
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (blocks[i][j] > 0) // 大于0表示是蛇,这里让蛇的身体颜色色调渐变
            {
                setfillcolor(HSVtoRGB(blocks[i][j] * 10, 0.9, 1));
            }
            else // 元素为0表示为空,颜色为灰色
            {
                setfillcolor(RGB(150, 150, 150));
            }

            // 在对应位置处,以对应颜色绘制小方格
            fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j+1) * BLOCK_SIZE, (i+1) * BLOCK_SIZE);
        }
    }

    // 绘制食物
    setfillcolor(RGB(0, 255, 0));
    fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
    
    if (fail)
    {
        setbkmode(TRANSPARENT); // 文字字体透明
        settextcolor(RGB(255, 0, 0)); // 文字颜色
        settextstyle(80, 0, _T("Consolas")); // 文字颜色、大小、样式

        outtextxy(240, 220, _T("Game Over"));
    }

    FlushBatchDraw();
}

// 与输入无关的更新
void updateWithoutInput()
{
    if (fail)
    {
        return;
    }

    static int waitIndex = 1; // 用于替代原有的用Sleep()来控制帧率的做法。
    waitIndex++;

    // 小蛇每隔10帧移动一次。
    if (waitIndex == 10)
    {
        moveSnake();
        waitIndex = 1;
    }
}

// 和输入有关的更新
void updateWithInput()
{
    if (kbhit())
    {
        char input = getch();

        if (!fail)
        {
            if (input == 'a' || input == 's' || input == 'd' || input == 'w')
            {
                moveDirection = input;
            }
        }
        if (input == 'q')
        {
            shouldExit = true;
        }
    }
}

int main()
{
    startup();
    while (!shouldExit)
    {
        show();
        updateWithInput();
        updateWithoutInput();
    }

    closegraph();

    return 0;
}

基于 Raylib

// 根据《C和C++游戏趣味编程》第七章 贪吃蛇 写出

#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#include "raylib_helper.hpp"

// 全局变量定义
#define HEIGHT 30       // 高度上一共30个小格子
#define WIDTH 40        // 宽度上一共40个小格子
#define BLOCK_SIZE 20    // 每个小格子的长宽大小

int blocks[HEIGHT][WIDTH] = { 0 }; // 记录所有的游戏数据
char moveDirection = 'd'; // move to right
bool fail = 0; // 是否游戏失败
int food_i;
int food_j;

// 初始化函数
void startup()
{
    blocks[HEIGHT / 2][WIDTH / 2] = 1; // 画面中间画蛇头,数字为1
    // 向左依次4个蛇身,数值依次为2, 3, 4, 5
    for (int i = 1; i <= 4; i++)
    {
        blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
    }

    food_i = rand() % (HEIGHT - 5) + 2;
    food_j = rand() % (WIDTH - 5) + 2;

    InitWindow(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE, "greedy snake");
    SetTargetFPS(60);
}

void show()
{
    BeginDrawing();
    {
        ClearBackground(BLACK);

        // 绘制每个方格
        for (int i = 0; i < HEIGHT; i++)
        {
            for (int j = 0; j < WIDTH; j++)
            {
                if (blocks[i][j] > 0)  // 大于0表示是蛇,这里让蛇的身体颜色色调渐变
                {
                    Color fill_color = ColorFromHSV(blocks[i][j] * 10, 0.9, 1);
                    DrawRectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, fill_color);                    
                }
                else // 元素为0表示为空,颜色为灰色
                {
                    DrawRectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, make_color(150, 150, 150, 255));
                }
                // easyx 的 fillrectangle() 会自动绘制边框颜色, raylib 绘制矩形(内部填充)时则并不会绘制边框颜色。
                DrawRectangleLines(j * BLOCK_SIZE, i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, make_color(200, 200, 200, 255));
            }
        }

        // 绘制食物
        DrawRectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, GREEN);

        if (fail)
        {
            DrawText("Game Over", 240, 200, 80, RED);
        }
    }
    EndDrawing();
}

void moveSnake()
{
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (blocks[i][j] > 0) // 蛇身
            {
                blocks[i][j]++;
            }
        }
    }

    int oldTail_i;
    int oldTail_j;
    int oldHead_i;
    int oldHead_j;
    int max = 0;
    for (int i = 0; i < HEIGHT; i++)
    {
        for (int j = 0; j < WIDTH; j++)
        {
            if (max < blocks[i][j])
            {
                max = blocks[i][j];
                oldTail_i = i;
                oldTail_j = j;
            }

            if (blocks[i][j] == 2) // 原来的蛇头
            {
                oldHead_i = i;
                oldHead_j = j;
            }
        }
    }

    int newHead_i = oldHead_i;
    int newHead_j = oldHead_j;
    
    if (moveDirection == 'w') // up
    {
        newHead_i = oldHead_i - 1;
    }
    if (moveDirection == 'd') // right
    {
        newHead_j = oldHead_j + 1;
    }
    if (moveDirection == 'a') // left
    {
        newHead_j = oldHead_j - 1;
    }
    if (moveDirection == 's') // down
    {
        newHead_i = oldHead_i + 1;
    }

    // 撞墙、撞到自身, 都算失败
    if ((newHead_j >= WIDTH || newHead_j < 0 || newHead_i >= HEIGHT || newHead_i < 0)
        || (blocks[newHead_i][newHead_j] > 0)
    )
    {
        fail = true;
        return;
    }

    blocks[newHead_i][newHead_j] = 1; // 生成新蛇头
    if (newHead_i == food_i && newHead_j == food_j) // 蛇头吃到食物
    {
        // 重新生成食物
        food_i = rand() % (HEIGHT - 5) + 2;
        food_j = rand() % (WIDTH - 5) + 2;
    }
    else
    {
        blocks[oldTail_i][oldTail_j] = 0; // 没吃到食物时,旧蛇尾变成空白,蛇长度不变
    }
}

void updateWithoutInput()
{
    if (fail)
    {
        return;
    }

    static int waitIndex = 1;
    waitIndex++;

    if (waitIndex == 10)
    {
        moveSnake();
        waitIndex = 1;
    }
}

// 相比于 easyx, 除了asdw键, 还可以使用方向键
void updateWithInput()
{
    if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))
        moveDirection = 'a';
    else if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))
        moveDirection = 's';
    else if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))
        moveDirection = 'd';
    else if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))
        moveDirection = 'w';
}

int main()
{
    startup();

    while (!WindowShouldClose())
    {
        show();
        updateWithInput();
        updateWithoutInput();
    }

    CloseWindow();
}
posted @ 2023-01-25 20:13  ChrisZZ  阅读(150)  评论(0编辑  收藏  举报