1

I am making an HTML5 canvas based game using JavaScript. The game is just a simple one, where a number of images are displayed on the canvas, along with a number of description boxes. The user is required to drag and drop each image to its corresponding description box.

Currently, I have the canvas displaying the images and description boxes. These are displayed straight away, as soon as the page loads, however, I want to add a 'start button' which the user will be required to click before the images and description boxes etc are displayed and they start playing the game.

The code for my page currently looks like this:

    <!DOCTYPE HTML>
<html>
  <head>
    <style>
  body {
    margin: 0px;
    padding: 0px;
  }
  canvas{
    border: 1px solid #9C9898;
    background:#F5F5F5;
  }
</style>
<div id="container"></div>
<script src="kinetic.js"></script>
<script src="drawdescriptionboxes.js"></script>
<script src="drawLevelOneElements.js"></script>
<script src="startGameDrawGameElementsDrawStartButton.js"></script>
<script>
/*Add the game elements' global variables */
var currentLevel = 1;
var totalLevels = 3;
var currentScore = 0;
var currentScorePositionX = 950;
var currentScorePositionY = 10;

/*Add code to draw images to random locations here */
    //var imageX = Math.floor(Math.random()*950);
    //var imageY = Math.floor(Math.random()*450);

    var stage = new Kinetic.Stage({
      container: "container",
      width: 1000,
      height: 500
    });
    var imagesLayer = new Kinetic.Layer();
    var canvas = imagesLayer.getCanvas();
    var context = canvas.getContext("2d");
    console.log("Foo ");

/*Load the images from the HTML into the JavaScript */
function loadImages(sources, callback){
    var imagesDir = "";
    var images = {};
    var loadedImages = 0;
    var numImages = 0;

    //console.log("length " + sources.length);
    for (var src in sources){
        numImages++;
    }
    //console.log("Num Images " + numImages);

    var index=0;
    console.log("length " + sources.length);
    for (index=0;index < numImages ;index++){
        console.log(index);
        images[index] = new Image();
        images[index].src = sources[index];
        console.log("Adding " + sources[index]);
        callback(images[index]);
        console.log("sources array length = " + sources.length);
    }

    stage.add(imagesLayer); // should only be added once!!
}

/*Function to check whether the item being dragged is near its description box */
function isNearDescriptionBox(itemImage, descriptionBox){
    var ii = itemImage;
    var db = descriptionBox;
    if(ii.attrs.x > db.x - 20 && ii.attrs.x < db.x + 20 && ii.attrs.y > db.y - 20 && ii.attrs.y < db.y +20){
        return true;
    }else{
        return false;
    }
}

/* This function draws the game elements */
function drawGameElements(){
    /* Draw a line for the 'score bar'. */
    context.moveTo(0, 25);
    context.lineTo(1000, 25);
    context.stroke();

    /* Draw current level/ total levels on the left, and current score on the right. */
    context.font = "11pt Calibri"; /* Text font & size */
    context.strokeStyle = "black"; /* Font colour */
    context.strokeText(currentLevel + "/" + totalLevels, 10, 15);
    context.strokeText(currentScore, 750, 15);
}

function initStage(images){
    var stage = new Kinetic.Stage({
        container: "container",
        width: 1000,
        height: 500
    });
    var descriptionLayer = new Kinetic.Layer();
    //var imagesLayer = new Kinetic.Layer();
    var allImages = [];
    var currentScore = 0;

    var descriptionBoxes = {
        assetsDescriptionBox: {
            x: 70,
            y: 400
        },
        liabilitiesDescriptionBox: {
            x: 300,
            y: 400
        },
        incomeDescriptionBox: {
            x: 530,
            y: 400
        },
        expenditureDescriptionBox: {
            x: 760,
            y: 400
        },
    };

    /*Code to detect whether image has been dragged to correct description box */
    for (var key in sources){
        /*Anonymous function to induce scope */
        (function(){
            var privateKey = key;
            var imageSource = sources[key];

            /*Check if image has been dragged to the correct box, and add it to that box's
                array and remove from canvas if it has */
            canvasImage.on("dragend", function(){
                var descriptionBox = descriptionBoxes[privateKey];
                if(!canvasImage.inRightPlace && isNearDescriptionBox(itemImage, descriptionBox)){
                    context.remove(canvasImage);
                    /*Will need to add a line in here to add the image to the box's array */
                }
            })

        })();
    }

}

