用c语言实现有界面的小游戏--贪吃蛇

简易贪吃蛇代码实现、
一个游戏的编程实现,要分为不同模块,分别实现各子模块再进行组装 ,
无论什么游戏必须有一个游戏界面,这是游戏的门面,因为不是美工出身,自己娱乐的小游戏,界面丑点,,,就丑点哈哈
这里写图片描述
界面生成文件
当我们不知道确切的代码怎么写的时候,可以先实现想想我们想实现什么功能,把函数头部定义声明好,里面的内容可以后续填充。特别注意读程序,英语好点,比较好。。。。


#ifndef __UI_H__
#define __UI_H__


struct UI {
    // 边缘宽度
    int marginTop;
    int marginLeft;

    // 游戏区域所占位数
    int gameWidth;
    int gameHeight;

    // 整个窗口大小宽度
    int windowWidth;
    int windowHeight;

    char *snakeBlock;   // 蛇的显示块
    char *wallBlock;    // 墙的显示块
    char *foodBlock;    // 食物的显示块
    int blockWidth;// 每个块的宽度,注意,上面几个块的宽度要相等,否则就对不齐了
};

// UI 游戏窗口界面初始化
struct UI * UIInitialize(int width, int height);
// 显示游戏向导
void UIDisplayWizard(const struct UI *pUI);
// 显示游戏整体,包括墙、右边的信息
void UIDisplayGameWindow(const struct UI *pUI, int score, int scorePerFood,int speed);
// 在x,y处显示食物
void UIDisplayFoodAtXY(const struct UI *pUI, int x, int y);
// 在x,y处显示蛇的一个结点
void UIDisplaySnakeBlockAtXY(const struct UI *pUI, int x, int y);
// 清空x,y处的显示块
void UICleanBlockAtXY(const struct UI *pUI, int x, int y);
// 显示分数信息
void UIDisplayScore(const struct UI *pUI, int score, int scorePerFood,int speed);
// 在中间显示游戏退出消息
void UIShowMessage(const struct UI *pUI, const char *message);
// 销毁 UI 资源
void UIDeinitialize(struct UI *pUI);
#endif

写项目时,我们一般,函数头声明和具体函数和子函数实现分开,放在不同的文件中,.h文件交互,.c/.cpp实现

#define _CRT_SECURE_NO_WARNINGS

#include "UI.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

// 移动光标到x,y处,注意,这里是相对整个屏幕的,而不是游戏盘的
static void _SetPos(int x, int y);
// 显示墙
static void _DisplayWall(const struct UI *pUI);
// 显示右侧信息
static void _DisplayDesc(const struct UI *pUI);
// 将游戏盘的x,y坐标转换为相对整个屏幕的x,y
static void _CoordinatePosAtXY(const struct UI *pUI, int x, int y);
// 重置光标到屏幕下方,主要是为了显示的美观
static void _ResetCursorPosition(const struct UI *pUI);


struct UI * UIInitialize(int width, int height)
{
    const int left = 2;
    const int top = 2;
    const int blockWidth = 2;   // @杨祥钰指出
    const int descWidth = 35;

    struct UI *pUI = (struct UI *)malloc(sizeof(struct UI));
    pUI->gameWidth = width;
    pUI->gameHeight = height;
    pUI->marginLeft = left;
    pUI->marginTop = top;
    pUI->windowWidth = left + (width + 2) * blockWidth + descWidth;
    pUI->windowHeight = top + height + 2 + 3;
    pUI->foodBlock = "█";
    pUI->snakeBlock = "█";
    pUI->wallBlock = "█";
    pUI->blockWidth = strlen(pUI->wallBlock);

    char modeCommand[1024];
    sprintf(modeCommand, "mode con cols=%d lines=%d", pUI->windowWidth, pUI->windowHeight);
    system(modeCommand);

    return pUI;
} 

