0

So, I'm trying to code a game where there is a ball that chases the player, and currently I'm coding the player physics. For some reason, as the player walks back and forth, it gets faster and faster, but I can't figure out why. I think it might have something to do with me multiplying the physicsX variable by .85, but I can't figure out how that would affect it. Here is my code-

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var baseX = 100;
var baseY = 50;
var physicsY = 0;
var physicsX = 0;

function coronavirus() {
  ctx.beginPath();
  ctx.fillStyle = "blue";
  ctx.arc(50, 50, 5, 0, 2 * Math.PI, false);
  ctx.fill();
}

function ground() {
  ctx.beginPath();
  ctx.fillStyle = "green";
  ctx.rect(0, 140, screen.width, 10);
  ctx.fill();
}

function person() {
  ctx.beginPath();
  ctx.strokeStyle = "black";
  ctx.arc(baseX, baseY, 5, 0, 2 * Math.PI, false); //head
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 5); //body
  ctx.lineTo(baseX, baseY + 20);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //left leg
  ctx.lineTo(baseX - 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //right leg
  ctx.lineTo(baseX + 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX - 5, baseY + 10); //arms
  ctx.lineTo(baseX + 5, baseY + 10);
  ctx.stroke();
}

function personHitbox() {
  ctx.beginPath();
  ctx.strokeStyle = "black"
  ctx.rect(baseX - 5, baseY - 5, 10, 32);
  ctx.stroke();
}

function personTouchingGround() {
  var personHitbox = {y: baseY - 5, height: 32}
  var ground = {y: 140} 
    if (personHitbox.y + personHitbox.height < ground.y) {
    touchingGround = false;
} else {
  touchingGround = true;
}
}

