1

I am trying to make a infinite scrolling game in javascript.

The following code works as expected (it doesn't work here because the images cannot be uploaded):

class Vec {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

class Rect {
  constructor(src) {
    this.pos = new Vec;
    this.vel = new Vec;
    this.image = new Image();
    this.image.src = src;
  }
  get left() {
    return this.pos.x;
  }
  get right() {
    return this.pos.x + this.image.width;
  }
  get top() {
    return this.pos.y;
  }
  get bottom() {
    return this.pos.y + this.image.height;
  }
}

class Player extends Rect {
  constructor() {
    super("images/frog-still.png");
    this.pos.x = 100;
    this.pos.y = HEIGHT - GROUND_Y - this.image.height;
  }
}

class Enemy extends Rect {
  constructor() {
    super("images/spike.png");
    this.image.width = this.image.width / 4;
    this.image.height = this.image.height / 4;
    this.pos.x = WIDTH;
    this.pos.y = HEIGHT - GROUND_Y - this.image.height;
  }
}

// setup
const ctx = document.getElementById("canvas").getContext("2d");

let isOver = false;

const WIDTH = 600;
const HEIGHT = 400;
const GROUND_Y = 50;

const player = new Player;

let enemies = [];
const MAX_ENEMY = 3;

let isJumping = false;
const JUMP_STRENGTH = -450;
const GRAVITY = 25;

document.addEventListener("keydown", (e) => {
  if (e.keyCode === 38 && isJumping === false) {
    isJumping = true;
    player.vel.y = JUMP_STRENGTH;
  }
}, false);

let lastUpdate = 0;
let random = Math.floor((Math.random() * 2501) + 1000);

function update(dt, now) {
  // update player
  player.vel.y += GRAVITY;
  player.pos.y += player.vel.y * dt;

  if (player.bottom >= HEIGHT - GROUND_Y) {
    isJumping = false;
    player.pos.y = HEIGHT - GROUND_Y - player.image.height;
    player.vel.y = 0;
  }

  // create enemy
  if (now - lastUpdate > random) {
    lastUpdate = now;
    random = Math.floor((Math.random() * 2501) + 1000);
    enemies.push(new Enemy);
  }

  for (let i = 0; i < enemies.length; i++) {
    // update enemy
    enemies[i].vel.x = -300 - (now / 500);
    enemies[i].pos.x += enemies[i].vel.x * dt;

    if (player.right > enemies[i].left && player.left < enemies[i].right && player.bottom > enemies[i].top)
      isOver = true;

    if (enemies[i].right < 0)
      enemies.splice(i, 1);
  }
}

function draw() {
  // draw background
  ctx.fillStyle = "#000";
  ctx.fillRect(0, 0, WIDTH, HEIGHT);

  // draw ground
  ctx.fillStyle = "#0f0";
  ctx.fillRect(0, HEIGHT - GROUND_Y, WIDTH, GROUND_Y);

  // draw player    
  ctx.drawImage(player.image, player.pos.x, player.pos.y, player.image.width, player.image.height);
  // draw enemy
  ctx.fillStyle = "#f00";
  for (let i = 0; i < enemies.length; i++) {
    ctx.drawImage(enemies[i].image, enemies[i].pos.x, enemies[i].pos.y, enemies[i].image.width, enemies[i].image.height);
  }
}

// game loop
const TIMESTEP = 1 / 60;
let accumulator = 0;
let lastRender = 0;

function loop(timestamp) {
  accumulator += (timestamp - lastRender) / 1000;
  lastRender = timestamp;
  while (accumulator >= TIMESTEP) {
    update(TIMESTEP, timestamp);
    accumulator -= TIMESTEP;
  }
  draw();

  if (!isOver)
    requestAnimationFrame(loop);
}

requestAnimationFrame(loop);
<!DOCTYPE html>
<html>

<head>
  <title>Jump_Over_It</title>
  <link href="css/default.css" rel="stylesheet" />
</head>

<body>
  <canvas id="canvas" width="600" height="400"></canvas>
  <script src="js/main.js"></script>
</body>

</html>

However, when add

this.image.width = this.image.width / 2; this.image.height = this.image.height / 2;

to the Player class (like I did with the Enemy class), the player disappears. If I do console.log(player.image), it says that the image width and the image height is equal to 0. Why is this happening?

How can I fix this problem?

SKero2004
  • 133
  • 1
  • 2
  • 15
  • Because the loading of an Image is always asynchronous. You must wait for its `onload` event to fire before doing anything with it. – Kaiido Jun 29 '18 at 04:20
  • @Kaiido Can you please give me the exact solution (like where to add the onload function in my code)? It would be very helpful. – SKero2004 Jun 29 '18 at 23:47

1 Answers1

0

I don't know why, but while the image is not rendered the width and height are 0. So, I created the method beforeRender() that is implemented by Player and Enemy. This method is invoked before the ctx.drawImage and update the graphics settings.

I know it is not the answer you are looking for, but it is my best.

class Vec {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

class Rect {
  constructor(src) {
    this.pos = new Vec;
    this.vel = new Vec;
    this.image = new Image;
    this.image.src = src;
    this.rendered = false;
  }
  get left() {
    return this.pos.x;
  }
  get right() {
    return this.pos.x + this.image.width;
  }
  get top() {
    return this.pos.y;
  }
  get bottom() {
    return this.pos.y + this.image.height;
  }

  get render() {
    if (!this.rendered) {
      if (this.beforeRender()) {
        this.rendered = true;
      }
    }
    return this.image;
  }

  beforeRender() {}
}

class Player extends Rect {
  constructor() {
    super("https://cdn4.iconfinder.com/data/icons/spring-flat-colorful/2048/5683_-_Frog-256.png");
    this.pos.x = 100;
    this.pos.y = HEIGHT - GROUND_Y - this.image.height;
  }

  beforeRender() {
    if (this.image.width && this.image.height) {
      this.image.width /= 2;
      this.image.height /= 2;
      return true;
    }
    return false;
  }

}

class Enemy extends Rect {
  constructor() {
    super("https://vignette.wikia.nocookie.net/scribblenauts/images/8/8d/Steel_Spike.png/revision/latest?cb=20130105173440");
    this.image.width /= 4;
    this.image.height /= 4;
    this.pos.x = WIDTH;
    this.pos.y = HEIGHT - GROUND_Y - this.image.height;
  }

  beforeRender() {
    if (this.image.width && this.image.height) {
      this.image.width /= 4;
      this.image.height /= 4;
      return true;
    }
    return false;
  }
}

// setup
const ctx = document.getElementById("canvas").getContext("2d");

let isOver = false;

const WIDTH = 600;
const HEIGHT = 400;
const GROUND_Y = 50;

const player = new Player;

let enemies = [];
const MAX_ENEMY = 3;

let isJumping = false;
const JUMP_STRENGTH = -450;
const GRAVITY = 25;

document.addEventListener("keydown", (e) => {
  if (e.keyCode === 38 && isJumping === false) {
    isJumping = true;
    player.vel.y = JUMP_STRENGTH;
  }
}, false);

let lastUpdate = 0;
let random = Math.floor((Math.random() * 2501) + 1000);

function update(dt, now) {
  // update player
  player.vel.y += GRAVITY;
  player.pos.y += player.vel.y * dt;

  if (player.bottom >= HEIGHT - GROUND_Y) {
    isJumping = false;
    player.pos.y = HEIGHT - GROUND_Y - player.image.height;
    player.vel.y = 0;
  }

  // create enemy
  if (now - lastUpdate > random) {
    lastUpdate = now;
    random = Math.floor((Math.random() * 2501) + 1000);
    enemies.push(new Enemy);
  }

  for (let i = 0; i < enemies.length; i++) {
    // update enemy
    enemies[i].vel.x = -300 - (now / 500);
    enemies[i].pos.x += enemies[i].vel.x * dt;

    if (player.right > enemies[i].left && player.left < enemies[i].right && player.bottom > enemies[i].top)
      isOver = true;

    if (enemies[i].right < 0)
      enemies.splice(i, 1);
  }
}

function draw() {
  // draw background
  ctx.fillStyle = "#000";
  ctx.fillRect(0, 0, WIDTH, HEIGHT);

  // draw ground
  ctx.fillStyle = "#0f0";
  ctx.fillRect(0, HEIGHT - GROUND_Y, WIDTH, GROUND_Y);

  // draw player    
  ctx.drawImage(player.render, player.pos.x, player.pos.y, player.image.width, player.image.height);

  // draw enemy
  ctx.fillStyle = "#f00";
  for (let i = 0; i < enemies.length; i++) {
    ctx.drawImage(enemies[i].image, enemies[i].pos.x, enemies[i].pos.y, enemies[i].image.width, enemies[i].image.height);
  }
}

// game loop
const TIMESTEP = 1 / 60;
let accumulator = 0;
let lastRender = 0;

requestAnimationFrame(loop);

function loop(timestamp) {
  accumulator += (timestamp - lastRender) / 1000;
  lastRender = timestamp;
  while (accumulator >= TIMESTEP) {
    update(TIMESTEP, timestamp);
    accumulator -= TIMESTEP;
  }
  draw();

  if (!isOver)
    requestAnimationFrame(loop);
}
<!DOCTYPE html>
<html>

<head>
  <title>Jump_Over_It</title>
  <link href="css/default.css" rel="stylesheet" />
</head>

<body>
  <canvas id="canvas" width="600" height="400"></canvas>
  <script src="js/main.js"></script>
</body>

</html>
lmarqs
  • 1,469
  • 13
  • 16