void UIDisplayWizard(const struct UI *pUI)
{
    int i;
    const char *messages[3] = {
        "欢迎来到贪吃蛇小游戏",
        "用↑.↓.←.→分别控制蛇的移动, F1为加速,F2为减速。",
        "加速将能得到更高的分数。"
    };

    i = 0;
    _SetPos(pUI->windowWidth / 2 - strlen(messages[i]) / 2, pUI->windowHeight / 2);
    printf("%s\n", messages[i]);
    _SetPos(pUI->windowWidth / 2 - strlen(messages[i]) / 2, pUI->windowHeight / 2 + 2);
    system("pause");
    system("cls");

    i = 1;
    _SetPos(pUI->windowWidth / 2 - strlen(messages[i]) / 2, pUI->windowHeight / 2);
    printf("%s\n", messages[i]);

    i = 2;
    _SetPos(pUI->windowWidth / 2 - strlen(messages[i]) / 2, pUI->windowHeight / 2 + 2);
    printf("%s\n", messages[i]);
    _SetPos(pUI->windowWidth / 2 - strlen(messages[i]) / 2, pUI->windowHeight / 2 + 4);
    system("pause");
    system("cls");
}

void UIDisplayGameWindow(const struct UI *pUI, int score, int scorePerFood,int speed)
{
    _DisplayWall(pUI);
    UIDisplayScore(pUI, score, scorePerFood,speed);
    _DisplayDesc(pUI);

    _ResetCursorPosition(pUI);
}

void UIDisplayFoodAtXY(const struct UI *pUI, int x, int y)
{
    _CoordinatePosAtXY(pUI, x, y);
    printf(pUI->foodBlock);

    _ResetCursorPosition(pUI);
}

void UIDisplaySnakeBlockAtXY(const struct UI *pUI, int x, int y)
{
    _CoordinatePosAtXY(pUI, x, y);
    printf(pUI->snakeBlock);

    _ResetCursorPosition(pUI);
}

void UICleanBlockAtXY(const struct UI *pUI, int x, int y)
{
    _CoordinatePosAtXY(pUI, x, y);
    int i;

    for (i = 0; i < pUI->blockWidth; i++) {
        printf(" ");
    }

    _ResetCursorPosition(pUI);
}

void UIDisplayScore(const struct UI *pUI, int score, int scorePerFood,int speed)
{
    int blockWidth = strlen(pUI->wallBlock);
    int left = pUI->marginLeft + (pUI->gameWidth + 2) * blockWidth + 2;
    _SetPos(left, pUI->marginTop + 8);
    printf("得分: %3d,每个食物得分: %3d", score, scorePerFood);
    _SetPos(left, pUI->marginTop + 9);
    printf("当前速度:%02f/秒", speed*0.001);
    _ResetCursorPosition(pUI);
}

void UIShowMessage(const struct UI *pUI, const char *message)
{
    // 左填充宽度 + (1(左边界个数)+游戏区域的个数/2) * 每个字符的宽度
    // - 字符串的宽度 / 2
    _SetPos(
        pUI->marginLeft + (
        (1 + pUI->gameWidth / 2) * pUI->blockWidth)
        - strlen(message) / 2,
        pUI->marginTop + 1 + pUI->gameHeight / 2);
    printf("%s\n", message);

    _ResetCursorPosition(pUI);
}

void UIDeinitialize(struct UI *pUI)
{
    free(pUI);
}

static void _DisplayWall(const struct UI *pUI)
{
    int left = pUI->marginLeft;
    int top = pUI->marginTop;
    int width = pUI->gameWidth;
    int height = pUI->gameHeight;
    int blockWidth = pUI->blockWidth;
    int i;

    // 上
    _SetPos(left, top);
    for (i = 0; i < width + 2; i++) {
        printf("%s", pUI->wallBlock);
    }

    // 下
    _SetPos(left, top + 1 + height);
    for (i = 0; i < width + 2; i++) {
        printf("%s", pUI->wallBlock);
    }

    // 左
    for (i = 0; i < height + 2; i++) {
        _SetPos(left, top + i);
        printf("%s", pUI->wallBlock);
    }

    // 右
    for (i = 0; i < height + 2; i++) {
        _SetPos(left + (1 + width) * blockWidth, top + i);
        printf("%s", pUI->wallBlock);
    }
}

static void _DisplayDesc(const struct UI *pUI)
{
    int left = pUI->marginLeft + (pUI->gameWidth + 2) * pUI->blockWidth + 2;
    const char *messages[] = {
        "不能穿墙,不能咬到自己。",
        "用 ↑ ↓ ← → 分别控制蛇的移动。",
        "F1 为加速,F2 为减速。",
        "ESC 退出游戏。 SPACE 暂停游戏。",

    };
    int i;

    for (i = 0; i < sizeof(messages) / sizeof(char *); i++) {
        _SetPos(left, pUI->marginTop + 10 + i);
        printf("%s\n", messages[i]);
    }
}

