2
\$\begingroup\$

Snake on a plane 2.0

This is the first iteration of an iterative review. The next iteration is found here: 3D 1st Person Snake Game #2 When I figure out how to add wings, this snake is going to have hyper-wings. (Note: I am not asking for help with these being implemented) My code creates a 3d version of the snake game in this post

<html>
  <style>
    canvas{
      position:fixed;
      left:0;
      top:0;
      width:99%;
      height:99%;
    }
    * {
      padding:0px;
      margin:0px
    }
    #score,#speed,#highscore,#maxspeed,#lag,#debug{
      position: fixed;
      z-index: 100;
      font-size:20px;
      font-family:Verdana;
      left:15px;
      width: 100%;
    }
  </style>
  <div id="stats">
    <div id="debug"></div>
    <div id="score">Score: 0</div>
    <style id="scorestyle">
      #score {
        top: 10px;
        display:block;
      }
    </style>
    <div id="speed">Speed: 1</div>
    <style id="speedstyle">
      #speed {
        top: 30;
        display:block;
      }
    </style>
    <div id="highscore">Highscore: 1</div>
    <style id="highscorestyle">
      #highscore {
        top:30;
        display:block;
      }
    </style>
    <div id="maxspeed">Highest Speed: 1</div>
    <style id="maxspeedstyle">
      #maxspeed {
        width: 100%;
        top:50;
        display:none;
      }
    </style>
    <span id="lag">Lag: 0ms</span>
    <style id="lagstyle">
      #lag {
        top: 70px;
        display:none;
      }
    </style>
    <div id="gameover" align="center">Game Over</div>
    <style>
      #gameover{
        position:absolute;
        z-index: 100;
        font-size:60px;
        font-family:Verdana;
        margin: 0;
        top: 50%;
        left: 50%;
        opacity:0;
        transform: translate(-50%, -50%);
      }
    </style>
  </div>
  <div id="canvas"></div>
  <p id="p"></p>
  <p id="e"></p>
  <script type="text/javascript" src="https://cdn.rawgit.com/alexgibson/shake.js/master/shake.js"></script>
  <script src="https://threejs.org/build/three.min.js"></script>
  <script>
    //========================
    // One times
    //========================
    window.random = Math.random
    window.floor = function(a){ return ~~a}
    window.newGeometry = THREE.Geometry
    window.newBufferGeometry = THREE.BufferGeometry
    window.newMesh = THREE.Mesh
    window.newLineSegments = THREE.LineSegments
    window.newMeshBasicMaterial = THREE.MeshBasicMaterial
    window.newVector3 = THREE.Vector3
    window.newLineBasicMaterial = THREE.LineBasicMaterial
    window.newBoxGeometry = THREE.BoxGeometry
    window.newBoxBufferGeometry = THREE.BoxBufferGeometry
    window.arena = new THREE.Group()
    window.applegeometry = new THREE.SphereBufferGeometry( 0.5, 32, 32 )
    window.superapplematerial= new newMeshBasicMaterial({color:"gold"})
    window.applematerial= new newMeshBasicMaterial({color:"limegreen"})
    window.floormaterial = new newMeshBasicMaterial({color:"lightgrey"});
    window.wallmaterial = new newMeshBasicMaterial( { color: "grey" } )
    window.linecube = new newBoxBufferGeometry( 1,1,1 )
    window.edges = new THREE.EdgesGeometry( linecube )
    window.geometry = new newBoxBufferGeometry( 1,1,1 )
    window.stringify = JSON.stringify
    const arenaLines = new THREE.Group();
    const arenaWall = new newGeometry();
    const arenaFloor = new newGeometry();
    const wall1 = new newBoxGeometry( 41, 1, 1 ).translate(20,1,-10);
    arenaWall.merge(wall1);
    arenaWall.merge(wall1.translate(0,0,40));
    const wall2 = new newBoxGeometry( 1, 1, 41 ).translate(40,1,10);
    arenaWall.merge(wall2);
    arenaWall.merge(wall2.translate(-40,0,0));
    arenaFloor.merge(new newBoxGeometry( 41, 1, 41 ).translate(20,0,10))
    arenaWall.mergeVertices()
    arenaFloor.mergeVertices()
    // Get the meshs
    window.arenaWallMesh = new newMesh(arenaWall, wallmaterial);
    window.arenaFloorMesh = new newMesh(arenaFloor, floormaterial);


  </script>
  <script id="script">
    'use strict';


    var speed = 100,
        maxwidth = 40,
        maxdepdth = 40,
        size = 0.5,
        score = 0,
        maxheight = 0,
        lagNum = 0,
        highscoreNum,
        showlag,
        maxspeedNum,
        showmaxspeed,
        showhighscore

    //========================
    // Threejs
    //========================

    var scene = new THREE.Scene(),
        camera= new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,60),
        renderer = new THREE.WebGLRenderer(),
        extrasLookup = {
          1:30,
          2:50,
          3:70,
          4:90
        }
    //========================
    // Reductions
    //========================
    const newScene = THREE.Scene

    if(localStorage.getItem("3dhighscore")==undefined){
      highscoreNum=0
    } else {
      highscoreNum = localStorage.getItem("3dhighscore")
    }
    if(localStorage.getItem("showlag")==undefined){
      showlag=false
    } else {
      showlag = localStorage.getItem("showlag")
    }
    if(localStorage.getItem("3dmaxspeed")==undefined){
      maxspeedNum=100
    } else {
      maxspeedNum = localStorage.getItem("3dmaxspeed")
    }
    if(showmaxspeed==undefined){
      showmaxspeed=false
    }
    if(showhighscore==undefined){
      showhighscore=true
    }

    renderer.setSize( window.innerWidth, window.innerHeight );
    document.getElementById("canvas").appendChild( renderer.domElement );

    function animate() {
      requestAnimationFrame( animate )
      renderer.render( scene, camera );
    }
    function Location(x,y,z){
      return [x,y,z]
    }

    //========================
    // Dom
    //========================
    const maxSpeedDom = document.getElementById("maxspeed")
    var maxSpeedDomStyle = maxSpeedDom.style
    var lagdom = document.getElementById("lag")
    var lagdomstyle = lagdom.style
    var highScoreDom = document.getElementById("highscore")
    var highScoreDomStyle = highScoreDom.style
    var scoredom = document.getElementById("score")
    var speeddom = document.getElementById("speed")
    var gameOverDom = document.getElementById("gameover")
    var gameOverDomStyle = gameOverDom.style;
    //========================
    // Snake
    //========================
    function Snake() {
      this.segments = [
        Location(7,1,5),
        Location(6,1,5),
        Location(5,1,5)
      ];
      this.direction = "forwards";
      this.nextDirection = "forwards";
      this.nextHead = Location(8,1,5);
    };
    Snake.prototype = {
      draw(){
        let snakeobject = new THREE.Group();
        const j = this.segments.length
        this.segments.forEach(function(item,index){
          let i = index
          let segmentColor=`rgb(${255-(255/j|0)*index+randomNumberBetween(-(j-index),j-index)},00,00)`
          const cube = new newMesh(geometry, new newMeshBasicMaterial({ color: segmentColor})),
                x = item[0],
                y = item[1],
                z = item[2];
          const pos = new newVector3(x,y,z)

          const line = new newLineSegments(edges, new newLineBasicMaterial({ color: segmentColor,linewidth:2 }));
          cube.position.copy(pos);
          line.position.copy(pos);
          snakeobject.add(line,cube);
        })
        scene.add(snakeobject);
      },
      move() {
        this.segments.unshift(this.nextHead);
        if(this.checkCollision()) {
          this.segments.shift();
          gameOver();
          return;
        };
        var nextHeadPos = this.nextHead;
        this.direction = this.nextDirection;
        (directionSwitch[this.direction])(nextHeadPos)
        if(stringify(this.segments[0]) === stringify(apple.pos)) {
          speed=0.95*speed;
          score++;
          if(apple.type === "superapple") {
            speed = 1.10*speed;
            score += 4;
          }
          apple.move();
        } else {
          this.segments.pop();
        };
      },
      changeDirection(key) {
        switch(key){
          case 39:
          case 68:
          case 37:
          case 65:
            keyLookup[key][this.direction]()
            break;
          case 192:
          case 55:
          case 56:
            keyLookup[key]()
            break;
                  }
      },
      checkCollision() {
        const head = this.segments[0]
        const headz = head[2];
        const headx = head[0];
        const wallCollision = headz === -10 || headz === 30 || headx === 0 || headx === 40;
        var selfCollision = false;
        this.segments.forEach(function(item, index) {
          if(index) {
            if(item[0] === head[0] && item[2] === head[2]) {
              selfCollision = true;
            };
          };
        });
        const collision = selfCollision || wallCollision;
        return collision;
      }
    }

    //========================
    // Drawing
    //========================
    function drawScore() {
      var extras = 1;
      if(score > highscoreNum) {
        highscoreNum = score;
        //localStorage.setItem("3dhighscore", highscore);
      }
      if(speed < maxspeed) {
        maxspeedNum = speed;
        //localStorage.setItem("3dmaxspeed", maxspeed);
      }

      scoredom.innerHTML=`Score: ${score}`;
      speeddom.innerHTML=`Speed: ${(((100/speed*100)|0)/100)}`;

      if(showhighscore) {
        extras++;
        highscore.style.display = "block";
        highscore.style.top = extrasLookup[extras]
        highscore.innerHTML = `Highscore: ${highscoreNum}`;

      } else {
        highScoreDomStyle.display = "none";
      };

      if(showmaxspeed) {
        extras++;
        maxspeed.style.display = "block";
        maxspeed.style.top = extrasLookup[extras]
        maxspeed.innerHTML = `Highest Speed: ${(((100/maxspeed*100)|0)/100)}`;
      } else {
        maxspeed.style.display = "none";
      };

      if(showlag) {
        extras++;
        lag.style.display = "block";
        lag.style.top = extrasLookup[extras]
        lag.innerHTML = `Lag: ${lagNum}ms`;
      } else {
        lagdomstyle.display = "none";
      };
    }
    function renderArena() {
      ;
    }

    //========================
    // Apple
    //========================
    function Apple(){
      this.pos = Location(2,1,2)
    }
    Apple.prototype.move = function(){

      // Set the location to a random x and z.
      var x = randomNumberBetween(2,38)
      var z = randomNumberBetween(-8,28)
      this.pos = Location(x,1,z)
      if(randomNumberBetween(1,11)===1&&score>15){                  
        apple.type="superapple"
      } else {
        apple.type="apple"
      }
    }
    Apple.prototype.draw = function() {
      var sphere

      // Generate a new sphere.
      if(this.type=="superapple") {
        sphere = new newMesh( applegeometry, superapplematerial );
      } else {
        sphere = new newMesh( applegeometry, applematerial );
      }

      // Get the data location.
      var applepos=apple.pos

      // Set the location of the visual apple to the data location.
      sphere.position.set(applepos[0],applepos[1],applepos[2])
      scene.add(sphere);
    }
    //========================
    // Misc
    //========================
    function newGame(key) {
      if(key==82) {

        // Stop the game.
        clearTimeout(gameLoop);
        s=function(){};

        // Start a new game.
        var game = document.getElementById("canvas");
        game.removeChild(game.childNodes[0]);
        eval(document.getElementById("script").innerHTML);
      }
    }
    gameOverDomStyle.opacity=0;
    function gameOver() {
      s = function() {
        camera.position.copy(new newVector3(20,30,10));
        camera.lookAt(new newVector3(20,0,10));
      };
      document.onkeydown=function(e) {
        newGame(e.keyCode);    
      }
      //listen to shake event
      var shakeEvent = new Shake({threshold: 15});
      shakeEvent.start();
      window.addEventListener('shake', function(){
        newGame(82)
      }, false);

      //stop listening
      function stopShake(){
        shakeEvent.stop();
      }

      //check if shake is supported or not.
      if(!("ondevicemotion" in window)){alert("Not Supported");}
      gameOverDomStyle.opacity = 1;
    }

    function randomNumberBetween(a,b) {
      return a+floor(random()*(b-a));
    }
    var apple = new Apple(),
        snake = new Snake()
    var directionSwitch={
      "forwards":function(a){this.nextHead = Location(a[0]+1, a[1], a[2])},
      "backwards":function(a){this.nextHead = Location(a[0]-1, a[1], a[2])},
      "left":function(a){this.nextHead = Location(a[0], a[1], a[2]-1)},
      "right":function(a){this.nextHead = Location(a[0], a[1], a[2]+1)}
    }
    for(var x in directionSwitch){
      directionSwitch[x] = directionSwitch[x].bind(snake)
    }
    var right = {
      "right":function(){this.nextDirection = "backwards"},
      "backwards":function(){this.nextDirection = "left"},
      "forwards":function(){this.nextDirection = "right"},
      "left":function(){this.nextDirection = "forwards"}
    }
    for(var x in right){
      right[x] = right[x].bind(snake)
    }
    var left = {
      "right":function(){snake.nextDirection = "forwards"},
      "forwards":function(){snake.nextDirection = "left"},
      "left":function(){snake.nextDirection = "backwards"},
      "backwards":function(){snake.nextDirection = "right"}
    }
    for(var x in left){
      left[x] = left[x].bind(snake)
    }
    var keyLookup = {
      "39":right,
      "68":right,
      "37":left,
      "65":left,
      "192":function(){showlag=!showlag},
      "55":function(){showhighscore=!showhighscore},
      "56":function(){showmaxspeed = !showmaxspeed}
    }
    snake.draw();
    apple.move();

    var background = new THREE.Color( "white" ),
        fog = new THREE.FogExp2( "random", 0.02625 );

    var s = function() {
      // Start for lag detection.
      var start = performance.now();

      // Make a fresh scene.
      scene = new newScene();

      // Move the snake.
      snake.move();

      // Render various things onto the scene.
      scene.add(arenaWallMesh,arenaFloorMesh)
      snake.draw();
      apple.draw();
      drawScore();


      // Get relevent snake parts.
      var head = snake.segments[0];
      var nextHead = snake.nextHead;

      // Set camera position and target.
      camera.position.set(head[0],head[1],head[2]);
      camera.lookAt(new newVector3(nextHead[0],nextHead[1],nextHead[2]));

      // Set background and fog.
      scene.background = background;
      scene.fog = fog;
      var end = performance.now();
      // Reschedule the next update
      gameLoop = setTimeout(function(){
        requestAnimationFrame(s)
      },speed)

      // End for lag detection and calculation of lag.
      lagNum = floor((end-start))
    }
    animate()
    var gameLoop = setTimeout(function(){
      requestAnimationFrame(s)
    },speed);
    document.onkeydown = function(e) {
      snake.changeDirection(e.keyCode);
    }
  </script>

