貪喫蛇-C++版

前面寫了貪喫蛇的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 <iostream>
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 <iostream>
#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

  • 蛇頭的下一個位置如果是現在蛇尾的位置,則蛇移動後不應該死亡,允許蛇轉圈(這個功能有isRool變量來控制)
#include "snake.h"
#include <windows.h>

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<ctime>
#include <windows.h>


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
這個文件中的主要篇幅是在處理用戶的輸入問題

  • 剛開始時蛇靜止,蛇頭向右,按下’a’蛇不動,不會死
  • 本次按鍵與蛇移動方向成180°角時,蛇繼續沿着前面的方面前進,不會死亡
  • 只對wasd四個按鍵做出反應,其餘按鍵不改變蛇的前進方向
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "wall.h"
#include"snake.h"
#include "food.h"
#include <conio.h>
#include <windows.h>

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章