Game Development Community

Physic response collisions on tilemap

by Thomas Andersson · in Torque Game Builder · 11/29/2005 (6:24 pm) · 16 replies

I have 2 rigid body objects colliding against and bouncing off each other nicely. I also have them colliding against the tilemap... except it's wet rag style (they don't bounce off).

Now, I could do this myself in onCollision, but I would think something like this is probably already in the engine. I tried setting collision for the layer the way they are set for the objects, but it's not working.

Is it possible to have T2D handle this automatically and if so, how do you do it?

#1
11/30/2005 (12:17 am)
Set the player's / objects collision response to Bounce:

$player.setCollisionResponse(BOUNCE);

And make sure the player's / objects collision response is blit for the right groups:

$player.setCollisionMasks(BIT($tile_map_group), BIT($tile_map_layer));

And finally make sure you have set the tile maps' layer and group:

tilemap.setGroup($tile_map_group);
tilemap.setLayer($tile_map_layer);

Player should then "bounce" automagically off the tile map.
#2
11/30/2005 (1:21 am)
Thanks for the response, Glenn.

I have everything responding to the proper collision groups - they are colliding. I have the collision response set to rigid instead of bounce though for the objects (it needs to stay that way).

Now, I want to be able to affect the normal physics properties like friction on the tiles (so that sliding along certain layers would make the player come to a quick stop etc.) in addition to making it bounce off. I'm looking for dampened bounces. Although, I can't even get it to do a physics response like BOUNCE unless it's off other objects or the world edges.
#3
11/30/2005 (2:38 am)
Without more detail, the only thing I can suggest is that you ensure that your "restitution" is set high, possibly "1.0". This should ensure that the objects' energy is fed back into it during the impulse response. Obviously any friction would reduce this total energy and therefore you wouldn't get the 'perfect' bounce.

I has been suggested that we add some of the rigid-body features in all the other collision-responses. Features such as friction/restituion would indeed be useful for responses such as "clamp" even if they were calculated differently. Duplicity on these fields may cause confusion though but I think it warrants further investigation so I've added a feature-request to our database (#856).

- Melv.
#4
11/30/2005 (3:28 am)
Thanks for the reply Melv - it's quite the honor for a t2d newbie like me to get the man himself to respond :)

I'll post some code when I get home from work to clarify what I mean. It certainly sounds possible though seeing as both of you seem to take Object vs Tile physics response for granted (it just appears to clamp the collision whatever I do, unlike object vs object). I must be doing something wrong. I was making my own bounce functions last night just because of it. It would be so much neater if I didn't have to do anything.

edit: for reference, I'm using the latest alpha build.
#5
11/30/2005 (4:35 pm)
Ok, I've tried changing to extremely small and extremely large physics values in the datablocks - and while things change when editing the ball material, spectacularily little happens when I change the tile material.

In the hopes that someone knows what I've done wrong, here's all of the code for my experiment except for the normal t2d scene setup.

function initGame()
{
// parameters (move to prefs file)
	$keyForce = 20;
	$updatetick = 30;
	$selectedLevel = 0;

	createLevel();
	createPlayers();
	setupControls();
	resetVariables();
	updateGame();
}

function resetVariables()
{
	playerUp($p1, false);
	playerDown($p1, false);
	playerLeft($p1, false);
	playerRight($p1, false);
	playerUp($p2, false);
	playerDown($p2, false);
	playerLeft($p2, false);
	playerRight($p2, false);
}

function createLevel()
{
	$lvlmap = new t2dTileMap() { scenegraph = t2dScene; };
	$lvlmap.loadTileMap("~/client/maps/level" @ $selectedLevel @ ".map");
	$lvll0 = $lvlmap.getTileLayer(0);
	$lvll0.setPosition("54 64");
	$lvll0.setGroup(1);
	$lvll0.setLayer(0);
	$lvll0.setCollisionMaterial(immovableMaterial);
	$lvll0.setCollisionResponse(BOUNCE);
//	$lvll0.setCollisionPhysics(true, false);
	$lvll0.setCollisionActive(false, true);
//	$lvll0.setCollisionMasks(BIT(0), BIT(0));
}

