0

I'm trying to make a snake game with HTML5 using the object literal pattern, but I can't seem to draw an image on the canvas. I've researched a lot and found for instance that I can't set the image src while the object is being created (so I've put that in a function). The image seems to be available as it shows up in the console and can be appended to the body. What am I missing please?

(function ($) {
  $.fn.preload = function() { // http://stackoverflow.com/a/476681
    this.each(function(){
        $('<img/>')[0].src = this;
    });
  }
})(jQuery);

$(document).ready(function() {
  $(['./images/grass-500x500.png']).preload(); // I've tried with and without this function (defined elsewhere)

  // Object literal pattern
  var game = {
    // Background image
    background: new Image(),
    setBGSource: function() {
      this.background.src = 'images/grass-500x500.png';
    },

    // Canvas details
    canvas: $("#canvas")[0],
    ctx: canvas.getContext("2d"),
    WIDTH: $("#canvas").width(),
    HEIGHT: $("#canvas").height(),

    // Game details
    CELL_WIDTH: 10,
    direction: null,
    food: null,
    score: null,
    snakeArray: [],

    init: function() {
      this.direction = "right";
      this.createSnake();
      this.setBGSource();
      this.draw();
    },

    createSnake: function() {
      var length = 5; // Initial length of snake
      for (var i = length - 1; i >= 0; i--) {
        this.snakeArray.push({
          x: i,
          y: 1 // y : 0 - initial position of snake in cell units
        });
      }
    },

    // Create food item
    createFood: function() {
      this.food = {
        x: Math.round(Math.random() * (WIDTH - CELL_WIDTH) / CELL_WIDTH),
        y: Math.round(Math.random() * (HEIGHT - CELL_WIDTH) / CELL_WIDTH)
      } // Position of food with x and and y between 0 and e.g 44
    },

    // Drawing function
    draw: function() {
      // Repaint the background on each frame
      console.log(this.background); // displays <img src="images/grass-500x500.png">
      this.ctx.drawImage(this.background, 0, 0); // WHY DOESN'T THIS WORK PLEASE?
      $('body').append(this.background); // appends image, no problem
    }

  };

  game.init();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Robin Andrews
  • 3,514
  • 11
  • 43
  • 111

1 Answers1

1

The problem here is that the image is being preloaded asynchronously while at the same time you call game.init() and thus game.draw(), which expects the image being loaded already.

Thus, unless the image is in your browser's cache, the asynchronous preloading might not have finished at the time of game.draw().

You need to wait until preloading has finished before calling game.init(). JavaScript offers some good tools in dealing with asynchronous execution, e.g. callbacks, promises etc.

Have a look here: https://gamedev.stackexchange.com/questions/24102/resource-loader-for-an-html5-and-javascript-game

Community
  • 1
  • 1
le_m
  • 19,302
  • 9
  • 64
  • 74
  • I'm using the Object Literal Pattern because I want to start writing better structured code. It looks like this might be causing more problems than it solves in this instance. Is there a way to put all that loader from the link into the object literal? Or does it necessarily need to remain outside? – Robin Andrews Mar 09 '17 at 17:42