首先在实现这个小游戏要考虑一下几个问题:
一、需求
1.只有一条蛇
2.只有一个食物
3.字符界面
4.结束有些的方式:(1) 撞墙上 (2)自己撞到自己身上
5.界面问题:只有边界部分有墙
二、自顶向下分析解决问题(model::数据结构、view:界面问题、controller:控制流)
蛇:当做链表处理,用坐标表示
食物:也用坐标表示
墙:已知宽度和宽度
三、食物的生成规则:随机,要在墙的范围内,不和蛇重叠
四、单个周期内需要做的事情:
1.蛇前进(没有吃到食物)
1) 添加头结点
2) 删除尾结点
2.游戏结束
1) 蛇撞墙了吗
2) 蛇撞到自己了吗
3.蛇要增长(吃到食物)
1) 添加头结点
2) 生成食物
3)加分(加到多少分时,速度增加)
4.方向键的判断来决定蛇的前进方向
view.h:
#pragma once
#include <windows.h>
#include <stdio.h>
#include "Model.h"
void SetPos(int X, int Y)
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord;
coord.X = X;
coord.Y = Y;
SetConsoleCursorPosition(hStdout, coord);
}
// 显示外墙
void DisplayWall(int width, int height)
{
int i;
// 上边的墙
SetPos(0, 0);
for (i = 0; i < width + 2; i++) {
printf("█");
}
// 下边的墙
SetPos(0, height + 1);
for (i = 0; i < width + 2; i++) {
printf("█");
}
// 左边的墙
for (i = 0; i < height + 2; i++) {
SetPos(0, i);
printf("█");
}
// 右边的墙
for (i = 0; i < height + 2; i++) {
SetPos(2 * (width + 1), i);
printf("█");
}
}
//显示蛇的位置
void DisplaySnakeNode(Position pos)
{
SetPos(2 * (pos.x + 1), pos.y + 1);
printf("◆");
}
//清楚数据
void CleanSnakeNode(Position pos)
{
SetPos(2 * (pos.x + 1), pos.y + 1);
printf(" ");
}
//显示食物
void DisplayFood(Position pos)
{
SetPos(2 * (pos.x + 1), pos.y + 1);
printf("⊙");
}
//显示蛇
void DisplaySnake(const Snake *pSnake)
{
Node *pNode = pSnake->pHead;
while (pNode) {
DisplaySnakeNode(pNode->data);
pNode = pNode->pNext;
}
}
controller.h
#pragma once
#include "Model.h"
#include "View.h"
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//蛇的初始化
void SnakeInit(Snake *pSnake)
{
int i;
pSnake->pHead = NULL;
for (i = 0; i < 3; i++) {
Position pos;
pos.x = i + 5;
pos.y = 5;
Node *pNewNode = (Node *)malloc(sizeof(Node));
assert(pNewNode);
pNewNode->data = pos;
pNewNode->pNext = pSnake->pHead;
pSnake->pHead = pNewNode;
}
pSnake->direction = RIGHT;
}
// 1 重叠
// 0 不重叠
int IsOverlapSnake(int x, int y, const Snake *pSnake)
{
Node *pNode;
for (pNode = pSnake->pHead; pNode; pNode = pNode->pNext) {
if (pNode->data.x == x && pNode->data.y == y) {
return 1;
}
}
return 0;
}
// 生成食物(食物初始化)
// 1. 随机
// 2. 范围内
// 3. 不和蛇重叠
void FoodInit(Position *pFood, int width, int height, const Snake *pSnake)
{
int x, y;
do {
x = rand() % width;
y = rand() % height;
} while (IsOverlapSnake(x, y, pSnake));
pFood->x = x;
pFood->y = y;
}
//game的初始化
void GameInit(Game *pGame)
{
pGame->width = 28;
pGame->height = 32;
system("mode con cols=60 lines=38");
SnakeInit(&(pGame->snake));
FoodInit(
&(pGame->foodPosition),
pGame->width,
pGame->height,
&(pGame->snake));
pGame->score = 0;
pGame->scorePerFood = 10;
pGame->speed = 200; // 毫秒
}
// 得到下一步的坐标
Position GetNextPosition(const Snake *pSnake)
{
Position nextPos;
nextPos.x = pSnake->pHead->data.x;
nextPos.y = pSnake->pHead->data.y;
switch (pSnake->direction) {
case UP:
nextPos.y -= 1;
break;
case DOWN:
nextPos.y += 1;
break;
case LEFT:
nextPos.x -= 1;
break;
case RIGHT:
nextPos.x += 1;
break;
}
return nextPos;
}
// 头插
void PushFront(Snake *pSnake, Position nextPos)
{
Node *pNewNode = (Node *)malloc(sizeof(Node));
assert(pNewNode);
pNewNode->data = nextPos;
pNewNode->pNext = pSnake->pHead;
pSnake->pHead = pNewNode;
DisplaySnakeNode(nextPos);
}
// 尾删
void PopBack(Snake *pSnake)
{
Node *pNode = pSnake->pHead;
while (pNode->pNext->pNext != NULL) {
pNode = pNode->pNext;
}
CleanSnakeNode(pNode->pNext->data);
free(pNode->pNext);
pNode->pNext = NULL;
}
// 1 表示撞了
// 0 表示没有
int IsCrashByWall(int width, int height, Position nextPos)
{
if (nextPos.x < 0 || nextPos.x >= width) {
return 1;
}
if (nextPos.y < 0 || nextPos.y >= height) {
return 1;
}
return 0;
}
// 1 表示撞了
// 0 表示没撞
int IsCrashHimself(const Snake *pSnake)
{
Node *pHead = pSnake->pHead; // 第一个结点
Node *pNode = pSnake->pHead->pNext; // 第二个结点
while (pNode != NULL) {
if (pHead->data.x == pNode->data.x && pHead->data.y == pNode->data.y) {
return 1;
}
pNode = pNode->pNext;
}
return 0;
}
void GameStart()
{
Game game;
GameInit(&game);
DisplayWall(game.width, game.height);
DisplaySnake(&(game.snake));
DisplayFood(game.foodPosition);
while (1) {
if (GetAsyncKeyState(VK_UP) && game.snake.direction != DOWN) {
game.snake.direction = UP;
}
else if (GetAsyncKeyState(VK_DOWN) && game.snake.direction != UP) {
game.snake.direction = DOWN;
}
else if (GetAsyncKeyState(VK_LEFT) && game.snake.direction != RIGHT) {
game.snake.direction = LEFT;
}
else if (GetAsyncKeyState(VK_RIGHT) && game.snake.direction != LEFT) {
game.snake.direction = RIGHT;
}
Position nextPos = GetNextPosition(&(game.snake));
if (nextPos.x == game.foodPosition.x && nextPos.y == game.foodPosition.y) {
// 吃到食物
// 蛇增长一格
PushFront(&(game.snake), nextPos);
// 加分
game.score += game.scorePerFood;
// 生成新食物
FoodInit(&(game.foodPosition),
game.width, game.height,
&(game.snake));
DisplayFood(game.foodPosition);
}
else {
// 没吃到食物
// 蛇向前一步
PushFront(&(game.snake), nextPos);
PopBack(&(game.snake));
}
if (IsCrashByWall(game.width, game.height, nextPos)) {
break;
}
if (IsCrashHimself(&(game.snake))) {
break;
}
Sleep(game.speed);
}
}
model.h
#pragma once
// 坐标原点位于左上角,
// 向右为 x 轴的正方向,
// 向下为 y 轴的正方向,
typedef struct Position {
int x;
int y;
} Position;
// 链表结点(单向)
typedef struct Node {
Position data;
struct Node *pNext;
} Node;
// 表示蛇当前的前进方向
typedef enum Direction {
UP, DOWN, LEFT, RIGHT
} Direction;
// 蛇的结构
typedef struct Snake {
Direction direction;
Node *pHead;
} Snake;
// 一盘游戏中用到的其他非独立字段
typedef struct Game {
Snake snake;
Position foodPosition;
int score; // 当前得分
int scorePerFood; // 每吃一个食物得分
int speed; // 蛇的前进速度
int width; // 宽
int height; // 高
} Game;
main.c
int main()
{
printf("欢迎\n");
system("pause");
system("cls");
printf("欢迎\n");
system("pause");
system("cls");
printf("热烈欢迎\n");
system("pause");
system("cls");
GameStart();
return 0;
}