function drawImage(imageObj) {
    //var layer = new Kinetic.Layer();

    //var imageX = Math.floor(Math.random()*950);
    //var imageY = Math.floor(Math.random()*450);

    /*Got the code from generating random coordinates from:
        http://stackoverflow.com/questions/4959975/random-between-two-numbers-in-javascript*/
    function randomPosition(from, to){
        return Math.floor(Math.random()*(to-from+1)+from);
    }

    var canvasImage = new Kinetic.Image({
      image: imageObj,
      width: 50,
      height: 50,
      // puts the image in teh middle of the canvas
      x: randomPosition(0, 950),  //imageX,    //stage.getWidth() / 2 - 50 / 2,
      y: randomPosition(30, 350),  //imageY,    //stage.getHeight() / 2 - 50 / 2,
      draggable: true
    });

    // add cursor styling
    canvasImage.on('mouseover', function() {
      document.body.style.cursor = 'pointer';
    });
    canvasImage.on('mouseout', function() {
      document.body.style.cursor = 'default';
    });

    imagesLayer.add(canvasImage);
}

/*This code loads the images to the canvas when the browser window loads */
window.onload = function(){
    var sources = [];
        sources[0] = document.getElementById("building").src,
        sources[1] = document.getElementById("chair").src,
        sources[2] = document.getElementById("drink").src,
        sources[3] = document.getElementById("food").src,
        sources[4] = document.getElementById("fridge").src,
        sources[5] = document.getElementById("land").src,
        sources[6] = document.getElementById("money").src,
        sources[7] = document.getElementById("oven").src,
        sources[8] = document.getElementById("table").src,
        sources[9] = document.getElementById("van").src,

        sources[10] = document.getElementById("burger").src,
        sources[11] = document.getElementById("chips").src,
        sources[12] = document.getElementById("drink").src,
        sources[13] = document.getElementById("franchiseFee").src,
        sources[14] = document.getElementById("wages").src,

        sources[15] = document.getElementById("admin").src,
        sources[16] = document.getElementById("cleaners").src,
        sources[17] = document.getElementById("electricity").src,
        sources[18] = document.getElementById("insurance").src,
        sources[19] = document.getElementById("manager").src,
        sources[20] = document.getElementById("rates").src,
        sources[21] = document.getElementById("training").src,
        sources[22] = document.getElementById("water").src,

        sources[23] = document.getElementById("burger").src,
        sources[24] = document.getElementById("chips").src,
        sources[25] = document.getElementById("drink").src,

        sources[26] = document.getElementById("creditors").src,
        sources[27] = document.getElementById("electricity").src,
        sources[28] = document.getElementById("food").src,
        sources[29] = document.getElementById("hirePurchase").src,
        sources[30] = document.getElementById("loan").src,
        sources[31] = document.getElementById("overdraft").src,
        sources[32] = document.getElementById("payeTax").src,
        sources[33] = document.getElementById("tax").src;

    drawStartButton();
    loadImages(sources, drawImage);
    //drawGameElements();
    //drawDescriptionBoxes();

};

I also have a hidden section in the <body></body> of the page, where I've loaded all of the images to be used into the HTML first, before manipulating them using the JS.

Currently, when I view the page in a browser, all of the images from the sources array are displayed on the canvas in random locations, along with the description boxes, score bar, and start button.

However, what I want to happen, is that when the page initially loads, only the start button is displayed on the canvas, and then when the user clicks this, everything else is displayed.

Presumably, to do this, I would want to delete/ comment out the calls to all of the functions except drawStartButton() at the end of the windows.onload = function(){...} function, and then have calls to the other function in an onmousedown event attached to the start button?