static void _SetPos(int x, int y)
{
    COORD position = { x, y };
    HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

    SetConsoleCursorPosition(hOutput, position);
}

static void _CoordinatePosAtXY(const struct UI *pUI, int x, int y)
{
    _SetPos(pUI->marginLeft + (1 + x) * pUI->blockWidth,
        pUI->marginTop + 1 + y);
}

static void _ResetCursorPosition(const struct UI *pUI)
{
    _SetPos(0, pUI->windowHeight - 1);
}

上面的界面文件各个游戏都可以借鉴,
蛇有关信息文件
这个文件就要开始,具体的实现贪吃蛇的食物啊,贪吃蛇身体移动等功能,

#ifndef __SNAKE_H__
#define __SNAKE_H__

// 坐标
struct Position {
    int x;
    int y;
};

// 链表结点
struct Node {
    struct Position position;
    struct Node *pNext;
};

// 蛇朝向
enum Direction {
    UP,
    DOWN,
    LEFT,
    RIGHT
};

// 蛇结构体
struct Snake {
    enum Direction direction;
    struct Node *pBody;
};

// 游戏结构体
struct Game {
    int width;  // 游戏宽度(不包括墙)
    int height; // 游戏高度(不包括墙)
    int score;  // 当前得分
    int scorePerFood;   // 每个食物得分
    int movingspeed;
    struct Snake snake; // 蛇
    struct Position foodPosition;   // 当前食物坐标
};

enum ExitStatus {
    QUIT,   // 正常退出
    KILLED_BY_WALL, // 撞墙了
    KILLED_BY_SELF  // 撞自己了
};

#endif

游戏进行有关文件,开始游戏,刷新游戏界面,判断游戏状态等


#ifndef __GAME_H__
#define __GAME_H__

#include "Snake.h"

// 创建并初始化游戏
struct Game * CreateGame();
// 开始游戏
void StartGame(struct Game *pGame);
// 销毁游戏资源
void DestroyGame(struct Game *pGame);

#endif

同样,以上是声明,下面是实现拉


#include "Game.h"
#include "UI.h"
#include <stdlib.h>
#include <stdbool.h>
#include <Windows.h>


// 初始化蛇结构体
static void _InitializeSnake(struct Snake *pSnake);
// 判断食物坐标是否和蛇有重叠
static bool _IsOverlapSnake(int x, int y, const struct Snake *pSnake);
// 生成一个食物
static void _GenerateFood(struct Game *pGame);
// 获取蛇即将进入的坐标
static struct Position _GetNextPosition(const struct Snake *pSnake);
// 判断蛇是否将吃到食物
static bool _IsWillEatFood(struct Position nextPosition, struct Position foodPosition);
// 增长蛇并且进行显示
static void _GrowSnakeAndDisplay(struct Snake *pSnake, struct Position foodPosition, const struct UI *pUI);
// 增长蛇头
static void _AddHead(struct Snake *pSnake, struct Position nextPosition, const struct UI *pUI);
// 删除蛇尾
static void _RemoveTail(struct Snake *pSnake, const struct UI *pUI);
// 移动蛇并且进行显示
static void _MoveSnakeAndDisplay(struct Snake *pSnake, struct Position nextPosition, const struct UI *pUI);
// 蛇是否撞墙了
static bool _IsKilledByWall(const struct Node *pHead, int width, int height);
// 蛇是否撞自己了
static bool _IsKilledBySelf(const struct Node *pHead, const struct Snake *pSnake);
// 蛇是否存活
static bool _IsSnakeAlive(const struct Game *pGame, enum ExitStatus *exitStatus);
// 处理方向指令
static void _HandleDirective(struct Game *pGame);
// 显示完整的蛇
static void _DisplaySnake(const struct UI *pUI, const struct Snake *pSnake);
// 暂停
static void _Pause();


struct Game * CreateGame()
{
    struct Game *pGame = (struct Game *)malloc(sizeof(struct Game));
    // TODO: 异常处理
    pGame->width = 28;
    pGame->height = 27;
    pGame->score = 0;
    pGame->scorePerFood = 10;
    pGame->movingspeed = 500;
    _InitializeSnake(&pGame->snake);
    _GenerateFood(pGame);

    return pGame;
}

