贪吃蛇原型实现基本思路
时间:2010-07-19 来源:bilipan
贪吃蛇的原型是在windows控制台下即在字符模式下利用C++编写贪吃蛇。
主要实现的效果就是,用户利用'w'、's'、'a'、'd'分别表示上下左右,当蛇碰到障碍物时通不过,当碰到食物时,分数加1,同时,在另外一个地方生成新的食物,并且身体会增加一个字符的长度。
在实现该原型前必须实现两项技术:
什么意思呢?主要是因为考虑到在通常情况下,在控制台下编写的程序,默认都是黑白色的,颜色单一,而且蛇和障碍物、食物之间无法被互相区分,所以加上更多的颜色,视觉上会更舒服些。字符位置控制呢,是必不可缺的。这个是因为在命令行模式下,程序是默认顺序接收用户输入的,偶尔可以跳至同一行稍前些的位置。但是,由于蛇要到处爬,所以这就遇到一个问题,如何让输入光标跳至窗口下的任意位置成了开发的基础和前提。具体的我后面会讲到我的解决方案。
我的实现思路:
首先,我要创建一个游戏背景,具体指的是贪吃蛇的移动范围。这个呢,我用二维的数组来表示。每个单元格有几个参数---是否存在障碍物、是否存在食物,如果有的话,就用true,否则为false。
接下来,就是要在背景上创建障碍物,在创建它们之前,首先得解决如何在指定的单元格式绘制单元。思路很简单,就是编写一个函数将光标定位到相应的坐标位置,然后输入一个空白的上色字符就解决了。然后就可以利用这个函数在真正的背景上相应的位置构建障碍物了。同时可以实现随机创建任意个、任意位置的障碍物了。
同理,食物的创建也一样。
背景、障碍物和食物都创建好了,接下来就要构建蛇的模型了。由于考虑到蛇在吃完食物后,身体会变长,所以我采用了顺序表的数据结构来记录蛇的信息。那如何来表现蛇的移动呢?我的思路是,将身体靠近头的信息传递到次靠近头的,同时将新的下一步要走的坐标信息传给头部,同时有擦除掉尾部信息即可。
这里的相对位置是指以背景左上方的点为原点即(0,0),右下方为(length-1,width-1)。
接着呢,就是要获取用户的方向控制操作,并执行它们。由于采用的二维数组的方式表示蛇的形状以及游戏背景,所以当接收到命令如果是‘上’时,横坐标不变,而纵坐标减1,同时擦除原形状,如此一来,蛇就向上爬了。擦除函数的原理很简单,就是将对应的背景单元格的信息全部还原为初始值,就可以了。
做完这个,不要以为就这样结束了,因为我们还没对蛇的运动范围作出限制,而且还要实现蛇的触发事件即碰到食物后身体变长,食物数增加1。
下面就是游戏原型的主函数:
这个贪吃蛇原型是我一时兴起弄的,考虑的也比较简单,况且是第一次写这种文章,所以哪里讲的不清楚或是程序有问题的,请大家多包涵。如果有建议或不清楚的地方,我会尽力解答的。谢谢。
主要实现的效果就是,用户利用'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; }
这个贪吃蛇原型是我一时兴起弄的,考虑的也比较简单,况且是第一次写这种文章,所以哪里讲的不清楚或是程序有问题的,请大家多包涵。如果有建议或不清楚的地方,我会尽力解答的。谢谢。
相关阅读 更多 +