This greedy snake seems to be a bit complicated. After playing for a long time, 20 points have not been obtained.

I am participating in the individual competition of the Nuggets Community Game Creative Contest. For details, please see:Game Creative Contest

Hi everybody, I'mone bowl week, a front end that doesn't want to be drunk (involuted). If the article written is fortunate enough to get your favor, I am very fortunate~

write in front

Everyone is familiar with the game Snake. The first time I came into contact with this game was on a Nokia mobile phone. I believe everyone has seen this mobile phone. It looks like this:

200px-Nokia_1110_DG_01.jpg

In this article, we use front-end technology to implement this small game. Most of the code of this game is written in TS+Less, just to practice TS.

The game demo is as follows:

demo.gif

Online experience address :ywanzhou.github.io/eating-snak…

HTML structure

First of all, let's determine our HTML structure. The code is as follows:

<body>
  <!-- 创建游戏的主窗口 -->
  <div id="main">
    <!-- 主要区域 -->
    <div id="state">
      <!-- 蛇 -->
      <div id="snake">
        <div></div>
      </div>
      <!-- 设置食物 -->
      <div id="food"></div>
    </div>
    <!-- 展示 -->
    <div id="score-panel">
      <div>Score:<span id="score">0</span></div>
      <div>Level: <span id="level">1</span></div>
    </div>
  </div>
</body>
复制代码

The HTML structure is relatively simple, and the CSS style is not complicated. You can do it yourself if you need it.githubCheck.

food

First, let's write the food class. First, we need to get the elements in the HTML structure, then we can get the coordinates of the x-axis and y-axis of the food, and finally define a method to change its coordinates. The sample code is as follows:

// 定义食物类 food
class Food {
  // 定义一个属性表示食物所对应的元素  名称element:类型是 HTMLElement
  element: HTMLElement
  constructor() {
    // ! 表示document.getElementById('food')已经确定可以找到对应的元素不会为空
    this.element = document.getElementById("food")!
  }
  // 获取 x y 轴坐标
  get X() {
    return this.element.offsetLeft
  }
  get Y() {
    return this.element.offsetTop
  }

  change() {
    // 生成一个随机的位置来表示食物  位置最小为0 最大为290  必须是10的倍数
    let top = Math.round(Math.random() * 29) * 10
    let left = Math.round(Math.random() * 29) * 10

    this.element.style.left = left + "px"
    this.element.style.top = top + "px"
  }
}

export default Food

复制代码

snakes

First we define attributes to represent elements, the code is as follows:

class Snake {
  // 表示蛇儿头
  head: HTMLElement
  // 表示蛇的身体(包括蛇头)
  bodies: HTMLCollection
  // 蛇的父级元素
  element: HTMLElement
  constructor() {
    // 元素初始化
    this.element = document.getElementById("snake")!
    this.head = document.querySelector("#snake > div") as HTMLElement
    this.bodies = this.element.getElementsByTagName("div")
  }
}

复制代码

Then define two methods, one for adding the snake's body and one for moving the snake. The sample code is as follows:

// 蛇增加身体的方法
addBody() {
  // 向element 中添加一个div
  this.element.insertAdjacentHTML("beforeend", "<div></div>")
}

// 添加一个蛇身体移动的方法
moveBody() {
  /* 
    将后一节的位置设置为前一节的位置,比如第二节的位置设置为蛇头的位置

    因为要先获取前一个节的位置所有需要从后往前遍历 i>0
  */

  for (let i = this.bodies.length - 1; i > 0; i--) {
    // 获取前边身体的位置   类型“Element”上不存在属性“offsetLeft”需要来一个类型断言
    let X = (this.bodies[i - 1] as HTMLElement).offsetLeft
    let Y = (this.bodies[i - 1] as HTMLElement).offsetTop

    // 将值设置到当前身体上
    ;(this.bodies[i] as HTMLElement).style.left = X + "px"
    ;(this.bodies[i] as HTMLElement).style.top = Y + "px"
  }
}
复制代码

Obtaining the coordinates of the snake head is relatively simple to implement. The code is as follows:

// 获取蛇的坐标(蛇头坐标)
get X() {
  return this.head.offsetLeft
}
get Y() {
  return this.head.offsetTop
}
复制代码

Define a method to detect if it hits the body, the code is as follows:

// 检查蛇头是否撞到身体的方法
checkHeadBody() {
  // 获取所有的身体,检查其是否和蛇头的坐标发生重叠
  /* 
    let i = 1; i < this.bodies.length; i++ 
    从1 开始不包含蛇头0 
  */
  for (let i = 1; i < this.bodies.length; i++) {
    let bd = this.bodies[i] as HTMLElement
    if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
      // 进入判断说明蛇头撞到了身体,游戏结束
      throw new Error("不要自己撞自己哦~")
    }
  }
}
复制代码

Then we set the coordinates of the snake head, and we need to add some logical judgments in the implementation process, such as not hitting the wall, turning up when not moving down, etc.;

code show as below:

