Mathguy3829
Mathguy3829

Reputation: 49

Jumping, moving, and gravity in my 2D javascript game

I'm having lots of issues with my gravity and jumping. My code doesn't execute how I want it to, and I've been messing around with it for a while. The jumping is bugging and the moving isn't as smooth as I want it. I want my gravity to work while i'm still jumping, so I set it to zero, but it just doesn't look natural. Here's my code. I hope someone can help

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = 800
canvas.height = 600

class Main {
    constructor(x, y, w, h) {
        this.x = x
        this.y = y
        this.w = w
        this.h = h
        this.lives = 3;
        this.speedX = 0;
        this.speedY = 0;
        this.gravity = 0.03;
        this.gravitySpeed = 0;
        this.dx = 0;
        this.dy = 0;
    }

    draw() {
        ctx.beginPath();
        ctx.rect(this.x, this.y, this.w, this.h);
        ctx.fill()
        ctx.closePath();
    }

    newPos() {
        this.gravitySpeed += this.gravity;
        this.x += this.speedX;
        this.y += this.speedY + this.gravitySpeed;   
    }

    update() {

        if(this.y >= canvas.height - 50) {
            this.y = canvas.height - 50
        }

        if (controller1.up) {this.dy -= 2, this.gravity = 0.01}; 
        if (controller1.right) {this.dx += 0.5};
        if (controller1.left) {this.dx -= 0.5};
        this.x += this.dx;  
        this.y += this.dy;
        this.dx *= 0.9;
        this.dy *= 0.9;
        this.draw();
    }
}

class Controller {
    constructor() {
      this.up = false;
      this.right = false;
      this.down = false;
      this.left = false;
      
      let keyEvent = (e) => {
        if (e.code == "KeyW" || e.code == "ArrowUp") {this.up = e.type == 'keydown'};
        if (e.code == "KeyD" || e.code == "ArrowRight") {this.right = e.type == 'keydown'};
        if (e.code == "KeyA" || e.code == "ArrowLeft") {this.left = e.type == 'keydown'};
        
      }
      addEventListener('keydown', keyEvent);
      addEventListener('keyup', keyEvent);
      addEventListener('mousemove', keyEvent)
      }
  }

let main1 = new Main(canvas.width / 2, canvas.height / 2, 50, 50)
let controller1 = new Controller();

function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    main1.update();
    requestAnimationFrame(animate)
}

function updatePos() {
    main1.newPos();
}

animate()
setInterval(updatePos, 10)
<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>Document</title>
</head>
<body>
    <canvas id="canvas"></canvas>

    <script src="main.js"></script>
</body>
</html>

Upvotes: 0

Views: 1153

Answers (2)

Justin
Justin

Reputation: 2958

I would set your gravity to a global variable to start. This will allow all objects you create to reference the same gravity value. Depending on how much gravity you set will change how much negative value you give your jump command. I also add a separate canvasCollision() function in the class for this example.

Also keep in mind I changed the canvas size for this example.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 300;
const gravity = 2;

class Main {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.lives = 3;
    this.speedX = 0.5;
    this.speedY = 35;
    this.jumping = false;
    this.vx = 0;
    this.vy = 0;
  }
  draw() {
    ctx.beginPath();
    ctx.rect(this.x, this.y, this.w, this.h);
    ctx.fill();
    ctx.closePath();
  }
  canvasCollision() {
    if (this.x <= 0) this.x = 0;
    if (this.y <= 0) this.y = 0;
    if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
    if (this.y + this.h >= canvas.height) {
      this.y = canvas.height - this.h;
      this.vy = 0;
      this.jumping = false;
    }
  }
  update() {
    if (controller1.left) this.vx -= this.speedX;
    if (controller1.up && !this.jumping) {
      this.vy -= this.speedY;
      this.jumping = true;
    }
    if (controller1.right) this.vx += this.speedX;
    this.vy += gravity;
    this.x += this.vx;
    this.y += this.vy;
    this.vx *= 0.9;
    this.vy *= 0.9;
    this.canvasCollision();
  }
}

class Controller {
  constructor() {
    this.up = false;
    this.right = false;
    this.down = false;
    this.left = false;

    let keyEvent = (e) => {
      if (e.code == "KeyW" || e.code == "ArrowUp") {
        this.up = e.type == "keydown";
      }
      if (e.code == "KeyD" || e.code == "ArrowRight") {
        this.right = e.type == "keydown";
      }
      if (e.code == "KeyA" || e.code == "ArrowLeft") {
        this.left = e.type == "keydown";
      }
    };
    addEventListener("keydown", keyEvent);
    addEventListener("keyup", keyEvent);
    addEventListener("mousemove", keyEvent);
  }
}

