<canvas id="canvas-background" width="800" height="600"></canvas>
<canvas id="canvas-demo" width="800" height="600"></canvas>
<div id="points">0</div>
<div id="info">Press "space" key to start</div>
<div id="help">The movement is relative to the snake, so you only use the left and right keys.</div>
#canvas-background {
  background-color: #f4f4f4;
  position: absolute;
  left: 0px;
  top: 0px;
  z-index: 1;
}

#canvas-demo {
  background-color: transparent;
  position: absolute;
  left: 0px;
  top: 0px;
  z-index: 10;
}

#points {
  position: absolute;
  left: 20px;
  top: 20px;
  z-index: 20;
  font-size: 25px;
  width: 50px;
  text-align: right;
  border: 1px solid #999999;
  padding-left: 10px;
  padding-right: 10px;
}

#info {
  position: absolute;
  left: 530px;
  top: 20px;
  z-index: 20;
  font-size: 20px;
  text-align: center;
  width: 250px;
}

#help {
  position: absolute;
  left: 530px;
  top: 550px;
  z-index: 20;
  font-size: 15px;
  text-align: center;
  width: 250px;
}
var SIZE = 20;
var WIDTH = 20;
var HEIGHT = 20;
var KEY = { LEFT: 37, RIGHT: 39, SPACE: 32 };

var speed = 50;
var gameLoop;

var canvas = document.getElementById("canvas-demo");
var cBackground = document.getElementById("canvas-background");
var point = new obelisk.Point(400, 50);
var pixelView = new obelisk.PixelView(canvas, point);
var pVBackground = new obelisk.PixelView(cBackground, point);
var dimension = new obelisk.CubeDimension(SIZE, SIZE, SIZE);

var matrix;
var snake;
var movX = 0;
var movY = -1;
var grow = 0;
var points = 0;

var playing = false;
var pressed = false;
var gameover = false;

function createArray(width, height) {
  var arr = new Array(width);
  for (var i = 0; i < width; i++) {
    arr[i] = new Array(height);
  }

  return arr;
}

function init() {
  points = 0;
  document.getElementById("points").innerHTML = points;
  speed = 50;
  movX = 0;
  movY = -1;
  grow = 0;
  playing = false;
  pressed = false;

  matrix = createArray(WIDTH, HEIGHT);
  snake = new Array(4);

  snake[0] = [Math.floor(WIDTH / 2), Math.floor(HEIGHT / 2)];
  snake[1] = [Math.floor(WIDTH / 2), Math.floor(HEIGHT / 2) + 1];
  snake[2] = [Math.floor(WIDTH / 2), Math.floor(HEIGHT / 2) + 2];
  snake[3] = [Math.floor(WIDTH / 2), Math.floor(HEIGHT / 2) + 3];

  putSnake();
  putFruit();
  //matrix[Math.floor(WIDTH / 2)][Math.floor(HEIGHT / 2) - 5] = 3;
}

function putFruit() {
  var oPosX = (posX = Math.floor(Math.random() * WIDTH));
  var oPosY = (posY = Math.floor(Math.random() * HEIGHT));

  while (matrix[posX][posY]) {
    posX = (posX + 1) % WIDTH;
    if (posX == WIDTH) {
      posX = posX % WIDTH;
      posY = (posY + 1) % HEIGHT;
    }

    if (oPosX == posX && oPosY == posY) {
      clearInterval(gameLoop);
      playing = false;
      document.getElementById("info").innerHTML =
        "Awesome!!!<br> You finish the game!!!";
    }
  }

  matrix[posX][posY] = 3;
}

function putSnake(last) {
  if (
    snake[0][0] < 0 ||
    snake[0][0] >= WIDTH ||
    snake[0][1] < 0 ||
    snake[0][1] >= HEIGHT
  ) {
    document.getElementById("info").innerHTML =
      'Game Over <br> You hit the wall <br> Press "space" to restart';
    clearInterval(gameLoop);
    playing = false;
    gameover = true;
  } else {
    if (matrix[snake[0][0]][snake[0][1]] == 2) {
      document.getElementById("info").innerHTML =
        'Game Over <br> You eat your own body <br> Press "space" to restart';
      clearInterval(gameLoop);
      playing = false;
      gameover = true;
    } else {
      if (matrix[snake[0][0]][snake[0][1]] == 3) {
        // eat a fruit
        grow += 2;
        putFruit();
        points += 1;
        if (speed < 50) {
          speed++;
          clearInterval(gameLoop);
          gameLoop = setInterval(update, 10000 / speed);
        }

        document.getElementById("points").innerHTML = points;
      }

      matrix[snake[0][0]][snake[0][1]] = 1;
      for (var i = 1; i < snake.length; i++) {
        matrix[snake[i][0]][snake[i][1]] = 2;
      }
      if (last) {
        if (!grow) {
          matrix[last[0]][last[1]] = 0;
        } else {
          snake.push([last[0], last[1]]);
          grow--;
        }
      }
    }
  }
}

