1

I'm working through some basic steps in an HTML, CSS and JScript game to get very simple collision detection working between a CHARACTER and an ENEMY.

I am looking for something that is simple enough to explain to children of age 8 - 11.

If the character and the enemy collide then "game over". I have tried various things, but require some help with the code and an explanation of getComputedStyle method and what Properties it gets, for me to effectively create the main if statement.

This is the relevant if function:

if(
        characterTop==enemyLeft //have tried characterRight==enemyLeft didn't work
    )
    {
        enemy.style.animation="none"; //remove the animation
        enemy.style.display="none";
        alert("Game Over");
        
    } 
},10);

And this is the setInterval function with the variables declared (which I use in the if function). I have created the var CharacterRight variable based on the varcharacterTop variable and enemyLeft, but it doesn't seem to work or I am not sure if I am going about it the right way.

In the current set up something strange happens. If I leave the character and do not move it, after 5 bounces (of the enemy) the game is over, even though, as per the if function, characterTop is not changing at all. So what is happening there?

In an answer, please:

a) Suggest a proposed solution for collision detection based on my existing code

b) Explanation of the GetComputedStyle and getProperties with documentation on it. For instance, if characterTop doesn't change (if I don't move the character) how come it suddenly outputs "game over" which suggests characterTop is == enemyLeft.

c) Best practices for simple collision detection (for absolute beginner's learning concepts and propose the simplest possible solution, given the existing code)

var checkDead =setInterval(function(){
    var characterTop = parseInt(window.getComputedStyle(character).getPropertyValue("top"));
    var enemyLeft = parseInt(window.getComputedStyle(enemy).getPropertyValue("left"));
    //ADD VARIABLE TO FIND CHARACTER RIGHT -if it collides with enemy left, you are out
    //remove the condition that the enemy has to be in the first 30 pixels of the game (left side)
    var characterRight =parseInt(window.getComputedStyle(character).getPropertyValue("right"));

For completeness, here is the whole code:

HTML

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
    
</head>
<body>
  <h1>Game</h1>
 
   
   
   <div id="game">
        <div id="sky"></div>
       <div id="ground"></div>
        <div id="enemy"></div>
       <div id="character"></div>
   </div>
   
<script src="script.js"></script>
</body>

</html>

CSS

*{
    padding:0;
    margin:22;
}

#game{
    width:500px;
    height:500px;
    border:4px solid #171918;
}

#character{
    width:30px;
    height:120px;
    background-color:green;
    position:relative;
    top:320px;
    border-radius:20px;
    /*animation: jump 300ms */
    
}

/* new class called animate */
.animate{
    animation: jump 500ms;
}


#enemy{
    width:60px;
    height:60px;
    background-color:red;
    border-radius:14px;
    position:relative;
    top:320px;
    left:440px;
    animation: moveenemy 1s infinite linear;
    
}

@keyframes moveenemy{
    0%{left:440px;}
    50%{top:58px;}
    100%{left:0px; top:320x;}
}

@keyframes jump{
    0%{top:380px;}
    30%{top:50px;}
    50%{top:40px;}
    100%{top:380px;}
}





/* adding sky*/
#sky{
    width:500px;
    height:340px;
    background-color:skyblue;
    position:absolute;
    top:118px;
}

/* adding ground */
#ground{
    width:500px;
    height:170px;
    background-color:bisque;
    position:absolute;
    top:450px;
}

JavaScript

var character = document.getElementById("character");
var enemy = document.getElementById("enemy");    
function jump(){
    if(character.classlist!="animate"){
    character.classList.add("animate");
    }
    setTimeout(function(){
        character.classList.remove("animate");
    },500);
    
}

//right movement

function right() {
  var leftVal =  parseInt(window.getComputedStyle(character).getPropertyValue("left"))
  character.style.left = (leftVal + 30) + "px";

}

//left movement
function left() {

  var leftVal =  parseInt(window.getComputedStyle(character).getPropertyValue("left"))
  character.style.left = (leftVal - 30) + "px";

}

var checkDead =setInterval(function(){
    var characterTop = parseInt(window.getComputedStyle(character).getPropertyValue("top"));
    var enemyLeft = parseInt(window.getComputedStyle(enemy).getPropertyValue("left"));
    //ADD VARIABLE TO FIND CHARACTER RIGHT -if it collides with enemy left, you are out
    //remove the condition that the enemy has to be in the first 30 pixels of the game (left side)
    var characterRight =parseInt(window.getComputedStyle(character).getPropertyValue("right"));
    
    console.log(characterRight);
    console.log(enemyLeft)
  
    if(
        characterTop==enemyLeft //have tried characterRight==enemyLeft didn't work
    )
    {
        enemy.style.animation="none"; //remove the animation
        enemy.style.display="none";
        alert("Game Over");
        
    } 
},10);


