Collision detection for one frame in JavaScript

215 Views Asked by At

I'm playing around with the Crafty.js game engine. I have a small game where the player is a ball and he drops down to platforms. Every time he hits the next platform, he gains a point. The score is stored as a variable and displayed on screen. I'm using Crafty's collision detection to detect when the player hits a new platform. If you're not familiar with Crafty, it's pretty simple, whenever the player hits the new platform, an event is fired and I can add one to the score.

My problem: The game is running around 60fps. Every time the canvas reloads, Crafty will detect whether or not the player is actually touching the platform. This results in my score variable incrementing by one every single frame the player is touching a level. This is far from what I want. I want the score to be incremented ONCE per platform. This is a problem that I don't know how to fix.

Other things I've tried to solve it: I also considered constantly measuring the distance that the player is from the starting point then I could tell (by division) what platform the player is on (since the platforms are equally vertically spaced). This was a problem however since Crafty was having issues giving me the current location of the player.

What I think would work: I think if I could have an event fired on the first frame that the player hits each platform, then that might work. (side note, If the player stays on one platform and jumps up and lands on the same platform a second time, I only want ONE point to be added. Not double jumping)

What I need from you guys: Have you ever had this issue? I really need to finish this game. And This minor technical problem is preventing me from finishing it. I'd love somebodys input.

2

There are 2 best solutions below

2
On

The simplest solution would seem to be setting up a variable for each platform that tracks whether the player has landed on that platform or not. Then, whenever the ball is in contact with a platform that hasn't been landed on yet, award a point and mark that platform as landed on.

If the platforms are in linear sequence, you could even have a single integer variable that tracks which platform the player is on.

0
On

I wanted to expand on Taymon's answer because it seems like a really good solution for this problem. I would simply add a boolean attribute flag to the platform component that determines whether or not it has been counted.

Crafty.c('Platform', { isCounted: false })

Then, the logic for handling that hit would check that flag before counting it.

Here's a live example:

var score = 0;

Crafty.init(800, 600, $('#game')[0])
Crafty.background('blue')

/**
 * because of the way gravity works
 * (or maybe I just don't really understand it),
 * I had to make separate entities for the
 * gravity and the actual hit testing for each
 * platform.
 **/
Crafty.c('Platform', {
  platform: function(x, y, visId) {
    this.addComponent('2D, DOM')
      .attr({
        h: 20,
        w: 200,
        x: x,
        y: y - 10,
        visId: visId
      });
    return this;
  },
  isCounted: false,
  visId: 0,
  vis: function() {
    return Crafty(this.visId);
  }
});

Crafty.c('PlatformVis', {
  platformVis: function(x, y) {
    this.addComponent('2D, DOM, Color, PlatformVis')
      .color('green')
      .attr({
        h: 20,
        w: 200,
        x: x,
        y: y
      });
    return this;
  }
});


// make some platforms
for (var i = 0; i < 5; ++i) {
  var x = i * 200;
  var y = i * 75 + 92;
  var vis = Crafty.e('PlatformVis').platformVis(x, y)
  Crafty.e('Platform').platform(x, y, vis.getId());
}


// player
Crafty.e('2D, DOM, Color, Twoway, Gravity, Collision')
  .color('red')
  .twoway(6, 14)
  .gravity('PlatformVis')
  .gravityConst(.8)
  .attr({
    x: 0,
    y: 0,
    h: 32,
    w: 32
  })
  .checkHits('Platform')
  .bind('HitOn', function(e) {
    var platform = e[0].obj;
    if (!platform.isCounted) {
      platform.isCounted = true;
      platform.vis().color('yellow');
      Crafty('Score').text(++score);
    }
  }, this);


// score hud
Crafty.e('2D, DOM, Text, Score')
  .text(score)
  .textColor('white')
  .textFont({
    size: '32px'
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/crafty/0.6.3/crafty-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Player uses the <b>Twoway</b> component, so use arrows/wasd to move/jump</p>
<div id="game"></div>