function createPlayers()
{
	$p1 = new t2dStaticSprite() { scenegraph = t2dScene; };
	$p1.setGroup(0);
	$p1.setLayer(0);
	$p1.setPosition("-35 0");
	$p1.setSize("5 5");
	$p1.setImageMap(ballIM);
	$p1.setWorldLimit(bounce, "-50 -37 50 37");
	$p1.setCollisionActive(true, true);
	$p1.setCollisionPhysics(true, true);
	$p1.setCollisionMaterial(ballMaterial);
	$p1.setCollisionPolyPrimitive(32);
	$p1.setCollisionDetection(POLYGON);
	$p1.setCollisionResponse(RIGID);
	$p1.setCollisionMasks(BIT(0)|BIT(1), BIT(0));
	$p1.setCollisionCallback(false);
	$p1.tag = "player";

	$p2 = $p1.clone();
	$p2.setPosition("0 10");
	$p2.setCollisionMaterial(ballMaterial);
	$p2.tag = "player";
}

/////////////////////////////////////////////////////////
//------------- CONTROLS
//

function setupControls()
{
	if ( isObject(playerMap) )
	{
		playerMap.pop();
		playerMap.delete();
	}
	new ActionMap(playerMap);
	playerMap.bindCmd(keyboard, "up", "playerUp($p1, true);", "playerUp($p1, false);");
	playerMap.bindCmd(keyboard, "down", "playerDown($p1, true);", "playerDown($p1, false);");
	playerMap.bindCmd(keyboard, "left", "playerLeft($p1, true);", "playerLeft($p1, false);");	
	playerMap.bindCmd(keyboard, "right", "playerRight($p1, true);", "playerRight($p1, false);");	
	playerMap.bindCmd(keyboard, "w", "playerUp($p2, true);", "playerUp($p2, false);");
	playerMap.bindCmd(keyboard, "s", "playerDown($p2, true);", "playerDown($p2, false);");
	playerMap.bindCmd(keyboard, "a", "playerLeft($p2, true);", "playerLeft($p2, false);");	
	playerMap.bindCmd(keyboard, "d", "playerRight($p2, true);", "playerRight($p2, false);");	
	playerMap.push();
}

function playerUp(%player, %val)
{
	%player.pressUp = %val;
}

function playerDown(%player, %val)
{
	%player.pressDown = %val;
}

function playerLeft(%player, %val)
{
	%player.pressLeft = %val;
}

function playerRight(%player, %val)
{
	%player.pressRight = %val;
}

/////////////////////////////////////////////////////////
//------------- GAME LOGIC
//

function updateGame()
{
	schedule($updatetick, 0, "updateGame");
	movePlayer($p1);
	movePlayer($p2);
}

function movePlayer(%player)
{
	if ( !(%player.pressUp | %player.pressDown | %player.pressLeft | %player.pressRight) )
	{
		%player.stopConstantForce();
	}
	else
	{
		%nX = 0;
		%nY = 0;
		if ( %player.pressUp )
			%nY -= $keyForce;
		if ( %player.pressDown )
			%nY += $keyForce;
		if ( %player.pressLeft )
			%nX -= $keyForce;
		if ( %player.pressRight )
			%nX += $keyForce;
		%player.setConstantForce(%nX SPC %nY, true);
	}
}
#6
12/01/2005 (11:41 am)
Anyone at all have any ideas?

Is it a bug in my code?
A bug in the latest alpha?
A missing feature?
#7
12/01/2005 (12:22 pm)
Kemi,

I'm about to go find some cold-remedy as I'm pretty ill and I've been awake now since 5am (it's 8.30pm now) working reported T2D problems. I'll have a look at your problem on Friday to see what I can find.

In the meantime though, could you replicate your problem with a smaller snippet of code as I've been wading though so much of peoples code that's not strictly relevant to the problem? If not, don't worry as I'll look at it when I've got more energy.

Talk soon,

- Melv.
#8
12/01/2005 (3:54 pm)
Thanks Melv. I hope you feel better soon.

Maybe the error lies in the object setup:
$p1.setGroup(0);
	$p1.setLayer(0);
	$p1.setCollisionActive(true, true);
	$p1.setCollisionPhysics(true, true);
	$p1.setCollisionMaterial(ballMaterial);
	$p1.setCollisionPolyPrimitive(32);
	$p1.setCollisionDetection(POLYGON);
	$p1.setCollisionResponse(RIGID);
	$p1.setCollisionMasks(BIT(0)|BIT(1), BIT(0));
	$p1.setCollisionCallback(false);
	$p1.setCollisionMaxIterations(2);
