C++贪吃蛇的简单实现版

本文介绍了作者使用C++实现贪吃蛇游戏的过程,核心思想是面向对象编程,包括游戏框架、蛇节点和运动三个主要对象。通过全局变量减少参数传递,并利用kbhit()、system("cls")等函数实现游戏交互。文中提到了两个关键改进:避免食物生成在蛇身上,以及检测蛇头与蛇身的碰撞。

近期看完C++Primer,决定小试身手,碰巧看到某博主用C++实现了贪吃蛇,所以我也决定试一试。

C++编程的核心思想之一就是OO思维(面向对象思维),需要一定的抽象能力,同样还需要更好的封装。


大概简述一下我的C++贪吃蛇编程思想:

对象有: 

(a) 游戏框架:实现游戏空间,包括外围墙、贪吃蛇和食物的显示;

由于游戏框架只有一个,所以直接设了全局变量frame,减少调用时的传参。

为Frame设置了友元类snakeNode和movement,能够直接使用私有成员window(修改游戏界面中蛇和食物的位置);

<span style="font-size:14px;">//Frame of the game
class Frame {
public:
	friend class snakeNode;
	friend class movement;
    unsigned width, height;
	Frame(): width(30), height(30) {}
	void setHeight(unsigned hgt) {
		height = hgt;
	}
	void setWidth(unsigned wid) {
		width = wid;
	}
	void initializeFrame();
	void displayFrame();
private:
	vector< vector<char> > window;
}frame;</span>

(b) 贪吃蛇节点:采用链表存储贪吃蛇各节点坐标,实际上是一个FIFO结构;

由于游戏中贪吃蛇只有一只,所以直接设了全局变量head和tail,减少调用时的传参。

<span style="font-size:14px;">class snakeNode {
public:
	friend class movement;
	snakeNode(int ix, int iy): x(ix), y(iy), next(nullptr), prior(nullptr) {}
	void addHead(int, int);
	void delTail();
private:
	int x, y;
	snakeNode *next, *prior;
}*head, *tail;</span>

(c) 运动:游戏的核心,随机地产生食物,贪吃蛇的运动,贪吃蛇方向的改变等;

<span style="font-size:14px;">class movement {
public:
	movement(): dir(LEFT) {randomFood();}
	void randomFood();
	void move();
	void changeDirection(char);
private:
	enum Direction dir;
	int fx, fy;
	bool outOfFrame(int h, int w) {
		return h < frame.height - 1 && h > 0 && w < frame.width - 1 && w > 0 ? false : true;
	}
	bool block(int h, int w) {
		for (snakeNode *snake = head; snake; snake = snake->next) {
			if (snake->x == h && snake->y == w)
				return true;
		}
		return false;
	}
};</span>


一些用到的库函数

--kbhit():顾名思义(keyboardhit),检测是否有键盘按键敲击动作,如有返回true,无则返回false,包含在conio.h头文件中;

--system(“cls”):清屏;

--Sleep(t):暂停t的时间间隔;

--getch():从键盘获取字符;


致谢:我是借鉴这个博主的C++贪吃蛇实现,同时修改了其中的2个问题:其一,随机生成食物时应检查是否生成在蛇节点上;其二,检查碰撞时除与外围墙碰撞外,还需检查蛇头与蛇身的碰撞。


最后,贪吃蛇的C++实现模板:

为复制方便,将类定义、成员函数实现及主函数都放在了一起,特此说明。

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib> 
#include <conio.h> 
#include <windows.h> 
using namespace std;

enum Direction {UP, DOWN, LEFT, RIGHT};
//Frame of the game
class Frame {
public:
	friend class snakeNode;
	friend class movement;
    unsigned width, height;
	Frame(): width(30), height(30) {}
	void setHeight(unsigned hgt) {
		height = hgt;
	}
	void setWidth(unsigned wid) {
		width = wid;
	}
	void initializeFrame();
	void displayFrame();
private:
	vector< vector<char> > window;
}frame;

