Search code examples
javascripthtmlcssgame-physics

Collision Implementation


I have a game that detects and implements side collisions. Everything works, however there is a movement similar to a vibration which occurs because of the time lapse between the detection and implementation. It works which is great but the vibration just really irritates me. Thank you for your help in advance.

Here's my code:

<!DOCTYPE html>
<html>
<head>
<title>Game</title>
<style>
html, body {
height: 100%;
width: 100%;
margin: 0;
}
#player {
background: #000;
position: fixed;
height: 10px;
width: 10px;
left: 0px;
top: 0px;
}
#powerup {
background: blue;
position: fixed;
height: 10px;
width: 10px;
}
</style>
</head>
<body onkeydown="move(event)">
<div id="player"></div>
<script>
var player = document.getElementById("player");
var powerup = document.getElementById("powerup");
var object = document.getElementsByClassName("object");
var counter = 0;
var addcounter = setInterval(createObject,1);

function createObject() {
counter++;
var newObject = document.createElement("DIV");
newObject.setAttribute("class", "object");
newObject.setAttribute("id", "powerup");
newObject.style.top = Math.floor(Math.random() * (window.innerHeight - 10)) + 10 + "px";
newObject.style.left = Math.floor(Math.random() * (window.innerWidth - 10)) + 10 + "px";
document.body.appendChild(newObject);
if (counter > 10) {
clearInterval(addcounter);
}
}

setInterval(collisionDetection,1);
function collisionDetection() {
for (i = 0; i < object.length; i++) { 
// Player Right to Object Left
if (player.offsetLeft + player.offsetWidth <= object[i].offsetLeft + 1 &&
    player.offsetLeft + player.offsetWidth >= object[i].offsetLeft &&
    player.offsetTop < object[i].offsetTop + object[i].offsetHeight &&
    player.offsetHeight + player.offsetTop > object[i].offsetTop) {
player.style.left = object[i].offsetLeft - player.offsetWidth + "px";   
}
// Player Left to Object Right
if (player.offsetLeft >= object[i].offsetLeft + object[i].offsetWidth - 1 &&
    player.offsetLeft <= object[i].offsetLeft + object[i].offsetWidth &&
    player.offsetTop < object[i].offsetTop + object[i].offsetHeight &&
    player.offsetHeight + player.offsetTop > object[i].offsetTop) {
player.style.left = object[i].offsetLeft + object[i].offsetWidth + "px";   
}
// Player Bottom to Object Top
if (player.offsetLeft < object[i].offsetLeft + object[i].offsetWidth &&
    player.offsetLeft + player.offsetWidth > object[i].offsetLeft &&
    player.offsetTop + player.offsetHeight >= object[i].offsetTop &&
    player.offsetTop + player.offsetHeight <= object[i].offsetTop + 1) {
player.style.top = object[i].offsetTop - player.offsetHeight + "px";   
}
// Player Top to Object Bottom
if (player.offsetLeft < object[i].offsetLeft + object[i].offsetWidth &&
    player.offsetLeft + player.offsetWidth > object[i].offsetLeft &&
    player.offsetTop <= object[i].offsetTop + object[i].offsetHeight &&
    player.offsetTop >= object[i].offsetTop + object[i].offsetHeight - 1) {
player.style.top = object[i].offsetTop + object[i].offsetHeight + "px";   
}
}
}

function move(event) {
var key = event.keyCode;
if (key == 40) {
player.style.top = player.offsetTop + 1 + "px";
}
if (key == 39) {
player.style.left = player.offsetLeft + 1 + "px";
}
if (key == 38) {
player.style.top = player.offsetTop - 1 + "px";
}
if (key == 37) {
player.style.left = player.offsetLeft - 1 + "px";
}
}
</script>
</body>
</html>