function personMoving() {
  if (touchingGround == false) {
    physicsY++;
    baseY = baseY + physicsY;
  }
  physicsX = 0;
  window.addEventListener('keydown', (e) => {
  if (e.key == "ArrowRight") {
    physicsX = physicsX + 0.001;
  }
  if (e.key == "ArrowLeft") {
    physicsX = physicsX - 0.001;
  }
  physicsX = physicsX * 0.85  
  baseX = baseX + physicsX;
});


}
function gameloop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  new coronavirus();
  new person();
  new personHitbox();
  new personTouchingGround();
  new personMoving();
  new ground();
  requestAnimationFrame(gameloop);
}
gameloop();
canvas {
  width: 100%;
  height: 100%;
  margin: 0%;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>repl.it</title>
    <link href="style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script src="script.js"></script>
  </body>
</html>
  • 1
    Please visit [help], take [tour] to see what and [ask]. Do some research, search for related topics on SO; if you get stuck, post a [mcve] of your attempt HERE!, noting input and expected output, preferably in a [Stacksnippet](https://blog.stackoverflow.com/2014/09/introducing-runnable-javascript-css-and-html-code-snippets/) – mplungjan May 30 '20 at 17:17
  • Edit - nevermind, I'll fix my post, I see the issue. – Gabriel Wood May 30 '20 at 17:20
  • Ok, I edited my post, it should be good now. – Gabriel Wood May 30 '20 at 17:28
  • Does not seem to speed up in Chrome – mplungjan May 30 '20 at 17:29
  • Note that the original code only moves by 0.001 pixels, which is tiny. A thousand keypress events triggered to move just 1 pixel could not possibly be the intent. It only appears to work because it is binding an enormous amount of keypress events due to binding on every animation frame. – user120242 May 30 '20 at 17:57

2 Answers2

2

Two issues:

  1. You keep adding to physicsX, so the amount of distance it moves keeps increasing. Just keep physicsX constant to move the same amount. (Actually doesn't, because every animation frame you are setting PhysicsX = 0, but the logic is wrong. When you fix the real error, this causes the unit not to move properly.)

  2. Move the 'keydown' event binding outside personMoving() so it only executes once. Your event listener binding is being executed every time you call personMoving(), so it is binding 'key' event over and over again, and then you get 'key' event triggered more and more over time.
    So after 100 animation frames each keypress will trigger the baseX and PhysicsX adding code 100 times.
    You should only bind the key event once.

I've also used Math.abs and *-1 for right left movement.

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var baseX = 100;
var baseY = 50;
var physicsY = 0;
var physicsX = 0;

function coronavirus() {
  ctx.beginPath();
  ctx.fillStyle = "blue";
  ctx.arc(50, 50, 5, 0, 2 * Math.PI, false);
  ctx.fill();
}

function ground() {
  ctx.beginPath();
  ctx.fillStyle = "green";
  ctx.rect(0, 140, screen.width, 10);
  ctx.fill();
}

function person() {
  ctx.beginPath();
  ctx.strokeStyle = "black";
  ctx.arc(baseX, baseY, 5, 0, 2 * Math.PI, false); //head
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 5); //body
  ctx.lineTo(baseX, baseY + 20);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //left leg
  ctx.lineTo(baseX - 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //right leg
  ctx.lineTo(baseX + 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX - 5, baseY + 10); //arms
  ctx.lineTo(baseX + 5, baseY + 10);
  ctx.stroke();
}

function personHitbox() {
  ctx.beginPath();
  ctx.strokeStyle = "black"
  ctx.rect(baseX - 5, baseY - 5, 10, 32);
  ctx.stroke();
}

function personTouchingGround() {
  var personHitbox = {
    y: baseY - 5,
    height: 32
  }
  var ground = {
    y: 140
  }
  if (personHitbox.y + personHitbox.height < ground.y) {
    touchingGround = false;
  } else {
    touchingGround = true;
  }
}

function personMoving() {
  if (touchingGround == false) {
    physicsY++;
    baseY = baseY + physicsY;
  }


}


  physicsX = 10;
  window.addEventListener('keydown', (e) => {
    if (e.key == "ArrowRight") {
      physicsX = Math.abs(physicsX);
    }
    if (e.key == "ArrowLeft") {
      physicsX = Math.abs(physicsX)*-1;
    }
//    physicsX = physicsX * 0.85
    baseX = baseX + physicsX;
//    put this console.log in your original code to see what I mean
//    console.log(baseX)
  });

function gameloop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  new coronavirus();
  new person();
  new personHitbox();
  new personTouchingGround();
  new personMoving();
  new ground();
  requestAnimationFrame(gameloop);
}
gameloop();
<canvas id="canvas"></canvas>
user120242
  • 14,918
  • 3
  • 38
  • 52
0

The playerX variable only decreases by 15% (0.15) every time a key is pressed. To fix this, simply move that line outside of the keypress event listener into the game loop (just somewhere where it'll be run on each iteration of the loop).

Edit: As @user120242 mentioned, the event listener should be moved outside of the loop too. However, my slightly modified solution preserves (I believe intended) horizontal velocity. The only problem is that there's a delay in the keypress with both solutions, which you can refer to How to fix delay in javascript keydown for.

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var baseX = 100;
var baseY = 50;
var physicsY = 0;
var physicsX = 0;
//move this outside
window.addEventListener('keydown', (e) => {
  if (e.key == "ArrowRight") {
      physicsX += 5;
  }
  if (e.key == "ArrowLeft") {
      physicsX -= 5;
  }
});

function coronavirus() {
  ctx.beginPath();
  ctx.fillStyle = "blue";
  ctx.arc(50, 50, 5, 0, 2 * Math.PI, false);
  ctx.fill();
}

function ground() {
  ctx.beginPath();
  ctx.fillStyle = "green";
  ctx.rect(0, 140, screen.width, 10);
  ctx.fill();
}

function person() {
  ctx.beginPath();
  ctx.strokeStyle = "black";
  ctx.arc(baseX, baseY, 5, 0, 2 * Math.PI, false); //head
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 5); //body
  ctx.lineTo(baseX, baseY + 20);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //left leg
  ctx.lineTo(baseX - 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX, baseY + 20); //right leg
  ctx.lineTo(baseX + 3, baseY + 27);
  ctx.stroke();
  ctx.moveTo(baseX - 5, baseY + 10); //arms
  ctx.lineTo(baseX + 5, baseY + 10);
  ctx.stroke();
}

function personHitbox() {
  ctx.beginPath();
  ctx.strokeStyle = "black"
  ctx.rect(baseX - 5, baseY - 5, 10, 32);
  ctx.stroke();
}

function personTouchingGround() {
  var personHitbox = {
    y: baseY - 5,
    height: 32
  }
  var ground = {
    y: 140
  }
  if (personHitbox.y + personHitbox.height < ground.y) {
    touchingGround = false;
  } else {
    touchingGround = true;
  }
}

function personMoving() {
  if (touchingGround == false) {
    physicsY++;
    baseY += physicsY;
  }
  //physicsX = 0; remove this line of code
  //moving the line here will guarantee physicsX decreases on each
  //iteration of the loop.
  physicsX *= 0.85;
  baseX += physicsX;
}

function gameloop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  new coronavirus();
  new person();
  new personHitbox();
  new personTouchingGround();
  new personMoving();
  new ground();
  requestAnimationFrame(gameloop);
}
gameloop();
<canvas id="canvas"></canvas>

Also, using += can simplify your code. For example: playerX = playerX + 15; is the same as playerX += 15;

bordeaux
  • 333
  • 3
  • 15
  • I tried that and it didn't work. You just mean something like this, right? function gameloop() { ctx.clearRect(0, 0, canvas.width, canvas.height); new coronavirus(); new person(); new personHitbox(); new personTouchingGround(); new personMoving(); physicsX = physicsX * 0.85; new ground(); requestAnimationFrame(gameloop); } gameloop(); – Gabriel Wood May 30 '20 at 17:35
  • Yes, and like @user120242 stated, you would move the event listener out (I forgot to state this in my original answer). Unlike their answer though, just moving that line out provides a much smoother movement, which you can see by running the code snippets. – bordeaux May 30 '20 at 19:01