由于是第一次尝试小游戏,主要是了解游戏开发流程、设计思想,借助前人的成果进行修改和尝试,修改调通运行起来,对于自己来说也是进了很大一步呀!
进入主题,贪吃蛇设计思路:
1.一张地图,四周有墙体。
2.一条蛇,由蛇身和蛇头组成。
3.食物,蛇吃到食物后身体变长,并且重新生成一个食物。
4.蛇需要移动,这是难实现的一个部分。
5.蛇撞到墙或者自己的身体就会死亡。
6.需要能用键盘控制蛇的运动方向。
1.整个地图用一个二维字符数组实现。
2.用一个二维数组snake [x] [3]来描述,snake[i][0] 的值用来描述头和身体,snake[i][0] =1表示头,snake[i][0] =0表示身体,snake[i][1] 和snake[i][2] 分别描述所在的x坐标和y坐标,i表示第i节身体。
3.通过产生随机数作为食物的坐标,这个坐标不能超过地图的边界也不能和蛇的坐标一样。
4.蛇的移动,将蛇头移动到下一个坐标的时候,后面的节依次重复前一个节的移动。只需要控制蛇头的方向,设定一个变量dir=1,表示向上,1,2,3,4分别表示上下左右。如果蛇头向上走,x不变,y-1。
5.蛇头撞墙或者自己的身体,通过判断蛇头坐标是否和墙的坐标重合或者和蛇的某一节重合,当碰撞发生后,停止刷新屏幕。
6.键盘输入方向键后,重置dir的值。
增加游戏的娱乐性,例如等级选择功能,通过输入数字来选择等级,等级越高,贪吃蛇移动速度越快,而且得分越高。
游戏的功能性,在游戏结束后输出得分情况,并提示是否继续游戏,而不是直接退出游戏,而是通过选择的方式决定继续游戏或者退出游戏,暂停功等。
1.这是第一次接触游戏开发相关的知识,感觉自己能力不足,需要学习的东西还很多。
2.现在也有一些开源算法库之类的资源,极大程度方便了初学者,要学会查找资源。因为自己平时用c多一些,所以此次游戏开发还是查找的c相关的实现,但是很遗憾时间的原因,还没有百分之百达到任务要求。
3.在学习小组了解到了pygame等实现方式,也在查找学习,配置环境争取后续可以使用python+pygame实现一个更有意思好看好玩的游戏。
4.这次感触最深的就是:要学会分析问题、拆分问题;把复杂的问题分解简化为一个一个小问题,再组合逻辑流程就可以解决一个大问题。从来没有想到自己也能通过代码得到一个小游戏呢!每个人的能力、起点都不相同,但是只要努力,就可以成为最好的自己!
回头看自己竟然坚持了一周打卡学习任务、也完成了大多的任务,虽然不是最棒最好的,希望自己能将好习惯延续,加油!
C++实现①
实现了基本功能,等价等级选择及暂停
SnakePosition.h
#ifndef _SNAKEPOSITION_H_
#define _SNAKEPOSITION_H_
#pragma warning(disable:4996)
#pragma once
using namespace std;
#define N 22
class SnakePosition
{
public:
SnakePosition();
~SnakePosition();
public:
int x, y; //x表示行,y表示列
void initialize(int &);//坐标初始化
};
#endif
SnakePosition.cpp
#include "SnakePosition.h"
SnakePosition::SnakePosition()
{
}
SnakePosition::~SnakePosition()
{
}
void SnakePosition::initialize(int &j)
{
x = 1;
y = j;
}
SnakeMap.h
#ifndef _SNAKEMAP_H_
#define _SNAKEMAP_H_
#pragma warning(disable:4996)
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include "SnakePosition.h"
//贪吃蛇的棋盘图
using namespace std;
class SnakeMap
{
public:
int x1;
int y1; // 随机出米
int gameover;
int x, y;
long start;
SnakeMap(int h = 4, int t = 1, int l = 4, char d = 77, int s = 0);
~SnakeMap();
SnakePosition position[(N - 2)*(N - 2) + 1]; //定义贪吃蛇坐标类数组,有(N-2)*(N-2)个坐标
private:
char s[N][N];//定义贪吃蛇棋盘,包括墙壁。
int grade, length;
int gamespeed; //前进时间间隔
char direction; // 初始情况下,向右运动
int head, tail;
int score;
bool gameauto;
public:
void initialize(); //初始化函数
void show_game();
int updata_game();
void setpoint();
void getgrade();
void display();
};
#endif
SnakeMap.cpp
#include "SnakeMap.h"
SnakeMap::SnakeMap(int h, int t , int l , char d , int s ) :length(l), direction(d), head(h), tail(t), score(s)
{
}
SnakeMap::~SnakeMap()
{
}
//定义初始化函数,将贪吃蛇的棋盘图进行初始化
void SnakeMap::initialize()
{
int i, j;
for (i = 1; i <= 3; i++)
s[1][i] = '*';
s[1][4] = '#';
for (i = 1; i <= N - 2; i++)
for (j = 1; j <= N - 2; j++)
s[i][j] = ' '; // 初始化贪吃蛇棋盘中间空白部分
for (i = 0; i <= N - 1; i++)
s[0][i] = s[N - 1][i] = '-'; //初始化贪吃蛇棋盘上下墙壁
for (i = 1; i <= N - 2; i++)
s[i][0] = s[i][N - 1] = '|'; //初始化贪吃蛇棋盘左右墙壁
}
//============================================
//输出贪吃蛇棋盘信息
void SnakeMap::show_game()
{
system("cls"); // 清屏
int i, j;
cout << endl;
for (i = 0; i < N; i++)
{
cout << '\t';
for (j = 0; j < N; j++)
cout << s[i][j] << ' '; // 输出贪吃蛇棋盘
if (i == 2) cout << "\t等级:" << grade;
if (i == 6) cout << "\t速度:" << gamespeed;
if (i == 10) cout << "\t得分:" << score << "分";
if (i == 14) cout << "\t暂停:按一下空格键";
if (i == 18) cout << "\t继续:按两下空格键";
cout << endl;
}
}
//输入选择等级
void SnakeMap::getgrade()
{
cin >> grade;
while (grade > 7 || grade < 1)
{
cout << "请输入数字1-7选择等级,输入其他数字无效" << endl;
cin >> grade;
}
switch (grade)
{
case 1: gamespeed = 1000; gameauto = 0; break;
case 2: gamespeed = 800; gameauto = 0; break;
case 3: gamespeed = 600; gameauto = 0; break;
case 4: gamespeed = 400; gameauto = 0; break;
case 5: gamespeed = 200; gameauto = 0; break;
case 6: gamespeed = 100; gameauto = 0; break;
case 7: grade = 1; gamespeed = 1000; gameauto = 1; break;
}
}
//输出等级,得分情况以及称号
void SnakeMap::display()
{
cout << "\n\t\t\t\t等级:" << grade;
cout << "\n\n\n\t\t\t\t速度:" << gamespeed;
cout << "\n\n\n\t\t\t\t得分:" << score << "分";
}
//随机产生米
void SnakeMap::setpoint()
{
srand(time(0));
do
{
x1 = rand() % (N - 2) + 1;
y1 = rand() % (N - 2) + 1;
} while (s[x1][y1] != ' ');
s[x1][y1] = '*';
}
char key;
int SnakeMap::updata_game()
{
gameover = 1;
key = direction;
start = clock();
while ((gameover = (clock() - start <= gamespeed)) && !kbhit());
//如果有键按下或时间超过自动前进时间间隔则终止循环
if (gameover)
{
getch();
key = getch();
}
if (key == ' ')
{
while (getch() != ' '){};//这里实现的是按空格键暂停,按空格键继续的功能,但不知为何原因,需要按两下空格才能继续。这是个bug。
}
else
direction = key;
switch (direction)
{
case 72: x = position[head].x - 1; y = position[head].y; break; // 向上
case 80: x = position[head].x + 1; y = position[head].y; break; // 向下
case 75: x = position[head].x; y = position[head].y - 1; break; // 向左
case 77: x = position[head].x; y = position[head].y + 1; // 向右
}
if (!(direction == 72 || direction == 80 || direction == 75 || direction == 77)) // 按键非方向键
gameover = 0;
else if (x == 0 || x == N - 1 || y == 0 || y == N - 1) // 碰到墙壁
gameover = 0;
else if (s[x][y] != ' '&&!(x == x1&&y == y1)) // 蛇头碰到蛇身
gameover = 0;
else if (x == x1 && y == y1)
{ // 吃米,长度加1
length++;
if (length >= 8 && gameauto)
{
length -= 8;
grade++;
if (gamespeed >= 200)
gamespeed -= 200; // 改变贪吃蛇前进速度
else
gamespeed = 100;
}
s[x][y] = '#'; //更新蛇头
s[position[head].x][position[head].y] = '*'; //吃米后将原先蛇头变为蛇身
head = (head + 1) % ((N - 2)*(N - 2)); //取蛇头坐标
position[head].x = x;
position[head].y = y;
show_game();
gameover = 1;
score += grade * 20; //加分
setpoint(); //产生米
}
else
{ // 不吃米
s[position[tail].x][position[tail].y] = ' ';//将蛇尾置空
tail = (tail + 1) % ((N - 2) * (N - 2));//更新蛇尾坐标
s[position[head].x][position[head].y] = '*'; //将蛇头更为蛇身
head = (head + 1) % ((N - 2) * (N - 2));
position[head].x = x;
position[head].y = y;
s[position[head].x][position[head].y] = '#'; //更新蛇头
gameover = 1;
}
return gameover;
}
main.h
// Snake.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
#include "SnakeMap.h"
#include "SnakePosition.h"
using namespace std;
//====================================
//主函数部分
//====================================
int main()
{
char ctn = 'y';
int nodead;
cout << "\n\n\n\n\n\t\t\t 欢迎进入贪吃蛇游戏!" << endl;//欢迎界面;
cout << "\n\n\n\t\t\t 按任意键马上开始。。。" << endl;//准备开始;;
getch();
while (ctn == 'y')
{
system("cls"); // 清屏
SnakeMap snake;
snake.initialize();
cout << "\n\n请输入数字选择游戏等级:" << endl;
cout << "\n\n\n\t\t\t1.等级一:速度 1000 \n\n\t\t\t2.等级二:速度 800 \n\n\t\t\t3.等级三:速度 600 ";
cout << "\n\n\t\t\t4.等级四:速度 400 \n\n\t\t\t5.等级五:速度 200 \n\n\t\t\t6.等级六:速度 100 \n\n\t\t\t7.自动升级模式" << endl;
snake.getgrade();//获取等级
for (int i = 1; i <= 4; i++)
{
snake.position[i].initialize(i);//初始化坐标
}
snake.setpoint(); // 产生第一个米
do
{
snake.show_game();
nodead = snake.updata_game();
} while (nodead);
system("cls"); //清屏
cout << "\n\n\n\t\t\t\tGameover!\n\n" << endl;
snake.display();//输出等级/得分情况
cout << "\n\n\n\t\t 是否选择继续游戏?输入 y 继续,n 退出" << endl;
cin >> ctn;
}
return 0;
}
==================================================================
c++实现②:
更加完善的功能和界面显示,增加游戏记录功能等
snake.h
#ifndef SNAKE
#define SNAKE
#include
#include
#include
//标记界面和菜单项的ID
enum ID_ { ID_1 = 1, ID_2, ID_3, ID_4, ID_5, ID_6, ID_7 };
//标记初级,中级,高级三种游戏等级
enum Rank_ { first = 150, middle = 100, high = 50 };
//贪吃蛇结构
struct Snake_data{
bool state; //标记蛇状态
UINT Snake_size; //标记蛇长度
std::vector Snake_coord; //蛇的当前坐标
COORD food_coord; //食物坐标
UINT rank; //标记等级
UINT mark; //分数
UINT key; //记录按键方向
ID_ ID_interface; //标记当前停留界面ID
short ID_option; //标记被选中菜单的ID
bool Flag; //标记刷新
};
//贪吃蛇类
class Snake_class{
public:
Snake_class(){}
Snake_class(Snake_data *data) : pSnake(data){}
void ShowMenu(); //显示菜单
void StartGame(); //开始游戏
void ShowPause(); //显示暂停界面
void SaveRecord(); //保存记录
void ShowRank(); //显示等级
void ShowRecord(); //显示存档记录列表
bool Read(size_t); //读取记录
void ShowArmory(); //显示英雄榜
void Die(); //死亡界面
size_t Create_file(); //存储文件
private:
void JudgeDie(); //判断死亡
bool Jesus(); //上榜判断
void ShowSnake(COORD&); //显示蛇
void ShowPointer(COORD, std::string); //显示指针
void Show_not(); //显示无数据
Snake_data *pSnake;
};
#endif;
snake.cpp
#include"SnakeNew.h"
#include
#include
#include
#include
//获取缓冲区句柄
static const HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
static CONSOLE_SCREEN_BUFFER_INFO info;
//保存蛇的记录数据文件
static const char *Snake_Record[] = { "d://SnakeRecord//Snake1.txt",
"d://SnakeRecord//Snake2.txt",
"d://SnakeRecord//Snake3.txt",
"d://SnakeRecord//Snake4.txt",
"d://SnakeRecord//Snake5.txt" };
static const char *S_Armory[] = { "d://SnakeRecord//Armory1.txt",
"d://SnakeRecord//Armory2.txt",
"d://SnakeRecord//Armory3.txt" };
//显示主菜单(完成已测试)
void Snake_class::ShowMenu(){
//获取缓冲区相关信息
GetConsoleScreenBufferInfo(handle, &info);
char *str[] = { "开 始 游 戏",
"难 度 等 级",
"读 取 存 档",
"英 雄 榜",
"退 出 游 戏" };
//输出菜单选项
short y = 3;
COORD cd = { info.srWindow.Right / 2 - 5, y };
for (size_t n = 0; n < sizeof(str) / sizeof(*str); ++n){
SetConsoleCursorPosition(handle, cd);
std::cout << *(str + n);
cd.Y += 2;
}
//判断指针指向哪个菜单选项
cd.X -= 2;
cd.Y = y;
switch (pSnake->ID_option){
case ID_1:
break;
case ID_2:
cd.Y = y + 2; break;
case ID_3:
cd.Y = y + 4; break;
case ID_4:
cd.Y = y + 6; break;
case ID_5:
cd.Y = y + 8; break;
}
ShowPointer(cd, pSnake->Flag ? std::string(" ") : std::string("><"));
}
//开始游戏(完成待测试)
void Snake_class::StartGame(){
COORD cd;
//判断是否已有食物
if (!pSnake->food_coord.X){
srand((unsigned int)time(NULL));
while (1){
//限制食物出现的坐标不与围墙相同
++(cd.X = rand() % 78);
++(cd.Y = rand() % 18);
//判断食物坐标是否与蛇身吻合
std::vector::iterator ite;
for (ite = pSnake->Snake_coord.begin(); ite != pSnake->Snake_coord.end(); ++ite){
if (ite->X == cd.X && ite->Y == cd.Y)
break;
}
if (ite == pSnake->Snake_coord.end()){
pSnake->food_coord.X = cd.X;
pSnake->food_coord.Y = cd.Y;
break;
}
}
}
SetConsoleCursorPosition(handle, pSnake->food_coord);
std::cout << "*";
//判定按键方向
cd.X = pSnake->Snake_coord.begin()->X;
cd.Y = pSnake->Snake_coord.begin()->Y;
switch (pSnake->key){
case VK_UP:
--cd.Y; break;
case VK_DOWN:
++cd.Y; break;
case VK_LEFT:
--cd.X; break;
case VK_RIGHT:
++cd.X; break;
}
ShowSnake(cd);
JudgeDie();
}
//显示暂停界面(完成已测试)
void Snake_class::ShowPause(){
COORD cd = { info.srWindow.Right / 2 - 10, info.srWindow.Bottom / 5 };
SetConsoleCursorPosition(handle, cd);
std::cout << "游 戏 暂 停 中 ......";
char *str[] = { "继 续 游 戏",
"保 存 游 戏",
"退 出 游 戏" };
//输出菜单选项
short X = info.srWindow.Right / 3;
cd.X = X / 2 - 5;
cd.Y = info.srWindow.Bottom / 3 * 2 - 4;
for (size_t i = 0; i < sizeof(str) / sizeof(*str); ++i){
SetConsoleCursorPosition(handle, cd);
std::cout << *(str + i);
cd.X += X;
}
//判断指针应指向的菜单选项
switch (pSnake->ID_option){
case ID_1:
cd.X = X / 2 - 7; break;
case ID_2:
cd.X = X / 2 + X - 7; break;
case ID_3:
cd.X = X / 2 + 2 * X - 7; break;
}
ShowPointer(cd, pSnake->Flag ? std::string(" ") : std::string("><"));
}
//保存记录(完成未测试)
void Snake_class::SaveRecord(){
std::ofstream outopen;
outopen.open(*(Snake_Record + Create_file()));
if (!outopen){
std::cerr << "\n打开文件失败!\n";
exit(0);
}
//保存记录到文件中,前面加"0"是为了后面检测是否文件为空使用
outopen << "0 " << pSnake->Snake_size << " ";
for (std::vector::iterator ite = pSnake->Snake_coord.begin();
ite != pSnake->Snake_coord.end(); ++ite)
outopen << ite->X << " " << ite->Y << " ";
outopen << pSnake->rank << " " << pSnake->mark << " " << pSnake->key;
outopen.close();
//输出保存成功
COORD cd = { info.srWindow.Right / 2 - 4, info.srWindow.Bottom / 3 * 2 - 1 };
SetConsoleCursorPosition(handle, cd);
std::cout << "保存成功!\a";
}
//显示等级(已测试)
void Snake_class::ShowRank(){
COORD cd = { info.srWindow.Right / 2 - 6, info.srWindow.Bottom / 3 + 2 };
char *str[] = { "初 级",
"中 级",
"高 级" };
for (size_t i = 0; i < sizeof(str) / sizeof(*str); ++i){
SetConsoleCursorPosition(handle, cd);
std::cout << *(str + i);
cd.Y += 2;
}
//判断指针所停留的选项
cd.X -= 2;
cd.Y = info.srWindow.Bottom / 3 + 2;
switch (pSnake->ID_option){
case ID_1:
break;
case ID_2:
cd.Y += 2; break;
case ID_3:
cd.Y += 4; break;
}
ShowPointer(cd, pSnake->Flag ? std::string(" ") : std::string("><"));
}
//显示存档记录(已测试)
void Snake_class::ShowRecord(){
COORD cd = { info.srWindow.Right / 2 - 12, 8 };
//输出记录
std::ifstream inopen;
SetConsoleCursorPosition(handle, cd);
for (size_t i = 0; i < sizeof(Snake_Record) / sizeof(*Snake_Record); ++i){
inopen.open(*(Snake_Record + i));
if (!inopen || (inopen.get() == EOF && i == 0)){
Show_not();
pSnake->ID_option = ID_7;//第7个选项标记,在按回车时检测下
return;
}
if (inopen.get() != EOF){
UINT _rank, _mark;
inopen >> _mark;
++(_mark *= 2);
while (_mark--)
inopen >> _rank;
inopen >> _mark;
switch (_rank){
case first:
std::cout << "初级"; break;
case middle:
std::cout << "中级"; break;
case high:
std::cout << "高级"; break;
}
std::cout << "\t\t\t " << _mark;
}
else
std::cout << " ---\t\t\t ---";
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
inopen.close();
inopen.clear();//重置流状态
}
std::cout << "\t 返 回 菜 单";
cd.X = info.srWindow.Right / 2 - 4;
cd.Y = 4;
SetConsoleCursorPosition(handle, cd);
std::cout << "存 档 记 录";
cd.X -= 10;
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "游戏等级\t\t当前分数";
//输出指针
cd.X = info.srWindow.Right / 2 - 14;
cd.Y = 8;
switch (pSnake->ID_option){
case ID_1:
break;
case ID_2:
cd.Y += 2; break;
case ID_3:
cd.Y += 4; break;
case ID_4:
cd.Y += 6; break;
case ID_5:
cd.Y += 8; break;
case ID_6:
cd.Y += 10; break;
}
ShowPointer(cd, pSnake->Flag ? std::string(" ") : std::string("><"));
}
//读取记录
bool Snake_class::Read(size_t i){
std::ifstream inopen(*(Snake_Record + i));
if (inopen.get() == EOF){
std::cout << "\a";
inopen.close();
return false;
}
inopen >> pSnake->Snake_size;
COORD cd;
pSnake->Snake_coord.clear();
for (int n = 1; n <= pSnake->Snake_size; ++n){
inopen >> cd.X >> cd.Y;
pSnake->Snake_coord.push_back(cd);
}
inopen >> pSnake->rank >> pSnake->mark >> pSnake->key;
inopen.close();
inopen.clear();
return true;
}
//显示英雄榜(未测试)
void Snake_class::ShowArmory(){
short nt = 0;
COORD cd = { info.srWindow.Right / 3, info.srWindow.Bottom / 3 };
std::ifstream inopen;
for (size_t i = 0; i < sizeof(S_Armory) / sizeof(*S_Armory); ++i){
UINT _rank = 0, _mark = 0;
inopen.open(*(S_Armory + i));
if (!inopen){
++nt;
continue;
}
inopen >> _rank >> _mark;
switch (_rank){
case first:
SetConsoleCursorPosition(handle, cd);
std::cout << "小牛 :初级\t\t " << _mark;
break;
case middle:
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "中牛 :中级\t\t " << _mark;
break;
case high:
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "大牛 :高级\t\t " << _mark;
break;
}
inopen.close();
inopen.clear();
}
if (nt == 3){
Show_not();
return;
}
cd.X = info.srWindow.Right / 2 - 3;
cd.Y = 4;
SetConsoleCursorPosition(handle, cd);
std::cout << "英 雄 榜";
cd.X -= 10;
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "\t等 级\t\t分 数";
cd.Y = info.srWindow.Bottom - 7;
SetConsoleCursorPosition(handle, cd);
std::cout << "按回车返回主菜单........";
}
//死亡界面(未测试)
void Snake_class::Die(){
COORD cd = { info.srWindow.Right / 2 - 10, info.srWindow.Bottom / 5 };
SetConsoleCursorPosition(handle, cd);
std::cout << " 您 已 out 了 !";
char *str[] = { "返 回 菜 单",
"退 出 游 戏" };
//输出菜单选项
short X = info.srWindow.Right / 2;
cd.X = X / 2 - 5;
cd.Y = info.srWindow.Bottom / 3 * 2 - 4;
for (size_t i = 0; i < sizeof(str) / sizeof(*str); ++i){
SetConsoleCursorPosition(handle, cd);
std::cout << *(str + i);
cd.X += X;
}
//判断指针应指向的菜单选项
switch (pSnake->ID_option){
case ID_1:
cd.X = X / 2 - 7; break;
case ID_2:
cd.X = X / 2 + X - 7; break;
}
ShowPointer(cd, pSnake->Flag ? std::string(" ") : std::string("><"));
if (Jesus()){
cd.X = X / 2;
cd.Y = info.srWindow.Bottom / 3;
SetConsoleCursorPosition(handle, cd);
std::cout << "哟...这分? ╮(╯▽╰)╭ 也能上榜。。。!";
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "上榜等级:";
switch (pSnake->rank){
case first:
std::cout << "初级"; break;
case middle:
std::cout << "中级"; break;
case high:
std::cout << "高级"; break;
}
std::cout << "\t上榜分数:" << pSnake->mark;
}
}
//存储记录文件(完成未测试)
size_t Snake_class::Create_file(){
std::ifstream inopen;
size_t fn = 0, fc = 0, iend = sizeof(Snake_Record) / sizeof(*Snake_Record);
//判断文件是否存在或文件已被存满
for (size_t i = 0; i < iend; ++i){
inopen.open(*(Snake_Record + i));
if (!inopen) ++fn;
else if (inopen.get() == EOF){
inopen.close();
return i;
}
else { ++fc; inopen.close(); }
}
if (fn == iend || fc == iend){
std::ofstream outopen;
//创建文本
for (size_t i = 0; i < iend; ++i){
outopen.open(*(Snake_Record + i));
outopen.close();
}
}
//返回打开成功的文件索引
return 0;
}
//判断死亡(未测试)
void Snake_class::JudgeDie(){
std::vector::iterator hbeg = pSnake->Snake_coord.begin(),
beg = hbeg + 1;
while (beg != pSnake->Snake_coord.end()){
if (beg->X == hbeg->X && beg->Y == hbeg->Y){
pSnake->state = FALSE;
return;
}
++beg;
}
COORD cd;
if (hbeg->X <= 0 || hbeg->Y <= 0 ||
hbeg->X >= info.srWindow.Right || hbeg->Y >= info.srWindow.Bottom - 5){
if (pSnake->Snake_size < 40){
pSnake->state = FALSE;
return;
}
//如果达到了40级可以穿墙
switch (pSnake->key){
case VK_UP:
cd.X = pSnake->Snake_coord.begin()->X;
cd.Y = info.srWindow.Bottom - 6;
break;
case VK_DOWN:
cd.X = pSnake->Snake_coord.begin()->X;
cd.Y = 1;
break;
case VK_LEFT:
cd.X = info.srWindow.Right - 1;
cd.Y = pSnake->Snake_coord.begin()->Y;
break;
case VK_RIGHT:
cd.X = 1;
cd.Y = pSnake->Snake_coord.begin()->Y;
break;
}
ShowSnake(cd);
}
}
//上榜判断(未测试)
bool Snake_class::Jesus(){
std::ifstream inopen;
size_t i;
//判断相应等级打开相应文件
switch (pSnake->rank){
case first:
i = 0; break;
case middle:
i = 1; break;
case high:
i = 2; break;
}
inopen.open(*(S_Armory + i));
if (inopen){
UINT _mark;
inopen >> _mark >> _mark;
if (_mark >= pSnake->mark){
inopen.close();
return FALSE;
}
}
std::ofstream outopen(*(S_Armory + i));//创建文件并输入
if (!outopen){
std::cerr << "打开英雄榜文件出错" << std::endl;
exit(0);
}
outopen << pSnake->rank << " " << pSnake->mark;
outopen.close();
return TRUE;
}
//显示蛇(未测试)
void Snake_class::ShowSnake(COORD& cd){
if (cd.X == pSnake->food_coord.X && cd.Y == pSnake->food_coord.Y){
//在最后添加一个坐标
pSnake->Snake_coord.push_back(*(pSnake->Snake_coord.rbegin()));
pSnake->food_coord.X = pSnake->food_coord.Y = 0;//标志食物已被吃掉
++pSnake->mark;
++pSnake->Snake_size;
}
COORD cod;
cod.X = (pSnake->Snake_coord.rbegin())->X;
cod.Y = (pSnake->Snake_coord.rbegin())->Y;
std::vector::reverse_iterator rbeg = pSnake->Snake_coord.rbegin();
while (rbeg != pSnake->Snake_coord.rend() - 1){
rbeg->X = (rbeg + 1)->X;
rbeg->Y = (rbeg + 1)->Y;
++rbeg;
}
//显示蛇
pSnake->Snake_coord.begin()->X = cd.X;
pSnake->Snake_coord.begin()->Y = cd.Y;
for (std::vector::iterator beg = pSnake->Snake_coord.begin();
beg != pSnake->Snake_coord.end(); ++beg){
SetConsoleCursorPosition(handle, *beg);
std::cout << "*";
}
SetConsoleCursorPosition(handle, cod);
std::cout << " ";
}
//显示指针(完成)
inline void Snake_class::ShowPointer(COORD cd, std::string str){
SetConsoleCursorPosition(handle, cd);
std::cout << str[0];
pSnake->ID_interface != ID_6 ? cd.X += 14 : cd.X = info.srWindow.Right / 3 * 2 + 3;
SetConsoleCursorPosition(handle, cd);
std::cout << str[1];
}
//显示无数据(完成)
inline void Snake_class::Show_not(){
COORD cd = { info.srWindow.Right / 2 - 4, info.srWindow.Bottom / 2 };
SetConsoleCursorPosition(handle, cd);
std::cout << "无 数 据 !";
cd.X -= 6;
cd.Y += 2;
SetConsoleCursorPosition(handle, cd);
std::cout << "请按回车返回主菜单......";
}
main.h
// main.cpp : 定义控制台应用程序的入口点。
//
#include
#include
#include"SnakeNew.h"
#define _max(ID) --snake.ID_option < ID_1?ID : snake.ID_option
#define _min(ID) ++snake.ID_option > ID?ID_1 : snake.ID_option
//钩子过程
LRESULT CALLBACK _HookProc(int, WPARAM, LPARAM);
void Wall(); //画墙函数
void Frame(); //画框函数
void initialize_();//初始化蛇结构数据
Snake_data snake;
Snake_class csnake(&snake);
static HANDLE handle; //控制台句柄
//控制台信息结构
static CONSOLE_SCREEN_BUFFER_INFO info;
int main(){
using namespace std;
//获取输出缓冲区句柄
handle = GetStdHandle(STD_OUTPUT_HANDLE);
//获取相关信息(主要是缓冲区大小)
GetConsoleScreenBufferInfo(handle, &info);
initialize_();
_mkdir("d://SnakeRecord"); //创建目录
CONSOLE_CURSOR_INFO cursor; //光标结构
cursor.dwSize = 10;
cursor.bVisible = 0; //0为隐藏光标
SetConsoleCursorInfo(handle, &cursor);//设置隐藏光标函数
//csnake.ShowMenu();
HHOOK hook;
MSG msg;
//相应的钩子有相应的过程函数MSDN中可看,安装键盘钩子
hook = SetWindowsHookEx(WH_KEYBOARD_LL, _HookProc, GetModuleHandle(NULL), 0);
while (1){
//判断蛇是否还活着
Wall();
if (!snake.state && snake.ID_interface == ID_2){
//csnake.Die();
snake.ID_interface = ID_4;
Frame();
}
if (snake.ID_interface == ID_2){
csnake.StartGame();
Sleep(snake.rank);
}
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){//循环接收消息
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
UnhookWindowsHookEx(hook); //卸载钩子
return 0;
}
//定义钩子过程(未完成)
LRESULT CALLBACK _HookProc(int message, WPARAM wParam, LPARAM lParam){
//判断是否弹起按键消息
if (wParam == WM_KEYUP){
bool state = true;
switch (((LPKBDLLHOOKSTRUCT)lParam)->vkCode){//lParam:指向一个结构
//按下上键
case VK_UP:
switch (snake.ID_interface){
case ID_1:
snake.Flag = true;
csnake.ShowMenu();
snake.Flag = false;
snake.ID_option = _max(ID_5);
csnake.ShowMenu();
break;
case ID_2:
if (snake.key != VK_DOWN && snake.key != VK_UP){
snake.key = VK_UP;
}
break;
case ID_5:
snake.Flag = true;
csnake.ShowRank();
snake.ID_option = _max(ID_3);
snake.Flag = false;
csnake.ShowRank();
break;
case ID_6:
snake.Flag = true;
csnake.ShowRecord();
snake.ID_option = _max(ID_6);
snake.Flag = false;
csnake.ShowRecord();
break;
}
break;
//按下下键
case VK_DOWN:
switch (snake.ID_interface){
case ID_1:
snake.Flag = true;
csnake.ShowMenu();
snake.Flag = false;
snake.ID_option = _min(ID_5);
csnake.ShowMenu();
break;
case ID_2:
if (snake.key != VK_UP && snake.key != VK_DOWN){
snake.key = VK_DOWN;
}
break;
case ID_5:
snake.Flag = true;
csnake.ShowRank();
snake.ID_option = _min(ID_3);
snake.Flag = false;
csnake.ShowRank();
break;
case ID_6:
snake.Flag = true;
csnake.ShowRecord();
snake.ID_option = _min(ID_6);
snake.Flag = false;
csnake.ShowRecord();
break;
}
break;
//按下左键
case VK_LEFT:
switch (snake.ID_interface){
case ID_2:
if (snake.key != VK_RIGHT && snake.key != VK_LEFT){
snake.key = VK_LEFT;
}
break;
case ID_3:
snake.Flag = true;
csnake.ShowPause();
snake.ID_option = _max(ID_3);
snake.Flag = false;
csnake.ShowPause();
break;
case ID_4:
snake.Flag = true;
csnake.Die();
snake.ID_option = _max(ID_2);
snake.Flag = false;
csnake.Die();
break;
}
break;
//按下右键
case VK_RIGHT:
switch (snake.ID_interface){
case ID_2:
if (snake.key != VK_LEFT && snake.key != VK_RIGHT){
snake.key = VK_RIGHT;
}
break;
case ID_3:
snake.Flag = true;
csnake.ShowPause();
snake.ID_option = _min(ID_3);
snake.Flag = false;
csnake.ShowPause();
break;
case ID_4:
snake.Flag = true;
csnake.Die();
snake.ID_option = _min(ID_2);
snake.Flag = false;
csnake.Die();
break;
}
break;
//按下空格和回车键
case VK_SPACE:
case VK_RETURN:
system("cls");
switch (snake.ID_interface){
case ID_1://主菜单界面
switch (snake.ID_option){
case ID_1:
snake.ID_interface = ID_2;//开始游戏界面ID
csnake.StartGame();
break;
case ID_2:
snake.ID_interface = ID_5;//等级界面
snake.ID_option = ID_1;
Frame();
//csnake.ShowRank();
break;
case ID_3:
snake.ID_interface = ID_6;//读取记录界面
snake.ID_option = ID_1;
csnake.ShowRecord();
break;
case ID_4:
snake.ID_interface = ID_7;//英雄榜界面
csnake.ShowArmory();
break;
case ID_5:
state = false;
break;
}
break;
case ID_2://游戏界面
snake.ID_interface = ID_3;
snake.ID_option = ID_1;
Frame();
//csnake.ShowPause();
break;
case ID_3://暂停界面
switch (snake.ID_option){
case ID_1:
snake.ID_interface = ID_2;
csnake.StartGame();
break;
case ID_2:
csnake.SaveRecord();
break;
case ID_3:
state = false;
break;
}
break;
case ID_4://死亡界面
switch (snake.ID_option){
case ID_1:
snake.ID_interface = ID_1;
snake.ID_option = ID_1;
initialize_();
csnake.ShowMenu();
break;
case ID_2:
state = false;
break;
}
break;
case ID_5://等级界面
switch (snake.ID_option){
case ID_1:
snake.rank = first;
break;
case ID_2:
snake.rank = middle;
break;
case ID_3:
snake.rank = high;
break;
}
snake.ID_interface = ID_1;
snake.ID_option = ID_1;
csnake.ShowMenu();
break;
case ID_6://读取界面
size_t id;
switch (snake.ID_option){
case ID_1:
id = 0; break;
case ID_2:
id = 1; break;
case ID_3:
id = 2; break;
case ID_4:
id = 3; break;
case ID_5:
id = 4; break;
}
//进行读取文件
if (snake.ID_option != ID_6&&snake.ID_option != ID_7){
initialize_();
if (csnake.Read(id)){
snake.ID_interface = ID_2;
csnake.StartGame();
}
else snake.ID_interface = ID_6;
}
else {
snake.ID_interface = ID_1;
snake.ID_option = ID_1;
csnake.ShowMenu();
}
break;
case ID_7://英雄榜界面
snake.ID_interface = ID_1;
snake.ID_option = ID_1;
csnake.ShowMenu();
break;
}
if (!state){
COORD cd = { info.srWindow.Right / 4, info.srWindow.Bottom / 5 * 4 };
SetConsoleCursorPosition(handle, cd);
exit(0);
}
snake.ID_option = ID_1;
break;
}
}
//消息传给下个钩子
return CallNextHookEx(NULL, message, wParam, lParam);
}
//画墙函数和调用相应界面(已测试完成)
void Wall(){
short i;
COORD coord = { 0, 0 }; //坐标结构
//绘墙
SetConsoleCursorPosition(handle, coord);
for (i = 0; i <= info.srWindow.Right; ++i)
std::cout << "#";
coord.X = info.srWindow.Right;
coord.Y = 1;
for (i = 1; i < info.srWindow.Bottom; ++i){
SetConsoleCursorPosition(handle, coord);
std::cout << "#";
++coord.Y;
}
coord.X = 0;
coord.Y = info.srWindow.Bottom;
for (i = 1; i <= info.srWindow.Right; ++i){
SetConsoleCursorPosition(handle, coord);
std::cout << "#";
++coord.X;
}
coord.X = 0;
coord.Y = 1;
for (i = 1; i < info.srWindow.Bottom; ++i){
SetConsoleCursorPosition(handle, coord);
std::cout << "#";
++coord.Y;
}
//判断所在界面显示相关界面的菜单选项及其它
int j = info.srWindow.Right / 4;
switch (snake.ID_interface){
case ID_1:
csnake.ShowMenu();
coord.X = j;
coord.Y = info.srWindow.Bottom - 6;
SetConsoleCursorPosition(handle, coord);
std::cout << "o(-\"-)o 贪 #^_^# 吃 →_→ 蛇 \\(^o^)/";
break;//显示菜单选项
case ID_2:
case ID_3:
case ID_4:
//绘输出信息框
coord.X = 1;
coord.Y = info.srWindow.Bottom - 5;
SetConsoleCursorPosition(handle, coord);
for (i = 1; i < info.srWindow.Right; std::cout << "#", ++i);
for (coord.X = j; coord.X <= info.srWindow.Right - j; coord.X += j){
coord.Y = info.srWindow.Bottom - 5;
for (i = coord.Y; i < info.srWindow.Bottom; ++i){
SetConsoleCursorPosition(handle, coord);
std::cout << "#";
++coord.Y;
}
}
//输出每个框的信息
coord.X = 2;
coord.Y -= 4;
SetConsoleCursorPosition(handle, coord);
std::cout << "游戏难度:";
coord.Y += 2;
SetConsoleCursorPosition(handle, coord);
std::cout << "\t ";
switch (snake.rank){
case first:
std::cout << "初 级"; break;
case middle:
std::cout << "中 级"; break;
case high:
std::cout << "高 级"; break;
}
//当前分数
coord.X += j;
coord.Y -= 2;
SetConsoleCursorPosition(handle, coord);
std::cout << " 当 前 分 数";
coord.X += j / 2 - 3;
coord.Y += 2;
SetConsoleCursorPosition(handle, coord);
std::cout << snake.mark;
//操作说明
coord.X = j * 2 + 1;
coord.Y = info.srWindow.Bottom - 4;
SetConsoleCursorPosition(handle, coord);
std::cout << " 操作说明: ";
coord.Y += 2;
SetConsoleCursorPosition(handle, coord);
std::cout << " ↑ ↓ ← →";
++coord.Y;
SetConsoleCursorPosition(handle, coord);
std::cout << " 空格: 暂停游戏";
//制作人
coord.X += j;
SetConsoleCursorPosition(handle, coord);
std::cout << "\t";
break;
case ID_5:
csnake.ShowRank(); break;//显示游戏等级
case ID_6:
csnake.ShowRecord(); break;//显示存档记录
case ID_7:
csnake.ShowArmory(); break;//显示英雄榜
}
}
//画框函数(完成)
void Frame(){
COORD coord = { 0, info.srWindow.Bottom / 3 };
SetConsoleCursorPosition(handle, coord);
for (short i = 0; i <= info.srWindow.Right; std::cout << "-", ++i);
coord.Y = info.srWindow.Bottom / 3 * 2;
SetConsoleCursorPosition(handle, coord);
for (short i = 0; i <= info.srWindow.Right; std::cout << "-", ++i);
switch (snake.ID_interface){
case ID_3:
csnake.ShowPause(); break;//显示暂停菜单
case ID_4:
csnake.Die(); break;//显示死亡界面菜单
case ID_5:
csnake.ShowRank(); break;//显示等级选项
}
}
//初始化蛇数据(完成)
void initialize_(){
snake.state = true; //蛇状态
snake.Snake_size = 5; //蛇初始化长度
//初始化蛇位置
COORD cd;
cd.Y = 3;
snake.Snake_coord.clear();
for (size_t i = 10; i > 5; --i){
cd.X = i;
snake.Snake_coord.push_back(cd);
}
snake.food_coord.X = 0;
snake.food_coord.Y = 0;
snake.rank = first; //默认等级
snake.mark = 0; //分数
snake.key = VK_RIGHT;
snake.ID_interface = ID_1;//界面标记
snake.ID_option = ID_1; //选项标记
snake.Flag = false;
}
参考:
https://blog.csdn.net/shawn_hou/article/details/27734217
http://www.cnblogs.com/sosoft/p/3508072.html
https://blog.csdn.net/mactavish141/article/details/78878280