void Frame::initializeFrame() {
	if (width <= 0 || height <= 0) {
		cerr << "WRONG FRAME DIMENSION!" << endl;
		exit(0);
	}
	window = vector<vector<char> >(height, vector<char>(width, ' '));
	for (int i = 0; i != height; ++i) {
		window[i][0] = window[i][width-1] = '#';
	}
	for (int j = 0; j != width; ++j) {
		window[0][j] = window[height-1][j] = '#';
	}
}

void Frame::displayFrame() {
	for (int i = 0; i != height; ++i) {
		for (char ch : window[i])
			cout << ch << ' ';
		cout << endl;
	}
}
//snakeNode
class snakeNode {
public:
	friend class movement;
	snakeNode(int ix, int iy): x(ix), y(iy), next(nullptr), prior(nullptr) {}
	void addHead(int, int);
	void delTail();
private:
	int x, y;
	snakeNode *next, *prior;
}*head, *tail;

void snakeNode::addHead(int ix, int iy) {
	snakeNode *newSnake = new snakeNode(ix, iy);
	newSnake->next = head;
	if (head)
		head->prior = newSnake;
	else
		tail = newSnake;
	head = newSnake;
	frame.window[ix][iy] = '~';
}

void snakeNode::delTail() {
	snakeNode *snakeTail = tail;
	frame.window[snakeTail->x][snakeTail->y] = ' ';
	tail = snakeTail->prior;
	if (tail)
		tail->next = nullptr;
	else
		head = nullptr;
	delete snakeTail;
}

//movement
class movement {
public:
	movement(): dir(LEFT) {randomFood();}
	void randomFood();
	void move();
	void changeDirection(char);
private:
	enum Direction dir;
	int fx, fy;
	bool outOfFrame(int h, int w) {
		return h < frame.height - 1 && h > 0 && w < frame.width - 1 && w > 0 ? false : true;
	}
	bool block(int h, int w) {
		for (snakeNode *snake = head; snake; snake = snake->next) {
			if (snake->x == h && snake->y == w)
				return true;
		}
		return false;
	}
};

void movement::randomFood() {
	srand((unsigned)time(0));
	bool onSnake = true;
	while (onSnake) {
		onSnake = false;
		fx = rand() % (frame.height - 2) + 1;
		fy = rand() % (frame.width - 2) + 1;
		for (snakeNode *snake = head; snake; snake = snake->next) {
			if (fx == snake->x && fy == snake->y) {
				onSnake = true; break;
			}
		}
	}
	frame.window[fx][fy] = 'x';
}

void movement::move() {
	int h = head->x, w = head->y;
	switch(dir) {
		case UP: --h; break;
		case DOWN: ++h; break;
		case LEFT: --w; break;
		case RIGHT: ++w; break;
	}
	if (outOfFrame(h, w) || block(h, w)) {
		cout << "Game Over!" << endl;
		exit(0);
	}
	if (h == fx && w == fy) {
		head->addHead(fx, fy);
		randomFood();
	}
	else {
		head->addHead(h, w);
		head->delTail();
	}
}

void movement::changeDirection(char key) {
	switch(key) {
		case 'w': {
			if (dir != DOWN) dir = UP;
			break;
		}
		case 's': {
			if (dir != UP) dir = DOWN;
			break;
		}
		case 'a': {
			if (dir != RIGHT) dir = LEFT;
			break;
		}
		case 'd': {
			if (dir != LEFT) dir = RIGHT;
			break;
		}
	}
}

int main() {
	unsigned speed, h, w;
	char key;
	cout << "Please input height and width!(10~39)" << endl;
	cout << "height = ";
	cin >> h;
	cout << "width = ";
	cin >> w;
	cout << "Please input speed!(1~500)" <<endl;
	cout << "speed = ";
	cin >> speed;
	frame.setHeight(h); frame.setWidth(w);
	frame.initializeFrame();
	head->addHead(frame.height / 2, frame.width / 2);
	movement myMove;
	while (true) {
		while (!kbhit()) {
			system("cls");
			myMove.move();
			frame.displayFrame();
			Sleep(speed);
		}
		key = getch();
		myMove.changeDirection(key);
	}
	return 0;
}

游戏界面:



如有错误,请批评指正,谢谢。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值