However, for some reason, when I comment out the call to loadImages(sources, drawImage); at the end of this function, and then view my page in the browser again, the canvas is no longer displayed on the page. I would have expected the canvas to still be there, just without the images displayed on it. Does anyone have any idea why this isn't happening, and how I can put it right?

The code for my loadImages(sources, drawImage); function is:

function loadImages(sources, callback){
    var imagesDir = "";
    var images = {};
    var loadedImages = 0;
    var numImages = 0;

    //console.log("length " + sources.length);
    for (var src in sources){
        numImages++;
    }
    //console.log("Num Images " + numImages);

    var index=0;
    console.log("length " + sources.length);
    for (index=0;index < numImages ;index++){
        console.log(index);
        images[index] = new Image();
        images[index].src = sources[index];
        console.log("Adding " + sources[index]);
        callback(images[index]);
        console.log("sources array length = " + sources.length);
    }

    stage.add(imagesLayer); // should only be added once!!
}

I can't see why removing a call to this function would stop the canvas from being displayed on the page... any ideas?

Noble-Surfer
  • 3,052
  • 11
  • 73
  • 118

1 Answers1

0

The last line of the function,

stage.add(imagesLayer); // should only be added once!!

adds the layer to the stage. While the stage is a container (div, for example), the layer is the actual canvas.

If you don't call loadImages(), the layer never gets added to the DOM, because that line never runs.

MildlySerious
  • 8,750
  • 3
  • 28
  • 30
  • How would I then set the `onmousedown` to call the functions I want when a mousedown is detected on the start button image? Presumably I need to create a JS variable that holds the start button image using `var startButton = document.getElementById("startButton").src;` and then would it be something like `startButton.onmousedown function(){ calls to functions };`? – Noble-Surfer Feb 15 '13 at 13:46
  • Because you're using canvas, or, Kinetic.js to be exact, it isn't that simple. *(also the .src would be the url to the image, not the DOM element)* I am assuming you use Kinetic.js for the start button as well instead of just positioning the button absolute? In this case, check out this tutorial for events with kinetic: http://www.html5canvastutorials.com/kineticjs/html5-canvas-path-mouseover/ – MildlySerious Feb 15 '13 at 13:50
  • The reason I've used Kinetic.js for most of the images was to add the 'draggable' property to them. I don't need to add this to the start button, so I would have thought it would be easier to just position it absolute. Once the start button has been clicked, it will disappear from the canvas and won't be displayed again. – Noble-Surfer Feb 15 '13 at 13:57
  • Alright. What you want propably comes down to this: `startButton = document.getElementById("startButton"); startButton.onclick = function() { this.style.display = 'none'; // followed by what other things you want to do }` – MildlySerious Feb 15 '13 at 13:59
  • That looks like it's what I want, but when I added that to my code, and viewed the page again in a browser, nothing happens when I clikc the start button, and the description boxes and other game elements (all except the draggable images) are already displayed on the canvas with the start button for some reason. I'm not sure what it is I'm doing wrong... – Noble-Surfer Feb 15 '13 at 14:08
  • http://jsfiddle.net/fkrGG/ This fiddle doesn't have all of the JS from all the separate JS files I have- would it be better if I was to add the script from these files in too? – Noble-Surfer Feb 15 '13 at 14:18
  • Check this out. I reformatted the code a little and added the definition of startbutton the way you find it 4 comments above. http://jsfiddle.net/fkrGG/1/ I also added window.onload around the script so the DOM is loaded before the document.getElementById function is accessed. – MildlySerious Feb 15 '13 at 14:30
  • Hey, thanks for the fiddle- I've changed my code to reflect the changes you've made, but I'm getting a console reference error that says that context is not defined in one of the JS files that my HTML file is using... but context clearly has been defined since it's being used in the JS embedded in the HTML file. Also, I've not changed the code in the file it's complaining about at all since it last was working... – Noble-Surfer Feb 15 '13 at 17:53