void StartGame(struct Game *pGame)
{
    struct UI *pUI = UIInitialize(pGame->width, pGame->height);
    enum ExitStatus exitStatus = QUIT;

    UIDisplayWizard(pUI);
    UIDisplayGameWindow(pUI, pGame->score, pGame->scorePerFood,pGame->movingspeed);
    _DisplaySnake(pUI, &pGame->snake);
    UIDisplayFoodAtXY(pUI, pGame->foodPosition.x, pGame->foodPosition.y);

    while (1) {
        if (GetAsyncKeyState(VK_ESCAPE)) {
            break;
        } else if (GetAsyncKeyState(VK_SPACE)) {
            _Pause();
        }
        else if (GetAsyncKeyState(VK_F1))
        {
            if (pGame->movingspeed > 100)
            {
                pGame->movingspeed -= 100;
                pGame->scorePerFood += 10;
                UIDisplayScore(pUI, pGame->score, pGame->scorePerFood, pGame->movingspeed);
            }
        }
        else if (GetAsyncKeyState(VK_F2))
        {
            if(pGame->movingspeed<600&&pGame->scorePerFood>10)
            pGame->movingspeed += 100;
            pGame->scorePerFood -= 10;
            UIDisplayScore(pUI, pGame->score, pGame->scorePerFood, pGame->movingspeed);
        }

        _HandleDirective(pGame);

        struct Position nextPosition = _GetNextPosition(&pGame->snake);

        if (_IsWillEatFood(nextPosition, pGame->foodPosition)) {
            pGame->score += pGame->scorePerFood;
            UIDisplayScore(pUI, pGame->score, pGame->scorePerFood,pGame->movingspeed);
            _GrowSnakeAndDisplay(&pGame->snake, pGame->foodPosition, pUI);
            _GenerateFood(pGame);
            UIDisplayFoodAtXY(pUI, pGame->foodPosition.x, pGame->foodPosition.y);
        }
        else {
            _MoveSnakeAndDisplay(&pGame->snake, nextPosition, pUI);
        }

        if (!_IsSnakeAlive(pGame, &exitStatus)) {
            break;
        }

        Sleep(pGame->movingspeed);
    }

    char *messages[3];
    messages[QUIT] = "游戏结束";
    messages[KILLED_BY_WALL] = "游戏结束,撞到墙了";
    messages[KILLED_BY_SELF] = "游戏结束,撞到自己了";
    UIShowMessage(pUI, messages[exitStatus]);
    system("pause");
    UIDeinitialize(pUI);
}

void DestroyGame(struct Game *pGame)
{
    struct Node *pNode, *pNext;

    for (pNode = pGame->snake.pBody; pNode != NULL; pNode = pNext) {
        pNext = pNode->pNext;
        free(pNode);
    }
    free(pGame);
}

static void _InitializeSnake(struct Snake *pSnake)
{
    const int length = 3;
    const int x = 5;
    const int y = 5;
    int i;
    struct Node *pNode;

    pSnake->direction = RIGHT;
    pSnake->pBody = NULL;

    for (i = 0; i < length; i++) {
        pNode = (struct Node *)malloc(sizeof(struct Node));
        pNode->position.x = x + i;
        pNode->position.y = y;
        pNode->pNext = pSnake->pBody;
        pSnake->pBody = pNode;
    }
}

static bool _IsOverlapSnake(int x, int y, const struct Snake *pSnake)
{
    struct Node *pNode;

    for (pNode = pSnake->pBody; pNode != NULL; pNode = pNode->pNext) {
        if (pNode->position.x == x && pNode->position.y == y) {
            return true;
        }
    }

    return false;
}

static void _GenerateFood(struct Game *pGame)
{
    int x, y;

    do {
        x = rand() % pGame->width;
        y = rand() % pGame->height;
    } while (_IsOverlapSnake(x, y, &pGame->snake));

    pGame->foodPosition.x = x;
    pGame->foodPosition.y = y;
}

static struct Position _GetNextPosition(const struct Snake *pSnake)
{
    int nextX, nextY;

    nextX = pSnake->pBody->position.x;
    nextY = pSnake->pBody->position.y;

    switch (pSnake->direction) {
    case UP:
        nextY -= 1;
        break;
    case DOWN:
        nextY += 1;
        break;
    case LEFT:
        nextX -= 1;
        break;
    case RIGHT:
        nextX += 1;
        break;
    }

    struct Position position = {
        .x = nextX,
        .y = nextY
    };

    return position;
}

static bool _IsWillEatFood(struct Position nextPosition, struct Position foodPosition)
{
    return nextPosition.x == foodPosition.x && nextPosition.y == foodPosition.y;
}

