前面写了贪吃蛇的C语言版本,现在给出C++版本。
两个版本的共同之处:
1、均使用了不带头结点单链表作为蛇的载体
2、食物均采用rand()进行位置设定
不同之处:
1、C语言版本单独建立了一个线程,用来处理用户的输入,C++版本始终是一个线程
2、蛇的移动、更新食物均需要更新界面,C语言采取system(“cls”);实现,C++采用设置光标位置来设置蛇的移动,显得更加流畅
========================================================
本篇涉及到的知识点:
1、多文件编程(类内成员的外部实现)
2、处理键盘输入_kbhit();(如果没有键盘输入则该函数返回0)
3、获取光标的操作(这个代码好像不需要记,随拿随用)
void getoxy1(HANDLE hOut1, int x, int y){
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOut1, pos);//
}
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
4、某一个类的对象作为另一个类的成员变量
class A {//类A
private:
B b;//设类B已经定义,此时B的一个对象b作为类A的成员变量
}
5、单链表的操作
==========================================================
游戏设计思路:
class Wall作为整个游戏的基础,其他类通过操作Wall内部的gameArray数组来实现蛇或食物的显现。
一张图显示三个类(Wall、Food、Snake)之间的关系
=========================================================
class Wall(wall.h wall,.cpp)–负责整体游戏的显现(路径、食物、蛇)
wall.h
#ifndef _WALL_HEAD_
#define _WALL_HEAD_
#include
using namespace std;
class Wall{
public:
enum{
ROW = 26,
COL = 26
};
//初始化墙壁
void initWall();
//画墙壁
void drawWall();
//根据索引设置二维数的内容
void setWall(int x, int y, char c);
//根据索引获取当前位置的符号
char getWall(int x, int y);
private:
char gameArray[ROW][COL];
};
#endif // !_WALL_HEAD_
;
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::drawWall(){
for (int i = 0; i < ROW; ++i){
for (int j = 0; j < COL; ++j){
cout << gameArray[i][j]<<" ";
}
if (i == 5)
cout << "\t制作人:yhz";
if (i == 6)
cout << "\tW 向上";
if (i == 7)
cout << "\tS 向下";
if (i == 8)
cout << "\tA 向左";
if (i == 9)
cout << "\tD 向右";
cout << endl;
}
}
void Wall::setWall(int x, int y, char c){
gameArray[x][y] = c;
}
char Wall::getWall(int x, int y){
return gameArray[x][y];
}
class Snake(snake.h snake.cpp)–蛇节点的删除、增加、移动(吃食与不吃食)
snake.h
#pragma once
#include
#include "wall.h"
#include "food.h"
using namespace std;
class Snake{
public:
enum{
UP = 'w',
DOWN = 's',
LEFT = 'a',
RIGHT = 'd'
};
Snake(Wall& tempWall,Food& tempFood);
//节点
struct Point
{
int x, y;
Point* next;
};
//初始化蛇
void initSnake();
//销毁节点
void destoryPoint();
//添加节点
void addPoint(int x,int y);
//删除节点(尾节点)
void delPoint();
//蛇移动(返回值判断是否移动成功) key表示移动按键
bool move(char key);
/************设定难度************/
//获取刷屏时间
int getSleepTime();
//获取蛇的长度
int countList();
//得分
int getScore();
private:
Point* pHead; //蛇头
Wall& wall; //蛇需要绘制在数组中
Food& food; //蛇与食物的位置判断
bool isRool; //蛇自己转圈
};
snake.cpp
#include "snake.h"
#include
void getoxy1(HANDLE hOut1, int x, int y){
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOut1, pos);//
}
HANDLE hOut1 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
Snake::Snake(Wall& tempWall, Food& tempFood) :wall(tempWall),food(tempFood){
pHead = NULL;
isRool = false;//蛇默认不转圈
}
//初始化蛇
void Snake::initSnake(){
destoryPoint(); //先将原来的蛇销毁
addPoint(5, 3);
addPoint(5, 4);
addPoint(5, 5);
}
//销毁所有节点
void Snake::destoryPoint(){
Point* pCur = pHead;
while (pHead){
pCur = pHead->next;
wall.setWall(pHead->x, pHead->y, ' ');//删除前修改蛇所在位置的标志
//TODO...
delete pHead;
pHead = pCur;
}
}
//添加节点
void Snake::addPoint(int x, int y){
Point* newPoint = new Point;
newPoint->x = x;
newPoint->y = y;
newPoint->next = NULL;
//如何蛇头不空,则更改蛇头为蛇身
if (pHead){
wall.setWall(pHead->x, pHead->y, '=');
getoxy1(hOut1, pHead->y*2, pHead->x);
cout << "=";
}
newPoint->next = pHead;
pHead = newPoint; //更新蛇头的位置
wall.setWall(pHead->x, pHead->y, '@');//设置蛇头的标志@
getoxy1(hOut1, pHead->y*2, pHead->x);
cout << "@";
}
void Snake::delPoint(){
//两个节点以上才删除
if (pHead == NULL || pHead->next == NULL)
return;
Point* pPre = pHead;
Point* pCur = pHead->next;
while (pCur->next){
pCur = pCur->next;
pPre = pPre->next;
}
//删除为节点
wall.setWall(pCur->x, pCur->y, ' ');
getoxy1(hOut1, pCur->y*2, pCur->x);
cout << " ";
delete pCur;
pCur = NULL;
pPre->next = NULL;
}
//蛇移动
bool Snake::move(char key){
//获取当前蛇头的位置
int x = pHead->x;
int y = pHead->y;
switch (key){
case UP:
--x;
break;
case DOWN:
++x;
break;
case LEFT:
--y;
break;
case RIGHT:
++y;
break;
default:
break;
}
//如果蛇头的下一个位置是蛇尾,则蛇不应死亡,而是转圈
//Point* pPre = pHead;
Point* pCur = pHead->next;
while (pCur->next){
pCur = pCur->next;
//pPre = pPre->next;
}
if (pCur->x == x&&pCur->y == y){
isRool = true;//蛇在循环
}
else{
//判断蛇头的目标位置是否可以去
if (wall.getWall(x, y) == '*' || wall.getWall(x, y) == '='){
//蛇的下一个位置是墙,则显示出蛇死的状态图
addPoint(x, y);
delPoint();
system("cls");
wall.drawWall();
cout << "最终得分:" << getScore() << endl;
cout << "game over!" << endl;
return false;
}
}
//移动成功:吃到食物 未吃到食物
if (wall.getWall(x, y) == '#'){
addPoint(x, y);
//重设食物
food.setFood();
}
else{//未吃到食物,前进
addPoint(x, y);
delPoint();
if (isRool){//此时蛇头快要碰上蛇尾,即蛇头蛇尾将要在同一个位置这个位置需要重绘
wall.setWall(x, y, '@');
getoxy1(hOut1, 2*y,x);
cout << "@";
}
}
return true;
}
//获取刷屏时间
int Snake::getSleepTime(){
int size = countList();
int sleepTime = 0;
if (size < 5)
sleepTime = 300;
else if (size>5 && size < 10)
sleepTime = 200;
else
sleepTime = 100;
return sleepTime;
}
//获取蛇的长度
int Snake::countList(){
Point* pTemp = pHead;
int size = 0;
while (pTemp){
pTemp = pTemp->next;
++size;
}
return size;
}
//得分为蛇吃掉食物的个数
int Snake::getScore(){
return countList() - 3;
}
class Food(food.h food.cpp)–随机获取食物的正确位置,将食物放置在gameArray中
food.h
#pragma once
#include "wall.h"
class Food{
public:
Food(Wall& tempWall);
void setFood();
private:
int foodX;
int foodY;
Wall& wall; //食物要绘制在二维数组中
};
food.cpp
#include "food.h"
#include
#include
void getoxy2(HANDLE hOut2, int x, int y){
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOut2, pos);//
}
HANDLE hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
Food::Food(Wall& tempWall) :wall(tempWall){
}
void Food::setFood(){
srand((unsigned)time(NULL));
//while保证食物放置在正确的位置
while (1){
foodX = rand() % (Wall::ROW - 2) + 1;
foodY = rand() % (Wall::COL - 2) + 1;
//cout << foodX <<" "<< foodY << endl;
//只有不是蛇身和蛇头的地方才可以生成食物
if (wall.getWall(foodX, foodY) == ' '){
wall.setWall(foodX, foodY, '#');
getoxy2(hOut2, foodY * 2, foodX);
cout << "#";
break;
}
}
}
主函数main.cpp
这个文件中的主要篇幅是在处理用户的输入问题
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include "wall.h"
#include"snake.h"
#include "food.h"
#include
#include
void getoxy(HANDLE hOut, int x, int y){
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOut,pos);//
}
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//定义显示器句柄变量
int main(){
bool isDead = false;//初始假设蛇未死亡
char preKey = NULL;//上一次的按键输入
Wall wall;
wall.initWall();
wall.drawWall();
Food food(wall);
food.setFood();
Snake snake(wall,food);
snake.initSnake();
getoxy(hOut, 0, Wall::ROW);
cout << "当前得分:" << snake.getScore() << endl;
while (!isDead){
char key = _getch();
//启动游戏时,蛇静止,按下左键‘a’蛇不动,也不会死,等待下一次有效按键
if (preKey == NULL&&key == snake.LEFT)
continue;
do{
//本次按键方向与上一次完全相反
if ((key == snake.UP&&preKey == snake.DOWN) ||
(key == snake.DOWN&&preKey == snake.UP) ||
(key == snake.RIGHT&&preKey == snake.LEFT) ||
(key == snake.LEFT&&preKey == snake.RIGHT)){
key = preKey;
}
else{
preKey = key;//按键与蛇前进方向不冲突 更新按键
}
//只有wasd时蛇才移动
if (key == snake.UP || key == snake.DOWN || key == snake.LEFT || key == snake.RIGHT){
if (snake.move(key)){
//system("cls");
//wall.drawWall();
getoxy(hOut, 0, Wall::ROW);
cout << "当前得分:" << snake.getScore() << endl;
Sleep(snake.getSleepTime());
}
else{
isDead = true;//蛇死亡
break; //退出内层while
}
}
else{//对wasd以外的按键不反应(不改变前进方向)
key = preKey;//强制将wasd以外的按键更改为上次的移动按键
}
preKey = key;
} while (!_kbhit());//当没有键盘输入的时候返回0
}
system("pause");
return 0;
}