let main1 = new Main(canvas.width / 2, canvas.height / 2, 50, 50);
let controller1 = new Controller();

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  main1.update();
  main1.draw();
  requestAnimationFrame(animate);
}
animate();
 <canvas id="canvas"></canvas>

Upvotes: 1

Sandsten
Sandsten

Reputation: 757

Great effort thus far. Game dev can be a bit tricky in the beginning to get things look and feel like you want it. I modified your main class a bit and made the jump a bit smoother. You are not using dt (delta time) which makes it easier to handle jumping with gravity (not the only way to do it). And remember that gravity is a constant, if you set it to 0 it means we have nothing to pull the player back onto the ground. Gravity is always there and when we jump we counter it for a second or two.

If you want to read some more about this you can google on "2D game physics + jumping" or something similar. Here are some links that I found on the topic.

https://gamedev.stackexchange.com/questions/32631/easy-way-to-do-gravity-in-a-simple-game https://www.gamedev.net/tutorials/_/technical/math-and-physics/a-verlet-based-approach-for-2d-game-physics-r2714/ https://gamedev.stackexchange.com/questions/60008/smooth-jumping-in-2d-platformers

I recommend the Game Development community on Stack Overflow for these questions too https://gamedev.stackexchange.com/

Feel free to ask if you have further questions

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = 600
canvas.height = 200

let time; // Current time
let prevTime = Date.now(); // Store previous time
let isGrounded; // Check if player is on the ground

class Main {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.lives = 3;
    this.speedX = 0;
    this.speedY = 0;
    this.gravity = .01;
    // this.gravitySpeed = 0;
    this.jumpSpeed = -1.5; // How fast to jump upwards
    this.dx = 0;
    this.dy = 0;
}
  draw() {
    ctx.beginPath();
    ctx.rect(this.x, this.y, this.w, this.h);
    ctx.fill()
    ctx.closePath();
  }

  newPos() {
    this.gravitySpeed += this.gravity;
    this.x += this.speedX;
  }

  update() {
    // Calculate how much time has passed since last update
    time = Date.now();
    const deltaTime = time - prevTime;

    // Update y-position based speed in y-direction
    // If we jump this.speed will be set to this.jumpSpeed
    this.y += this.speedY * deltaTime;
    // Gravity should always affect the player!
    // The ground check will make sure we don't fall through the floor
    this.y += this.gravity * deltaTime;
    // Make sure to reduce our player's speed in y by gravity!
    this.speedY += this.gravity * deltaTime;

    // Only allow the player to jump if he is on the ground
    if (controller1.up && isGrounded) {
      // Set the player y-speed to jump speed
      this.speedY = this.jumpSpeed;
    };

    if (controller1.right) {this.dx += 0.5};
    if (controller1.left) {this.dx -= 0.5};
    
    this.x += this.dx;
    // this.y += this.dy;
    this.dx *= 0.9;
    this.dy *= 0.9;

    // Ground check
    if(this.y >= canvas.height - 50) {
      this.y = canvas.height - 50;
      isGrounded = true;
    } else {
      isGrounded = false;
    }

    this.draw();
    
    // Store the current time to use for calculation in next update
    prevTime = Date.now();
  }

}

class Controller {
  constructor() {
    this.up = false;
    this.right = false;
    this.down = false;
    this.left = false;

    let keyEvent = (e) => {
      if (e.code == "KeyW" || e.code == "ArrowUp") {this.up = e.type == 'keydown'};
      if (e.code == "KeyD" || e.code == "ArrowRight") {this.right = e.type == 'keydown'};
      if (e.code == "KeyA" || e.code == "ArrowLeft") {this.left = e.type == 'keydown'};
    }

    addEventListener('keydown', keyEvent);
    addEventListener('keyup', keyEvent);
    addEventListener('mousemove', keyEvent)
  }
}

let main1 = new Main(canvas.width / 2, canvas.height / 2, 50, 50)
let controller1 = new Controller();

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  main1.update();
  requestAnimationFrame(animate)
}

function updatePos() {
  main1.newPos();
}

animate()
setInterval(updatePos, 10)
<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>Document</title>
  <style>
    canvas {
      background-color: azure;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>

  <script src="main.js"></script>
</body>

</html>

Upvotes: 2

Related Questions