0

When trying to draw an image on an obstacle in an html canvas game I get this error message: Uncaught InvalidStateError: Failed to execute 'drawimage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.

The obstacle is invisible but the game still stops if the player collides with where it should be. The other stationary obstacles' images in the game are visible. What do I do?

I tried uploading the images in my html to let them load before using them but it still doesn't work. Other articles about similar situations do not cover this same context and do not help me who is quite new to programming.

Here is the code:

<!DOCTYPE html>
<html>
<head>

<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="stylesheet.css"> <link/>


</head>
<body onload="startGame()">

<h2> My Lost Game </h2>

<p>Press the 'up', 'down', 'left' and 'right' keys on your keyboard to move. Avoid the obstacles and reach the goal.</p><br>

<p></p>

<script>

var myObstacle = [];


function startGame() {
myGameArea.start();
myGameGoal = new component(75, 95, "player2.jpg", 710, 215, "image");
myGamePiece = new component(75, 95, "player.jpg", 10, 215, "image");

myObstacle2 = new component (110, 150, "obstacle2.jpg", 350, 0, "image");
myObstacle3 = new component (110, 150, "obstacle3.jpg", 530, 350, "image");

}

var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
    this.canvas.width = 800;
    this.canvas.height = 500;
    this.context = this.canvas.getContext("2d");
    document.body.insertBefore(this.canvas, document.body.childNodes[4]);
    this.frameNo = 0;
    this.interval = setInterval(updateGameArea, 20);
    window.addEventListener('keydown', function (e) {
        myGameArea.keys = (myGameArea.keys || []);
        myGameArea.keys[e.keyCode] = true;
    })
    window.addEventListener('keyup', function (e) {
        myGameArea.keys[e.keyCode] = false; 
    })

},
clear : function() {
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);},

stop : function() {
    clearInterval(this.interval);
}
}

function component(width, height, color, x, y, type) {
this.type = type;
if (type == "image") {
    this.image = new Image();
    this.image.src = color;
}
this.width = width;
this.height = height;
this.x = x;
this.y = y;    

this.update = function() {
    ctx = myGameArea.context;
    if (type == "image") {
        ctx.drawImage(this.image, 
            this.x, 
            this.y,
            this.width, this.height);
    } else {
        ctx.fillStyle = color;
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }
}
this.newPos = function() {
    this.x += this.speedX;
    this.y += this.speedY;        
}
this.crashWith = function(otherobj) {
    var myleft = this.x;
    var myright = this.x + (this.width);
    var mytop = this.y;
    var mybottom = this.y + (this.height);
    var otherleft = otherobj.x;
    var otherright = otherobj.x + (otherobj.width);
    var othertop = otherobj.y;
    var otherbottom = otherobj.y + (otherobj.height);
    var crash = true;
    if ((mybottom < othertop) ||
           (mytop > otherbottom) ||
           (myright < otherleft) ||
           (myleft > otherright)) {
       crash = false;
    }
    return crash;
}
}

function updateGameArea() {
 var x, y;
for (i = 0; i < myObstacle.length; i += 1) {
    if (myGamePiece.crashWith(myObstacle[i])) {
        myGameArea.stop();
        return;
    } 
}
myGameArea.clear();
myGameArea.frameNo += 1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
    x = 200;
    y = 300;
    myObstacle.push(new component(110, 150, "obstacle1.jpg", x, y, "image"));
}
for (i = 0; i < myObstacle.length; i += 1) {
    myObstacle[i].y += -1;
    myObstacle[i].update();
}




myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.keys && myGameArea.keys[37]) {myGamePiece.speedX = -3; }
if (myGameArea.keys && myGameArea.keys[39]) {myGamePiece.speedX = 3; }
if (myGameArea.keys && myGameArea.keys[38]) {myGamePiece.speedY = -3; }
if (myGameArea.keys && myGameArea.keys[40]) {myGamePiece.speedY = 3; }


myObstacle2.update();
myObstacle3.update();
myGameGoal.update();
myGamePiece.newPos();
myGamePiece.update();
}

function everyinterval(n) {
if ((myGameArea.frameNo / n) % 1 == 0) {return true;}
return false;
}

</script>



</body>
</html>
gman
  • 100,619
  • 31
  • 269
  • 393
  • You need to preload the images before calling update. The image loading is async and if the image(s) hasn't been loaded in time there will nothing to draw, hence the error. There are several answers here that deals with this problem. –  Jul 31 '17 at 13:51
  • @K3N which browser does throw or even just logs an error when an not yet loaded image is passed to drawImage ? The only way I were ever able to make it throw was either passing an non valid first argument, or an non valid number of arguments. **To OP**, your error message is truncated. Please provide the whole of it. – Kaiido Jul 31 '17 at 14:12
  • I cannot answer why you don't get an error message in your attempt, or even why error message for others vary. However, unless there is an invalid context involved (which show throw an error on getContext()) the cause is almost always an image that hasn't loaded (the other reason is usually wrong element being used, ie. non-image/video/canvas). In this case the context seem valid, but image loading isn't handled properly and you can see the object is updated. –  Jul 31 '17 at 14:40
  • @K3N I'm not sure I get your last comment (and not even if it was an answer to mine). My point was exactly that drawImage should not throw. Yes OP is doing things wrong but his error message, (unfortunately truncated) should not be thrown. drawImage is allowed to draw not yet loaded images without throwing. Drawing an invalid Image argument should throw an `TypeError`, calling it with invalid nb of args too. There is something happening here, and it's definitely not the loading of an Image. – Kaiido Jul 31 '17 at 14:46

0 Answers0