网上看到一篇c++控制台实现贪吃蛇的博客,感觉思路很容易理解很适合当一个小项目练习。代码粘贴在下边(非我原创)。
用到的win32函数:_kbhit()检测有没有键盘输入,有返回1,没有返回0
_getch()获取一个输入字符
System("cls") 清屏
类:1 frame 框架类,是整个游戏的框架
2 snake 蛇本身
3 movement 运动
问题:1 让蛇动起来的方法就是不断地刷新屏幕(system("cls")->move->displayFrame),产生一种运动的感觉。
2 蛇有蛇头和蛇尾,整个蛇用链表表示,当head坐标等于food,就吃到了食物,那么就addHead,相当于把这个位置加到了蛇上。
注意一旦清屏,意味着已经进入了while(!_khbit)循环,运动方向就定了,下一次要到达的坐标已经知道了。
如果没吃到食物就整个链表向dir方向推移,加头删尾。
3 判断当蛇撞到自身或者墙,则GG,撞自身需要遍历一遍蛇。
#include<Windows.h>
#include <ctime>
#include <cstdlib>
#include <conio.h>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
enum Direction { UP, DOWN, LEFT, RIGHT };
class Frame {
public:
friend class Snake;
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;
}
}
class Snake
{
friend class Movement;
public:
Snake(int x = 0, int y = 0){
this->x = x;
this->y = y;
next = NULL;
prior = NULL;
}
void addHead(int, int);
void delTail();
private:
int x, y;
Snake *next, *prior;
} *head, *tail; //全局的蛇头和蛇尾
void Snake::addHead(int ix, int iy)
{
Snake *snode = new Snake(ix, iy);
snode->next = head;
if (head)
head->prior = snode;
else
tail = snode;
head = snode;
frame.window[ix][iy] = '~';
}
void Snake::delTail()
{
Snake *tmp = tail;
frame.window[tail->x][tail->y] = ' ';
tail = tail->prior;
tail->next = NULL;
if (tail)
tail->next = nullptr;
else
head = nullptr;
delete tmp; //回收资源
}
class Movement
{
public:
Movement() :dir(LEFT){ randomFood(); }
void randomFood();
void move();
void changeDirection(char);
private:
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 (Snake *tmp = head; tmp != NULL; tmp = tmp->next){
if (tmp->x == h&&tmp->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 (Snake *tmp = head; tmp != NULL; tmp = tmp->next){
if (tmp->x == fx&&tmp->y == fy){
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;
frame.initializeFrame();
head->addHead(frame.height / 2, frame.width / 2);
Movement myMove;
while (true) {
while (!_kbhit()) {
system("cls");
myMove.move();
frame.displayFrame();
Sleep(300);
}
key = _getch();
myMove.changeDirection(key);
}
return 0;
}