I am trying to make a game in Javascript with Repl.it, but every few seconds the player's images flickers. I cannot figure out why this is happening? I am loading some images onto a canvas and it only started flickering once I added in the animate() function and started changing the image. Can someone please explain this? (go to this website to see the flicker and play the game too: https://advanced-chrome-dino-game.isaacroot.repl.co/) My code:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var ground_size = 75;
var up = false;
var game_over = false;
const start_speed = 8;
var score = 0;
function randBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
class Player {
constructor(x_pos) {
this.color = "black";
this.image = "dino_1.png"
this.img = new Image();
this.width = 120;
this.height = 120;
this.x = x_pos;
this.y = c.height - ground_size - this.height;
this.speed = 0;
this.jump_power = 22.5;
this._gravity = 1;
this.an_time = 7;
this.an_count = 0;
}
ground_height() {
return c.height - ground_size - this.height;
}
on_ground() {
if (this.y == this.ground_height()) {
return true;
} else {
return false;
}
}
jump() {
if (this.on_ground()) {
this.speed = this.jump_power;
this.image = "dino_jump.png";
}
}
gravity() {
this.speed -= this._gravity;
}
animate() {
if (this.an_count <= 0) {
if (this.image == "dino_1.png") {
this.image = "dino_2.png";
} else {
if (this.image == "dino_2.png") {
this.image = "dino_1.png"
}
}
this.an_count = this.an_time;
} else {
this.an_count -= 1;
}
}
react() {
this.y -= this.speed
if (this.y > this.ground_height()) {
this.y = this.ground_height();
this.speed = 0;
}
if (this.on_ground() && this.image == "dino_jump.png") {
this.image = "dino_1.png"
}
}
rect() {
return [this.x, this.y, this.width, this.height]
}
}
class Obstacle {
constructor(x_pos, speed) {
this.color = "red";
this.img = new Image();
this.img.src = "cactus_big.png";
this.width = 52 * 1.5;
this.height = 75 * 1.5;
this.x = x_pos;
this.y = c.height - ground_size - this.height;
this.speed = speed;
}
ground_height() {
return c.height - ground_size - this.height;
}
on_ground() {
if (this.y == this.ground_height()) {
return true;
} else {
return false;
}
}
react() {
if (this.x <= 0 - this.width) {
this.x = randBetween(c.width, c.width + 500);
}
this.x -= this.speed;
}
rect() {
return [this.x, this.y, this.width, this.height]
}
}
player = new Player(75);
obstacle1 = new Obstacle(c.width, start_speed);
function inRect(rect1, rect2) {
var rect1_left = rect1[0];
var rect1_right = rect1[0] + rect1[2];
var rect1_top = rect1[1];
var rect1_bottom = rect1[1] + rect1[3];
var rect2_left = rect2[0];
var rect2_right = rect2[0] + rect2[2];
var rect2_top = rect2[1];
var rect2_bottom = rect2[1] + rect2[3];
var horizontal = false;
var vertical = false;
if (rect1_right > rect2_left && rect2_right > rect1_left) {
horizontal = true;
}
if (rect1_bottom > rect2_top && rect2_bottom > rect1_top) {
vertical = true;
}
return (vertical && horizontal);
}
function draw() {
ctx.beginPath();
ctx.fillStyle = "white";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#4b4b4b";
ctx.fillRect(0, c.height - ground_size, c.width, c.height);
ctx.fillStyle = player.color;
// ctx.fillRect(player.x, player.y, player.width, player.height);
player.img.src = player.image;
ctx.drawImage(player.img, player.x, player.y, player.width, player.height)
ctx.fillStyle = obstacle1.color;
// ctx.fillRect(obstacle1.x, obstacle1.y, obstacle1.width, obstacle1.height);
ctx.drawImage(obstacle1.img, obstacle1.x, obstacle1.y, obstacle1.width, obstacle1.height)
ctx.fillStyle = "black";
ctx.font = "30px Arial";
ctx.fillText("Score: " + Math.round(score / 25).toString(), 20, 50);
if (game_over) {
ctx.font = "80px Arial";
}
ctx.stroke();
}
document.addEventListener('keydown', (event) => {
if (event.key == 'ArrowUp' || event.key == 'Spacebar') {
up = true;
} else if (event.key == 'ArrowDown') {
//pass
}
});
document.addEventListener('keyup', (eventt) => {
if (eventt.key == 'ArrowUp') {
up = false;
} else if (event.key == 'ArrowDown') {
//pass
}
});
function update_speed() {
obstacle1.speed += 0.001;
}
function run() {
if (up == true) {
player.jump()
}
player.gravity();
player.react();
player.animate();
obstacle1.react();
if (inRect(player.rect(), obstacle1.rect())) {
game_over = true
}
score += obstacle1.speed
draw();
update_speed();
if (game_over == false) {
requestAnimationFrame(run);
}
}
run();
I checked the code and I realized you were loading images each time into the player.img variable.
function createImage(path){
let image = new Image();
image.src = path;
return image;
}
var images = {
dino:[
createImage("dino_1.png"),
createImage("dino_2.png"),
createImage("dino_jump.png")
],
cactus:createImage("cactus_big.png")
}
If you use my code here, you need to change the code to use those images instead. Because now, each time you draw, you are loading an image. Like this, all images are stored and I tried it, no more flickering.
Well, all you have to do is have the images get preloaded so your index.html should look like:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Block Runner</title>
<link href="style.css" rel="stylesheet" type="text/css" />
<link rel="preload" href="cactus_big.png" as="image">
<link rel="preload" href="dino_1.png" as="image">
<link rel="preload" href="dino_2.png" as="image">
<link rel="preload" href="dino_jump.png" as="image">
<link rel="preload" href="ground.png" as="image">
</head>
<body>
<canvas id="myCanvas" width="900" height="600" style="border:0px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script src="script.js"></script>
</body>
</html>
That should make it so that it doesn't flicker.
Related
I have created a simple game using Canvas in Javascript where the player has to avoid obstacles by jumping over or shrinking. The obstacle images, which are animated moving towards the left, flickers at some sort of interval. The interval increases in parallel with the gameSpeed variable, making me wonder if this might have something to do with either the spawnObstacle() or the update() functions. Another reason why I believe so is because neither the drawn playerImg.png nor the drawn Score/Highscore ever flickers. I have tried several different solutions found online, but none seem to solve my problem.
Here is my index.js and index.html below:
import {registerNewHighscore, makeList} from "./globalHs.js";
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const newGameBtn = document.getElementById("newGame");
const seeInstrBtn = document.getElementById("instrBtn");
const seeHsBtn = document.getElementById("hsBtn");
const goBackBtn = document.getElementById("backBtn");
let score;
let highscore;
let scoreText;
let highscoreText;
let player;
let gravity;
let obstacles = [];
let gameSpeed;
let keyPressed;
let isKeyPressed = false
let active = true;
let rotation = 0;
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
function createImage(path){
let image = new Image();
image.src = path;
return image;
}
const obsImg = createImage("img/chatbubble.png");
const rockImg = createImage("img/rock.png");
const roadblockImg = createImage("img/roadblock.png");
const playerImg = createImage("img/logoPlayer.png");
newGameBtn.addEventListener('click', function() {
document.getElementById("newGame").style.display = "none";
document.getElementById("header").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("main").style.display = "block";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("hsBoard").style.display = "none";
start();
});
seeInstrBtn.addEventListener('click', function(){
document.getElementById("header").style.display = "none";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("newGame").style.display = "none";
document.getElementById("instr").style.display = "block";
document.getElementById("instr").style.visibility = "visible";
document.getElementById("backBtn").style.display = "block";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("hsBoard").style.display = "none";
document.getElementById("backBtn").style.top = "50%";
});
seeHsBtn.addEventListener('click', function(){
document.getElementById("header").style.display = "none";
document.getElementById("hsBtn").style.display = "none";
document.getElementById("newGame").style.display = "none";
document.getElementById("instrBtn").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("hsBoard").style.display = "block";
document.getElementById("backBtn").style.display = "block";
document.getElementById("backBtn").style.top = "70%";
makeList();
});
goBackBtn.addEventListener('click', function() {
goBack();
});
function goBack() {
document.getElementById("backBtn").style.display = "none";
document.getElementById("instr").style.display = "none";
document.getElementById("header").style.display = "block";
document.getElementById("newGame").style.display = "block";
document.getElementById("instrBtn").style.display = "block";
document.getElementById("hsBtn").style.display = "block";
document.getElementById("hsBoard").style.display = "none";
};
document.addEventListener('keydown', function(evt) {
if (isKeyPressed) return;
isKeyPressed = true;
keyPressed = evt.code;
});
document.addEventListener('keyup', function(evt) {
if (evt.code !== keyPressed) return; // only respond to the key already pressed
isKeyPressed = false;
keyPressed = null;
});
function randomIntInRange (min, max){
return Math.round(Math.random() * (max - min) + min);
}
class Player{
constructor (x, y, r, w, h, playerImg){
this.playerImg = playerImg;
this.x = x;
this.y = y;
this.r = r;
this.w = r*2;
this.h = r*2;
this.dy = 0;
this.jumpForce = 18;
this.originalRad = r;
this.grounded = false;
this.jumpTimer = 0;
/* this.newRotation = 0; */
}
animate () {
if (['Space', 'KeyW'].includes(keyPressed)) {
this.jump();
} else{
this.jumpTimer = 0;
}
if (['ShiftLeft', 'KeyS'].includes(keyPressed)){
/* this.newRotation = rotation * 2; */
this.r = this.originalRad / 2;
this.w = this.originalRad;
this.h = this.originalRad;
} else {
/* this.newRotation = rotation; */
this.r = this.originalRad;
this.w = this.r * 2;
this.h = this.r * 2;
}
this.y += this.dy;
if (this.y + this.r < canvas.height){
this.dy += gravity;
this.grounded = false;
} else{
this.dy = 0;
this.grounded = true;
this.y = canvas.height - this.r;
}
this.draw();
}
jump () {
if (this.r != this.originalRad) return;
if (this.grounded && this.jumpTimer == 0){
this.jumpTimer = 1.5;
this.dy = -this.jumpForce;
} else if (this.jumpTimer > 0 && this.jumpTimer < 15){
this.jumpTimer++;
this.dy = -this.jumpForce - (this.jumpTimer / 50);
}
}
draw () {
ctx.translate(this.x, this.y);
ctx.rotate(rotation);
ctx.translate(-(this.x), -(this.y));
ctx.drawImage(this.playerImg, (this.x-this.r), (this.y-this.r), this.w, this.h);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
}
class Obstacle {
constructor (x, y, w, h, obsImg){
this.obsImg = obsImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
obsImg.width = this.w;
obsImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x, this.y, this.w, this.h,);
ctx.drawImage(this.obsImg, this.x, this.y, this.w*1.1, this.h);
ctx.closePath();
}
/////// CIRCLE
/*draw () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, (2 * Math.PI), false)
ctx.fillStyle = this.c;
ctx.fill();
ctx.closePath();
}*/
/////// ELLIPSE
/*draw () {
ctx.beginPath();
ctx.ellipse(this.x, this.y, this.radX, this.radY, 0, 0, 2 * Math.PI);
ctx.fillStyle = this.c;
ctx.fill();
ctx.stroke();
}*/
}
class Rock {
constructor (x, y, w, h, rockImg){
this.rockImg = rockImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
rockImg.width = this.w;
rockImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x+20, this.y+40, this.w-20, this.h-40);
ctx.drawImage(this.rockImg, this.x-20, this.y, this.w*1.5, this.h*1.5);
ctx.closePath();
}
}
class Roadblock {
constructor (x, y, w, h, roadblockImg){
this.roadblockImg = roadblockImg;
this.x = x,
this.y = y,
this.w = w;
this.h = h;
this.dx = -gameSpeed;
roadblockImg.width = this.w;
roadblockImg.height = this.h;
}
update (){
this.x += this.dx;
this.dx = -gameSpeed;
}
draw () {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(this.x, this.y+15, this.w, this.h,);
ctx.drawImage(this.roadblockImg, this.x, this.y, this.w, this.h*1.15);
ctx.closePath();
}
}
class Text{
constructor(t, x, y, a, c, s){
this.t = t;
this.x = x;
this.y = y;
this.a = a;
this.c = c;
this.s = s;
}
draw () {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.font = this.s + "px";
ctx.textAlign = this.a;
ctx.fillText(this.t, this.x, this.y);
ctx.closePath();
}
}
function getDistance(player, obstacle) {
var distX = Math.abs(player.x - (obstacle.x + obstacle.w / 2));
var distY = Math.abs(player.y - (obstacle.y + obstacle.h / 2));
if (distX > (obstacle.w / 2 + player.r)) { return false; }
if (distY > (obstacle.h / 2 + player.r)) { return false; }
if (distX <= (obstacle.w)) { return true; }
if (distY <= (obstacle.h)) { return true; }
var dx = distX - obstacle.w / 2;
var dy = distY - obstacle.h / 2;
return (dx * dx + dy * dy <= (player.r*player.r));
}
let initialSpawnTimer = 200;
let spawnTimer = initialSpawnTimer;
function spawnObstacle (){
let sizeX;
let sizeY;
let type = randomIntInRange(0, 2);
let obstacle = new Obstacle(
canvas.width + sizeX,
canvas.height - sizeX,
sizeX,
sizeY,
obsImg
);
if (type == 0){
sizeX = randomIntInRange(100, 160);
sizeY = sizeX / 2;
obstacle = new Rock(
canvas.width + sizeX,
canvas.height - sizeY,
sizeX,
sizeY,
rockImg
);
} else if (type == 1){
sizeX = randomIntInRange(80, 160);
sizeY = sizeX / 2;
obstacle = new Obstacle(
canvas.width + sizeX,
canvas.height - sizeX,
sizeX,
sizeY,
obsImg
);
obstacle.y -= player.originalRad + randomIntInRange(-50, 150);
} else if (type == 2){
sizeX = 150;
sizeY = sizeX / 2;
obstacle = new Roadblock(
canvas.width + sizeX,
canvas.height - sizeY,
sizeX,
sizeY,
roadblockImg
);
}
obstacles.push(obstacle);
}
function start () {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.font = "40px Courier New";
active = true;
gameSpeed = 6;
gravity = 1;
score = 0;
highscore = 0;
if (localStorage.getItem('highscore')){
highscore = localStorage.getItem('highscore');
}
player = new Player(100, 0, 50, 100, 100, playerImg);
scoreText = new Text("Score: " + score, 45, 45, "left", "#212121", "40");
highscoreText = new Text("Highscore: " + highscore, 45,
90, "left", "gold", "40");
window.requestAnimationFrame(update);
}
let lastTime;
function update (time) {
if (lastTime != null) {
const delta = time - lastTime;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
spawnTimer--;
if (spawnTimer <= 0){
spawnObstacle();
spawnTimer = initialSpawnTimer - gameSpeed * 8;
if (spawnTimer < 60){
spawnTimer = randomIntInRange(40, 80);
}
}
for (let i = 0; i < obstacles.length; i++){
let o = obstacles[i];
o.draw();
o.update();
if (o.x + o.y < 0){
obstacles.splice(i, 1);
}
if (getDistance(player, o)) {
active = false;
obstacles = [];
spawnTimer = initialSpawnTimer;
gameSpeed = 6;
window.localStorage.setItem('highscore', highscore);
score -= 1;
highscore -= 1;
if (score >= highscore){
registerNewHighscore(highscore+1);
}
goBack();
}
}
lastTime = time;
if (active){
window.requestAnimationFrame(update);
}
player.animate();
score++;
scoreText.t = "Score: " + score;
scoreText.draw();
if (score > highscore){
highscore = score;
highscoreText.t = "Highscore: " + highscore;
}
highscoreText.draw();
rotation+=Math.PI/180 * 2 + gameSpeed * 0.01;
gameSpeed += 0.002;
}
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kindly Waiting Game V2</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="world">
<div id="header">The Kindly Game</div>
<div id="newGame" onmouseover="this.style.backgroundColor = 'goldenrod'"
onmouseout="this.style.backgroundColor= 'gold'">New Game</div>
<div id="instrBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Instructions</div>
<div id="hsBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Highscores</div>
<div id="instr" style="display: none">Avoid the <br>chatbubbles, <br>roadblocks, <br>and rocks!<br><br>
Instructions: <br>Space/W: Jump <br>ShiftLeft/S: Shrink</div>
<div id="hsBoard" style="display: none">Highscores:</div>
<div id="backBtn" onmouseover="this.style.backgroundColor = 'goldenrod'" onmouseout="this.style.backgroundColor
= 'gold'">Back</div>
<div id="main"></div>
<div id="score"></div>
<div id="cloudLarge"></div>
<div id="cloudMedium"></div>
<div id="cloudSmall"></div>
<div id="cloudSmall2"></div>
<div id="ufo"></div>
<div id="airplane"></div>
<div id="ye"></div>
</div>
<canvas id="game" width="640" height="400"></canvas>
<script src="globalHs.js" type="module"></script>
<script src="index.js" type="module"></script>
</body>
</html>
Your issue is caused by this block of code inside your update() function:
if (o.x + o.y < 0){
obstacles.splice(i, 1);
}
Now while I don't know the exact logic why you're checking the sum of the obstacles' horizontal and vertical position, you're actually removing something from an array with the .splice() method. As this is happening inside a for-loop you're actually modifying the length of the array while it might still loop over it.
You can fix this by looping over the array from the last to the first element:
for (let i = obstacles.length-1; i>=0; i--) {
let o = obstacles[i];
o.draw();
o.update();
if (o.x + o.y < 0) {
obstacles.splice(i, 1);
}
if (getDistance(player, o)) {
active = false;
obstacles = [];
spawnTimer = initialSpawnTimer;
gameSpeed = 6;
window.localStorage.setItem('highscore', highscore);
score -= 1;
highscore -= 1;
if (score >= highscore) {
registerNewHighscore(highscore + 1);
}
goBack();
}
}
I'm working to a JS game where I have a player named Ronin and the game is something like a 2d platformer. I worked on canvas to make him and I made him move x pixels to left/ right at keys A/D onkeydown. The problem is that I can't erase his 'copies' (previous positions of the character that now should dissapear). I would not want to use ctx.clearRect(), so please give me another solution.
Thanks in advance!
WalkLeft1------WalkLeft2------WalkRight1------WalkRight2
/* Walk Left/Right 1/2 in the description no matter with the'broken' state file, it's because it can't draw a file that he does not know :) */
/* ------------------------------------------ */
/* files in my directory:
index.html
style.css
script.js
Costumes >
WalkLeft >
WalkLeft1.jpg
WalkLeft2.jpg
WalkRight >
WalkRight1.jpg
WalkRight2.jpg
*/
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Scene = function () {
this.x = 0;
this.y = 0;
this.w = canvas.width;
this.h = canvas.height;
this.style = {
fillStyle: "white",
strokeStyle: "red",
lineWidth: 5,
}
this.draw = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = this.style.fillStyle;
ctx.strokeStyle = this.style.strokeStyle;
ctx.lineWidth = this.style.lineWidth;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
Character = function () {
this.width = 77;
this.height = 92;
this.x = 10;
this.vx = 20;
this.padding = 10;
this.y = canvas.height - this.height - 10;
this.img0 = document.getElementById("img0");
this.img1 = document.getElementById("img1");
this.img2 = document.getElementById("img2");
this.img3 = document.getElementById("img3");
this.counter = {
walkLeft: 0,
walkRight: 0,
};
this.draw = {
walkLeft: () => {
if (this.counter.walkLeft == 0) {
ctx.drawImage(this.img0, this.x, this.y, this.width, this.height);
this.counter.walkLeft = 1;
} else if (this.counter.walkLeft == 1) {
ctx.drawImage(this.img1, this.x, this.y, this.width, this.height);
this.counter.walkLeft = 0;
}
},
walkRight: () => {
if (this.counter.walkRight == 0) {
ctx.drawImage(this.img2, this.x, this.y, this.width, this.height);
this.counter.walkRight = 1;
} else if (this.counter.walkRight == 1) {
ctx.drawImage(this.img3, this.x, this.y, this.width, this.height);
this.counter.walkRight = 0;
}
}
};
}
const scene = new Scene();
scene.draw();
var player = new Character();
var initInterval = setInterval(
function () {
player.draw.walkRight();
}, 400
);
var currentInterval = undefined;
document.addEventListener("keydown", function (e) {
if (e.key == "a" || e.key == "ArrowLeft") {
if (initInterval) {
clearInterval(initInterval);
initInterval = undefined;
}
if (currentInterval) {
clearInterval(currentInterval);
currentInterval = undefined;
}
setTimeout( // for first 300ms delay onchange
function () {
player.draw.walkLeft();
}, 1);
if(player.x - player.vx > player.padding) {
player.x -= player.vx;
} else {
player.x = player.padding;
}
currentInterval = setInterval(
function () {
player.draw.walkLeft();
}, 300);
}
if (e.key == "d" || e.key == "ArrowRight") {
if (initInterval) {
clearInterval(initInterval);
initInterval = undefined;
}
if (currentInterval) {
clearInterval(currentInterval);
currentInterval = undefined;
}
setTimeout( // for first 300ms delay onchange
function () {
player.draw.walkRight();
}, 1);
if(player.x + player.vx < canvas.width - player.padding) {
player.x += player.vx;
} else {
player.x = canvas.width - player.padding;
}
currentInterval = setInterval(
function () {
player.draw.walkRight();
}, 300);
}
});
body {
overflow: hidden;
margin: 0;
padding: 0;
}
<html>
<head>
<title></title>
</head>
<link rel="stylesheet" href="style.css">
<body>
<canvas id="canvas"></canvas>
<div id="images">
<img id="img0" width="77px" height="92px" src="Costumes\WalkLeft\WalkLeft1.jpg" />
<img id="img1" width="77px" height="92px" src="Costumes\WalkLeft\WalkLeft2.jpg" />
<img id="img2" width="77px" height="92px" src="Costumes\WalkRight\WalkRight1.jpg" />
<img id="img3" width="77px" height="92px" src="Costumes\WalkRight\WalkRight2.jpg" />
</div>
<script src="script.js"></script>
</body>
</html>
Work in layers. You put your background layer images in one canvas, your character and enemies in another, then copy the contents of each layer starting from the back, in order, onto a 'buffer' canvas. once that's done, you copy the result onto the visible on-screen canvas. Throw away the buffer (or clear it) at the end of each loop.
If a layer doesn't change, just keep it; if it does, create a new canvas for each frame (or clear the canvas you used). You can create as many layers as you like this way (or as many as you've got memory).
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 as a preload() function 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/>
<script>
function preloader()
{
// counter
var i = 0;
// create object
imageObj = new Image();
// set image list
images = new Array();
images[0]="player.jpg"
images[1]="player2.jpg"
images[2]="obstacle.jpg"
images[3]="obstacle2.jpg"
images[3]="obstacle3.jpg"
// start preloading
for(i=0; i<=3; i++)
{
imageObj.src=images[i];
}
}
</script>
</head>
<body onload="startGame(), preloader()">
<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>
You need to create a separate image object for each image
The code below loads images from an array of image URLs and returns an array of images in the same order as the URL names
var loading = 0; // number of images still loading
function preloader(imageURLs){
// function creates and loads an image
function createImage(url){
loading += 1; // count up when loading images
const image = new Image;
image.src = url;
image.onload = () => loading -= 1; // count down when loaded
return image;
}
const images = imageURLs.map(createImage);
return images;
}
const imageList = preloader(["player.jpg", "player2.jpg", "obstacle3.jpg"]);
// at this point loading greater than zero
When loading is 0 you know all images have loaded
Thus in your render loop
function mainLoop(){
if(loading === 0){ // all images have loaded
ctx.drawImage(imageList[0],0,0); // draw first image
ctx.drawImage(imageList[1],0,0); // draw second image
ctx.drawImage(imageList[2],0,0); // draw third image
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
Or you can have it call a function when all loaded
var loading = 0; // number of images still loading
function preloader(imageURLs, callback){
// function creates and loads an image
function createImage(url, callback){
loading += 1; // count up when loading images
const image = new Image;
image.src = url;
image.onload = () => {
loading -= 1; // count down when loaded
if(loading === 0){ // all done
callback(); // call callback
}
}
return image;
}
const images = imageURLs.map(createImage);
return images;
}
function start(){ // function to start main loop when images have loaded
requestAnimationFrame(mainLoop);
}
// second argument is the callback function that is called when all images have loaded
const imageList = preloader(["player.jpg", "player2.jpg", "obstacle3.jpg"], start);
function mainLoop(){
ctx.drawImage(imageList[0],0,0); // draw first image
ctx.drawImage(imageList[1],0,0); // draw second image
ctx.drawImage(imageList[2],0,0); // draw third image
requestAnimationFrame(mainLoop);
}
This question already has answers here:
Bing Maps API v8 - pushpin SVG URI image
(1 answer)
JavaScript Failed to execute 'drawImage'
(3 answers)
Uncaught TypeMismatchError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D'
(1 answer)
Failed to execute 'drawImage' on 'CanvasRenderingContext2D'
(1 answer)
Closed 5 years ago.
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>
I am trying to create a basic HTML game using the Canvas element, but I am having trouble with it. Unfortunately, I don't know where the error is in my code, so I have posted the entirety of the document I'm working on below.
My problem: The canvas is not displaying when I run the HTML document.
This code's based off of (as in it pretty much is) the Movement tutorial from w3schools for using the Canvas, available at https://www.w3schools.com/graphics/tryit.asp?filename=trygame_movement_keyboard, but when chaing variable names I must've broken something, because after looking at this for hours I can't figure out what I'm missing.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
}
</style>
</head>
<body onload = "initial()">
<script>
var playerOne;
function initial() {
playerOne = new canvasObject(30, 30, "red", 225, 225);
gameArea.start();
}
var gameArea = {
canvas : document.createElement("canvas");
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
e.preventDefault();
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
},
stop : function() {
clearInterval(this.interval);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function canvasObject(width, height, color, x, y, type)
{
this.type = type;
this.width = width;
this.height = height;
this.speed = 0;
this.angle = 0;
this.moveAngle = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = gameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.fillStyle = color;
ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height);
ctx.restore();
}
this.newPos = function() {
this.angle += this.moveAngle * Math.PI / 180;
this.x += this.speed * Math.sin(this.angle);
this.y -= this.speed * Math.cos(this.angle);
}
}
function updateGameArea() {
gameArea.clear();
playerOne.moveAngle = 0;
playerOne.speed = 0;
if (gameArea.keys && gameArea.keys[37]) {
playerOne.moveAngle = -1;
}
if (gameArea.keys && gameArea.keys[39]) {
playerOne.moveAngle = 1;
}
if (gameArea.keys && gameArea.keys[38]) {
playerOne.speed= 1;
}
if (gameArea.keys && gameArea.keys[40]) {
playerOne.speed= -1;
}
playerOne.newPos();
playerOne.update();
}
</script>
</body>
</html>
gameArea is an object. Object properties are separated with ,, not ;. Hence, there is a syntax error after the definition of canvas.
var playerOne;
function initial() {
playerOne = new canvasObject(30, 30, "red", 225, 225);
gameArea.start();
}
var gameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function (e) {
e.preventDefault();
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = (e.type == "keydown");
})
},
stop : function() {
clearInterval(this.interval);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function canvasObject(width, height, color, x, y, type)
{
this.type = type;
this.width = width;
this.height = height;
this.speed = 0;
this.angle = 0;
this.moveAngle = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = gameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.fillStyle = color;
ctx.fillRect(this.width / -2, this.height / -2, this.width, this.height);
ctx.restore();
}
this.newPos = function() {
this.angle += this.moveAngle * Math.PI / 180;
this.x += this.speed * Math.sin(this.angle);
this.y -= this.speed * Math.cos(this.angle);
}
}
function updateGameArea() {
gameArea.clear();
playerOne.moveAngle = 0;
playerOne.speed = 0;
if (gameArea.keys && gameArea.keys[37]) {
playerOne.moveAngle = -1;
}
if (gameArea.keys && gameArea.keys[39]) {
playerOne.moveAngle = 1;
}
if (gameArea.keys && gameArea.keys[38]) {
playerOne.speed= 1;
}
if (gameArea.keys && gameArea.keys[40]) {
playerOne.speed= -1;
}
playerOne.newPos();
playerOne.update();
}
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
}
<body onload = "initial()">
You have a syntax error:
var gameArea = {
canvas : document.createElement("canvas");
...
}
You've used ; instead of ,