这是一个贪吃蛇游戏的控制台版本。
游戏制作的思路是用二维数组表示地图,用链表表示蛇身,通过对链表的增添节点、删除节点模拟蛇的移动、进食。通过随机函数随机生成食物,蛇运动速度随得分增多而增加。
Snake类的定义。
class Snake
{
private:
int map[25][25]; //地图
int length; //蛇的长度
int direction_x, direction_y;
PsnakeItem head, tail; //蛇头、蛇尾
int start_x, start_y; //出发点
int level; //级别
int grade; //分数
int maxgrade; //最高分
public:
Snake()
{
for (int i = 0; i < 25; i++)
for (int j = 0; j < 25; j++)
map[i][j] = 0;
length = 0;
direction_x = 1;
direction_y = 0;
start_x = 15;
start_y = 15;
level = 1; //游戏等级
grade = 0; //分数
maxgrade = 0; //最高分
head = tail = NULL;
}
void SetColor(int colorID); //设置颜色
void Welcome(void); //绘制欢迎界面
void DrawInterface(void); //绘制游戏界面
void DrawBox(int position_x, int position_y); //绘制方块
void RedrawBox(int position_x, int position_y); //擦除方块
void MakeBlock(void); //随机生成食物
void UpdataHead(int position_x, int position_y); //在蛇的头部增加一个节点
void DeleteTail(void); //删去蛇尾部最后一个节点
void DrawSnake(void);
void Move(void);
void Run(void);
int Judge(void);
void Updata(void); //更新分数及等级
};
蛇身节点
typedef struct snakenode NsnakeItem; //定义节点
struct snakenode {
int position_x;
int position_y;
NsnakeItem *Next;
};
typedef NsnakeItem *PsnakeItem; //节点指点
这里采用宏定义的方式简化表示各种方块。
#define blank 0
#define body 1
#define wall 2
#define top 3
#define food 4
通过变量direction_x,direction_y来表示蛇的当前运动方向,通过W A S D改变方向。以上为游戏的底层实现方法。
对于界面的绘制,调用系统函数来设定光标位置。
void SetPos(int i, int j)//设定光标位置 i代表列数 j代表行数
{
COORD pos = { (short)i,(short)j };//坐标对象,用来设置光标的行列位置
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
由于游戏中使用■表示个元素,其宽度为普通字符2倍,所以如下定义方块绘制函数。
void Snake::DrawBox(int position_x, int position_y)
{
int nx = position_x, ny = position_y;
SetPos( position_x * 2, position_y );
cout << "■";
}
下面是游戏实现的核心,蛇的运动及与键盘交互的部分。
void Snake::Run(void)
{
int i = 0;//用于控制下落时间
char x; //储存用户输入的字符
int Count;//计数器
#pragma region draw;
//蛇的初始绘制
UpdataHead(10, 12);
UpdataHead(10, 13);
UpdataHead(10, 14);
UpdataHead(10, 15);
UpdataHead(11, 15);
UpdataHead(12, 15);
UpdataHead(13, 15);
UpdataHead(14, 15);
DrawSnake();
DrawInterface();
//绘制结束
#pragma endregion;
MakeBlock();
while (1)
{
if (level < 10)
Count = 500 - level * 50; //快慢 ,Count决定了下落运动间隔
else
Count = 50 - level * 2;
if (i >= Count) //如果i大于Count,表明时间过了Count毫秒
{
i = 0;
Move(); //蛇的移动
}
if (_kbhit()) //读取字符
{
x = _getch();
if (x == 'a' || x == 'A')
{
if (!(direction_x == 1 && direction_y == 0))
{
direction_x = -1;
direction_y = 0;
}
}
else if (x == 'd' || x == 'D')
{
if (!(direction_x == -1 && direction_y == 0))
{
direction_x = 1;
direction_y = 0;
}
}
else if (x == 'w' || x == 'W')
{
if (!(direction_x == 0 && direction_y == 1))
{
direction_x = 0;
direction_y = -1;
}
}
else if (x == 's' || x == 'S')
{
if (!(direction_x == 0 && direction_y == -1))
{
direction_x = 0;
direction_y = 1;
}
}
else if (x == 'p' || x == 'P')
{
while (1)
{
x = _getch();
if (x == 'p' || x == 'P')
break;
}
}
while (_kbhit()) //此处作用为除去运行中缓冲区多余字符
x = _getch();
}
Sleep(1); //延迟1毫秒
i++;
}
}
程序由VS2017编译。以下是源代码
// pch.h
#ifndef PCH_H
#define PCH_H
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
#include <Windows.h>
#include <conio.h>
#include <time.h>
#include <stdio.h>
#include<stdlib.h>
#include<cstdio>
#include<string>
#include <algorithm>
typedef struct snakenode NsnakeItem; //定义节点
struct snakenode {
int position_x;
int position_y;
NsnakeItem *Next;
};
typedef NsnakeItem *PsnakeItem; //节点指针
struct scoredata{
char name[10];
int grade;
};
typedef struct scoredata *Scoredata;
// TODO: 添加要在此处预编译的标头
#endif //PCH_H
#include "pch.h"
#include <iostream>
#pragma region DefinationOfBlock
#define blank 0
#define body 1
#define wall 2
#define top 3
#define food 4
#pragma endregion
class Snake
{
private:
int map[25][25]; //地图
int length; //蛇的长度
int direction_x, direction_y;
PsnakeItem head, tail; //蛇头、蛇尾
int start_x, start_y; //出发点
int level; //级别
int grade; //分数
int maxgrade; //最高分
public:
Snake()
{
for (int i = 0; i < 25; i++)
for (int j = 0; j < 25; j++)
map[i][j] = 0;
length = 0;
direction_x = 1;
direction_y = 0;
start_x = 15;
start_y = 15;
level = 1; //游戏等级
grade = 0; //分数
maxgrade = 0; //最高分
head = tail = NULL;
}
void SetColor(int colorID); //设置颜色
void Welcome(void); //绘制欢迎界面
void DrawInterface(void); //绘制游戏界面
void DrawBox(int position_x, int position_y); //绘制方块
void RedrawBox(int position_x, int position_y); //擦除方块
void MakeBlock(void); //随机生成食物
void UpdataHead(int position_x, int position_y); //在蛇的头部增加一个节点
void DeleteTail(void); //删去蛇尾部最后一个节点
void DrawSnake(void);
void Move(void);
void Run(void);
int Judge(void);
void Updata(void); //更新分数及等级
};
void SetPos(int i, int j)//设定光标位置 i代表列数 j代表行数 特殊字符列需要比行多一个位置输出
{
COORD pos = { (short)i,(short)j };//坐标对象,用来设置光标的行列位置
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
void Snake::SetColor(int colorID)
{
int n_color;
switch (colorID)
{
case 0: n_color = 0x08; break;
case 1: n_color = 0x0C; break;
case 2: n_color = 0x0D; break;
case 3: n_color = 0x0E; break;
case 4: n_color = 0x0A; break;
case 5: n_color = 0x0B; break;
case 6: n_color = 0xDC; break;
case 7: n_color = 0xEC; break;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), n_color);
}
void Snake::DrawBox(int position_x, int position_y)
{
int nx = position_x, ny = position_y;
SetPos( position_x * 2, position_y );
cout << "■";
}
void Snake::RedrawBox(int position_x, int position_y)
{
int nx = position_x, ny = position_y;
SetPos(position_x * 2, position_y);
cout << " ";
}
void Snake::DrawInterface(void)
{
SetPos(0, 0);
SetColor(0);
FILE *fp; //从文件中读取地图
if ((fp = fopen("map.txt", "r")) == NULL)
{
printf("File open error!\n");
exit(0);
}
for (int i = 0; i < 25; i++)
for (int j = 0; j < 25; j++)
fscanf(fp, "%d", &map[j][i]);
if (fclose(fp))
{
printf("Can not close the file!\n");
exit(0);
} //读取结束
for (int i = 0; i < 25; i++)
for (int j = 0; j < 25; j++)
if(map[i][j] == wall)
DrawBox(i, j);
for (int i = 27; i < 37; i++) //游戏副边框
{
DrawBox(i, 9);
DrawBox(i, 19);
}
for (int i = 9; i <= 19; i++)
{
DrawBox(27, i);
DrawBox(37, i);
}
if ((fp = fopen("score.dat", "r")) == NULL)
{
printf("File open error!\n");
exit(0);
}
fscanf(fp, "%d", &maxgrade);
if (fclose(fp))
{
printf("Can not close the file!\n");
exit(0);
}
SetPos(30*2, 11);
SetColor(2);
cout << "等级:" << level;
SetPos(30*2, 13);
cout << "分数:" << grade;
SetPos(30 * 2, 15);
cout << "最高分:" << maxgrade;
SetColor(0);
}
void Snake::DrawSnake(void)
{
PsnakeItem ptravel = head;
SetColor(7);
DrawBox(ptravel->position_x, ptravel->position_y);
ptravel = ptravel->Next;
SetColor(6);
while (ptravel != NULL)
{
DrawBox(ptravel->position_x, ptravel->position_y);
ptravel = ptravel->Next;
}
SetColor(0);
}
void Snake::UpdataHead(int position_x, int position_y)
{
PsnakeItem pnew = (PsnakeItem)malloc(sizeof(NsnakeItem));
pnew->position_x = position_x;
pnew->position_y = position_y;
if (head == NULL)
{
pnew->Next = NULL;
head = tail = pnew;
}
else
{
pnew->Next = head;
head = pnew;
map[head->Next->position_y][head->Next->position_x] = body;
}
if(map[position_y][position_x]== blank|| map[position_y][position_x] == food)
map[position_y][position_x] = top;
if (!Judge())
{
if (grade > maxgrade)
{
FILE *p;
if ((p = fopen("score.dat", "w")) == NULL)
{
printf("File open error!\n");
exit(0);
}
fprintf(p, "%d", grade);
if (fclose(p))
{
printf("Can not close the file!\n");
exit(0);
}
}
system("cls");
printf("Game Over");
Sleep(1000);
for (int i = 0; i < 25; i++)
for (int j = 0; j < 25; j++)
map[i][j] = blank;
direction_x = 1; direction_y = 0;
grade = 0;
level = 1;
head = NULL;
Welcome();
}
}
void Snake::DeleteTail(void)
{
PsnakeItem ptravel = head;
while (ptravel->Next != tail)
{
ptravel = ptravel->Next;
}
map[tail->position_y][tail->position_x] = blank;
free(tail);
tail = ptravel;
tail->Next = NULL;
}
void Snake::Move(void)
{
int nextPosition_x = head->position_x + direction_x; //下个节点的坐标
int nextPosition_y = head->position_y + direction_y;
if (map[nextPosition_y][nextPosition_x] == food)
{
UpdataHead(nextPosition_x, nextPosition_y); //将节点更新为头
DrawSnake();
MakeBlock();
Updata();
}
else
{
UpdataHead(nextPosition_x, nextPosition_y); //将节点更新为头
RedrawBox(tail->position_x, tail->position_y); //擦
DeleteTail(); //除去尾部节点
DrawSnake();
}
}
int Snake::Judge(void)
{
if (map[head->position_y][head->position_x] == wall || map[head->position_y][head->position_x] == body)
return 0;
else
return 1;
}
void Snake::MakeBlock(void)
{
SetColor(3);
srand((int)time(0));
int x, y;
do {
x = rand() % 25;
y = rand() % 25;
} while (map[y][x] == wall || map[y][x] == body || map[y][x] == top||map[y][x] == food);
DrawBox(x, y);
map[y][x] = food; //添加地图food
SetColor(0);
}
void Snake::Updata(void)
{
grade += 10;
level = (grade+40) / 40;
SetColor(2);
SetPos(30 * 2, 11);
cout << "等级:" << level;
SetPos(30 * 2, 13);
cout << "分数:" << grade;
SetPos(30 * 2, 15);
cout << "最高分:" << maxgrade;
SetColor(0);
}
void Snake::Run(void)
{
int i = 0;//用于控制下落时间
char x; //储存用户输入的字符
int Count;//计数器
#pragma region draw;
//蛇的初始绘制
UpdataHead(10, 12);
UpdataHead(10, 13);
UpdataHead(10, 14);
UpdataHead(10, 15);
UpdataHead(11, 15);
UpdataHead(12, 15);
UpdataHead(13, 15);
UpdataHead(14, 15);
DrawSnake();
DrawInterface();
//绘制结束
#pragma endregion;
MakeBlock();
while (1)
{
if (level < 10)
Count = 500 - level * 50; //快慢 ,Count决定了下落运动间隔
else
Count = 50 - level * 2;
if (i >= Count)
{
i = 0;
Move();
}
if (_kbhit())
{
x = _getch();
if (x == 'a' || x == 'A')
{
if (!(direction_x == 1 && direction_y == 0))
{
direction_x = -1;
direction_y = 0;
}
}
else if (x == 'd' || x == 'D')
{
if (!(direction_x == -1 && direction_y == 0))
{
direction_x = 1;
direction_y = 0;
}
}
else if (x == 'w' || x == 'W')
{
if (!(direction_x == 0 && direction_y == 1))
{
direction_x = 0;
direction_y = -1;
}
}
else if (x == 's' || x == 'S')
{
if (!(direction_x == 0 && direction_y == -1))
{
direction_x = 0;
direction_y = 1;
}
}
else if (x == 'p' || x == 'P')
{
while (1)
{
x = _getch();
if (x == 'p' || x == 'P')
break;
}
}
while (_kbhit())
x = _getch();
}
Sleep(1);
i++;
}
}
void Snake::Welcome(void)
{
char choose; //界面选择
SetColor(0);
while (1)
{
SetPos(0, 0);
cout << "■■■■■■■■■■■■■■■■■■■" << endl;
cout << "■ 贪吃蛇控制台版 ■" << endl;
cout << "■■■■■■■■■■■■■■■■■■■" << endl;
cout << "■ A,D左右移动 WS左右移动 ■" << endl;
cout << "■ P 暂停 ■" << endl;
cout << "■■■■■■■■■■■■■■■■■■■" << endl;
cout << "■ ■" << endl;
cout << "■ 1.运行游戏 ■" << endl;
cout << "■ ■" << endl;
cout << "■ 2.退出游戏 ■" << endl;
cout << "■ ■" << endl;
cout << "■■■■■■■■■■■■■■■■■■■" << endl;
choose = _getch();
switch (choose)
{
case '1': system("cls"); Run(); break;
case '2': exit(0); break;
default: break;
}
}
}
int main()
{
Snake snake;
snake.Welcome();
}
以下为资源文件map.txt为地图,score.dat储存最高分。
//map.txt
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
//score.dat
0
以下为运行截图