//left
addEventListener("keydown", function(e) {
    if(e.keyCode == 37) left()
})

//jump (up)
addEventListener("keydown", function(e) {
  if (e.keyCode === 38) {
    jump()
  }
})

//right
addEventListener("keydown", function(e) {
  if (e.keyCode === 39) {
    right()
  }
})

I've looked at this documentation, but it doesn't seem to refer to objects. https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle

Compoot
  • 2,227
  • 6
  • 31
  • 63
  • Just to make sure I've understood correctly, the situation is that you want to do something when an element (enemy) and another element (character) overlap even just slightly? Is that right? – A Haworth Feb 05 '21 at 10:41
  • @AHaworth - that's right - for the simplest possible solution (for teaching beginners - and by beginner's I mean young students) – Compoot Feb 05 '21 at 10:43

1 Answers1

2

Taking the points raised in order:

a) Suggest a proposed solution for collision detection based on my existing code

You cannot use getComputedStyle here (see point b). Below is code using getBoundingClientRect with collision detection (i.e. when the enemy and the character overlap at all)

b) Explanation of the GetComputedStyle and getProperties with documentation on it. For instance, if characterTop doesn't change (if I don't move the character) how come it suddenly outputs "game over" which suggests characterTop is == enemyLeft.

getComputedStyle does just that, it runs through all the style settings and merges them so you get the current style for the element. See for example https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle

The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain.

The problem we have in this case is that it's not the computed styles we are after, it is the actual, of this moment, position of an element which is being flown around the window by a CSS animation. Therefore use getBoundingClientRect to get the actual current x and ys of the elements.

The test in the original of the character's top against the enemy's left doesn't tell you anything. But occasionally it's possible they might match and so you'd get a Game Over.

c) Best practices for simple collision detection (for beginner's learning concepts)

I don't honestly think I can pontificate on this as I don't know how much geometry/algebra etc the students already have. The code used below detects whether the enemy is completely above the character or vice versa and if not whether one is completely to the left of the other. Otherwise they overlap.

Here is complete code. Unfortunately SO's snippet system did not seem to respond to the keydown events so I couldn't present it as a runnable snippet right here.

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    *{
    padding:0;
    margin:22;
}

#game{
    width:500px;
    height:500px;
    border:4px solid #171918;
}

#character{
    width:30px;
    height:120px;
    background-color:green;
    position:relative;
    top:320px;
    border-radius:20px;
    /*animation: jump 300ms */
    
}

/* new class called animate */
.animate{
    animation: jump 500ms;
}


#enemy{
    width:60px;
    height:60px;
    background-color:red;
    border-radius:14px;
    position:relative;
    top:320px;
    left:440px;
    animation: moveenemy 1s infinite linear;    
}

@keyframes moveenemy{
    0%{left:440px;}
    50%{top:58px;}
    100%{left:0px; top:320x;}
}

@keyframes jump{
    0%{top:380px;}
    30%{top:50px;}
    50%{top:40px;}
    100%{top:380px;}
}





/* adding sky*/
#sky{
    width:500px;
    height:340px;
    background-color:skyblue;
    position:absolute;
    top:118px;
}

/* adding ground */
#ground{
    width:500px;
    height:170px;
    background-color:bisque;
    position:absolute;
    top:450px;
}
</style>    
</head>
<body>
  <h1>Game</h1>
 
   
   
   <div id="game">
        <div id="sky"></div>
       <div id="ground"></div>
        <div id="enemy"></div>
       <div id="character"></div>
   </div>
   
<script>
var character = document.getElementById("character");
var enemy = document.getElementById("enemy");    
function jump(){
    if(character.classlist!="animate"){
    character.classList.add("animate");
    }
    setTimeout(function(){
        character.classList.remove("animate");
    },500);
    
}

//right movement

function right() {
  var leftVal =  parseInt(window.getComputedStyle(character).getPropertyValue("left"))
  character.style.left = (leftVal + 30) + "px";

}

