Accéder au contenu principal

Snake game (pure JS)

Building a mini snake game in JS is one of these things every webcoder should be able to do. Since I hadn't tackled that "challenge" yet (and mostly because I was bored like hell and wanted to code something without using jQuery for a change), I decided to give it a shot. This is very basic, but it taught me a two interesting things I was unaware of:

  • it seems that a DIV cannot natively receives the focus, but a simple workaround to cope with this is to give the DIV a tabIndex value
  • if you duplicate an array (Array.splice(0)), references are maintained (so as far as I understand, if you modify the original array, the copy will be modified in sync...sounds pretty weird, but I must admit that there was something fishy with the values returned during my initial attempts to clone an array)

Anyway, you can find the result of that one-hour experiment at the following URL:
http://www.manuthommes.be/toolbox/snakejs/

Note: Left/Right arrows to turn.

Quick review of the code now:

/* let's define a few variables: the orientation of the snake (0-3), two 2D arrays to store the position of each part of the body (x and y coordinates) and the score */
        dir=0;
        snake=new Array(new Array());
        oldsnake=new Array(new Array());       
        snake[0][0]=160;
        snake[0][1]=120;
        score=0;
    
// main function -> create the board dynamically + the head of the snake
        function new_game(){
            b=document.createElement("div");
                b.id="board";
                b.tabIndex="1";
                document.getElementsByTagName("body")[0].appendChild(b);
            s=document.createElement("div");
                s.id="sn_head";
                document.getElementById("board").appendChild(s);   
            addBonus();
// here add an event listener on the board
            b.addEventListener("keydown",turn ,false);
            var tmr=setInterval(function(){
// every 150ms, move the snake
                move();
                }, 150);
// focus on the board to make sure the user can start playing right away!
            b.focus();
        }
        
// function invoked when the head of the snake collided with the bonus
// we get random X and Y positions and then create dynamically the object (div)
        function addBonus(){
            xpos=Math.floor((Math.random()*32))*10;
            ypos=Math.floor((Math.random()*24))*10;
            s=document.createElement("div");
                s.id="bonus";
                document.getElementById("board").appendChild(s);   
            document.getElementById("bonus").style.left=xpos+"px";           
            document.getElementById("bonus").style.top=ypos+"px";               
        }       

        function move(){         
// in the array oldsnake, we get the position of the head and other body parts BEFORE their position is updated
            oldsnake[0][0]=parseInt(document.getElementById("sn_head").style.left);
            oldsnake[0][1]=parseInt(document.getElementById("sn_head").style.top);   
            var allParts=document.getElementsByClassName("snake");           
            for (var i=1; i<allParts.length; i++) {
                oldsnake[i][0]=parseInt(allParts[i-1].style.left);
                oldsnake[i][1]=parseInt(allParts[i-1].style.top);   
            } 
// move the snake either to the left, to the right, up or down depending on the value of variable dir
            switch(dir){
                case 0: snake[0][0]=snake[0][0]-10;break;
                case 1: snake[0][1]=snake[0][1]-10;break;
                case 2: snake[0][0]=snake[0][0]+10;break;
                case 3: snake[0][1]=snake[0][1]+10;break;               
            }
// if the snake hits a border, move it the other side
            if(snake[0][0]<0)snake[0][0]=310; if(snake[0][0]>310)snake[0][0]=0;
            if(snake[0][1]<0)snake[0][1]=230; if(snake[0][1]>230)snake[0][1]=0;
// update the position of the head
            document.getElementById("sn_head").style.left=snake[0][0]+"px";           
            document.getElementById("sn_head").style.top=snake[0][1]+"px";
// collision with the bonus?
            testCollision();
// position the body parts:
// body part 1 goes at the position of the OLD head (before it was updated)
// body part 2 goes at the position of the OLD body part 1 (before it was updated)
// ...and so on and so forth until we've moved the whole snake        
            var allParts=document.getElementsByClassName("snake");
            for(var i=1;i<snake.length;i++){
                snake[i][0]=oldsnake[i-1][0];
                snake[i][1]=oldsnake[i-1][1];
                allParts[i-1].style.left=snake[i][0]+"px";
                allParts[i-1].style.top=snake[i][1]+"px";               
            }
        }
        
/* if X/Y of snake head equals X/Y of bonus, the latter is removed, the score is increased, the snake gets bigger and we update the score board */
        function testCollision(){
            if(snake[0][0]==xpos && snake[0][1]==ypos){
                document.getElementById("board").removeChild(document.getElementById("bonus"));
                addBonus(); score=score+10;
                growBody();
                document.getElementById("scoreboard").innerHTML="Score:&nbsp;"+score;
            }
        }
        
// add a new body part and update the arrays (initialize a new item at the end of the arrays)
        function growBody(){
            s=document.createElement("div");
                s.className="snake";
                document.getElementById("board").appendChild(s);
            snake[snake.length]=[];   
            oldsnake[oldsnake.length]=[];           
        }
        
// the function called by the event listener
// if key pressed = 37 (left arrow), direction is decreased by 1 (reset to 3 if it's below 0, which would be a value non processed)
// if key pressed = 38 (rightarrow), direction is increased by 1 (reset to 0 if it's greater than 3, which would be a value non processed)    
    function turn(ev){
            if(ev.keyCode==37){
                dir-=1;
                if(dir<0)dir=3;
            }
            if(ev.keyCode==39){
                dir+=1;
                if(dir>3)dir=0;
            }   
        }

To be implemented: collision of the head with the body (basically going through the array of body parts and testing their X/Y coordinates with the ones of the head), potentially decreasing the timer interval when the score gets bigger, fancy stuff...hf :)

Commentaires

Posts les plus consultés de ce blog

A binary clock in Javascript

Just a short post to share a tiny project I recently worked on: a binary clock. The script is written in pure Javascript (no jQuery for a change), and the design of the buttons was rapidly made in Photoshop, so nothing fancy...but I really wanted to create this :-) http://manuthommes.be/toolbox/binaryclock/ As usual, the code is not obfuscated so you can have a look and feel free to copy/edit.

Lightbox effect with jQuery (3) - ready-to-use library

I've been pretty busy with work over the past days, but I managed to finalize my lightbox javascript library still. Although I can't confirm it is 100% cross-browser, it seems to be running well under IE8/9, Firefox and Chrome. I reckon it should work as well on Safari, but you never know. The actual library and a sample HTML file can be found here: https://sourceforge.net/projects/delightbox/files/ How to use ? First, include jQuery and, needless to say, the library. Second, call the function init from dlb , once the DOM has been fully created and the page is ready. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="./delightbox-latest.js"></script>         <script>         jQuery(document).ready(function(){             dl...

jQuarax - a parallax plugin

This plugin is still in the making and complete documentation (as well as a downloadable package) will be available later. Sample code and quick variable explanation further below. jQuarax-latest.js:  var jquarax = {   load: loader }; function loader(){   jQuery(document).ready(function(){     // parallax script     $(".jquarax-div-container").scroll(function(){       scrollPos = parseInt($(".jquarax-div-container").scrollTop());       scrollPosPC = (100/parseInt($(".jquarax-div-container").css("height")))*(parseInt($(".jquarax-div-container").scrollTop()));       $(".jquarax-object").each(function(e){         var unit = $(this).attr("frmode");          if(unit=="%"){scrollPos=scrollPosPC;}         var startIt = parseInt($(this).attr("frstart"));  ...