玩命加载中 . . .

Processing笔记05—贪吃蛇小游戏


贪吃蛇是非常经典的小游戏,大家肯定接触过,它在当年只能打电话发短信的诺基亚手机中是唯一的娱乐项目了。它的操作非常简单,很多编程语言都可以来实现它。我们已经学习了Processing中基本图形的绘制以及鼠标键盘的识别响应,本篇我们使用Processing来编程实现贪吃蛇小游戏。

1. 蛇身的绘制

这应该是贪吃蛇游戏中最主要的部分了,在程序中使用数组来保存组成蛇身的每个方块的坐标值。

// 保存组成蛇身的每个方格位置
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);
  }
}

2. 食物的产生

当食物被吃掉以后,我们需要在指定长宽的区域内随机生成食物的坐标数据,这里使用了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;
}

3. 方向控制

这里使用前篇介绍的按键识别操作。对方向按键进行判断,然后对应改变蛇的运动方向。

 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(),每当按下一个键,其中的代码就会运行一次,使用这种方式监听按键更加方便灵活。

4. 判断游戏结束

当蛇头坐标超过显示区域,即蛇撞墙,或蛇头位置坐标与蛇身其他方块坐标相同,即自己吃了自己,都会导致游戏结束。

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;
}

5. 移动速度

程序中使用millis()函数来获取自程序开始到当前的时间。每当draw()中代码运行一次,我们都重新获取一次当前时间,然后减掉之前的时间来计算出经过的时间,然后与移动间隔时间进行比较,当大于间隔时间时,说明需要刷新移动蛇身一次,然后重新获取一次时间,为后续比较做准备。

 time_passed = millis() - time_start; //计算出经过的时间
 time_interval = 1000 / speed; //计算移动间隔时间

 if (time_passed > time_interval && snake_direction != 'P' && game_start)//游戏刷新条件
  {
     ...
    time_start = millis(); //重新获取时间
  }

6. 吃到食物

当蛇头坐标移动到与食物坐标相同时,就代表食物被吃到,这时蛇身长度要加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);//控制最大速度
}

7. 实现效果

snake

贪吃蛇的实现还是非常简单的,当然代码还有很多需要优化的地方,比如说我们在随机生成食物坐标的时候,需要排除蛇身中方块的坐标,即食物不能直接出现在蛇身中,你可以试着来优化下。我们还可以直接将代码导出成exe可执行文件,你也来试一试吧。


关注公众号「TonyCode」,更多精彩内容分享。

扫码关注


文章作者: Tony
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Tony !