Recently, the company’s business is not busy, taking advantage of the time to learn about vue3 new knowledge, incidentally made a snake, code address

Snake implementation principle

Game interface Settings

Given that we’re in a data-driven age, I’m going to abandon the idea of manipulating dom directly and instead manipulate arrays. Set up a 25×25 square game interface (as shown below)

  1. The grid array stores information as [top to bottom coordinate position, left to right coordinate position, the grid color (0 is white, 1 is red, 2 is green, 3 is yellow)]
  2. Store all of these squares as a two-dimensional array, which is the current interface of the game (make the two-dimensional array responsive so that the color of each square is displayed in a timely manner)

The game interface is initialized as follows

let checkerboardInfo = reactive([]); Reactive reference data type Settings are handled by Reactive () in VUe3

// Set up the game interface
const setCheckerboard = () = > {
  for (let i = 0; i < 25; i++) {
    for (let j = 0; j < 25; j++) {
      checkerboardInfo.push([i, j, 0]); }}}Copy the code
Food and snake Settings

The initialization position of the snake is in the middle of the game interface (only the head of the snake is initialized. The head of the snake is represented by a red background and the body of the snake is represented by a green background).

// A 2-d array of snake coordinates
let snakeInfo = [];

// Initialize (snake is always generated in the center)
const setSnake = () = > {
  snakeInfo.push([12.12]);
  
  // The snake head is represented by the coordinate +1
  // The snake body is represented by the coordinate +2
  // Change the background color of this grid
  changeCheckerboard([12.12].1)}; setSnake();Copy the code

In gluttonous snakes, food will randomly appear on the map one at a time, and when eaten by a snake, a new food will randomly appear on the map again. Food cannot come out of the snake, nor can it appear outside the game interface (food is represented by a yellow background)

// The coordinate point where food is stored
let food = null;

// Set the food (make sure the food does not appear on the snake)
const setFood = () = > {
  food = null;
  
  // Randomly generate food coordinates (within the game interface)
  const A = Math.floor(Math.random()*(25));
  const B = Math.floor(Math.random()*(25));

  // Set the benchmark to see if there are food coordinates in the snake 2d array
  let flag = false;
  
  // Walk through the snake array
  for (let index = 0; index < snakeInfo.length; index++) {
    let [x, y, num] = snakeInfo[index];
    
    // If the food coordinates jump out of the loop in the snake array
    if( A===x && B===y ){
      flag = true;
      break}};// If the food coordinates point, then recursively regenerate the food coordinates
  if(flag){
    setFood();
  }else{
    // Food is represented by the coordinate +3, which changes the background color of this grid
    changeCheckerboard([A, B], 3)
    food = [A, B];
  }
}
setFood();
Copy the code

Extract a method called changeCheckerboard to render a single square color

// Change the checkerboard grid color ([A, B] is the coordinate, color is what color needs to render (0 is white, 1 is red, 2 is green, 3 is yellow))
const changeCheckerboard = ([A, B], color) = > {
  for (let i = 0; i < checkerboardInfo.length; i++) {
      let [x, y, num] = checkerboardInfo[i];
      
      // Change the grid color if the coordinate value of the two-dimensional array is equal to the value of the passed coordinate
      if( A===x && B===y ){
      
        Vue3 has changed the way reactive data is written so that direct changes to array values can be heard
        checkerboardInfo[i][2]=color;
        break}}}Copy the code

Extract a method to clear the background color of all squares in the current game interface

// Clear the board color
const clearCheckerboard = () = > {
  for (let index = 0; index < checkerboardInfo.length; index++) {
    if(checkerboardInfo[index][0]===food[0]&&checkerboardInfo[index][1]===food[1]) {continue
    };
    checkerboardInfo[index][2] = 0}};Copy the code
The movement of the snake

Snake movement is divided into five cases:

  1. No food on the move
  2. Eat food while moving
  3. Move beyond the boundaries of the game interface
  4. Move and eat yourself
  5. I didn’t eat myself or go beyond the boundaries of the game
Did not eat food while moving (picture below)

The conclusion is drawn through observation: the length of the snake does not change, that is, the length of the two-dimensional array storing the snake does not change, but the coordinate position of the moved square is stored in the two-dimensional array storing the snake, and then the last coordinate of the two-dimensional array storing the snake is deleted

// There is no food (arr is the grid where the snake moves)
const notEatFood = (arr) = > {

  // Add a coordinate array to the head of the array and a coordinate array to the tail (length unchanged)
  // The snake head is always the first coordinate
  snakeInfo.unshift(arr);
  snakeInfo.pop();
  clearCheckerboard();
  
  // Change the information stored in the 2d array of the game interface by traversing the 2d array of the snake
  for (let index = 0; index < snakeInfo.length; index++) {
    if( index === 0 ){
      
      changeCheckerboard(arr, 1)}else{
      changeCheckerboard(snakeInfo[index], 2)}}}Copy the code
Eat food while moving

Before eating food

After eating the food

Through observation, it is concluded that the length of the snake +1, that is, new coordinate points are added to the two-dimensional array storing the snake, and the moving grid coordinate positions are stored in the two-dimensional array storing the snake. After eating the food, it can be regenerated into food