Or tile layer setup:
$lvll0.setGroup(1);
	$lvll0.setLayer(0);
	$lvll0.setCollisionMaterial(immovableMaterial);
	$lvll0.setCollisionResponse(BOUNCE);
	$lvll0.setCollisionActive(false, true);
Or perhaps it's because of how I control the object with constant force:
if ( !(%player.pressUp | %player.pressDown | %player.pressLeft | %player.pressRight) )
	{
		%player.stopConstantForce();
	}
	else
	{
		%nX = 0;
		%nY = 0;
		if ( %player.pressUp )
			%nY -= $keyForce;
		if ( %player.pressDown )
			%nY += $keyForce;
		if ( %player.pressLeft )
			%nX -= $keyForce;
		if ( %player.pressRight )
			%nX += $keyForce;
		%player.setConstantForce(%nX SPC %nY, true);
	}

Those are the only things I can think of. I've tried changing the datablocks to all manner of bizarre things (as well as the standard one). Hope this can be solved as it would make everything cleaner and neater.

Thanks again.
#9
12/04/2005 (4:50 am)
Could you upost the "ballMaterial" datablock?

- Melv.
#10
12/04/2005 (4:52 am)
Whilst I'm waiting for the datablock, let me first make a couple of observations so that you understand what I see so far.

First, the tile-layer; this has a collision-material being set to immovable. In-fact, tile-layers do not send collisions, they only receive them. This means that objects collide with them and the objects plus the tile-layer can response but tile-layers cannot send collisions meaning they can never hit things themselves. To this end, setting a collision-material on a tile-layer is not much use apart from the material you're using which is the "immovable" one. You can make tile-layers immovable and this is the only thing that is used by setting the collision-material. It might be easier to use "$lv110.setImmovable(true)".

Next, the tile-layer has a collision-response set on it which isn't ever going to be used because you've set it to be immovable. The way to think of collision-materials/responses are things that are used by the object that is sending the collision, not the receiving one. For instance, if an object A that has the "clamp" response hits an immovable object B, object A will response with a "clamp" response whilst object B won't do anything because you've set it as immovable so there's no need to set a collision-response. Cool?

Moving onto the object, one of the things to notice straight away is that the objects collision-frame is a 32-sided regular polygon. It looks like you want to approximate a circle with lots of edges so you should use the "circle" collision-detection which is *much* faster.

Max-iterations is set to "2" so I'm going to assume that you know the consequences of this. After the first collision, if there's still some tick-time left e.g. the collision happened before the object "used" its frame-interval, it'll do another sweep/col-det and repeat. It'll do this twice unless either there's no collision or the tick-time is "used". The higher the iteration value, the more processing is potentially needed. This is a very powerful option but should be used with care.

I totally understand that some collision-documentation will help here and that's being worked on so all this stuff will be made clear then. :)

- Melv.
#11
12/04/2005 (6:02 am)
Hi Melv and thanks again for your help.

Firstly, I think you just answered why it's not working for me. I tried to make the tile layer give the same response as any other object you'd collide with (except for it not moving itself). If this doesn't work I suspect I have to make my own collision functions for that.