static void _GrowSnakeAndDisplay(struct Snake *pSnake, struct Position foodPosition, const struct UI *pUI)
{
    struct Node *pHead = (struct Node *)malloc(sizeof(struct Node));
    pHead->position.x = foodPosition.x;
    pHead->position.y = foodPosition.y;
    pHead->pNext = pSnake->pBody;
    pSnake->pBody = pHead;

    UIDisplaySnakeBlockAtXY(pUI, foodPosition.x, foodPosition.y);
}

static void _AddHead(struct Snake *pSnake, struct Position nextPosition, const struct UI *pUI)
{
    struct Node *pNode = (struct Node *)malloc(sizeof(struct Node));

    pNode->position.x = nextPosition.x;
    pNode->position.y = nextPosition.y;
    pNode->pNext = pSnake->pBody;

    pSnake->pBody = pNode;

    UIDisplaySnakeBlockAtXY(pUI, nextPosition.x, nextPosition.y);
}

static void _RemoveTail(struct Snake *pSnake, const struct UI *pUI)
{
    struct Node *pNode = pSnake->pBody;

    // 基于什么前提?
    while (pNode->pNext->pNext != NULL) {
        pNode = pNode->pNext;
    }

    UICleanBlockAtXY(pUI, pNode->pNext->position.x, pNode->pNext->position.y);

    free(pNode->pNext);
    pNode->pNext = NULL;
}

static void _MoveSnakeAndDisplay(struct Snake *pSnake, struct Position nextPosition, const struct UI *pUI)
{
    _RemoveTail(pSnake, pUI);
    _AddHead(pSnake, nextPosition, pUI);
}

static bool _IsKilledByWall(const struct Node *pHead, int width, int height)
{
    if (pHead->position.x < 0 || pHead->position.x >= width) {
        return true;
    }

    if (pHead->position.y < 0 || pHead->position.y >= height) {
        return true;
    }

    return false;
}

static bool _IsKilledBySelf(const struct Node *pHead, const struct Snake *pSnake)
{
    struct Node *pNode;

    // 基于什么前提么?
    for (pNode = pSnake->pBody->pNext; pNode != NULL; pNode = pNode->pNext) {
        if (pHead->position.x == pNode->position.x && pHead->position.y == pNode->position.y) {
            return true;
        }
    }

    return false;
}

static bool _IsSnakeAlive(const struct Game *pGame, enum ExitStatus *exitStatus)
{
    struct Node *pHead = pGame->snake.pBody;

    if (_IsKilledByWall(pHead, pGame->width, pGame->height)) {
        *exitStatus = KILLED_BY_WALL;
        return false;
    }

    if (_IsKilledBySelf(pHead, &pGame->snake)) {
        *exitStatus = KILLED_BY_SELF;
        return false;
    }

    return true;
}

static void _HandleDirective(struct Game *pGame)
{
    if (GetAsyncKeyState(VK_UP) && pGame->snake.direction != DOWN) {
        pGame->snake.direction = UP;
    } else if (GetAsyncKeyState(VK_DOWN) && pGame->snake.direction != UP) {
        pGame->snake.direction = DOWN;
    }
    else if (GetAsyncKeyState(VK_LEFT) && pGame->snake.direction != RIGHT) {
        pGame->snake.direction = LEFT;
    }
    else if (GetAsyncKeyState(VK_RIGHT) && pGame->snake.direction != LEFT) {
        pGame->snake.direction = RIGHT;
    }
}

static void _DisplaySnake(const struct UI *pUI, const struct Snake *pSnake)
{
    struct Node *pNode;

    if (pSnake->pBody == NULL) {
        return;
    }

    for (pNode = pSnake->pBody; pNode != NULL; pNode = pNode->pNext) {
        UIDisplaySnakeBlockAtXY(pUI, pNode->position.x, pNode->position.y);
    }
}

static void _Pause()
{
    while (1)
    {
        Sleep(300);
        if (GetAsyncKeyState(VK_SPACE))
        {
            break;
        }
    }
}

主函数尽量写的小巧,东西少。嗯嗯你懂的。

#include <stdlib.h>
#include <time.h>
#include "Game.h"
#include "UI.h"

int main(int argc, const char *argv[])
{
    srand((unsigned int)time(NULL));

    struct Game *pGame = CreateGame();
    StartGame(pGame);
    DestroyGame(pGame);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40921797/article/details/80165172