Canvas JS game, Can't make the character jump only once after touching the ground

Viewed 71

this is my code for a canvas game, my character is able to go left right and jump. The problem is that i want to make the character jump only once, then touch the ground again and now, to be able to jump again. In my code if i press w multiple times will jump forever.

const canvas = document.querySelector("canvas");
const c = canvas.getContext('2d')

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const gravity = 0.5;
let isGrounded;

class Player {
  constructor() {
    this.position = {
      x: 100,
      y: 100
    }
    this.width = 100
    this.height = 100
    this.velocity = {
      x: 0,
      y: 1
    }
  }
  draw() {
    c.fillStyle = 'black'
    c.fillRect(this.position.x, this.position.y,
      this.width, this.height)
  }

  update() {
    this.draw();
    this.position.x += this.velocity.x;
    this.position.y += this.velocity.y;

    if (this.position.y + this.height + this.velocity.y >= canvas.height)
      this.velocity.y = 0
    else this.velocity.y += gravity;
  }
}

class Platform {
  constructor({
    x,
    y
  }) {
    this.position = {
      x: x,
      y: y
    }
    this.width = 400
    this.height = 20
  }

  draw() {
    c.fillStyle = 'black'
    c.fillRect(this.position.x, this.position.y,
      this.width, this.height)
  }
}

const player = new Player()
const platforms = [
  new Platform({
    x: 150,
    y: 800,
  }),
  new Platform({
    x: 700,
    y: 900
  }),
];

const keys = {
  right: {
    pressed: false
  },
  left: {
    pressed: false
  },
  up: {
    pressed: false
  }
}

let scrollOffset = 0

function animate() {
  const JUMP_SPEED = 5
  requestAnimationFrame(animate)
  c.clearRect(0, 0, canvas.width, canvas.height)
  player.update();
  platforms.forEach(platform => {
    platform.draw()
  })

  if (keys.right.pressed && player.position.x < 400) {
    player.velocity.x = 5
  } else if (keys.left.pressed && player.position.x > 100) {
    player.velocity.x = -5
  } else {
    player.velocity.x = 0

    if (keys.right.pressed) {
      scrollOffset += 5;
      platforms.forEach((platform) => {
        platform.position.x -= 5;
      });
    } else if (keys.left.pressed) {
      scrollOffset -= 5
      platforms.forEach((platform) => {
        platform.position.x += 5;
      });
    }
  }
  //platform collision detection
  platforms.forEach(platform => {
    if (player.position.y + player.height <= platform.position.y &&
      player.position.y + player.height + player.velocity.y >= platform.position.y &&
      player.position.x + player.width >= platform.position.x &&
      player.position.x <= platform.position.x + platform.width) {
      player.velocity.y = 0
    }
  })
  if (scrollOffset > 300) {
    console.log('u win')
  }
}

animate()

window.addEventListener('keydown', ({keyCode}) => {
  switch (keyCode) {
    case 65:
      keys.left.pressed = true;
      break;
    case 83:
      break;
    case 68:
      keys.right.pressed = true;
      break;
    case 87:
      keys.up.pressed = true;
      player.velocity.y = -10
      break;
  }
})

window.addEventListener("keyup", ({keyCode}) => {
  switch (keyCode) {
    case 65:
      keys.left.pressed = false;
      break;
    case 83:
      break;
    case 68:
      keys.right.pressed = false;
      break;
    case 87:
      keys.up.pressed = false;
      break;
  }
});
<canvas></canvas>

1 Answers

You could give the player a jumping flag, and check for that flag:

class Player {
  constructor() {
    this.position = {
      x: 100,
      y: 100
    }
    this.jumping = false;
    ...
  }
  ...
  jump() {
    if (!this.jumping) {
      this.jumping = true;
      this.velocity.y -= 10; // you want relative velocity, not absolute.
    }
  }
}

Then you can use a simple if (!this.jumping) jump when the jump keydown happens:

const keyDownHandlers = {
  "a": () => {
     keys.left.pressed = true;
   },
   "d": () => {
     keys.left.pressed = true;
   },
   "w": () => {
     keys.up.pressed = true;
     player.jump();
   }
};

window.addEventListener('keydown', ({ key } => {
  keyDownHandlers[key]?.();
})

But that will also allow the player to jump when they're falling (whenever you add that), so really what you want is a check to see if the player is currently in a collision state with a fixture or not. especially if those fixtures move (e.g. you want to be able to jump while standing on an elevator, despite your velocity vector already having a non-zero y value).

class Player {
  constructor() {
    this.position = {
      x: 100,
      y: 100
    }
    ...
  }
  ...
  jump() {
    if (this.isOnFixture()) {
      this.velocity.y -= 10;
    }
  }
  isOnFixture() {
    // you get to implement this one
  }
}

And since you already have collision detection code written, the onFixture() function for a player should mostly be a matter of moving the code that you're currently running in anime either into the player class, as into a dedicate function that you can then call inside isOnFixture().

Related