Home > database >  Trying to hitTestObject in javascript with no success
Trying to hitTestObject in javascript with no success

Time:01-05

I have this code and I have done something like this before and I got it to work, I just want the code below to be kind of similar but in working form. I know it can be done, there is always a way.

var playerX = document.getElementById("character").style.left;
var playerY = document.getElementById("character").style.top;
var playerW = document.getElementById("character").style.width;
var playerH = document.getElementById("character").style.height;
        
var enemy100X = document.getElementById("enemy100").style.left;
var enemy100Y = document.getElementById("enemy100").style.top;
var enemy100W = document.getElementById("enemy100").style.width;
var enemy100H = document.getElementById("enemy100").style.height;
        
        
if (enemy100X > playerX - 15 &&
    enemy100X < playerX   15 && 
    enemy100Y > playerY - 10 &&
    enemy100Y < playerY   10) {
    enemyDetection = 1;
}
        

Is there a way to get this to work? And also to note that both objects basically have the same Y position off by 3 VH. And also both Objects rotate.

I have tried this method, even swapping playerX with enemy100X and it dont work.

if (playerX   playerW >= enemy100X &&
    playerX <= enemy100X   enemy100W &&
    playerY   playerH >= enemy100Y &&
    playerY <= enemy100Y   enemy100H) {
    enemyDetection = 1;
}

And these are the images:

ENEMY PLAYER

CodePudding user response:

getBoundingClientRect

You are using the element's style to deal with the size and position but it should be the other way around. Having a state that you use to derive the style would be more appropriate.

That's also because the .style property of an element will only return the element inline style properties and it would need to be initialized before such information could be used as an input for your game mechanics.

Plus its values (top, left, width, height) will be strings containing also the unit (px) and they would need to be parsed everytime back and forth.

If you need to know the actual size and position you should rely on getBoundingClientRect.

https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect

The Element.getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.

Here's a demo showing the two approaches and their result. I also stripped down the style properties fetching with object destructuring:

const character = document.getElementById("character");

function getSizeAndPositionWrong(target){
  const {left, top, width, height} = target.style;
  return {left, top, width, height};
}

function getSizeAndPositionCorrect(target){
  return  target.getBoundingClientRect();
}

console.log( getSizeAndPositionWrong(character) );
console.log( getSizeAndPositionCorrect(character) );
<div id="character"></div>

Fetching and Setting the element's style property

So I showed how to better fetch those values and how to make them reliable to, for example, do a reliable collision detection.

Anyway to keep things simple, before rethinking the whole logic, this is a demo dealing with the element style properties to keep track of the position and size.

The very critical factor was initializing those values before the game begins.

Consider that since the .style property will return the inline style of the element, if the element was styled with css rules it won't have impact here.

In this demo there are just two boxes approaching each other at a given speed and the game will end as soon as a collision will be detected.

//returns the size and position of a target element
function getSizeAndPosition(target){
  const {left, top, width, height} = target.style;
  return {left, top, width, height};
}

//sets the position of a target element
function setPosition(target, position){
  return Object.assign(target.style, position);
}

//returns the parsed position
function getParsedPosition(position){
  debugger;
  return {
    left: parseInt(position.left),
    top: parseInt(position.top),
    width: parseInt(position.width),
    height: parseInt(position.height)
  };
}

//returns the encoded position
function getEncodedPosition(position){
  return {
    left: `${position.left}px`,
    top: `${position.top}px`
  }
}

//returns the position translated by an offset
function getTranslatedPosition(position, offset){
  const parsedPosition = getParsedPosition(position);
  const translatedPosition = {
    left: parsedPosition.left   offset.left,
    top: parsedPosition.top   offset.top
  };
  return getEncodedPosition(translatedPosition);
}

//returns true or false if the two boxes collided
function checkCollision(){
  const p1 = getParsedPosition(getSizeAndPosition(character));
  const p2 = getParsedPosition(getSizeAndPosition(enemy));

  if (p1.left   p1.width >= p2.left &&
      p1.top   p1.height >= p2.height &&
      p1.left <= p2.left   p2.width &&
      p1.top <= p2.top   p2.height)
    return true;
  
  return false;
}