Processing speed in collision for these balls (there are two total) is not very important. What's important is that they never behave strangely. It's a two player game on one keyboard in a 4 screen sized arena. The players are going to be able to bounce the other out of the arena (so it's important that they have some kind of physics response that feels ok) and they should be able to use walls for fast turning (so I need some sort of bounce - would prefer if not perfectly the same speed).

Because of this, I have made the ball's collision more precise. What happened when I set it to circle was that it seemed to go through tiles more than it does with a high resolution poly-model. It still does that, depending on speed, with the current 32 edge model - that's why I tried increasing max iterations. The max iterations bit (and many other lines of code) are leftovers from experimenting to get the desired functionality. I tried setting it to unlimited as well but it doesn't have the effect I'm hoping on tile layers. If I want the balls to be able to go fast, they will go straight through the tile layer with a wobble.

I read through the entire section of collisions and physics, tilemaps, bugs etc. looking for people wanting the same thing as me, but I couldn't find anything. That's when I got afraid it wasn't possible to do out of the box so to speak and I posted this to see if it was! It appears that I have my work cut out for me.

Per request, here's the balls' datablock. Please note that this is just what's there currently and I must have tested a meeeeeeeeeeeeeeeeeeeellion different combinations - although they were under the assumption that the tile-layer could somehow generate the same response.
datablock t2dCollisionMaterialDatablock(ballMaterial)
{
	automassinertia = false;
	friction = 0.02;
	restitution = 1.5;
	density = 0.1;
	forceScale = 3.0;
	damping = 0.5;
	mass = 2.0;
	inertialmoment = 1.0;
};
#12
12/05/2005 (3:53 am)
Are you getting any errors?

Using a restitution out of range should give an error. It needs to be 0->1. Your setting here will be ignored.

You certainly shouldn't be getting collision tunnelling with any of the collision-frames e.g. circle/polygon. Do they only tunnel through tile-layers?

- Melv.
#13
12/05/2005 (3:56 am)
I'll have to rig something up to test the tunnelling again and perhaps post something showing you bouncing using rigid-body. It's certainly much better performance-wise to use circles with "bounce" response and damping. At the moment, restitution isn't blended into the "bounce" mode but I'm looking into that.

Essentially, it doesn't matter what "type" of object you bounce from, the response should be the same because the response is independant of the object "type" as it just comes down to a response from a contact-point/normal.

- Melv.
#14
12/05/2005 (4:33 am)
Kemi,

I've put together a little example below. Note that I've used the calls for things like "restitution" to keep things functional and in one unit. Looking at your material above, you've set the mass to be quite large e.g. "2". That might seem like a small number but the the rigid-body equations multiply any impulse from the collision by the inverse-mass meaning that any impulse response gets halved (1/2). Try using something like "0.1" but certainly lower than "1".

Anyway, here's the example that causes the object to rebound. Note that you can use the "circle" response as well here.

// ************************************************************************
	//
	// Add your custom code here...
	//
	// ************************************************************************
	
	t2dBeginScene( t2dScene );
	
	// Create Tile Layer.
	%tileMap = new t2dTileMap();
	%tileLayer = %tileMap.createTileLayer( "10 2 10 10" );
	%tileLayer.setArea("-50 15 50 35");
	%tileLayer.setImmovable(true);
	%tileLayer.setCollisionActive(false,true);
	for ( %x = 0; %x < 10; %x++ )
	{
		for ( %y = 0; %y < 2; %y++ )
		{
			%tilePos = %x SPC %y;
			%tileLayer.setStaticTile( %tilePos, tileMapImageMap );
			%tileLayer.setTileCollisionActive( %tilePos, true );
		}
	}
	
	// Create Sprite.	
	%sprite = new t2dStaticSprite();
	%sprite.setPosition("0 0");
	%sprite.setImageMap(ggLogoImageMap);
	
	%sprite.setCollisionActive(true,false);
	%sprite.setCollisionPhysics(true,false);
	%sprite.setCollisionDetection(polygon);
	%sprite.setCollisionResponse(rigid);
	%sprite.setCollisionPolyPrimitive(24);
	
	%sprite.setAutoMassInertia(false);
	%sprite.setMass(0.1);
	%sprite.setRestitution(1);
	%sprite.setFriction(0);
	%sprite.setDamping(0);
	
	
	// Start it moving.
	%sprite.setLinearVelocityY( 15 );
	// Debug.
	%sprite.setDebugOn(bits("1 5"));
		
	t2dEndScene();

Hope this helps,

- Melv.
#15
12/05/2005 (10:37 am)
Tried to keep my values (all of them at the same time!) inside 1.0 and it seems to be working. What would be neat would be to set different materials for the tiles so that you can have rubber tiles, marble tiles etc. Just as a suggestion for the future (to let the physics and level design handle how everything behaves).

Thanks for outstanding support though, Melv. Cheers.
#16
12/05/2005 (11:45 am)
It's certainly possible but the trouble is that tiles don't move through the world even when they're panning, they're just virtually moving, certainly not in the physics sense. The only way to do this would be for the object that collides with them to use the target objects' physics parameters rather than its own. That could be confusing though.

There are other ways to do this of course but not stock albeit fairly easy to implement.

No problem on the help, it's what I'm here for and I discover ways to make things easier along the way so it's all good. :)

- Melv.