Solution

  • The jittery movement is caused because the movement is happening in the "keydown" callback, which isn't called every frame that a key is held. Instead, you can store the key state in a variable when the key is pressed, and then mark it as not pressed when the key is released. I've modified your example to make the movement smoother.

    I've also added an update function that checks the pressed key so the movement of the character happens every frame.

    With some more work you could make a manager that allows you to check which keys are pressed. You can also move your collision detection function to after the character moves to make sure the player is in the right spot before the frame ends.

    Hope that helps!

    <!DOCTYPE html>
    <html>
    <head>
    <title>Game</title>
    <style>
    html, body {
    height: 100%;
    width: 100%;
    margin: 0;
    }
    #player {
    background: #000;
    position: fixed;
    height: 10px;
    width: 10px;
    left: 0px;
    top: 0px;
    }
    #powerup {
    background: blue;
    position: fixed;
    height: 10px;
    width: 10px;
    }
    </style>
    </head>
    <!-- NOTE: Added a keyup event -->
    <body onkeydown="keydown(event)" onkeyup="keyup(event)">
    <div id="player"></div>
    <script>
    var player = document.getElementById("player");
    var powerup = document.getElementById("powerup");
    var object = document.getElementsByClassName("object");
    var counter = 0;
    var addcounter = setInterval(createObject,1);
    
    function createObject() {
    counter++;
    var newObject = document.createElement("DIV");
    newObject.setAttribute("class", "object");
    newObject.setAttribute("id", "powerup");
    newObject.style.top = Math.floor(Math.random() * (window.innerHeight - 10)) + 10 + "px";
    newObject.style.left = Math.floor(Math.random() * (window.innerWidth - 10)) + 10 + "px";
    document.body.appendChild(newObject);
    if (counter > 10) {
    clearInterval(addcounter);
    }
    }
    
    setInterval(collisionDetection,1);
    function collisionDetection() {
    for (i = 0; i < object.length; i++) { 
    // Player Right to Object Left
    if (player.offsetLeft + player.offsetWidth <= object[i].offsetLeft + 1 &&
        player.offsetLeft + player.offsetWidth >= object[i].offsetLeft &&
        player.offsetTop < object[i].offsetTop + object[i].offsetHeight &&
        player.offsetHeight + player.offsetTop > object[i].offsetTop) {
    player.style.left = object[i].offsetLeft - player.offsetWidth + "px";   
    }
    // Player Left to Object Right
    if (player.offsetLeft >= object[i].offsetLeft + object[i].offsetWidth - 1 &&
        player.offsetLeft <= object[i].offsetLeft + object[i].offsetWidth &&
        player.offsetTop < object[i].offsetTop + object[i].offsetHeight &&
        player.offsetHeight + player.offsetTop > object[i].offsetTop) {
    player.style.left = object[i].offsetLeft + object[i].offsetWidth + "px";   
    }
    // Player Bottom to Object Top
    if (player.offsetLeft < object[i].offsetLeft + object[i].offsetWidth &&
        player.offsetLeft + player.offsetWidth > object[i].offsetLeft &&
        player.offsetTop + player.offsetHeight >= object[i].offsetTop &&
        player.offsetTop + player.offsetHeight <= object[i].offsetTop + 1) {
    player.style.top = object[i].offsetTop - player.offsetHeight + "px";   
    }
    // Player Top to Object Bottom
    if (player.offsetLeft < object[i].offsetLeft + object[i].offsetWidth &&
        player.offsetLeft + player.offsetWidth > object[i].offsetLeft &&
        player.offsetTop <= object[i].offsetTop + object[i].offsetHeight &&
        player.offsetTop >= object[i].offsetTop + object[i].offsetHeight - 1) {
    player.style.top = object[i].offsetTop + object[i].offsetHeight + "px";   
    }
    }
    }
    
    
    // NOTE: Storing the pressed key in a variable
    var key = -1;
    
    function keyup() { key = -1; }
    
    function keydown(event) {
    key = event.keyCode;
    }
    
    // NOTE: Running the update function every frame
    function update() {
    if (key == 40) {
    player.style.top = player.offsetTop + 1 + "px";
    }
    if (key == 39) {
    player.style.left = player.offsetLeft + 1 + "px";
    }
    if (key == 38) {
    player.style.top = player.offsetTop - 1 + "px";
    }
    if (key == 37) {
    player.style.left = player.offsetLeft - 1 + "px";
    }
    
      requestAnimationFrame(update);
    }
    
    update();
    </script>
    </body>
    </html>