Here is a snippet:

<html>
  <style>
    canvas{
      position:fixed;
      left:0;
      top:0;
      width:99%;
      height:99%;
    }
    * {
      padding:0px;
      margin:0px
    }
    #score,#speed,#highscore,#maxspeed,#lag,#debug{
      position: fixed;
      z-index: 100;
      font-size:20px;
      font-family:Verdana;
      left:15px;
      width: 100%;
    }
  </style>
  <div id="stats">
    <div id="debug"></div>
    <div id="score">Score: 0</div>
    <style id="scorestyle">
      #score {
        top: 10px;
        display:block;
      }
    </style>
    <div id="speed">Speed: 1</div>
    <style id="speedstyle">
      #speed {
        top: 30;
        display:block;
      }
    </style>
    <div id="highscore">Highscore: 1</div>
    <style id="highscorestyle">
      #highscore {
        top:30;
        display:block;
      }
    </style>
    <div id="maxspeed">Highest Speed: 1</div>
    <style id="maxspeedstyle">
      #maxspeed {
        width: 100%;
        top:50;
        display:none;
      }
    </style>
    <span id="lag">Lag: 0ms</span>
    <style id="lagstyle">
      #lag {
        top: 70px;
        display:none;
      }
    </style>
    <div id="gameover" align="center">Game Over</div>
    <style>
      #gameover{
        position:absolute;
        z-index: 100;
        font-size:60px;
        font-family:Verdana;
        margin: 0;
        top: 50%;
        left: 50%;
        opacity:0;
        transform: translate(-50%, -50%);
      }
    </style>
  </div>
  <div id="canvas"></div>
  <p id="p"></p>
  <p id="e"></p>
  <script type="text/javascript" src="https://cdn.rawgit.com/alexgibson/shake.js/master/shake.js"></script>
  <script src="https://threejs.org/build/three.min.js"></script>
  <script>
    //========================
    // One times
    //========================
    window.random = Math.random
    window.floor = function(a){ return ~~a}
    window.newGeometry = THREE.Geometry
    window.newBufferGeometry = THREE.BufferGeometry
    window.newMesh = THREE.Mesh
    window.newLineSegments = THREE.LineSegments
    window.newMeshBasicMaterial = THREE.MeshBasicMaterial
    window.newVector3 = THREE.Vector3
    window.newLineBasicMaterial = THREE.LineBasicMaterial
    window.newBoxGeometry = THREE.BoxGeometry
    window.newBoxBufferGeometry = THREE.BoxBufferGeometry
    window.arena = new THREE.Group()
    window.applegeometry = new THREE.SphereBufferGeometry( 0.5, 32, 32 )
    window.superapplematerial= new newMeshBasicMaterial({color:"gold"})
    window.applematerial= new newMeshBasicMaterial({color:"limegreen"})
    window.floormaterial = new newMeshBasicMaterial({color:"lightgrey"});
    window.wallmaterial = new newMeshBasicMaterial( { color: "grey" } )
    window.linecube = new newBoxBufferGeometry( 1,1,1 )
    window.edges = new THREE.EdgesGeometry( linecube )
    window.geometry = new newBoxBufferGeometry( 1,1,1 )
    window.stringify = JSON.stringify
    const arenaLines = new THREE.Group();
    const arenaWall = new newGeometry();
    const arenaFloor = new newGeometry();
    const wall1 = new newBoxGeometry( 41, 1, 1 ).translate(20,1,-10);
    arenaWall.merge(wall1);
    arenaWall.merge(wall1.translate(0,0,40));
    const wall2 = new newBoxGeometry( 1, 1, 41 ).translate(40,1,10);
    arenaWall.merge(wall2);
    arenaWall.merge(wall2.translate(-40,0,0));
    arenaFloor.merge(new newBoxGeometry( 41, 1, 41 ).translate(20,0,10))
    arenaWall.mergeVertices()
    arenaFloor.mergeVertices()
    // Get the meshs
    window.arenaWallMesh = new newMesh(arenaWall, wallmaterial);
    window.arenaFloorMesh = new newMesh(arenaFloor, floormaterial);


  </script>
  <script id="script">
    'use strict';


    var speed = 100,
        maxwidth = 40,
        maxdepdth = 40,
        size = 0.5,
        score = 0,
        maxheight = 0,
        lagNum = 0,
        highscoreNum,
        showlag,
        maxspeedNum,
        showmaxspeed,
        showhighscore

    //========================
    // Threejs
    //========================

    var scene = new THREE.Scene(),
        camera= new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,60),
        renderer = new THREE.WebGLRenderer(),
        extrasLookup = {
          1:30,
          2:50,
          3:70,
          4:90
        }
    //========================
    // Reductions
    //========================
    const newScene = THREE.Scene

    if(true /*localStorage.getItem("3dhighscore")==undefined*/){
      highscoreNum=0
    } else {
      highscoreNum = localStorage.getItem("3dhighscore")
    }
    if(true /*localStorage.getItem("showlag")==undefined*/){
      showlag=false
    } else {
      showlag = localStorage.getItem("showlag")
    }
    if(true /*localStorage.getItem("3dmaxspeed")==undefined*/){
      maxspeedNum=100
    } else {
      maxspeedNum = localStorage.getItem("3dmaxspeed")
    }
    if(showmaxspeed==undefined){
      showmaxspeed=false
    }
    if(showhighscore==undefined){
      showhighscore=true
    }

    renderer.setSize( window.innerWidth, window.innerHeight );
    document.getElementById("canvas").appendChild( renderer.domElement );

    function animate() {
      requestAnimationFrame( animate )
      renderer.render( scene, camera );
    }
    function Location(x,y,z){
      return [x,y,z]
    }

    //========================
    // Dom
    //========================
    const maxSpeedDom = document.getElementById("maxspeed")
    var maxSpeedDomStyle = maxSpeedDom.style
    var lagdom = document.getElementById("lag")
    var lagdomstyle = lagdom.style
    var highScoreDom = document.getElementById("highscore")
    var highScoreDomStyle = highScoreDom.style
    var scoredom = document.getElementById("score")
    var speeddom = document.getElementById("speed")
    var gameOverDom = document.getElementById("gameover")
    var gameOverDomStyle = gameOverDom.style;
    //========================
    // Snake
    //========================
    function Snake() {
      this.segments = [
        Location(7,1,5),
        Location(6,1,5),
        Location(5,1,5)
      ];
      this.direction = "forwards";
      this.nextDirection = "forwards";
      this.nextHead = Location(8,1,5);
    };
    Snake.prototype = {
      draw(){
        let snakeobject = new THREE.Group();
        const j = this.segments.length
        this.segments.forEach(function(item,index){
          let i = index
          let segmentColor=`rgb(${255-(255/j|0)*index+randomNumberBetween(-(j-index),j-index)},00,00)`
          const cube = new newMesh(geometry, new newMeshBasicMaterial({ color: segmentColor})),
                x = item[0],
                y = item[1],
                z = item[2];
          const pos = new newVector3(x,y,z)

          const line = new newLineSegments(edges, new newLineBasicMaterial({ color: segmentColor,linewidth:2 }));
          cube.position.copy(pos);
          line.position.copy(pos);
          snakeobject.add(line,cube);
        })
        scene.add(snakeobject);
      },
      move() {
        this.segments.unshift(this.nextHead);
        if(this.checkCollision()) {
          this.segments.shift();
          gameOver();
          return;
        };
        var nextHeadPos = this.nextHead;
        this.direction = this.nextDirection;
        (directionSwitch[this.direction])(nextHeadPos)
        if(stringify(this.segments[0]) === stringify(apple.pos)) {
          speed=0.95*speed;
          score++;
          if(apple.type === "superapple") {
            speed = 1.10*speed;
            score += 4;
          }
          apple.move();
        } else {
          this.segments.pop();
        };
      },
      changeDirection(key) {
        switch(key){
          case 39:
          case 68:
          case 37:
          case 65:
            keyLookup[key][this.direction]()
            break;
          case 192:
          case 55:
          case 56:
            keyLookup[key]()
            break;
                  }
      },
      checkCollision() {
        const head = this.segments[0]
        const headz = head[2];
        const headx = head[0];
        const wallCollision = headz === -10 || headz === 30 || headx === 0 || headx === 40;
        var selfCollision = false;
        this.segments.forEach(function(item, index) {
          if(index) {
            if(item[0] === head[0] && item[2] === head[2]) {
              selfCollision = true;
            };
          };
        });
        const collision = selfCollision || wallCollision;
        return collision;
      }
    }

    //========================
    // Drawing
    //========================
    function drawScore() {
      var extras = 1;
      if(score > highscoreNum) {
        highscoreNum = score;
        //localStorage.setItem("3dhighscore", highscore);
      }
      if(speed < maxspeed) {
        maxspeedNum = speed;
        //localStorage.setItem("3dmaxspeed", maxspeed);
      }

      scoredom.innerHTML=`Score: ${score}`;
      speeddom.innerHTML=`Speed: ${(((100/speed*100)|0)/100)}`;

      if(showhighscore) {
        extras++;
        highscore.style.display = "block";
        highscore.style.top = extrasLookup[extras]
        highscore.innerHTML = `Highscore: ${highscoreNum}`;

      } else {
        highScoreDomStyle.display = "none";
      };

      if(showmaxspeed) {
        extras++;
        maxspeed.style.display = "block";
        maxspeed.style.top = extrasLookup[extras]
        maxspeed.innerHTML = `Highest Speed: ${(((100/maxspeed*100)|0)/100)}`;
      } else {
        maxspeed.style.display = "none";
      };

      if(showlag) {
        extras++;
        lag.style.display = "block";
        lag.style.top = extrasLookup[extras]
        lag.innerHTML = `Lag: ${lagNum}ms`;
      } else {
        lagdomstyle.display = "none";
      };
    }
    function renderArena() {
      ;
    }

    //========================
    // Apple
    //========================
    function Apple(){
      this.pos = Location(2,1,2)
    }
    Apple.prototype.move = function(){

      // Set the location to a random x and z.
      var x = randomNumberBetween(2,38)
      var z = randomNumberBetween(-8,28)
      this.pos = Location(x,1,z)
      if(randomNumberBetween(1,11)===1&&score>15){                	
        apple.type="superapple"
      } else {
        apple.type="apple"
      }
    }
    Apple.prototype.draw = function() {
      var sphere

      // Generate a new sphere.
      if(this.type=="superapple") {
        sphere = new newMesh( applegeometry, superapplematerial );
      } else {
        sphere = new newMesh( applegeometry, applematerial );
      }

      // Get the data location.
      var applepos=apple.pos

      // Set the location of the visual apple to the data location.
      sphere.position.set(applepos[0],applepos[1],applepos[2])
      scene.add(sphere);
    }
    //========================
    // Misc
    //========================
    function newGame(key) {
      if(key==82) {

        // Stop the game.
        clearTimeout(gameLoop);
        s=function(){};

        // Start a new game.
        var game = document.getElementById("canvas");
        game.removeChild(game.childNodes[0]);
        eval(document.getElementById("script").innerHTML);
      }
    }
    gameOverDomStyle.opacity=0;
    function gameOver() {
      s = function() {
        camera.position.copy(new newVector3(20,30,10));
        camera.lookAt(new newVector3(20,0,10));
      };
      document.onkeydown=function(e) {
        newGame(e.keyCode);    
      }
      //listen to shake event
      var shakeEvent = new Shake({threshold: 15});
      shakeEvent.start();
      window.addEventListener('shake', function(){
        newGame(82)
      }, false);

      //stop listening
      function stopShake(){
        shakeEvent.stop();
      }

      //check if shake is supported or not.
      if(!("ondevicemotion" in window)){alert("Not Supported");}
      gameOverDomStyle.opacity = 1;
    }

    function randomNumberBetween(a,b) {
      return a+floor(random()*(b-a));
    }
    var apple = new Apple(),
        snake = new Snake()
    var directionSwitch={
      "forwards":function(a){this.nextHead = Location(a[0]+1, a[1], a[2])},
      "backwards":function(a){this.nextHead = Location(a[0]-1, a[1], a[2])},
      "left":function(a){this.nextHead = Location(a[0], a[1], a[2]-1)},
      "right":function(a){this.nextHead = Location(a[0], a[1], a[2]+1)}
    }
    for(var x in directionSwitch){
      directionSwitch[x] = directionSwitch[x].bind(snake)
    }
    var right = {
      "right":function(){this.nextDirection = "backwards"},
      "backwards":function(){this.nextDirection = "left"},
      "forwards":function(){this.nextDirection = "right"},
      "left":function(){this.nextDirection = "forwards"}
    }
    for(var x in right){
      right[x] = right[x].bind(snake)
    }
    var left = {
      "right":function(){snake.nextDirection = "forwards"},
      "forwards":function(){snake.nextDirection = "left"},
      "left":function(){snake.nextDirection = "backwards"},
      "backwards":function(){snake.nextDirection = "right"}
    }
    for(var x in left){
      left[x] = left[x].bind(snake)
    }
    var keyLookup = {
      "39":right,
      "68":right,
      "37":left,
      "65":left,
      "192":function(){showlag=!showlag},
      "55":function(){showhighscore=!showhighscore},
      "56":function(){showmaxspeed = !showmaxspeed}
    }
    snake.draw();
    apple.move();

    var background = new THREE.Color( "white" ),
        fog = new THREE.FogExp2( "random", 0.02625 );

    var s = function() {
      // Start for lag detection.
      var start = performance.now();

      // Make a fresh scene.
      scene = new newScene();

      // Move the snake.
      snake.move();

      // Render various things onto the scene.
      scene.add(arenaWallMesh,arenaFloorMesh)
      snake.draw();
      apple.draw();
      drawScore();


      // Get relevent snake parts.
      var head = snake.segments[0];
      var nextHead = snake.nextHead;

      // Set camera position and target.
      camera.position.set(head[0],head[1],head[2]);
      camera.lookAt(new newVector3(nextHead[0],nextHead[1],nextHead[2]));

      // Set background and fog.
      scene.background = background;
      scene.fog = fog;
      var end = performance.now();
      // Reschedule the next update
      gameLoop = setTimeout(function(){
        requestAnimationFrame(s)
      },speed)

      // End for lag detection and calculation of lag.
      lagNum = floor((end-start))
    }
    animate()
    var gameLoop = setTimeout(function(){
      requestAnimationFrame(s)
    },speed);
    document.onkeydown = function(e) {
      snake.changeDirection(e.keyCode);
    }
  </script>

