Using Farseer for my Tile Engine. Question about collision detection.

Oct 21, 2009 at 4:14 PM

Hello guys,

First off, I want to say I really like the Farseer Physics Engine. It was pretty easy to replace my own physics code and switch to Farseer. I made the switch because I figured that a physics engine would do a better job than me since I'm fairly new to game programming and the physics involved.

So yea, I'm making a 2D platformer game that uses a tile engine. The tiles make up the level, like ground and platforms are all made of tiles. The player can have collision with certain types of tiles, and so far that works. The tiles are 64 x 64 pixels and the player is 110 x 170 pixels. The player does collide with the tiles but I noticed some strange behaviour. Like the player would all of a sudden shift in the ground or walls. Figured out this had something witht the way I draw everything, because I just made the rotation 0 in everyday draw call. So I added the rotation of the Bodies to the draw calls, for the player and every tile. This fixed the first problem. But I don't really want the player or the tiles to rotate. Well the tiles wouldn't rotate because they all have static bodies. So after looking around on these discussion boards, I read that I just have to set the players Moment Of Inertia to float.PositiveInfinity. This worked fine, but I still have some doubts and a minor problem.

When I move my player around by applying force to its body, it will sometimes sorta jump like it moves up a tiny slope that's on the ground. So I kinda have doubts about my collision code which I will post soon :P

I load the map from a xml file and loop through all the tiles to check their collision type. If a tile is "passable" then its just like air and has no texture, so I use the Geom.IgnoreCollisionWith() function to tell the simulator to ignore collision with the player for this type of tile. The player can collide with everything else. But it just keeps bumping against some sort of invisible object.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void LoadMap()
{
tiles = new Tile[(int)(map.Size.X * map.Size.Y)];

// Fill the tile array with the values from the baselayer. for (int t = 0; t < tiles.Length; t++)
{
tiles[t] = LoadTile(map.BaseLayer[t]);
}

int currentTile = 0;
for (int row = 0; row < map.Size.Y; row++)
{
for (int col = 0; col < map.Size.X; col++)
{
tiles[currentTile].Body = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, map.TileSize.X, map.TileSize.Y, 1);

tiles[currentTile].Body.Position = new FVector2(col * map.TileSize.X, row * map.TileSize.Y);
tiles[currentTile].Body.IsStatic = true;

tiles[currentTile].Geom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, tiles[currentTile].Body, map.TileSize.X, map.TileSize.Y);

if (tiles[currentTile].Collision == TilesCollision.Passable)
{
tiles[currentTile].Geom.IgnoreCollisionWith(player.Geom);
}

currentTile++;
}
}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


I hope anyone has some suggestions or can think of possible solutions. Thanks for reading :D

 

 

Oct 22, 2009 at 2:00 AM

You are correct to set the Moment of Inertia to prevent bodies from rotating. The issue with your player juming may be similar to the one described here (solution at bottom of post).

Oct 22, 2009 at 7:25 AM

Are you assigning your "air" tiles as actual farseer geoms? If so, try not including your "air" tiles in your tiles array.

 

What I did was first run my map through an xy  loop to count the total size of the map, and save it in map[y,x] array.

 

 

Through each iteration, pass the map[y,x] array through a switch to assign each 'tile character' in your map as either an 'air' tile,

a passable tile (non-platform tiles), a Farseer tile (platform tiles), player, enemy, ect.

 

Use the xy values to store the world position of each object in the map. e.g.   new Vector2(x * tileWidth, y * tileHeight);

 

Then do a foreach loop on your FarseerTileArray, PassableTileArray, EnemiesArray, etc. to create the your map objects

as either a farseer body & geom tile, non-physics tiles, player body & geom, enemy body & geom, and so on.

And dont assign anything to the "air" tiles. (In my case all the '0's in my map).

 

I not exactly sure that is your problem but it might help.

 

 

Oct 22, 2009 at 3:28 PM
Edited Oct 22, 2009 at 3:34 PM

Thanks for the replies ^_^

 

Cowdozer, thanks posting a solution, but it seems rather "hack-ish" dont you think? Well I'm ok with it if it works I guess. Just trying to look for elegant solutions you know :P

Also, I'm not sure what to do about the friction of the player. When I move it and stop pressing left or right, it keeps sliding a lot further than it should. I messed around with the

FrictionCoefficient, but that didn't really work because I'm probably forgetting about something :P

 

Oh and by the way, I noticed the player actually trips over the position where 2 tiles connect :S Ugh. Has anyone else ever used a Tile Engine with Farseer?

 

slapgw, I'm pretty much using the concept of the starter kits. Using parts from the platformer kit, like the tile types etc. And load my maps from xml files like the rpg starter kit does.

