How to create a 3D snake game with Javascript (attached source code and game link)

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Hightopo
    New Member
    • Nov 2022
    • 1

    How to create a 3D snake game with Javascript (attached source code and game link)

    Let's take a look at the final effect of the game at first:
    Watch it on YouTube

    Now let's talk about how to achieve it with Javascript.
    The game is made using HT for Web, with about 100 lines of code.

    Initialize the scene

    First, let's do some initialization work, including initializing the 3D scene, setting the ground grid, and enabling event monitoring, etc. The main code and comments are as follows:
    Code:
    w = 40; // the grid gap
      m = 20; // the count of rows/columns of grid
      d = w * m / 2;
      food = null;            
      dm = new ht.DataModel(); // create one data model, which saves all the  grid, snake and food
      g3d = new ht.graph3d.Graph3dView(dm); // initialize one 3d scene  
      // config the grid on ground             
      g3d.setGridVisible(true);
      g3d.setGridColor('#29B098');
      g3d.setGridSize(m);
      g3d.setGridGap(w);    
    
      // add the 3d scene to body
      view = g3d.getView();            
      view.className = 'main';
      document.body.appendChild(view);   
    
      // monitor the resize event and mouse click event
      window.addEventListener('resize', function (e) {  g3d.invalidate(); }, false);                                                                                            
      g3d.sm().setSelectionMode('none');
      view.addEventListener(ht.Default.isTouchable ? 'touchstart' : 'mousedown', function(e){                
          if(isRunning){
              var p = g3d.getHitPosition(e); // get the position in scene when mousedown
              // calculate the move direction with the click position 
              if(Math.abs(p[0]) < d && Math.abs(p[2]) < d){
                  if(direction === 'up' || direction === 'down'){
                      direction = p[0] > snake[0].p3()[0] ? 'right' : 'left';                       
                  }
                  else if(direction === 'left' || direction === 'right'){
                      direction = p[2] > snake[0].p3()[2] ? 'down' : 'up';                                             
                  }                        
              }
          }else if(ht.Default.isDoubleClick(e)){
              start(); // double click the scene to start the game
          }                
      }, false);                        
      start();            
      // the snake move forward every 200ms
      setInterval(function(){ if(isRunning){ isRunning = next(); } }, 200);
    Create the food

    Every time the gluttonous snake eats a piece of food, its body will grow longer. At this point, new food needs to be created and randomly and placed in a new location. When creating food, its position should not coincide with the previous position, nor repeated with the current snake body.
    Code:
    /**
     * Create the food and place it in a random position
     * The food should not dup with snake or last food
     *
     */
    function createFood(){
      var x = getRandom(), y = getRandom();
      // skip the snake body and last food
      while(touchFood(x, y) || touchSnake(x, y)){ x = getRandom(); y = getRandom(); }
      if(food) dm.remove(food);            
      food = createNode(x, y); 
      food.s({'shape3d': 'sphere',  'shape3d.color': 'red'});
    }        
    /**
     * whether the given position (x, y) is dup with the snake body
     *
     * @param {*} x
     * @param {*} y
     * @return {*} 
     */
    function touchSnake(x, y){
      for(var i=0; i<snake.length; i++){                
          if(snake[i].a('x') === x && snake[i].a('y') === y){ return true; }
      }
      return false;
    }        
    /**
     * whether the given position (x, y) is dup with the food
     *
     * @param {*} x
     * @param {*} y
     * @return {*} 
     */
    function touchFood(x, y){
      return food && food.a('x') === x && food.a('y') === y;
    }
    Create the snake

    In the first step, we set the grid size and gap. In this way, the length and width of the entire grid and the size of each block of the snake are determined. In this step, we add boundaries to the mesh and then generate the snake.
    Code:
    /**
     * clear the scene, create the snake and wall
     *
     */
    function start(){
      dm.clear(); snake = []; score = 0; direction = 'up'; isRunning = true;
      // create wall
      shape = new ht.Shape();
      shape.setPoints(new ht.List([
          {x: -d, y: d},
          {x: d, y: d},
          {x: d, y: -d},
          {x: -d, y: -d},
          {x: -d, y: d}
      ]));
      shape.setThickness(4);
      shape.setTall(w);
      shape.setElevation(w/2);
      shape.s({'all.color': 'rgba(20, 120, 120, 0.5)', 'all.transparent': true, 'all.reverse.cull': true});
    
      dm.add(shape);                 
      // create the snake       
      for(var i=0; i<m/2; i++) { snake.push(createNode(m/2 + i, m/2)); }            
      createFood();                        
    }
    Make the snake move forward

    After having the snake and the food, the next step is to deal with the logic of the snake walking. include:

    1. whether the snake has reached the border or touched its body
    2. when the snake touched the food, its body gets longer
    3. in other cases, move forward
    Code:
    /**
     * calculate nest position based on direction. and check:
     * 1. whether the snake has reached the border or touched its body
     * 2. when the snake touched the food, its body gets longer
     * 3. in other cases, move forward
     *
     * @return {*} 
     */
    function next(){
      var node = snake[0], x = node.a('x'), y = node.a('y');
      if(direction === 'up') y--;
      if(direction === 'down') y++;       
      if(direction === 'left') x--;
      if(direction === 'right') x++;
      if(x < 0 || x >= m || y < 0 || y >= m || touchSnake(x, y)){ return false; }                        
      if(touchFood(x, y)){
          score++;                
          snake.splice(0, 0, createNode(x, y));                
          createFood();
      }else{
          snake.splice(0, 0, createNode(x, y));
          dm.remove(snake.pop());                
      }
      return true;
    }
    At this point, the whole snake game is complete. Quite simple, right? Double click on the scene to start the game or click on the scene to change the direction of the snake movement.

    You can also directly click the link below to try the game:
    3D Gluttonous Snake

    To get the source code, just open the game link and press F12.
  • HarrySto
    New Member
    • Nov 2022
    • 10

    #2
    By the way, a little off-topic, but it may be useful. There are good resources for code review and analysis - AppRefactoring and Code Scene

    Comment

    Working...