头文件:
游戏界面:
#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);
// 在x,y处显示食物,x,y都是字符个数
void UIDisplayFoodAtXY(const struct UI *pUI, int x, int y);
// 在x,y处显示蛇的一个结点,x,y都是字符个数
void UIDisplaySnakeBlockAtXY(const struct UI *pUI, int x, int y);
// 清空x,y处的显示块,x,y都是字符个数
void UICleanBlockAtXY(const struct UI *pUI, int x, int y);
// 显示分数信息
void UIDisplayScore(const struct UI *pUI, int score, int scorePerFood);
// 在游戏区域居中显示游戏退出消息
void UIShowMessage(const struct UI *pUI, const char *message);
// 销毁 UI 资源
void UIDeinitialize(struct UI *pUI);
#endif
贪吃蛇本蛇游走:
#ifndef __SNAKE_H__
#define __SNAKE_H__
// 游戏区域坐标(对应到 UI 显示的时候就是字符个数)
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; // 游戏区域宽度(不包括墙,对应到 UI 部分就是字符个数)
int height; // 游戏区域高度(不包括墙,对应到 UI 部分就是字符个数)
int score; // 当前得分
int scorePerFood; // 每个食物得分
struct Snake snake; // 蛇
struct Position foodPosition; // 当前食物坐标
};
#endif
创建游戏:
#ifndef __GAME_H__
#define __GAME_H__
#include "Snake.h"
// 创建并初始化游戏
struct Game * CreateGame();
// 开始游戏
void StartGame(struct Game *pGame);
// 销毁游戏资源
void DestroyGame(struct Game *pGame);
#endif
.C文件:
游戏界面:
#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 descWidth = 50;
struct UI *pUI = (struct UI *)malloc(sizeof(struct UI));
pUI->gameWidth = width;
pUI->gameHeight = height;
pUI->marginLeft = left;
pUI->marginTop = top;
pUI->windowHeight = top + height + 2 + 3;
pUI->foodBlock = "█";
pUI->snakeBlock = "█";
pUI->wallBlock = "█";
pUI->blockWidth = strlen(pUI->wallBlock);
pUI->windowWidth = left + (width + 2) * pUI->blockWidth + descWidth;
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)
{
_DisplayWall(pUI);
UIDisplayScore(pUI, score, scorePerFood);
_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 blockWidth = strlen(pUI->wallBlock);
int left = pUI->marginLeft + (pUI->gameWidth + 2) * blockWidth + 2;
_SetPos(left, pUI->marginTop + 8);
printf("得分: %3d,每个食物得分: %3d", score, scorePerFood);
_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);
}
游戏设计:
#include "Game.h"
#include "UI.h"
#include <stdlib.h>
#include <stdbool.h>
#include <Windows.h>
static void _Pause();
static void _InitializeSnake(struct Snake *pSnake)
{
}
static int _IsOverlapSnake(const struct Snake *pSnake, int x, int y)
{
}
static void _GenerateNewFood(struct Game *pGame)
{
}
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;
// 关于蛇的初始化和放置食物部分,感兴趣的同学可以做扩展练习
_InitializeSnake(&pGame->snake);
_GenerateNewFood(pGame);
return pGame;
}
static void _DisplaySnake(const struct UI *pUI, const struct Snake *pSnake)
{
}
static struct Position _GetNextPosition(const struct Snake *pSnake)
{
struct Position nextPosition = { 0 };
return nextPosition;
}
static int _IsWillEatFood(struct Position foodPosition, struct Position nextPosition)
{
}
static void _GrowAndMoveAndDisplay(const struct UI *pUI,
struct Snake *pSnake,
struct Position nextPosition)
{
}
static void _UpdateScoreAndDisplay(const struct UI *pUI, struct Game *pGame)
{
}
static void _AddHeadAndDisplay(const struct UI *pUI, struct Snake *pSnake,
struct Position nextPosition)
{
}
static void _RemoveTailAndDisplay(const struct UI *pUI, struct Snake *pSnake)
{
}
static void _MoveAndDisplay(const struct UI *pUI, struct Snake *pSnake,
struct Position nextPosition)
{
}
static int _IsKilledByWall(const struct Snake *pSnake, int width, int height)
{
}
static int _IsKilledBySelf(const struct Snake *pSnake)
{
}
static void _HandleDirection(struct Snake *pSnake)
{
if (GetAsyncKeyState(VK_UP)) {
if (pSnake->direction != DOWN) {
pSnake->direction = UP;
}
}
else if (GetAsyncKeyState(VK_DOWN)) {
if (pSnake->direction != UP) {
pSnake->direction = DOWN;
}
}
else if (GetAsyncKeyState(VK_LEFT)) {
if (pSnake->direction != RIGHT) {
pSnake->direction = LEFT;
}
}
else if (GetAsyncKeyState(VK_RIGHT)) {
if (pSnake->direction != LEFT) {
pSnake->direction = RIGHT;
}
}
}
void StartGame(struct Game *pGame)
{
struct UI *pUI = UIInitialize(pGame->width, pGame->height);
UIDisplayWizard(pUI);
UIDisplayGameWindow(pUI, pGame->score, pGame->scorePerFood);
UIDisplayFoodAtXY(pUI, pGame->foodPosition.x, pGame->foodPosition.y);
_DisplaySnake(pUI, &pGame->snake);
while (1) {
// 方向
_HandleDirection(&pGame->snake);
if (GetAsyncKeyState(VK_ESCAPE)) {
break;
}
if (GetAsyncKeyState(VK_SPACE)) {
_Pause();
}
struct Position nextPosition = _GetNextPosition(&pGame->snake);
if (_IsWillEatFood(pGame->foodPosition, nextPosition)) {
_GrowAndMoveAndDisplay(pUI, &pGame->snake, nextPosition);
_UpdateScoreAndDisplay(pUI, pGame);
_GenerateNewFood(pGame);
UIDisplayFoodAtXY(pUI, pGame->foodPosition.x, pGame->foodPosition.y);
} else {
_MoveAndDisplay(pUI, &pGame->snake, nextPosition);
}
if (_IsKilledByWall(&pGame->snake, pGame->width, pGame->height)) {
break;
}
else if (_IsKilledBySelf(&pGame->snake)) {
break;
}
Sleep(300);
}
UIShowMessage(pUI, "游戏结束");
UIDeinitialize(pUI);
}
static void _DestroySnake(struct Snake *pSnake)
{
}
void DestroyGame(struct Game *pGame)
{
struct Node *pNode, *pNext;
_DestroySnake(&pGame->snake);
free(pGame);
}
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);
system("pause");
return 0;
}