Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by momander

Two questions:
1. Has anyone tried using distanceTo() to detect collisions, instead of using the default collision boxes? Any advice?
2. Is there a bug in getEntitiesByType() or am I using it wrong?


My game features mostly round entities, so rectangular collision boxes did not work well. Two round entities could look like they didn't touch on screen, but the corners of their collision boxes could overlap, causing a collision. Instead I did this:

collides: ig.Entity.COLLIDES.NEVER

Then I detect the distance between all entities in entities' update(). If the distance is less the radius of the two entities, an explosion occurs. This in effect creates circular collision zones around all entities. Here is the first draft of my code:

    var vessels = ig.game.getEntitiesByType(EntityVessel);
    for (var vessel in vessels) {
      if (vessel != this) {
        if (this.distanceTo(vessel) < (this.size.x/2 + vessel.size.x/2)) {
          ig.game.spawnEntity(EntityExplosion, this.pos.x, this.pos.y);
          this.kill();
          vessel.kill();
        }
      }
    }

This caused an error:

Uncaught TypeError: Cannot read property 'x' of undefined
/space/lib/impact/entity.js:192

I played with the code, and tested the version below. To my great surprise it worked. Explosions are caused when an entity gets into another entity's circular collision zone.

    var length = ig.game.getEntitiesByType(EntityVessel).length;
    for (i=0; i<length; i++) {
      var vessel = ig.game.getEntitiesByType(EntityVessel)[i];
      if (vessel != this) {
        if (this.distanceTo(vessel) < (this.size.x/2 + vessel.size.x/2)) {
          ig.game.spawnEntity(EntityExplosion, this.pos.x, this.pos.y);
          this.kill();
          vessel.kill();
        }
      }
    }

Why did the second code snippet work, and not the first one? And is this a good way of doing circular collision zones? I'd appreciate any feedback.

Martin

1 decade ago by alexandre

I was never able to use fast array enumeration (for el in array) effectively in my current Impact project, though this is probably due to my not understanding fast enumeration in JS. I ended up doing what you did and it works fine.

Also, to simplify collision testing between your entities, you could use Entity.check instead of Entity.update: check will be called whenever overlap happens between entities whose checkAgainst property was set to the type of one another.

1 decade ago by Arantor

Using 'fast enumation' in JS is actually one of the more bizarre things of the language, and strongly not recommended.

You see, in JS, a surprising number of things are first class objects, including arrays. That's why you can actually extend the basic array structure with your own functions if needed.

If you traverse that way across an array, it's not just going to be its values you pick up on, but anything in the array construct, including any methods which wouldn't be included in the length property. And while a method will enumerate into the loop, it won't have things like the x property that's causing it to choke.

But if you're really going for it, I would imagine this might be a touch faster: (not tested)

    var vessels = ig.game.getEntitiesByType(EntityVessel), s = this.size.x / 2;
    for (var i=0, length = vessels.length; i<length; i++) {
      var vessel = vessels[i];
      if (vessel != this) {
        if (this.distanceTo(vessel) < (s + vessel.size.x/2)) {
          ig.game.spawnEntity(EntityExplosion, this.pos.x, this.pos.y);
          this.kill();
          vessel.kill();
        }
      }
    }

(No sense in doing anything in the loop that can't be done outside of it. For only a few entities it might not make much difference but given how many times this might run, it's worth optimising it where practical, and keeping things in the right scope is just headache-avoidance.)

Also, I suspect you may get an interesting situation if there's a multi-way collision, where objects 1, 2 and 3 collide simultaneously (1 to 2, 2 to 3, for the sake of argument), where depending on order, 1 to 2 can be resolved first, killing both 1 and 2 but the collision of 2 to 3 may not resolve properly as a result. I'm not sure exactly if this will be the case or not but it's something to bear in mind.

1 decade ago by momander

Wow, that is wacky behavior of Javascript. Thanks for explaining it! I usually work in Python.

Thanks for the pointer to check() and checkAgainst!

1 decade ago by monkeyArms

I used to do this all the time too, and every once in a while I would get caught by a similar bug that I didn't understand. One day I started using jslint, which threw errors every time I didn't use this type of loop with a hasOwnProperty() check.

For example, consider this:

var foo = ['bar', 'baz'];

for (key in foo) {
    console.log( foo[key] );
}

this should run without any problems. But now extend Javascript's built in Array class with a handy helper method:

var foo = ['bar', 'baz'];

Array.prototype.remove = function(from, to)
{
	var rest = this.slice((to || from) + 1 || this.length);
	this.length = from < 0 ? this.length + from : from;
	return this.push.apply(this, rest);
};

for (key in foo) {
    console.log( foo[key] );
}

this will include the remove() function in the result output. To make this type of loop 'safe', use hasOwnProperty(), i.e:

var foo = ['bar', 'baz'];

Array.prototype.remove = function(from, to)
{
	var rest = this.slice((to || from) + 1 || this.length);
	this.length = from < 0 ? this.length + from : from;
	return this.push.apply(this, rest);
};

for (key in foo) {
	if (foo.hasOwnProperty(key)) {
		console.log( foo[key] );
	}
}

This will give you what you want.

Incidently, since 99% of the time I have jQuery included in my coding environment, I really like to use jQuery.each() for looping because it does this automatically for me, and can give me direct access to each array element without having to call it by it's key:

var foo = ['bar', 'baz'];

$.each(foo, function( key, arrayItem ){
	console.log( arrayItem );
})

Note: jQuery.each() is of course slower than just doing a normal loop - when speed and overhead are a major factor (as they are when you are trying to maintain 60fps canvas draws) I would not use it.
Page 1 of 1
« first « previous next › last »