您的位置:首页 > 其它

贪吃蛇原型实现基本思路

2012-03-30 15:34 585 查看
贪吃蛇的原型是在windows控制台下即在字符模式下利用C++编写贪吃蛇。

主要实现的效果就是,用户利用'w'、's'、'a'、'd'分别表示上下左右,当蛇碰到障碍物时通不过,当碰到食物时,分数加1,同时,在另外一个地方生成新的食物,并且身体会增加一个字符的长度。

在实现该原型前必须实现两项技术:

字符上色,这个利用了网上的代码

字符位置控制

什么意思呢?主要是因为考虑到在通常情况下,在控制台下编写的程序,默认都是黑白色的,颜色单一,而且蛇和障碍物、食物之间无法被互相区分,所以加上更多的颜色,视觉上会更舒服些。字符位置控制呢,是必不可缺的。这个是因为在命令行模式下,程序是默认顺序接收用户输入的,偶尔可以跳至同一行稍前些的位置。但是,由于蛇要到处爬,所以这就遇到一个问题,如何让输入光标跳至窗口下的任意位置成了开发的基础和前提。具体的我后面会讲到我的解决方案。

我的实现思路:

首先,我要创建一个游戏背景,具体指的是贪吃蛇的移动范围。这个呢,我用二维的数组来表示。每个单元格有几个参数---是否存在障碍物、是否存在食物,如果有的话,就用true,否则为false。

//这个是背景的单元格数据结构
const length = 40;
const width = 20;
struct square{
bool blocked;  //是否有障碍物
bool food;       //是否有食物
int x;               //单元格在背景中的相对横坐标
int y;               //单元格在背景中的相对纵坐标
}bg[length][width];  //直接创建游戏背景

//设置背景
void setBG(int length, int width){
HANDLE hOut;
COORD OutChar;
OutChar.X = 10;
OutChar.Y = 10;
int i = 0;
int j = 0;
for(i = 0; i < width; i++){
for(j = 0; j < length; j++){
bg[i][j].x = i;
bg[i][j].y = j;
bg[i][j].blocked = false;
bg[i][j].food = false;
OutChar.X = j+10;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,OutChar);
cout << col(BG_WHITE,true) << " ";
}
cout << endl;
OutChar.Y = i+10;
SetConsoleCursorPosition(hOut,OutChar);
}
}


接下来,就是要在背景上创建障碍物,在创建它们之前,首先得解决如何在指定的单元格式绘制单元。思路很简单,就是编写一个函数将光标定位到相应的坐标位置,然后输入一个空白的上色字符就解决了。然后就可以利用这个函数在真正的背景上相应的位置构建障碍物了。同时可以实现随机创建任意个、任意位置的障碍物了。

//构造障碍物
void createBlock(int x, int y, unsigned short color){
HANDLE hOut;
COORD OutChar;
OutChar.X = x;
OutChar.Y = y;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,OutChar);  //定位光标输入
cout << col(color, true) << " ";  //一个颜色为color的空白字符
}

//生成单个障碍物
void createWall(int x,int y){
createBlock(x+10,y+10,BG_GREEN);
bg[x][y].blocked = true;
}

//判断所指坐标是否被占用
bool checkExisted(int x,int y){
if(bg[x][y].blocked == true || bg[x][y].food == true){
return false;
}
return true;
}

//随机生成障碍物
void rand_createWall(void){
srand((unsigned)time(NULL));
int n = rand() % 70+10;
int pos_x = 0;
int pos_y = 0;
int i = 0;
for(i = 0; i < n; i++){
pos_x = rand() % length;
pos_y = rand() % (width-1);
if(checkExisted(pos_x,pos_y) == true){ //防止障碍物重叠
createWall(pos_x,pos_y);
}else{
n++;
}
//createWall(pos_x,pos_y);
}
}


同理,食物的创建也一样。

//创建食物
void createFood(int x,int y){
createBlock(x+10,y+10,BG_BLUE);
bg[x][y].food = true;
}

//随机创建食物
void rand_createFood(void){
srand((unsigned)time(NULL));
int n = 1;//rand() % 20;
int pos_x = 0;
int pos_y = 0;
int i = 0;
for(i = 0; i < n; i++){
pos_x = rand() % length;
pos_y = rand() % (width-1);
if(checkExisted(pos_x,pos_y) == true){  //防止在障碍物上生成食物
createFood(pos_x,pos_y);
}else{
n++;
}
}
}


背景、障碍物和食物都创建好了,接下来就要构建蛇的模型了。由于考虑到蛇在吃完食物后,身体会变长,所以我采用了顺序表的数据结构来记录蛇的信息。那如何来表现蛇的移动呢?我的思路是,将身体靠近头的信息传递到次靠近头的,同时将新的下一步要走的坐标信息传给头部,同时有擦除掉尾部信息即可。

//物体信息,这是蛇的单元模型
const objLen = 5;
struct obj{
int x;
int y;
}snake[objLen];

