How to create a 3D snake game with Javascript (attached source code and game link)
Tags: javascript, tutorial, gamedev, webgl
Published: 2022-11-29 09:59:31

Let's take a look at the final effect of the game at first:
[The video of the game](https://youtu.be/8hrx16-jX_U)

![The snapshoot of the game](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kvllptf434fxocf6wur.gif)

Now let's talk about how to achieve it with Javascript.
The game is made using [HT for Web](https://www.hightopo.com/demos/en-index.html), 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);
```

**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;
}      
```

**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();                        
}        
```

**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;
}
```

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](https://hightopo.com/demo/snake_20151106/GreedySnake.html)

To get the source code, just open the game link and press F12.

[Find more cool demos here](https://hightopo.com/demos/en-index.html)