说实话,编程语言的学习挺枯燥的,对于已经过了尝鲜期,开始接触比较深层次的初学者来说更是如此。而作为耐得住寂寞的理工/IT男,“苦中作乐”已为常态。写点小游戏娱乐娱乐,满足创造欲的同时巩固、拓展一下所学知识,美哉美哉。写贪吃蛇是最好不过的选择了。
而这篇半吊子文章,教程?心得?破绽百出的胡扯?都算吧,各位随便看看就好,若有不同见识,不吝赐教。
贪吃蛇流程
- 对于一个贪吃蛇这样一个单线程就能实现的小游戏,游戏框架应该是这样的:
int main() {
init(); //初始化操作
while(1) {
input(); //输入
update(); //数据更新
repaint(); //画面更新
}
return 0;
}
- 而对于贪吃蛇这个游戏来说:
- 初始化操作应该绘制边框,蛇以及食物的初始化。
- 输入则应检测是否按下上下左右(或者WSAD)按键。
- 更新数据前应先判断死亡(撞墙和吃到蛇身)及是否吃到食物,若游戏继续则更新蛇和食物的数据。
- 更新画面则是根据蛇和食物的坐标重绘画面。
关于蛇的实现
- 我看过比较主流的有两种思路:
- 一是使用一个大数组(整型数组或者自定义类的数组),分别存储地图的蛇、边框、食物和空白的数据,数据更新即对大数组进行更新。
- 二则是我所采用的,用数组、链表这类的存储蛇的数据,配合部分win32函数对此进行更新。
- 而数组较为固定,(个人认为)不利于管理。
- c++有自带已写好的双向链表list,不必自己写,省去了一些功夫
list相关
若读者不熟悉list的用法,以下是本例所用到的。
-
首先要包含头文件List
#include <List>
-
创建空链表
list<type>name;
-
部分函数:
front(), back():返回第一个、最后一个元素 push_front(value), push_back(value):从首部、尾部追加值 pop_back():删除最后一个元素 begin(), end():返回第一个元素、最后一个元素下一个元素的迭代器(遍历相关)
`
- 迭代器
-
可以理解为遍历list的工具,遍历时相当于每个元素的指针,使用时应引入头文件Iterator
#include<Iterator>
-
一般遍历用法
for(list<type>listName::iterator iter = listName.begin();iter != listName.end();++iter) { //to do }
-
类的建立
先定义了一个Entity类:
class Entity {
public:
Entity(int, int);
void setX(int);
void setY(int);
void setPosition(int, int);
int getX();
int getY();
void virtual print() {
} //根据子类绘制图案
bool isHit(Entity); //判断两个Entity是否在同意位置
private:
int x,y; //x和y坐标
};
Entity类方法的实现:
Entity::Entity(int x, int y) {
setX(x);
setY(y);
}
void Entity::setX(int x) {
this->x = x;
}
void Entity::setY(int y) {
this->y = y;
}
void Entity::setPosition(int x, int y) {
this->setX(x);
this->setY(y);
}
int Entity::getX() {
return x;
}
int Entity::getY() {
return y;
}
bool Entity::isHit(Entity other) {
if(x == other.getX() && y == other.getY())
return true;
else
return false;
}
Entity类继而派生出Snake类(代表蛇身)和Food类(代表食物)
- Snake类及其实现
#include <iostream>
#include <Windows.h>
#define BODY '*' //以'*'作为蛇身
class Snake : public Entity {
public:
Snake(int, int);
static int getToilX();
static void setToilX(int);
static int getToilY();
static void setToilY(int);
static int getMoveOrder();
static void setMoveOrder(int);
void print();
private:
static int toilX, toilY; //尾部坐标
static int moveOrder; //当前移动命令
};
void gotoXy(int x, int y);
int Snake::toilX = -1;
int Snake::toilY = -1;
int Snake::keyOrder = VK_LEFT; //初始为向左
Snake::Snake(int x, int y):Entity(x, y) {
}
int Snake::getToilX() {
return toilX;
}
void Snake::setToilX(int toilX) {
Snake::toilX = toilX;
}
int Snake::getToilY() {
return toilY;
}
void Snake::setToilY(int toilY) {
Snake::toilY = toilY;
}
int Snake::getMoveOrder() {
return getMoveOrder;
}
void Snake::setMoveOrder(int moverOrder) {
Snake::moverOrder = moveOrder;
}
void Snake::print() {
gotoXy(getX(), getY()); //根据前往蛇身坐标指向的地方
std::cout<<BODY;
}
- Food类只重写了print()函数,即以’@'代表食物
游戏初始
#include <Windows.h>
#include <ctime>
#include <cstdlib>
#include <List>
#include <iterator>
#include <iostream>
using namespace std;
#define SNAKE list<Snake>
#define MAP_HEIGHT 25
#define MAP_LENGHT 25
SNAKE snake;
Food food(-1, -1);
void init() {
initSnake();//初始化蛇
hideCursor();//设置光标不可见
paintMap();//绘制地图边框
paintSnake();//绘制蛇
createFood();//创建食物
}
初始化蛇
void initSnake