\$\endgroup\$
  • 3
    \$\begingroup\$ I'm starting to think that some games are just not meant to be 3d... \$\endgroup\$ – Simon Forsberg May 11 '18 at 20:08
  • \$\begingroup\$ It gave me: Uncaught SecurityError: Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag. in the code snippet. \$\endgroup\$ – Solomon Ucko May 12 '18 at 1:13
  • \$\begingroup\$ @SolomonUcko fixed. \$\endgroup\$ – FreezePhoenix May 12 '18 at 11:22
  • \$\begingroup\$ This is pretty cool. It seems to have performance problems though, but I need to dig deeper why they occur. \$\endgroup\$ – Sumurai8 May 12 '18 at 14:24
  • \$\begingroup\$ @Sumurai8 I found it. \$\endgroup\$ – FreezePhoenix May 12 '18 at 15:19
3
\$\begingroup\$

Disclaimer: This is a partial review considering the amount of code. I try to pick out the most important parts which should make reviewing the rest easier in a follow-up question. Or if someone else wants to review here of course.

Code style

Styling

Put your styling in a separate style file and do not scatter them through your markup in style tags. It's a nightmare to maintain, and a nightmare to read.

id attributes

You have a relatively high number of ids in your html markup. Since an id should only exist once on a page, you should be careful with what ids you use. In general you probably want to have a single id for the top element of your game and use classes for the rest of your game elements. Then use querySelectorAll to select the different elements.

