分析贪吃蛇游戏,蛇的身体在吃到食物之后增长,因此可以用链表来储存蛇身体的节点。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#define GAME_WIN_WIDTH 15
#define GAME_WIN_HEIGHT 15
#define DIRECTION_UP 'w'
#define DIRECTION_DOWN 's'
#define DIRECTION_LEFT 'a'
#define DIRECTION_RIGHT 'd'
//定义二维向量
struct vector2
{
int x;
int y;
};
//定义贪吃蛇身体节点
struct node {
node *next;
vector2 pos;//节点位置
};
enum MapFlag
{
NONE = 0,//空
BODY, //蛇身体
FOOD, //食物
WALL //墙壁
};
enum GameState
{
showStartBG = 1,
start,
playing,
over,
showOverBG
};
//定义贪吃蛇结构
struct Snake {
node * head; //贪吃蛇头部节点
int currentDirection; //当前移动方向
vector2 food;
vector2 sceneSize; //背景地图总大小
vector2 offset; //蛇可活动范围相对地图偏移值
int mapBackground[GAME_WIN_WIDTH + 2][GAME_WIN_HEIGHT + 2]; //背景地图
};
//为蛇添加头部节点
node* createHeader(int x, int y, node * body) {
node * head = new node;
head->next = body;
head->pos.x = x;
head->pos.y = y;
return head;
}
//为贪吃蛇进行数据初始化
void initSnake(Snake& s) {
s.currentDirection = DIRECTION_DOWN;
s.food = { 3,4 };
s.head = createHeader(5, 5, NULL);
s.offset = { 1,1 };
s.sceneSize = { GAME_WIN_WIDTH + 2 ,GAME_WIN_HEIGHT + 2 };
memset(s.mapBackground, 0, s.sceneSize.x * s.sceneSize.y * sizeof(int));
for (int i = 0; i < s.sceneSize.x; i++) {
s.mapBackground[i][0] = MapFlag::WALL;
s.mapBackground[i][s.sceneSize.y - 1] = MapFlag::WALL;
}
for (int i = 0; i < s.sceneSize.y; i++) {
s.mapBackground[0][i] = MapFlag::WALL;
s.mapBackground[s.sceneSize.x - 1][i] = MapFlag::WALL;
}
}
//删除蛇尾
void deleteTail(node * head) {
do
{
if (!head->next) {
return;
}
if (!head->next->next) {
delete head->next;
head->next = NULL;
}
} while (head = head -> next);
}
void deleteSnake(node * head) {
node* p = NULL;
while (head) {
p = head;
head = head->next;
delete p;
}
}
node * onMove(int direction, node* head) {
node *p = nullptr;
switch (direction)
{
case DIRECTION_DOWN:
p = createHeader(head->pos.x, head->pos.y + 1, head);
break;
case DIRECTION_UP:
p = createHeader(head->pos.x, head->pos.y - 1, head);
break;
case DIRECTION_LEFT:
p = createHeader(head->pos.x - 1, head->pos.y, head);
break;
case DIRECTION_RIGHT:
p = createHeader(head->pos.x + 1, head->pos.y , head);
break;
default:
break;
}
if (p) {
deleteTail(p);
}
return p;
}
vector2 getNextPos(Snake s) {
node *head = s.head;
vector2 nextPos;
switch (s.currentDirection)
{
case DIRECTION_DOWN:
nextPos = { head->pos.x, head->pos.y + 1 };
break;
case DIRECTION_UP:
nextPos = { head->pos.x, head->pos.y - 1 };
break;
case DIRECTION_LEFT:
nextPos = { head->pos.x - 1, head->pos.y };
break;
case DIRECTION_RIGHT:
nextPos = { head->pos.x + 1, head->pos.y };
break;
default:
break;
}
return nextPos;
}
node* onEatFood(Snake &s) {
vector2 pos = getNextPos(s);
node * head = s.head;
//---------随机重新生成食物---------
int randomseed = 0;
int adder = 0;
int x, y;
randomseed = LOWORD(time(0));
adder = randomseed << 15;
x = randomseed = ((randomseed) * 17 + adder) % GAME_WIN_WIDTH;
y = (randomseed * 17 + adder) % GAME_WIN_HEIGHT;
s.food.x = x;
s.food.y = y;
//---------------------------------
return createHeader(pos.x, pos.y, head);
}
bool checkDir(int currentDir,int newDir) {
switch (newDir)
{
case DIRECTION_DOWN:
return currentDir != DIRECTION_UP;
break;
case DIRECTION_UP:
return currentDir != DIRECTION_DOWN;
break;
case DIRECTION_LEFT:
return currentDir != DIRECTION_RIGHT;
break;
case DIRECTION_RIGHT:
return currentDir != DIRECTION_LEFT;
break;
default:
break;
}
return false;
}
bool isDie(Snake s) {
vector2 newHeadPos = getNextPos(s);
node* head = s.head;
if (newHeadPos.x < 0 || newHeadPos.y < 0) {
return true;
}
if (s.mapBackground[newHeadPos.x + s.offset.x][newHeadPos.y + s.offset.y] == MapFlag::WALL) {
return true;
}
do {
if (head->pos.x == newHeadPos.x && head->pos.y == newHeadPos.y) {
return true;
}
} while (head = head->next);
return false;
}
bool canEat(Snake s) {
vector2 dest = getNextPos(s);
return dest.x == s.food.x && dest.y == s.food.y;
}
void printfStartGame() {
printf("-------------------------------------------------\n");
printf("-------------------------------------------------\n");
printf("----- -----\n");
printf("----- press any key to start -----\n");
printf("----- -----\n");
printf("-------------------------------------------------\n");
printf("-------------------------------------------------\n");
}
void printfGameOver() {
printf("-------------------------------------------------\n");
printf("-------------------------------------------------\n");
printf("----- -----\n");
printf("----- game over!!! -----\n");
printf("----- -----\n");
printf("-------------------------------------------------\n");
printf("-------------------------------------------------\n");
}
void printGameScene(int *gameMap,Snake s , vector2 sceneSize,vector2 offset) {
int tempMap[GAME_WIN_WIDTH + 2][GAME_WIN_HEIGHT + 2];
memcpy(tempMap, gameMap, sceneSize.x * sceneSize.y * sizeof(int));
node * head = s.head;
do {
tempMap[head->pos.x + offset.x][head->pos.y + offset.y] = MapFlag::BODY;
} while (head = head->next);
tempMap[s.food.x +offset.x][s.food.y + offset.y] = MapFlag::FOOD;
for (int h = 0; h < sceneSize.y; h++) {
for (int w = 0; w < sceneSize.x; w++) {
switch (tempMap[w][h])
{
case MapFlag::NONE:
printf(" ");
break;
case MapFlag::BODY:
printf("*");
break;
case MapFlag::FOOD:
printf("@");
break;
case MapFlag::WALL:
printf("#");
break;
default:
break;
}
}
printf("\n");
}
}
void renderScene(GameState st, Snake s) {
system("cls");
switch (st)
{
case showStartBG:
printfStartGame();
break;
case start:
printfStartGame();
break;
case playing:
printGameScene((int*)s.mapBackground, s, s.sceneSize, s.offset);
break;
case over:
printfGameOver();
break;
case showOverBG:
printfGameOver();
break;
default:
break;
}
}
int main()
{
char input;
GameState state = GameState::showStartBG;
float speed = 2.0 / 1000; //step per msecond
time_t lastFrame = 0;
time_t laseMoveFrame = 0;
time_t currentFrame = 0;
Snake player;
while (true)
{
lastFrame = currentFrame;
currentFrame = GetTickCount64();
if (_kbhit()) {
input = _getch();
switch (state)
{
case GameState::showStartBG:
initSnake(player);
renderScene(state, player);
state = GameState::start;
laseMoveFrame = currentFrame;
break;
case GameState::start:
state = GameState:: playing;
break;
case GameState::playing:
if (checkDir(player.currentDirection, input)) {
player.currentDirection = input;
}
break;
case GameState::showOverBG:
renderScene(state, player);
state = GameState::over;
break;
case GameState::over:
state = GameState::showStartBG;
break;
default:
break;
}
}
if (state == GameState::playing && (currentFrame - laseMoveFrame) * speed > 1) {
laseMoveFrame = currentFrame;
if (isDie(player)) {
state = GameState::over;
}
else if(canEat(player))
{
player.head = onEatFood(player);
}
else
{
player.head = onMove(player.currentDirection, player.head);
}
renderScene(state, player);
}
}
return 0;
}