//game loop!
function gameInfiniteLoop(){
  const offset1 = {left: 5, top: 1};
  const offset2 = {left: -5, top: 1};

  const character_currPos = getSizeAndPosition(character);
  const character_nextPos = getTranslatedPosition(character_currPos, offset1);
  setPosition(character, character_nextPos);
  
  const enemy_currPos = getSizeAndPosition(enemy);
  const enemy_nextPos = getTranslatedPosition(enemy_currPos, offset2);
  setPosition(enemy, enemy_nextPos);

  const didCollide = checkCollision();
  
  if(didCollide){
    console.log(`Did they collide? ${didCollide}`);
    clearInterval(loopTimer);
  }
}

const character = document.getElementById("character");
const enemy = document.getElementById("enemy");

//initialize the top, left, width and height style props
setPosition(character, {top: '0px', left: '0px', width: '50px', height: '50px'});
setPosition(enemy, {top: '0px', left: '500px', width: '50px', height: '50px'});

//calls a game iteration every 2seconds
const loopTimer = setInterval(gameInfiniteLoop, 10);
#gameboard{
  position: relative;
}

.box{
  position: absolute;
  /*
  width: 50px;
  height: 50px;
  */
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-family: sans-serif;
  font-size: 15px;
}

#character{
  border: solid 4px aqua;
  background: blue;
}

#enemy{
  border: solid 4px purple;
  background: red;
}
<div id="gameboard">
  <div id="character" >Char.</div>
  <div id="enemy" >Enemy</div>
</div>

CodePudding user response:

So the code that was applied above by Diego D probably works, its just that I have been designing my game a whole different way. I mainly rely on if statements, I always try to avoid loops due to them having problems and never working correctly, they cause to many bugs. So my advice, stay away from any type of loop. They leave gaps for interfaces.

But anyway this was my approach on doing it.

if (enemyDetection === 0) {
    if (document.getElementById("enemy100").style.visibility === "visible" && enemy100Rotate === 0) {
        document.getElementById("enemy00").style.transform = "rotate(180deg)";
        randomEnemy100Y = randomEnemy100Y - 1;
        getDetection100Y = randomEnemy100Y - 1;
        document.getElementById("enemy100").style.top = randomEnemy100Y   "vh";
        document.getElementById("enemy100").style.left = randomEnemy100X   "vw";
        }
    if (document.getElementById("enemy100").style.visibility === "visible" && enemy100Rotate === 1) {
        document.getElementById("enemy00").style.transform = "rotate(270deg)";
        randomEnemy100X = randomEnemy100X   1;
        getDetection100X = randomEnemy100X   1;
        document.getElementById("enemy100").style.top = randomEnemy100Y   "vh";
        document.getElementById("enemy100").style.left = randomEnemy100X   "vw";
        }
    if (document.getElementById("enemy100").style.visibility === "visible" && enemy100Rotate === 2) {
        document.getElementById("enemy00").style.transform = "rotate(0deg)";
        randomEnemy100Y = randomEnemy100Y   1;
        getDetection100Y = randomEnemy100Y   1;
        document.getElementById("enemy100").style.top = randomEnemy100Y   "vh";
        document.getElementById("enemy100").style.left = randomEnemy100X   "vw";
    }
    if (document.getElementById("enemy100").style.visibility === "visible" && enemy100Rotate === 3) {
        document.getElementById("enemy00").style.transform = "rotate(90deg)";
        randomEnemy100X = randomEnemy100X - 1;
        getDetection100X = randomEnemy100X - 1;
        document.getElementById("enemy100").style.top = randomEnemy100Y   "vh";
        document.getElementById("enemy100").style.left = randomEnemy100X   "vw";
    }
}

if (getDetection100X > characterXpos - 5 && getDetection100X < characterXpos   5 && getDetection100Y > characterYpos - 10 && getDetection100Y < characterYpos   5) {
    enemyDetection = 1;
}

I added two variables, getDetectionX witch counts by 1 for each step the enemy takes on the X scale and I did the same for getDetectionY. The variables must match the Enemies attributes. (X,Y).

Then I compared the characterXpos and characterYpos with the new variables in the if statement.

Nice work around and much less code.

  • Related