DEV Community

Hightopo
Hightopo

Posted on • Edited on

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:
The video of the game

The snapshoot of the game

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:

  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);
Enter fullscreen mode Exit fullscreen mode

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.

/**
 * 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;
}      
Enter fullscreen mode Exit fullscreen mode

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.

/**
 * 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();                        
}        
Enter fullscreen mode Exit fullscreen mode

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
/**
 * 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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Find more cool demos here

Top comments (2)

Collapse
 
htho profile image
Hauke T.

This is a nice take at the game.
Just today I published my take of the Worm/Snake game in 2D:
dev.to/htho/wibbles-web-nibbles-re...

Collapse
 
batty_bash_96cdc57dac4af1 profile image
Batty Bash

ITS NICE AND LOVELY