// 设置蛇头的坐标
set X(value: number) {
  // 如果新值和旧值相同,则直接返回不再修改
  if (this.X === value) {
    return
  }
  // 判断蛇有没有在规定的范围内移动
  if (value < 0 || value > 290) {
    throw Error("你的小蛇撞墙了~")
  }

  // 修改x时,是在修改水平坐标,蛇在左右移动,蛇在向左移动时,不能向右掉头,反之亦然
  /*  
    this.bodies[1]  表示先检查有没有第二节身体
    (this.bodies[1] as HTMLElement).offsetLeft === value 表示第二节身体的X值与蛇头的X值相同
  */
  if (
    this.bodies[1] &&
    (this.bodies[1] as HTMLElement).offsetLeft === value
  ) {
    if (value > this.X) {
      value = this.X - 10
    } else {
      value = this.X + 10
    }
  }
  // 移动身体
  this.moveBody()
  this.head.style.left = value + "px"

  // 检查有没有撞到自己
  this.checkHeadBody()
}

set Y(value: number) {
  // 如果新值和旧值相同,则直接返回不再修改
  if (this.Y === value) {
    return
  }
  // 判断蛇有没有在规定的范围内移动

  if (value < 0 || value > 290) {
    throw Error("你的小蛇撞墙了~")
  }

  // 修改Y时,是在修改垂直坐标,蛇在左右移动,蛇在向上移动时,不能向下掉头,反之亦然
  if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
    if (value > this.Y) {
      value = this.Y - 10
    } else {
      value = this.Y + 10
    }
  }

  // 移动身体
  this.moveBody()
  this.head.style.top = value + "px"
  // 检查有没有撞到自己
  this.checkHeadBody()
}
复制代码

Finally export this class.

export default Snake
复制代码

grade class

Now we define a method to achieve the recording of scores and grades, the code is as follows:

class ScorePanel {
  // 分别记录分数和等级
  score = 0;
  level = 1;

  // 分数和等级所在的元素,在构造函数中进行初始化
  ScoreEle: HTMLElement;
  LevelEle: HTMLElement;
  // 设置一个变量限制等级
  maxLevel: number;
  upScore: number;

  constructor(maxLevel: number = 10, upScore: number = 10) {
    this.ScoreEle = document.getElementById("score")!;
    this.LevelEle = document.getElementById("level")!;
    this.maxLevel = maxLevel;
    this.upScore = upScore;
  }

  // 设置一个加分的方法
  addScore() {
    // 使分数自增
    this.ScoreEle.innerHTML = ++this.score + "";
    if (this.score % this.upScore === 0) {
      this.UpLevel();
    }
  }
  UpLevel() {
    // 使等级自增
    if (this.level < this.maxLevel) {
      this.LevelEle.innerHTML = ++this.level + "";
    }
  }
}

export default ScorePanel;

复制代码

Game Controller

We have implemented the previous definitions. Now we will apply the previous three classes. The implementation code is as follows:

import ScorePanel from "./ScorePanel";
import Snake from "./snake";
import Food from "./Food";

// 游戏控制器,控制其他的所有类
class GameControl {
  // 定义三个属性
  snake: Snake;
  food: Food;
  // 记分牌
  scorePanel: ScorePanel;
  // 创建一个属性用来存储蛇的移动方向(就是键盘的方向键)
  direction: string = "";
  // 创建一个属性用来记录游戏是否结束
  isLive = true;

  constructor() {
    this.snake = new Snake();
    this.food = new Food();
    this.scorePanel = new ScorePanel(10, 1);

    // 游戏初始化
    this.init();
  }
  // 初始化游戏的方法,调用后游戏开始
  init() {
    document.addEventListener("keydown", this.keydownHandler.bind(this));
    this.run();
  }
  // 创建一个键盘按下的响应函数
  keydownHandler(event: KeyboardEvent) {
    // 当用户按下键盘的键之后,需要判断用户按下的键是否符合目标的键

    //  当用户按下键盘时存储键
    this.direction = event.key;
  }

  // 定义一个蛇移动起来的方法
  run() {
    // 获取蛇的坐标
    let X = this.snake.X;
    let Y = this.snake.Y;

    // 根据按键方向来修改蛇的坐标值
    switch (this.direction) {
      // 上移 top减少
      case "ArrowUp":
        Y -= 10;
        break;
      case "ArrowDown":
        Y += 10;
        break;
      case "ArrowLeft":
        X -= 10;
        break;
      case "ArrowRight":
        X += 10;
        break;
      default:
        break;
    }

    // 检查蛇是否吃到了食物
    this.checkEat(X, Y);

    // 修改蛇的X和Y值
    try {
      this.snake.X = X;
      this.snake.Y = Y;
    } catch (e: any) {
      // 捕获异常弹出消息提示
      alert(e.message);
      // isLive改为false 表示游戏结束
      this.isLive = false;
    }
    // 开启一个定时器run()一直执行
    this.isLive &&
      setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);
  }

  // 定义一个方法,用来检查蛇是否吃到食物
  checkEat(X: number, Y: number) {
    if (X === this.food.X && Y === this.food.Y) {
      // 食物位置刷新
      this.food.change();
      // 加一分
      this.scorePanel.addScore();
      // 蛇的身体增加一格
      this.snake.addBody();
    }
  }
}

export default GameControl;

复制代码

At this point most of the code for the game has been completed.

write at the end

All code has been placedGitHubAbove, the code is not complicated and is only suitable for learning.

PS: If you are a big guy, or think this article is garbage, please ignore this article, thank you.

Guess you like

Origin juejin.im/post/7079437726658854920