// Eat food
const eatFood = (arr) = > {

  // When you eat the food, add a new coordinate array to the beginning of the snake array (array length + 1)
  snakeInfo.unshift(arr);
  clearCheckerboard();
  for (let index = 0; index < snakeInfo.length; index++) {
    if( index === 0 ){
      changeCheckerboard(arr, 1)}else{
      changeCheckerboard(snakeInfo[index], 2)}};// The food coordinates must be regenerated after the food is eaten
  setFood();
};
Copy the code
Move beyond the boundaries of the game interface
const judgmentBoundary = () = > {
  for (let index = 0; index < snakeInfo.length; index++) {
  
    // Any coordinates on the snake outside the game interface return false
    if( (snakeInfo[index][0] >24||snakeInfo[index][0] <0) || (snakeInfo[index][1] >24||snakeInfo[index][1] <0) ){
      return false; }};return true
};
Copy the code
Move and eat yourself
// Check if you touch the snake while moving
const didYouTouchTheSnake = (arr) = >{
  for (let index = 0; index < snakeInfo.length; index++) {
    // Any coordinate on the snake equal to the point passed in returns false
    if(arr[0]===snakeInfo[index][0]&&arr[1]===snakeInfo[index][1]) {return true}}return false
}
Copy the code
Mobile function

The next movement of the snake must be one of the four coordinates surrounding the snake’s head position (as shown below). So listen to the keyboard event and get the value of the case in the current direction (40: below; 38:; 37: left; 39: right;) To control the movement of the snake

// Direction (vue3 basic type data needs to use.value to get the set or changed value)
let direction = ref(0);

/ / event
const listener = (event) = >{
  // Only listen for four keys
  const keyCodeArr =  [37.38.39.40];
  if(keyCodeArr.includes(event.keyCode)){
    direction.value = event.keyCode;
    // Just click on it and it will instantly move the snake
    snakeMove(direction.value)
  }
}

// Mount event listeners on Windows
window.addEventListener("keydown", listener)
Copy the code
// Set the bar for failure
const isFail = ref(false); // Basic data types are handled using ref() in VUe3

// Move the snake
const snakeMove = (num) = > {
  // Skip directly after failure
  if( isFail.value ){
    return
  }

  // The coordinates of where to go next
  let arr = [...snakeInfo[0]].// The coordinate array for the next step can only be the four coordinates around the greedy snake head.
  switch (num) {
    // The keyboard corresponds to the following numbers
    / / 40: under; 38; On; 37: left; 39: right;
    case 37:
      arr[1] = arr[1] -1
      break;
    case 38:
      arr[0] = arr[0] -1
      break;
    case 39:
      arr[1] = arr[1] +1
      break;
    case 40:
      arr[0] = arr[0] +1
      break;
  };

  // When the snake touches itself
  if(didYouTouchTheSnake(arr)){
    isFail.value = true;
  }

  // Determine whether food is eaten
  if(food[0]===arr[0]&&food[1]===arr[1]){
    eatFood(arr);
  }else{
    notEatFood(arr);
  }

  // When the snake is out of bounds
  if(! judgmentBoundary()){ isFail.value =true; }};Copy the code

The snake is a snake that can only be moved by repeatedly clicking the button.

How do you make the snake move on its own?

By monitoring the change of the direction (direction) value and using the timer, as long as the direction key value changes to do the following process

// Hold the null pointer to the timer
let timer = null

watchEffect(() = >{

  // Use a listener to monitor the value of the changed direction (move along the changed direction every 0.2s)
  switch (direction.value) {
    // After the value changes, turn off the previous timer and then start a new timer command about snake body movement
    case 37:
      timer&&clearInterval(timer);
      timer = setInterval(() = >{
        snakeMove(direction.value)
      },200)
      break;
    case 38:
      timer&&clearInterval(timer);
      timer = setInterval(() = >{
        snakeMove(direction.value)
      },200)
      break;
    case 39:
      timer&&clearInterval(timer);
      timer = setInterval(() = >{
        snakeMove(direction.value)
      },200)
      break;
    case 40:
      timer&&clearInterval(timer);
      timer = setInterval(() = >{
        snakeMove(direction.value)
      },200)
      break;
    case 0:
      timer&&clearInterval(timer);
      break;
  };
});
Copy the code
Vue component structure relationship

home.vue -> Checkerboard.vue ->CheckerboardItem.vue

home.vue

<template>
  <div class="home">
    <Checkerboard :checkerboardInfo='checkerboardInfo' />
  </div>
</template>
Copy the code

checkerboard.vue

<template>
  <div class="checkerboard">
    <CheckerboardItem v-for="item in checkerboardInfo" :key="item" :checkerboardItemInfo='item'/>
  </div>
</template>
Copy the code

CheckerboardItem.vue

The color of the current grid is determined only by the stored grid color information (the third value in the grid information array)

const props = defineProps({
  checkerboardItemInfo:Array});// Deconstruct the third value of the grid information array with toRefs (only toRefs can keep the reference data deconstructed and responsive)
let [ x, y, num] = toRefs(props.checkerboardItemInfo)

let color = ref(' ');

// Use the listener to complete the data listening, set different values for the background color
watchEffect(() = >{
  switch (num.value) {
    case 0:
      color.value = 'while'
      break;
    case 1:
      color.value = 'red'
      break;
    case 2:
      color.value = 'green'
      break;
    case 3:
      color.value = 'orange'
      break; }})Copy the code
<style lang="less" scoped>
.checkerboardItem{
  Vue3.2 can use v-bind to bind responsive data in CSS
  background-color: v-bind(color);
}
</style>
Copy the code

Ok, snake fix, like coin ➕ concern oh, brothers (this is the first article in the next [dog head (#^.^#)])