//left movement
function left() {

  var leftVal =  parseInt(window.getComputedStyle(character).getPropertyValue("left"))
  character.style.left = (leftVal - 30) + "px";

}

var checkDead = setInterval(function(){
       
    // Get the positions of the top, left and bottom, right of the enemy and the character    
    let enemyPos = enemy.getBoundingClientRect() // this will give us the left, right, top and bottom positions of the enemy
    let characterPos = character.getBoundingClientRect() // this will give us the left, right, top and bottom positions of the character

    // now let's see if there is any overlap between the enemy and the character

   // we are going to check whether one of them is to the left of the other.
   // For example if the left side of the enemy is bigger than the right side of the character then the enemy is to the right of the character
    if (enemyPos.left >= characterPos.right || characterPos.left >= enemyPos.right) {
       // Now we know they don't overlap as either the enemy is to the right of the character or vice versa so do nothing
    }
    
       // if we still don't know, we see whether the character is above the enemy or vice versa
    else if (enemyPos.bottom <= characterPos.top || characterPos.bottom <= enemyPos.top) {
       // Now we know they don't overlap as either the enemy is above the character or vice versa so do nothing
    }
      
      // neither of them is totally to the left or totally above the other so they must overlap somewhere- GOTCHA!
    else {
        enemy.style.animation="none"; //remove the animation
        enemy.style.display="none";
        alert("Game Over");
        clearInterval(checkDead); //I've added this but you may want to start again completely or....
    }        
  },10 );


//left
addEventListener("keydown", function(e) {
    if(e.keyCode == 37) left()
})

//jump (up)
addEventListener("keydown", function(e) {
  if (e.keyCode === 38) {
    jump()
  }
})

//right
addEventListener("keydown", function(e) {
  if (e.keyCode === 39) {
    right()
  }
})
</script>

</body>

</html>
A Haworth
  • 30,908
  • 4
  • 11
  • 14
  • This is genius and I am just reading through ...the problem is I was after something simple enough for a Year 7 pupil to understand and was hoping I could build on existing simple princples with character top and enemy left etc.....am just having a look. Thank you!! – Compoot Feb 05 '21 at 13:47
  • 1
    One thing I thought of looking at was seeing if the character's right hand side is anywhere inside the enemy - that would make the maths easier, you only have to worry about the x and forget the y, and it would sort of have the idea of a 'collision' - the enemy runs into you - but I don't know if it would be satisfying enough - perhaps I should try playing properly, though my reactions aren't those of year 7s. – A Haworth Feb 05 '21 at 14:26
  • Thank you - you have been so helpful! :) – Compoot Feb 05 '21 at 14:37
  • ...but pedagogically struggling to make this user friendly ...! are you able to add detailed comments please? @A Haworth – Compoot Feb 05 '21 at 14:49
  • 1
    I'll have a go - you can tell I lifted the code (as quoted in the answer) and it worked so I left it. I think spelling it out in a more linear fashion may be better, will ponder shortly. – A Haworth Feb 05 '21 at 14:52
  • I have taken out the convoluted testing algorithm and replaced it with simply looking to see if either of the rectangles is totally above or to the left of the other - I think this is fine given they aren't rotating but you may like to check! – A Haworth Feb 05 '21 at 15:46
  • oh, thank you so much! Is the order in which they are coded, the order in which they are written in the code? I'm checking now and this is far more workable with! Also - clearInterval(checkDead) - I think that doesn't work - meant to go back to the beginning of the game? – Compoot Feb 05 '21 at 15:50
  • I think so - but check because I always have to remember that top is 0 at the top and more than that further down - unlike 'normal' graphs – A Haworth Feb 05 '21 at 15:56
  • it works except for the clearinterval - not sure what that is meant to do ....I'm using location.reload(); to go back to the beginning without having to refresh manually. – Compoot Feb 05 '21 at 15:57
  • do you have any suggestions for beginners (documentation) to refer to and learn - independently. I don't think any such thing exists for Javascript etc but it should! Like a children's documentation version! – Compoot Feb 05 '21 at 15:58
  • 1
    I don't I'm afraid, I confess I use W3Schools (which some on SO don't approve of) to test out little things as it is helpful with it's Try it yourself inserts, but there must be something for younger readers that's less dry. Good Luck. – A Haworth Feb 05 '21 at 16:02
  • 1
    I have just been recommended [link]https://www.codecademy.com/ - the recommender has led that sort of age group through practical coding projects. – A Haworth Feb 05 '21 at 18:34