function moveSnake() {
  var last = [snake[snake.length - 1][0], snake[snake.length - 1][1]];
  for (var i = snake.length - 1; i > 0; i--) {
    snake[i][0] = snake[i - 1][0];
    snake[i][1] = snake[i - 1][1];
  }
  snake[0][0] = snake[0][0] + movX;
  snake[0][1] = snake[0][1] + movY;
  return last;
}

function update() {
  var last = moveSnake();
  putSnake(last);
  draw();
  pressed = false;
}

var colorBG = new obelisk.SideColor().getByInnerColor(
  obelisk.ColorPattern.GRAY
);

function drawBackground() {
  pVBackground.clear();

  for (var i = 0; i < WIDTH; i++) {
    var sideX0 = new obelisk.SideX(dimension, colorBG);
    var p3dX0 = new obelisk.Point3D(SIZE * i, 20 * SIZE, -SIZE);
    pVBackground.renderObject(sideX0, p3dX0);

    var sideY0 = new obelisk.SideY(dimension, colorBG);
    var p3dY0 = new obelisk.Point3D(20 * SIZE, i * SIZE, -SIZE);
    pVBackground.renderObject(sideY0, p3dY0);

    var sideY1 = new obelisk.SideY(dimension, colorBG);
    var p3dY1 = new obelisk.Point3D(0 * SIZE, i * SIZE, 0);
    pVBackground.renderObject(sideY1, p3dY1);

    var sideX1 = new obelisk.SideX(dimension, colorBG);
    var p3dX1 = new obelisk.Point3D(SIZE * i, 0 * SIZE, 0);
    pVBackground.renderObject(sideX1, p3dX1);
  }

  for (var i = 0; i < WIDTH; i++) {
    for (var j = 0; j < HEIGHT; j++) {
      var p3d = new obelisk.Point3D(i * SIZE, j * SIZE, 0);
      var brick = new obelisk.Brick(dimension, colorBG);
      pVBackground.renderObject(brick, p3d);
    }
  }
}

var colorBlue = new obelisk.CubeColor().getByHorizontalColor(
  obelisk.ColorPattern.BLUE
);
var cubeBlue = new obelisk.Cube(dimension, colorBlue);

var colorGreen = new obelisk.CubeColor().getByHorizontalColor(
  obelisk.ColorPattern.GRASS_GREEN
);
var cubeGreen = new obelisk.Cube(dimension, colorGreen);

var colorRed = new obelisk.PyramidColor().getByRightColor(
  obelisk.ColorPattern.WINE_RED
);
var pyramidRed = new obelisk.Pyramid(dimension, colorRed);

function draw() {
  pixelView.clear();

  for (var i = 0; i < WIDTH; i++) {
    for (var j = 0; j < HEIGHT; j++) {
      switch (matrix[i][j]) {
        case 1:
          // draw head snake
          var p3d = new obelisk.Point3D(i * SIZE, j * SIZE, 0);
          pixelView.renderObject(cubeBlue, p3d);
          break;
        case 2:
          // draw body snake
          var p3d = new obelisk.Point3D(i * SIZE, j * SIZE, 0);
          pixelView.renderObject(cubeGreen, p3d);
          break;
        case 3:
          // draw apple
          var p3d = new obelisk.Point3D(i * SIZE, j * SIZE, 0);
          pixelView.renderObject(pyramidRed, p3d);
          break;
        default:
          break;
      }
    }
  }
}

function onkeydown(e) {
  if (e.keyCode < 112 || e.keyCode > 123) {
    e.preventDefault();
  }

  if (playing) {
    if (!pressed) {
      switch (e.keyCode) {
        case KEY.LEFT:
          pressed = true;
          if (movY) {
            movX = movY;
            movY = 0;
          } else {
            movY = -movX;
            movX = 0;
          }
          break;
        case KEY.RIGHT:
          pressed = true;
          if (movY) {
            movX = -movY;
            movY = 0;
          } else {
            movY = movX;
            movX = 0;
          }
          break;
      }
    }
  } else {
    if (e.keyCode == KEY.SPACE) {
      if (gameover && e.keyCode == KEY.SPACE) {
        init();
        draw();
      }
      playing = true;
      pressed = true;
      gameLoop = setInterval(update, 10000 / speed);
      document.getElementById("info").innerHTML = "Eat all the fruit that you can";
    }
  }
}

document.addEventListener("keydown", onkeydown, false);

init();
drawBackground();
draw();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://nosir.github.io/obelisk.js/dist/obelisk.min.1.0.2.js