Is there a better way for putting game logic in beginContact

Started by
5 comments, last by Aardvajk 9 years, 4 months ago

Hi there,

i am making a platformer game using box2d. My player have two ground sensors defined as small circle fixtures on feet level.

Now i want to let the player to jump, but only when on ground - i could achieve that by detecting if any one of my player ground sensors are touching another fixture - using an counter as a userdata reference for example in a contact listener for beginContact/endContact. This works fine, but...

... i may have more Entities in my game, like Jumppads, Spikes, Ladders, Enemies etc.

All these entities need a way to know which other fixtures does collide with it, so every entity can decide itself what it should do, maybe push the other enemy around, or kill it instantly.

What i dont like, is using beginContact, endContact and put the logic in there - thats a very nasty way. I dont want to put all the entity logic in one single contact listener!

The general idea was to let every entity have a list of other entities which are automatically handled by just the beginContact and endContact, but i couldnt´t figure out yet.

Or does someone have a better solution for that problem? There must be some state machine magic to solve this... i am pretty sure.

Thanks,

Final

Advertisement

Hi there,

i am making a platformer game using box2d. My player have two ground sensors defined as small circle fixtures on feet level.

Now i want to let the player to jump, but only when on ground - i could achieve that by detecting if any one of my player ground sensors are touching another fixture - using an counter as a userdata reference for example in a contact listener for beginContact/endContact. This works fine, but...

... i may have more Entities in my game, like Jumppads, Spikes, Ladders, Enemies etc.

All these entities need a way to know which other fixtures does collide with it, so every entity can decide itself what it should do, maybe push the other enemy around, or kill it instantly.

What i dont like, is using beginContact, endContact and put the logic in there - thats a very nasty way. I dont want to put all the entity logic in one single contact listener!

The general idea was to let every entity have a list of other entities which are automatically handled by just the beginContact and endContact, but i couldnt´t figure out yet.

Or does someone have a better solution for that problem? There must be some state machine magic to solve this... i am pretty sure.

Thanks,

Final

In these types of high-level aspect, you can consider doing what you want basically. But using an reference counter for each collision mich be not a good way. Your game logic must be somekind related with the current game state.

What looks more reasonably for your problem is to let the entity handle not the collision itself but some kind of notification instead. Just create a virtual function, pass an enumerated value to this function with a number that the entity should know, and inherit this class. I did never used Box2D, but if there is a way to do this:


ContactListener::BeginContact( CONTACT* Contact ) {
      Entity* EntityA = (Entity*) Contact.UserPointerA();

      EntityA->Notify( JUMP );
      EntityA->AddChildEntity( (Entity*) Contact.UserPointerB() );
}

I think that there is no problem with this.

You can also create an structure and pass to it:


ContactListener::BeginContact( CONTACT* Contact ) {
      FILTERED_EVENT FilteredEvent;
      
      FilteredEvent.CollisionTime = Contact.CollisionTime;
      FilteredEvent.CollideWith = Contact.GetUserPointerA();

      (Entity*)( Contact.UserPointerB() )->OnEvent( FilteredEvent );
}

This way your entity doesn't know what a contact is.

Also, you're talking about a parent-child entity relationship. Having this kind of thing it is not a bad problem, since you should propagate through the parent child nodes or notify the parent using an child node when something happens with one of them, etc. You can consider creating some kind of "CPhysicsEntity" that is an entity with physics attributes that can have child objects attached to it that are an "CPhysicsEntity" also. This "CPhysicsEntity" just have pointer or pointers to the rigid bodies, and since you're using an existing physics library, it is easy to implement.

i use a flag to indicate an object is "airborne".

when airborne, movement update is via initial horizontal and vertical velocities and acceleration of gravity to produce ballistic movement. when the object intersects the ground, its considered to have landed, "airborne" is set to false, damage based on ground collision velocity is applied (if any), then movement update returns to normal non-airborne style. needless to say, input controls like jump, turn, move etc don't work when you're airborne (or shouldn't unless you have unrealistic physics in your game universe). so the simple way is to use a switch in update: if airborne, update airborne style. else process normal input (for player), or use normal movement update (for non-player objects).

always try for the simplest possible solution first. attempt to UNDER engineer, not over-engineer things.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Thanks for the tips.

I now using a list for every which is contained of other entities (collision list) and just add/remove an entity to/from that list.

The only difference is what an item in the collision is a pair of an counter how many this entity does being touched and the entity itself.

Works great so far...

Here is a bit code:


	@Override
	public void beginContact(Contact contact) {
		if (contact.getFixtureA().isSensor() || contact.getFixtureB().isSensor()) {
			GameObject sensorObject = (GameObject) contact.getFixtureA()
					.getBody().getUserData();
			GameObjectList sensorList = sensorObject.getCollisionList();
			GameObject enemyObject = (GameObject) contact.getFixtureB()
					.getBody().getUserData();
			GameObjectList enemyList = enemyObject.getCollisionList();
			sensorList.add(enemyObject);
			enemyList.add(sensorObject);
		}
	}

	@Override
	public void endContact(Contact contact) {
		if (contact.getFixtureA().isSensor() || contact.getFixtureB().isSensor()) {
			GameObject sensorObject = (GameObject) contact.getFixtureA()
					.getBody().getUserData();
			GameObjectList sensorList = sensorObject.getCollisionList();
			GameObject enemyObject = (GameObject) contact.getFixtureB()
					.getBody().getUserData();
			GameObjectList enemyList = enemyObject.getCollisionList();
			sensorList.remove(enemyObject);
			enemyList.remove(sensorObject);
		}
	}

Norman Barrows is right, simplest approach first, and it's in fact what usually works best. But when things start to get complicated you need a way of managing things a little better. Heres what I'm currently working on doing. I implement my game logic related to collisions in lua script and bind the box2d to lua through lua bind. Taking this approach, each level will have its own lua script (where you can reuse existing scripts if need be) where collisions will be handled, but only collisions that are very specific to that level. E.g. a player jumps on a switch which opens a specific trap door.

You can inialiaze your game objects (might be box2d bodies) at the start of a level to the lua script via meta tables and keep a reference to them. Then on a collision, call the a lua function from your c++ and check if the fixtures/body is the one your interested in and some game logic. I havnt quite finished yet, but I'm at the point where I can implement the game logic via my lua script (my source is on git, I'll update the question when I'm done). I'm taking this approach since im still prototying my ideas and I think it's worth it to spend a little time to experiment, but its hacky since I don't know if it will end up being clean or not and I havnt refactored code yet.

I have implemented this recently with Bullet, although Box2D probably has similar features:

Grab the player collision object, cast a ray right below. If it hits something, compute length from the hit point to the feet of the player, if its less than a small value (say, a centimeter), its on the ground, otherwise its on the air.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Just to expand on the last comment, you can also look at the slope of the current floor and do a bit of trig to work out the minimum distance from the base of the capsule to the floor to consider the player on the ground.

The distance along a ray from the middle of the base of a capsule to the floor is longer the more sloped the floor is.

I wrote a journal entry about this a while back. Its proven a useful technique when locking a capsule character controller to the floor so I hope it is of some interest:

http://www.gamedev.net/blog/515/entry-2259572-further-charactercontroller-developments/

This topic is closed to new replies.

Advertisement