The code from my first post shows how I load the tiles and I use Geom.IgnoreCollisionWith(player.Geom). After reading your reply, I decided to change my for loop so

it only adds the body and geom of impassable tiles to the PhysicsSimulator. I didn't notice any performance gain, but I guess there's no need for bodies and geoms for

the passable tiles.

 

Well here's my new code. The part I changed is blue.

 

 public void LoadMap()
{
tiles = new Tile[(int)(map.Size.X * map.Size.Y)];

// Fill the tile array with the values from the baselayer. for (int t = 0; t < tiles.Length; t++)
{
tiles[t] = LoadTile(map.BaseLayer[t]);
}

int currentTile = 0;
for (int row = 0; row < map.Size.Y; row++)
{
for (int col = 0; col < map.Size.X; col++)
{
if (tiles[currentTile].Collision == TilesCollision.Impassable)
{
tiles[currentTile].Body = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, map.TileSize.X, map.TileSize.Y, 1);

tiles[currentTile].Body.Position = new FVector2(col * map.TileSize.X, row * map.TileSize.Y);
tiles[currentTile].Body.IsStatic = true;
tiles[currentTile].Geom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, tiles[currentTile].Body, map.TileSize.X, map.TileSize.Y);
}


currentTile++;
}
}
}

 

 

 

 

Oct 22, 2009 at 9:49 PM

From what I could find out, the tripping is a difficult problem to overcome.

Instead of using applyforce or linearVelocity to move the player, try just updating the player.body.position when a directional button is pushed,

that will eliminate the 'tripping' and 'jitter'. And use the physics for everything else (jumping, collision, etc).

Hope this isn't too 'hackish' for you. You can always optimize later if you can make it work.

 

 

Oct 24, 2009 at 1:45 PM

Hey Thanks! Manually updating the players position seems to work well. This solved the tripping problem nicely. Though I keep wondering if it's meant to be used this way.

Like how do other people do it? Do they apply forces or impulses to their player?

 

Well anyway I'm happy with this solution for now ^_^

Oct 25, 2009 at 7:09 AM

Great! Glad it worked for you too.

I can't remember where I read this, but something to keep in mind about game programming is that it is usually "50% fake",  just as long as it accomplishes what you want to do.

A very 'convenient' concept for those with limited programming knowledge (like myself).  : )

Oct 25, 2009 at 4:55 PM

So you're saying most games use a couple of hacked together algorithms? Well I'm kinda experienced with C++ and C#,

but that doesn't make programming a game easy for me lol.

 

Well anyway, I've noticed that when my player collides with an enemy or something it starts sliding and bouncing again. So there's still a collision response :/.

Is there an easy way to disable this behavior?

 

Also, I'm thinking about switching to the box on a ball principle. Because it would be easier to walk on hills etc.

 

Edit:

Ok so I just tried the box on a ball, but its just weird now. I have a 2 geoms for the rectangle and circle and of course 2 bodies. I've linked them together with a

RevoluteJoint created with the JointFactory. I apply 2000 torque to the circle but it slow and trips over the tiles anyway :S I also tried a PinJoint but that didn't really

work either. I also had to do geom.IgnoreCollisionWith(feetGeom), because the rectangle and circle kept colliding with each other otherwise. What am I missing?

 

Oct 25, 2009 at 9:13 PM

I did not say that. "Most games" that people buy and play have millions of dollars behind their development.

Of course they are going to have extremely talented people with many years of experience working on them.

I too have a great handle on C# (2 1/2 years in business web apps) and some C++, but I would still consider myself having 'limited' capabilities at game making. (Although I love trying my hand at it!)

I doubt very seriously that you (or me) will be making a AAA game here.

What you keep calling "hacked together" are actually concepts that are working for me (including inclines/declines).

So while you continue on your quest for the 'perfect' game, I'll continue to progress my "hacked together" project and optimize where I can later on.

 

If anybody can give any new ideas on how to eliminate the 'tripping', then I would prefer not to move the player this way,

but the bottom line (for me) is that manually updating the players position works relatively well for what I'm doing.

 

 

Oct 25, 2009 at 10:20 PM

I guess I didn't really get that 50% fake part :P. Also I didn't mean to call your work "hacked together". I just have this annoying habit that makes me keep looking around

for the perfect solution, even when it seems far away and not worth the trouble. I always end up wasting a lot of time because of this. In this case I should have started

adding other features and artwork a couple of days ago :P

 

I'm still updating the position manually and added the OnCollision event for the geom. When this gets triggered I set the X value of the LinearVelocity to 0 and return true.

Thanks again for all the help.