//创建蛇
LinList<struct obj> newSnake;
void createSnake(void){
int i = 0;
for(i = 0; i < objLen; i++){
snake[i].x = i;
snake[i].y = 0;
newSnake.Insert(snake[i],i);
}
}

//绘制蛇
void drawSnake(int len){
int i = 0;
struct obj t;
for(i = 0; i < len; i++){
t = newSnake.GetData(i);
createBlock(t.x,t.y,BG_RED);
}
}

//增长蛇的身体
void insreaseSnake(int x,int y){
struct obj t;
t.x = x;
t.y = y;
newSnake.Insert(t,0);
createBlock(x,y,BG_RED);
}

//传递蛇的信息
void transSnake(int x,int y,int len){
int i = 0;
struct obj t1,t2;
for(i = 0; i < len-1; i++){
t1 = newSnake.GetData(i);
t2 = newSnake.GetData(i+1);
newSnake.Delete(i);
t1.x = t2.x;
t1.y = t2.y;
newSnake.Insert(t1,i);
}
newSnake.Delete(newSnake.Size()-1);
t1.x = x;
t1.y = y;
newSnake.Insert(t1,newSnake.Size()-1);
}


这里的相对位置是指以背景左上方的点为原点即(0,0),右下方为(length-1,width-1)。

接着呢,就是要获取用户的方向控制操作,并执行它们。由于采用的二维数组的方式表示蛇的形状以及游戏背景,所以当接收到命令如果是‘上’时,横坐标不变,而纵坐标减1,同时擦除原形状,如此一来,蛇就向上爬了。擦除函数的原理很简单,就是将对应的背景单元格的信息全部还原为初始值,就可以了。

//清除物体移动轨迹
void removeTrack(int x, int y){
HANDLE hOut;
COORD OutChar;
OutChar.X = x;
OutChar.Y = y;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,OutChar);
cout << col(BG_WHITE,true) << " ";
}

//移动物体
int x = 10;
int y = 10;
int tail_x = 0;
int tail_y = 0;
void moveBlock(char direction){
HANDLE hOut2;
COORD OutChar2;
OutChar2.X = x;
OutChar2.Y = y;
struct obj t;
t = newSnake.GetData(0);
tail_x = t.x;
tail_y = t.y;
hOut2 = GetStdHandle(STD_OUTPUT_HANDLE);
removeTrack(t.x,t.y);
switch(direction){
case 'w':{
OutChar2.Y--;
y--;
SetConsoleCursorPosition(hOut2,OutChar2);
break;
}
case 's':{
OutChar2.Y++;
y++;
SetConsoleCursorPosition(hOut2,OutChar2);
break;
}
case 'a':{
OutChar2.X--;
x--;
SetConsoleCursorPosition(hOut2,OutChar2);
break;
}
case 'd':{
OutChar2.X++;
x++;
SetConsoleCursorPosition(hOut2,OutChar2);
break;
}
}

transSnake(x,y,newSnake.Size());
drawSnake(newSnake.Size());
}


做完这个,不要以为就这样结束了,因为我们还没对蛇的运动范围作出限制,而且还要实现蛇的触发事件即碰到食物后身体变长,食物数增加1。

//判断是否碰到障碍物或边界
bool checkView(char direction){
if(direction == 'w' && y >= 10){
if(y == 10 || bg[x-10][y-10-1].blocked == true){return false;}
}
else if(direction == 's' && y < 10+width){
if(y == 10+width-2 || bg[x-10][y-10+1].blocked == true){return false;}
}
else if(direction == 'a' && x >= 10){
if(x == 10 || bg[x-10-1][y-10].blocked == true){return false;}
}
else if(direction == 'd' && x < 10+length){
if(x == 10+length-1 || bg[x-10+1][y-10].blocked == true){return false;}
}
return true;
}

//判断是否吃到食物
bool checkFood(int x, int y){
if(bg[x-10][y-10].food == true){return true;}
return false;
}


下面就是游戏原型的主函数:

int main()
{
HANDLE hOut;
COORD OutChar;

OutChar.X = 0;
OutChar.Y = 0;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,OutChar);

/*
struct square **bgR = new square*[width];
struct square *bgC = new square[length];
for(int i = 0; i < width; i++){
bgR[i] = bgC;
}
*/
//设置背景
setBG(length,width);
//设置障碍物
rand_createWall();
//设置食物
rand_createFood();
//创建蛇
createSnake();
//移动物体
char direction;
int score = 0;
for(;;){
direction = getch();
if(checkView(direction) == true){//判断能否移动
moveBlock(direction);
if(checkFood(x,y) == true){//判断是否吃到食物
bg[x-10][y-10].food = false;
score++;
insreaseSnake(tail_x,tail_y);//增长身体
rand_createFood();//吃完后随机在创建一个食物
}
}
OutChar.X = 0;
OutChar.Y = 0;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut,OutChar);
cout << col(BG_WHITE,true) << "Scores: " << score;
}
return 0;
}


这个贪吃蛇原型是我一时兴起弄的,考虑的也比较简单。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: