wall.h 文件:
#pragma once
#include <iostream>
using namespace std;
// 创建游戏墙壁的类
class Wall
{
public:
// 利用枚举设置墙的宽和高
enum
{
ROW = 26,
COL = 26
};
// 声明初始化墙壁函数
void InitWall();
// 声明设置当前所在位置的符号的函数
void setWall(int x, int y, char c); //参数1:x轴坐标,参数2:y轴坐标,参数3:要设置的符号
// 声明获取当前位置的符号 要返回当前所在位置的符号
char getWall(int x, int y); //参数1:x轴坐标,参数2:y轴坐标
// 声明 画墙壁 函数
void drawWall();
private:
// 声明游戏的二维数组
char gameArray[ROW][COL];
};
wall.cpp 文件 :
#include "wall.h"
// 初始化游戏的墙壁
void Wall::InitWall()
{
// 利用二维数组初始化游戏墙壁
// 给游戏的二维数组初始化
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
// 设置墙壁
if (i == 0 || j == 0 || i == ROW - 1 || j == COL - 1)
{
// 将该位置设置成墙壁
gameArray[i][j] = '*';
}
else
{
// 将该位置设置成空格
gameArray[i][j] = ' ';
}
}
}
}
// 设置当前所在位置的符号
void Wall::setWall(int x, int y, char c)
{
// 设置当前位置的符号
gameArray[x][y] = c;
}
// 获取当前所在位置的符号
char Wall::getWall(int x, int y)
{
// 直接返回当前所在的位置
return gameArray[x][y];
}
// 画游戏墙壁
void Wall::drawWall()
{
// 给游戏的二维数组初始化
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
// 输出二维数组
cout << gameArray[i][j] << ' ';
}
// 显示一些提示
if (i == 9)
{
cout << "Welcome game";
}
if (i == 11)
{
cout << "a : ←";
}
if (i == 13)
{
cout << "s : ↓";
}
if (i == 15)
{
cout << "d : →";
}
if (i == 17)
{
cout << "w : ↑";
}
cout << endl;
}
}
food.h 文件:
#pragma once
#include <iostream>
#include "wall.h"
using namespace std;
// 创建食物的类
class Food
{
public:
// 构造函数
Food(Wall & tempWall);
// 初始化食物
void InitFood();
public:
// 维护住 Wall 类
Wall& wall; // 注意要用引用的方式
private:
// 声明食物的坐标
int FoodX; // x轴坐标
int FoodY; // y轴坐标
};
food.cpp 文件:
#include "food.h"
#include <windows.h>
void gotoxy2(HANDLE hOut2, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut2, pos);
}//光标定位函数
HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);
// 构造函数 用初始化列表的方式给墙类的对象初始化
Food::Food(Wall& tempWall) : wall(tempWall)
{
// 给食物的坐标初始化
this->FoodX = rand() % (Wall::ROW - 1) + 1;
this->FoodY = rand() % (Wall::COL - 1) + 1;
}
// 初始化食物
void Food::InitFood()
{
// 添加随机函数种子
srand((unsigned int)time(NULL));
// 循环生成食物 当成功生成食物则退出循环
while (1)
{
// 给食物的坐标 生成随机数
this->FoodX = rand() % (Wall::ROW - 1) + 1;
this->FoodY = rand() % (Wall::COL - 1) + 1;
// 设置食物不产生在蛇的身上,或是墙上
if (wall.getWall(this->FoodX, this->FoodY) == ' ')
{
// 调用Wall 类的成员函数 setWall设置食物
wall.setWall(this->FoodX, this->FoodY, '#');
gotoxy2(hOut2, this->FoodY * 2, this->FoodX);
cout << '#';
// 成功生成食物则退出循环
break;
}
}
}
snake.h 文件:
#pragma once
#include <iostream>
#include "wall.h"
#include "food.h"
using namespace std;
// 创建蛇的类
class Snake
{
public:
// 构造函数
Snake(Wall & tempWall, Food &tempFood);
public:
// 枚举移动方向
enum
{
LEFT = 'a', // 向左
DOWN = 's', // 向下
RIGHT = 'd', // 向右
UP = 'w' // 向上
};
// 声明获取分数变量
int score;
// 声明速度变量
int speed;
// 创建蛇的节点结构体
struct Node
{
int x, // 蛇的横坐标
y; // 蛇的纵坐标
Node* pNext; //指向下一个节点的指针
};
// 声明 初始化蛇 函数
void InitSnake();
// 添加节点
void addNode(int x, int y);
// 销毁节点
void destroyNode();
// 删除节点
void delNode();
// 声明蛇的移动 函数, 注意这里是要判断是否移动成功
bool SnakeMove(char key);
public:
// 维护住 Wall 类
Wall& wall; //注意这要用引用的方式创建类的对象
// 维护住 Food 类
Food& food; // 注意要用引用的方式创建类的对对象
private:
// 创建头节点指针
Node* pHead;
};
snake.cpp 文件:
#include "snake.h"
#include <windows.h>
void gotoxy1(HANDLE hOut1, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut1, pos);
}//光标定位函数
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);
// 构造函数 利用初始化列表的方式给Wall 类的对象, Food的对象 初始化
Snake::Snake(Wall& tempWall, Food& tempFood) : wall(tempWall), food(tempFood)
{
// 给头节点指针初始化
this->pHead = NULL;
// 初始化得分
this->score = 0;
// 初始化速度
this->speed = 300;
}
// 初始化蛇
void Snake::InitSnake()
{
// 保证初始时候是没有节点的
destroyNode();
// 添加节点,初始化蛇
addNode(20, 10);
addNode(20, 11);
addNode(20, 12);
}
// 添加节点
void Snake::addNode(int x, int y)
{
// 创建新节点 将新节点插入到链表中
Node* pTemp = new Node;
// 给节点成员赋值
pTemp->x = x;
pTemp->y = y;
pTemp->pNext = NULL;
if (NULL != pHead)
{
//
//pTemp->pNext = pHead;
// 调用 Wall 类的设置当前位置函数,将头节点的位置设置成 =
wall.setWall(pHead->x, pHead->y, '=');
gotoxy1(hOut1, pHead->y * 2, pHead->x); // 更新光标位置
cout << '=';
}
// 不管头是否为空,都将新节点赋给头节点
//pHead = pTemp;
// 新来的节点指向头
pTemp->pNext = pHead;
// 头节点前移
pHead = pTemp;
// 重置头节点所在位置的符号
wall.setWall(pHead->x, pHead->y, '@');
gotoxy1(hOut1, pHead->y * 2, pHead->x);
cout << '@';
}
// 销毁节点 注意这里是要释放所有的节点
void Snake::destroyNode()
{
// 释放所有的节点
// 记录头节点
Node* temp = pHead;
// 遍历
while (NULL != pHead)
{
// 抠出头节点
temp = pHead->pNext;
// 将头节点的位置设置成 空格
wall.setWall(pHead->x, pHead->y, ' ');
// 释放头节点
delete pHead;
// 将头节点置空
pHead = NULL;
// 给头节点重新赋值
pHead = temp;
}
}
// 删除节点 尾删除
void Snake::delNode()
{
// 注意这里要有两个节点及以上节点才进行尾删除
if (NULL == pHead || NULL == pHead->pNext)
return;
#if 0
// 尾删除方法一:
// 记录头
Node* temp = pHead;
// 遍历找尾节点
while (NULL != temp->pNext)
{
// 节点前移
temp = temp->pNext;
}
// 将尾节点所在的位置符号设置空格
wall.setWall(temp->x, temp->y, ' ');
gotoxy1(hOut1, temp->y * 2, temp->x);
cout << ' ';
// 释放
delete temp;
// 将尾节点置成空
temp = NULL;
#else
// 尾删除方法二:
// 双指针法,遍历
Node* pPnext = pHead->pNext; // 相对于蛇头的下一个节点, 要找的尾节点
Node* pPre = pHead; // 蛇头的节点,要找的尾节点的前一个节点
// 遍历
while (NULL != pPnext->pNext)
{
// 节点前移
pPre = pPre->pNext;
pPnext = pPnext->pNext;
}
// 找到pPnext就是尾节点
// 将尾节点设置所在的位置符号设置成空格
wall.setWall(pPnext->x, pPnext->y, ' ');
gotoxy1(hOut1, pPnext->y * 2, pPnext->x);
cout << ' ';
// 删除尾节点
delete pPnext;
// 将尾节点置空
pPnext = NULL;
// 将尾节点的前一个节点指向下一个节点的指针赋空
pPre->pNext = NULL;
#endif
}
// 处理蛇的移动
bool Snake::SnakeMove(char key)
{
// 获取蛇头的坐标
int x = pHead->x; // 获取蛇头的x轴坐标
int y = pHead->y; // 获取蛇头的y轴坐标
// 分类处理 用户按下的方向键
switch (key)
{
case LEFT: // 向左移
y--;
break;
case RIGHT: // 向右移
y++;
break;
case UP: // 向上移
x--;
break;
case DOWN: // 向下移
x++;
break;
default:
break;
}
// 判断是否继续游戏 注意一定要向进行判断是否撞墙或是吃到蛇身,
//要不然成功移动蛇之后,以增加节点,初始坐标就变了,即使蛇撞到墙,当前蛇头就与墙的坐标不对等了
if (wall.getWall(x, y) == '*' || wall.getWall(x, y) == '=')
{
gotoxy1(hOut1, Wall::COL*2, Wall::ROW - 5);
// 蛇撞墙了,结束游戏
cout << "Game Over!!!";
return false;
}
// 判断蛇是否成功吃到食物
if (wall.getWall(x, y) == '#')
{
// 增加节点
addNode(x, y);
// 重新产生食物
food.InitFood();
// 得分+10
score += 10;
// 速度-20
speed -= 20;
}
else
{
// 代表蛇移动成功
// 先增加节点,后删除节点
addNode(x, y); // 增加节点
delNode(); // 删节点
}
// 判断蛇是否移动成功 如果是这样写移动时,吃到食物,会隔一会才显示增长
//if (wall.getWall(x, y) != '#' || wall.getWall(x, y) != '*')
//{
// // 代表蛇移动成功
// // 先增加节点,后删除节点
// addNode(x, y); // 增加节点
// delNode(); // 删节点
//}
return true;
}
game.cpp 文件:
#include <iostream>
#include "wall.h"
#include "snake.h"
#include "food.h"
#include <windows.h>
#include <conio.h>
using namespace std;
void gotoxy(HANDLE hOut, int x, int y)//其中x,y是与正常理解相反的,注意区分
{
COORD pos;
pos.X = x; //横坐标
pos.Y = y; //纵坐标
SetConsoleCursorPosition(hOut, pos);
}//光标定位函数
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量,并且这个只能在每个头文件中单独定义句柄和函数,否则无效
int main()
{
// 创建Wall 类的对象
Wall wall;
// 调用初始化墙壁
wall.InitWall();
// 调用设置墙壁 测试是否成功画d出蛇
wall.drawWall();
// 调用设置墙壁
//wall.drawWall();
// 创建Food 类的对象
Food food(wall);
// 调用初始化食物
food.InitFood();
// 创建Snake 类的对象
Snake snake(wall, food);
// 调用Snake 的初始化成员函数
snake.InitSnake();
// 定义一个标识 判断是否继续游戏
bool isDeath = false;
// 定义一个记录前一次方向键的变量
char preKey = NULL;
gotoxy(hOut, Wall::COL * 2, Wall::ROW - 7);
cout << "得分:" << snake.score;
while (!isDeath)
{
// 定义一个变量接受用户输入反向键
char key = _getch();
do
{
// 禁锢开始游戏时向左移动
if (preKey == NULL && key == Snake::LEFT)
{
// 将preKey 赋给key
continue;
}
// 设置用户按下规定的4个方向键之外的键盘其他键时,将上一次的方向键赋给当前其他的键
if (key == Snake::LEFT || key == Snake::RIGHT || key == Snake::UP || key == Snake::DOWN)
{
// 禁锢当前要移动的方向键和上一次方向键相反
if (key == Snake::LEFT && preKey == Snake::RIGHT ||
key == Snake::RIGHT && preKey == Snake::LEFT ||
key == Snake::UP && preKey == Snake::DOWN ||
key == Snake::DOWN && preKey == Snake::UP)
{
// 将上一次方向键赋给当前要一定的方向键
key = preKey;
}
// 记录上一次的方向键
preKey = key;
// 判断蛇是否成功
if (snake.SnakeMove(key) == true)
{
// 清屏
//system("cls");
// 重新画墙
//wall.drawWall();
// 休眠
Sleep(snake.speed);
gotoxy(hOut, Wall::COL * 2, Wall::ROW - 7);
cout << "得分:" << snake.score;
}
else
{
// 退出
isDeath = true; // 将标志置成 true
gotoxy(hOut, Wall::COL * 2, Wall::ROW - 3);
break;
}
}
else
{
// 将上一次的方向键赋给当前的key
key = preKey;
}
} while (!_kbhit());
}
测试是否成功将所有的节点全部销毁
//snake.destroyNode();
调用设置墙壁 测试是否成功画出蛇
//wall.drawWall();
测试是否成功删尾巴
//snake.delNode();
调用设置墙壁 测试是否成功画出蛇
//wall.drawWall();
system("pause");
return 0;
}
程序运行结果