贪吃蛇是非常经典的小游戏,大家肯定接触过,它在当年只能打电话发短信的诺基亚手机中是唯一的娱乐项目了。它的操作非常简单,很多编程语言都可以来实现它。我们已经学习了Processing中基本图形的绘制以及鼠标键盘的识别响应,本篇我们使用Processing来编程实现贪吃蛇小游戏。
这应该是贪吃蛇游戏中最主要的部分了,在程序中使用数组来保存组成蛇身的每个方块的坐标值。
// 保存组成蛇身的每个方格位置
int[] x = new int[snake_length_max];
int[] y = new int[snake_length_max];
当蛇头位置改变以后,我们从尾部开始遍历数组,将每个方块坐标位置向后移动,然后将新的蛇头位置赋值到数组第一元素,然后重新绘制所有方块。
void draw_snake()
{
//从尾部开始更新蛇身方块坐标
for (int i=snake_length-1; i>0; i--) {
x[i] = x[i-1];
y[i] = y[i-1];
}
// 设置蛇头新的坐标
x[0] = snake_head_x;
y[0] = snake_head_y;
// 设置蛇身填充颜色
fill(#3874F6);
// 开始画蛇
for (int i=0; i<snake_length; i++) {
rect(x[i], y[i], grid, grid);
}
}
当食物被吃掉以后,我们需要在指定长宽的区域内随机生成食物的坐标数据,这里使用了**random()**函数,用来生成坐标值,这个坐标值需要是方块边长的整数倍。
void draw_food(int max_width, int max_high)
{
//食物填充颜色
fill(#F71E1E);
//如果食物被吃掉,则随机生成一个
if (food_eaten)
{
food_x = int(random(0, max_width) / grid) * grid;
food_y = int(random(0, max_high) / grid) * grid;
}
rect(food_x, food_y, grid, grid);
food_eaten = false;
}
这里使用前篇介绍的按键识别操作。对方向按键进行判断,然后对应改变蛇的运动方向。
if (snake_direction != 'P'&& keyPressed && key == CODED)
{
switch(keyCode) {
case LEFT:
if (snake_direction != 'R') {
snake_direction = 'L';
}
break;
case RIGHT:
if (snake_direction != 'L') {
snake_direction = 'R';
}
break;
case DOWN:
if (snake_direction != 'U') {
snake_direction = 'D';
}
break;
case UP:
if (snake_direction != 'D') {
snake_direction = 'U';
}
break;
}
在刷新显示的时候,会根据移动方向的不同,对蛇头的坐标进行改变,当重新对蛇身进行绘制的时候,整个蛇就进行了一次移动。
//移动方向选择
switch(snake_direction) {
case 'L':
snake_head_x -= grid;
break;
case 'R':
snake_head_x += grid;
break;
case 'D':
snake_head_y += grid;
break;
case 'U':
snake_head_y -= grid;
break;
}
此外,增加了暂停键“P”或“p”,以及运行键“R”或“r”的判断。
if (key == 'p' || key == 'P')
{
game_pause++;
if (game_pause%2 == 1)
{
snake_direction_temp = snake_direction;
snake_direction = 'P';
} else {
snake_direction = snake_direction_temp;
}
}
....
if (keyPressed && (key == 'r' || key == 'R'))
{
...
}
值得注意的是,这些对于按键的监听,没有放在draw() 函数中,而是使用了键盘事件函数 keyPressed(),每当按下一个键,其中的代码就会运行一次,使用这种方式监听按键更加方便灵活。
当蛇头坐标超过显示区域,即蛇撞墙,或蛇头位置坐标与蛇身其他方块坐标相同,即自己吃了自己,都会导致游戏结束。
boolean check_snake_die()
{
// 撞墙了
if ( snake_head_x < 0 || snake_head_x >= width || snake_head_y < 0 || snake_head_y >= height) {
show_game_over();
return true;
}
// 自己吃自己
if ( snake_length > 2 ) {
for ( int i=1; i<snake_length; i++ ) {
if ( snake_head_x == x[i] && snake_head_y == y[i] ) {
show_game_over();
return true;
}
}
}
return false;
}
程序中使用millis() 函数来获取自程序开始到当前的时间。每当draw() 中代码运行一次,我们都重新获取一次当前时间,然后减掉之前的时间来计算出经过的时间,然后与移动间隔时间进行比较,当大于间隔时间时,说明需要刷新移动蛇身一次,然后重新获取一次时间,为后续比较做准备。
time_passed = millis() - time_start; //计算出经过的时间
time_interval = 1000 / speed; //计算移动间隔时间
if (time_passed > time_interval && snake_direction != 'P' && game_start)//游戏刷新条件
{
...
time_start = millis(); //重新获取时间
}
当蛇头坐标移动到与食物坐标相同时,就代表食物被吃到,这时蛇身长度要加1,重新生成食物。程序中每吃掉5个食物,移动速度就会增加1。
//蛇吃到食物
if (snake_head_x == food_x && snake_head_y == food_y)
{
food_eaten = true; //可重新生成食物
snake_length++;
if ( snake_length%5 == 1) {
speed++;
}
speed = min(20, speed);//控制最大速度
}
贪吃蛇的实现还是非常简单的,当然代码还有很多需要优化的地方,比如说我们在随机生成食物坐标的时候,需要排除蛇身中方块的坐标,即食物不能直接出现在蛇身中,你可以试着来优化下。
我们还可以直接将代码导出成exe可执行文件,你也来试一试吧。
关注公众号「TonyCode」,后台回复“snake”,获取贪吃蛇完整程序。
回复「1024」获取1000G学习资料。
个人博客