1
\$\begingroup\$

we are using Farseer in our project and I try to find a setup to achieve the following quite complicated collision behavior:

  • Friendly Units can push each other aside. However, the push should be a "soft-push", so that multiple units can squeeze through narrow gaps.
  • Enemy Units should not be pushed. Ever. Moving units colliding with enemies should stop dead.
  • Additionally, I have some other type of collider, lets call them "Wall" ;). Neither friends nor foes should ever be able to stand inside a wall.

Whether two objects are considered "enemies" can change dynamically during the game. The state is always symmetric (If Bob is an enemy of Alan, then Alan is always an enemy of Bob) but we have more than two factions (up to 16).

(Basically, its a similar collision behavior like Starcraft, except that friendly units can temporarily squeeze through navmesh gaps.. much like in Age of Empire..)

Actually, I got a quite elaborated solution so far. It has some problems, but kind of gets most of my wishlist done.. Well, most of the time. Here is what I did..

Lets first start with the setup without any walls (because that's what is working better ;):

  • I have 2 bodies for each unit. One dynamic the other kinematic.
  • the dynamic body has two equal fixtures. One sensor (to soft-push friends), the other a normal fixture (this is what I apply forces from outside game code).
  • The kinematic body has one normal fixture. (This body is to dead-stop on enemies)
  • I hooked a BeforeCollision callback to the normal fixture of the dynamic sensor. This filters out all collisions except with the kinematic body of enemies. This way, the dynamic body can not push enemies.
  • Another BeforeCollision filter on the kinematic body just excludes collisions with the sensor fixture of our own dynamic body.
  • Now, after every world update, I run through all the dynamic bodies:
    • I will copy the new position into the kinematic body.
    • For each touching contact of the sensor with another dynamic body, I will manually calculate a separating force and apply these to both dynamic bodies.

Everything clear so far? To summarize: Dynamic fixture moved by game code, stops dead colliding with kinematic body of enemies. Sensor register friends and I calculate push-forces by hand. Kinematic body is synched every frame to the dynamic body.

Now the problem: Walls.

I need to enforce that no Body ever ends up inside a wall. More precise, the center point of any body must not be inside any Wall - shape (the circle can and should overlap with the wall, only the center point is interesting).

I tried several ideas to achieve this but running out of steam here. The currently best solution I could come up with: - The dynamic body gets yet another circle fixture that is extremely small, like 0.001 meter. - This one has a CCD on and a collision group that only allows collision with walls (which are in their own category) - Walls are all fixtures of a static body.

This approach works as long as no "teleporting" of units is involved. Lets say Alan is being teleported on a spot overlapping with Bob. Now, Alan's kinematic body will push Bob's dynamic body and vice versa. Everything is fine. But if Bob was standing right next to a wall, Farseer will still move Bob slightly into the wall, since the resolver need to resolve the unmovable kinematic body of Alan and the unmovable wall.

Yet another post-update loop through all bodies that scans all units whether they are inside walls and manually resolve their positions is way beyond our performance limits.

I briefly thought about some solution involving two separate Farseer worlds, but my head starts to spin trying to figure out which body position need to be copied where and when... ^^

I haven't looked into joints yet, but my gut feeling is that they don't really help here. Except with a third body? Do I really need 3 bodies per unit? :(

Any ideas?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.