使用Kaboom制作贪食蛇

237 阅读1分钟

1 目的

使用Replit.com的Kaboom库实现贪食蛇。

2 修复Demo的错误

修复Replit.com案例代码中的碰撞事件错误。

主要修改了 area() 的参数,官方案例代码中的 area()无参数,会导致 snake 的身体块儿自己与自己接触也属于碰撞。

3 总结

3.1 Kaboom

Kaboom库文档非常的简陋,但是作为框架十分易用和简单。

3.2 Replit

Replit十分好用,国内CSDN的inscode有可能就是抄袭的它。 可以免费在线创建 Python、PHP、Java项目,拥有免费数据库、免费存储。 国内还不是很流行,这也好,不然过不多久就要被墙了。

4 游戏实现简要说明

4.1 蛇身体的变化

贪食蛇全网各个语言的版本都有,但是对蛇身体的实现方式有两种。

4.1.1 尾移到头

尾部节点移动到新头部节点位置。

4.1.2 加头去尾

直接创建新头部,移除尾部。

建议用第二种方案,下面代码也是采用第二种方案。

5 运行效果

image.png

点击 fcck--hiqilei.repl.co/ 可以试玩。

6 关键源代码

import kaboom from "kaboom"
import "kaboom/global"

kaboom();

const block_size = 20;
const block_margin = 2;

const map = addLevel(
  [
    "==============",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "=            = ",
    "==============",
  ],
  {
    tileWidth: block_size,
    tileHeight: block_size,
    pos: vec2(0, 0),
    tiles: {
      "=": () => [rect(block_size, block_size), color(255, 0, 0), area(), "wall"],
    }
  }
);

const directions = {
  UP: "up",
  DOWN: "down",
  LEFT: "left",
  RIGHT: "right",
};

let current_direction = directions.RIGHT;
let run_action = false;
let snake_length = 3;
let snake_body = [];


function respawn_snake() {
  destroyAll("snake");

  snake_body = [];
  snake_length = 3;

  for (let i = 1; i <= snake_length; i++) {
    let segment = add([
      rect(block_size, block_size),
      pos(block_size, block_size * i),
      color(0, 0, 255),
      // 主要是改动了这些,缩小了蛇身体的碰撞区域
      area({ shape: new Rect(vec2(0+block_margin), block_size-2*block_margin, block_size-2*block_margin) }),
      "snake",
    ]);
    snake_body.push(segment);
  }
  current_direction = directions.RIGHT;
}

function respawn_all() {
  run_action = false;
  wait(0.5, function() {
    respawn_snake();
    respawn_food();
    run_action = true;
  });
}

respawn_all();



onKeyPress("up", () => {
  if (current_direction != directions.DOWN) {
    current_direction = directions.UP;
  }
});

onKeyPress("down", () => {
  if (current_direction != directions.UP) {
    current_direction = directions.DOWN;
  }
});

onKeyPress("left", () => {
  if (current_direction != directions.RIGHT) {
    current_direction = directions.LEFT;
  }
});

onKeyPress("right", () => {
  if (current_direction != directions.LEFT) {
    current_direction = directions.RIGHT;
  }
});




let move_delay = 0.2;
let timer = 0;
onUpdate(() => {
  if (!run_action) return;
  timer += dt();
  if (timer < move_delay) return;
  timer = 0;

  let move_x = 0;
  let move_y = 0;

  switch (current_direction) {
    case directions.DOWN:
      move_x = 0;
      move_y = block_size;
      break;
    case directions.UP:
      move_x = 0;
      move_y = -1 * block_size;
      break;
    case directions.LEFT:
      move_x = -1 * block_size;
      move_y = 0;
      break;
    case directions.RIGHT:
      move_x = block_size;
      move_y = 0;
      break;
  }

  // Get the last element (the snake head)
  let snake_head = snake_body[snake_body.length - 1];

  snake_body.push(
    add([
      rect(block_size, block_size),
      pos(snake_head.pos.x + move_x, snake_head.pos.y + move_y),
      color(0, 0, 255),
      // 主要是改动了这些,缩小了蛇身体的碰撞区域
      area({ shape: new Rect(vec2(0+block_margin), block_size-2*block_margin, block_size-2*block_margin) }),
      "snake",
    ])
  );

  if (snake_body.length > snake_length) {
    let tail = snake_body.shift(); // Remove the last of the tail
    destroy(tail);
  }
});



let food = null;

function respawn_food() {
  let new_pos = rand(vec2(1, 1), vec2(13, 13));
  new_pos.x = Math.floor(new_pos.x);
  new_pos.y = Math.floor(new_pos.y);
  new_pos = new_pos.scale(block_size);

  if (food) {
    destroy(food);
  }
  food = add([
    rect(block_size, block_size),
    color(0, 255, 0),
    pos(new_pos),
    area({ shape: new Rect(vec2(0+block_margin), block_size-2*block_margin, block_size-2*block_margin) }),
    "food",
  ]);
}

onCollide("snake", "food", (s, f) => {
  snake_length++;
  respawn_food();
});


onCollide("snake", "wall", (s, w) => {
  run_action = false;
  shake(12);
  respawn_all();
});

onCollide("snake", "snake", (s, t) => {
  run_action = false;
  shake(12);
  respawn_all();
});