ES6

You are using ES6 syntax, but only sporadically. This will cause odd scoping of some of your variables. If you want to use ES6, use every aspect of it. This includes consistent use of let and const instead of var and class declarations as... well... classes.

Assigning to window

You are assigning to window. Avoid this at all costs. In fact, you want your code to be self-contained and do not have it influence outside code, or have outside code influence your own.

First, wrap your code in an Immediately Invoked Function Expression (IIFE). This is a function that is invoked with all its dependencies.

(function (THREE) {
  // your code
})(window.THREE)

If you then want to use parts of THREE without the prefix, you can use ES6 dereferencing.

const {
  Geometry,
  BufferGeometry,
  Mesh
} = THREE;

Remember that while this may save you a few key strokes, it will also make it harder to determine what a function actually does. For example, I notice that you have a line window.stringify = JSON.stringify;. The latter variant makes it clear that you are turning something into JSON, while the former lacks this information. People not familiar with the code base will also likely ignore your "shortcuts" and just use the full name.

Variable names

Some of your variable names are inconsistent (as in: they are not in camelcase where your other variables are), but more importantly you have a fair number of variables that simply do not describe what they contain. This includes all your single letter variables that do not describe a position on the x, y or z-axis. Other variables include your left and right declaration, which should (a) not be there and (b) should be named counterClockwiseTranslation and clockwiseTranslation or something similar, drawScore which does draw a lot more than just that, and changeDirection which does a lot more than changing the direction or even taking only inputs that changes direction.

Mixing function declarations, variables and running code

You are currently mixing your function declarations, your variables and initialisation and running code, which makes reading your code a lot harder than it should be. Try to do the following:

  • Put all your "global" variables in the top. If you have too many of them you are likely doing something wrong and have to move them to appropriate components
  • Then put your classes and function declarations that you do not want inside a class
  • Put all your initialisation code in an init function
  • Finally call the init function. If you use a class to encapsulate the entire thing, put the initialisation in the constructor instead so it runs automatically.
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.