Skip to main content
GameDev.net gamedev.net
🔒 Locked

Applying downward force when standing on a platform? (2D)

Started by Andrew Russell May 11, 2009 at 10:50 PM 10 replies 4.4k views
Original Post
Andrew Russell
Andrew Russell
You will have to excuse me for leaving most of the details on my blog. Click here to view the associated blog post that has a bit more detail and a downloadable example. What I basically have is this situation: Update: IGNORE THE DOT - it is not a joint or anything. These are just boxes. The green ones are static. Sorry if this confused anyone. I have a player "standing" on a dynamic physics object. Because of the way I have implemented player movement, the player "snaps" to the surface of the object. As such (in this penetration-separation based physics engine - specifically Farseer), no force gets applied to object being stood on. I have a half-working solution that involves applying the force of the player's weight (mass multiplied by gravity) to the object it is standing on. This is obviously incorrect. I suspect that what I need to do involves transferring momentum somehow. So my question is: what do I need to do to make this work realistically? Can it be done just by applying forces to the object being stood on? How do I calculate those forces? [Edited by - Andrew Russell on May 12, 2009 1:37:36 AM]
Blobberson
Blobberson
The force in the y direction would be (mass of player)*(gravity)*cos (angle between gravity direction, and the angle of the object)

Is there any reason why you're not using a pre-designed/tested/debugged physics engine? (chipmunk and box2d come to mind)
Andrew Russell
Andrew Russell
I am using a physics engine - Farseer (I need a native C# engine). I am just "turning off" bits of the physics engine for the player object when it is in contact with the ground (this is more-or-less unavoidable).

I am fairly sure that the force you are suggesting is incorrect (or incomplete?). I am already applying a Y-direction force of mass*gravity (which doesn't work correctly). The cosine will always be close to one so it's essentially the same (I tried it and it was).

I have been directed to material on inelastic collision, conservation of momentum, and contact forces. But my physics isn't really up-to-snuff enough to understand how to move this from a 1D system into a 2D system with rotation, etc.


To give you an idea of what I'm working with, I can access these properties of a physics object:
- Linear Velocity
- Angular Velocity
- Position
- Rotation
- Mass
- Moment of Inertia
- Center of Mass (not really)
- Torque to be applied at the next update
- Force to be applied at the next update
- Velocity at an arbitrary point (read only)

I can also:
- Apply a force or torque at the center of mass
- Apply a linear or angular impulse at the center of mass
- Apply a force at an arbitrary point


So basically, I'm trying to find a solution in terms of the above.

[Edited by - Andrew Russell on May 12, 2009 7:12:51 PM]
Dmytry
Dmytry
Quote:

I have a half-working solution that involves applying the force of the player's weight (mass multiplied by gravity) to the object it is standing on. This is obviously incorrect.

The force player exerts on the surface, assuming that player uses no jetpack of some sort, is -mass*(acceleration of player's body + up*g) where up is upwards vector and g is gravity. That's how it happens IRL.
To handle cases when you don't know acceleration itself, but instead, you know player's dv, you can apply impulse of:
-mass*(player's dv + dt*up*g)
to the point of impact.
To apply impulse to arbitrary point, apply that impulse to centre of mass, and apply angular impulse that equals impulse crossproduct (point of application - point of centre of mass). (in 2D, cross product has only one component, corresponding to the z axis of 3D cross product, and equal to a.x*b.y-a.y*b.x )

Since the "engine" you're using is garbage a priori (does not support elementary, very commonly used cases), this could still be a problem (impulse can push objects into one another, or be altogether handled incorrectly).
Speaking of physics engines, BTW. The physics for 2D engine, lacking the tensor of inertia thing of 3D, appears elementary. That's why there is a lot of 2D 'physics' engines that can not be used naturally even for the most elementary game, and require special hacks even for such common thing as walking character. You really need to be careful what 2D physics engine you choose.

[Edited by - Dmytry on May 12, 2009 2:58:58 AM]
Andrew Russell
Andrew Russell
Thanks Dmytry. I think I understand all that. I'll find out tomorrow when I go to implement it. [smile]

I'm afraid there's not much choice in physics engines for my purpose (C#, XNA targeting Xbox). Farseer seems to be the most optimised for Xbox.


I'm not sure I agree with your assessment that a walking character can be implemented without "special hacks" (or fully implementing a pair of legs). The most obvious situation that comes to mind is a going from an upwards sloping edge to a downwards sloping edge. Without snapping to the ground (or some other trickery), the player will go airborne at some point.

Actually - I suppose something that didn't use a discrete timestep could do it. If you could adjust the velocity at the exact moment the player went over the apex... But - point me in the direction of a realtime C# physics engine than can do that? [smile]
Dmytry
Dmytry
Quote:
Original post by Andrew Russell
Thanks Dmytry. I think I understand all that. I'll find out tomorrow when I go to implement it. [smile]

I'm afraid there's not much choice in physics engines for my purpose (C#, XNA targeting Xbox). Farseer seems to be the most optimised for Xbox.


I'm not sure I agree with your assessment that a walking character can be implemented without "special hacks" (or fully implementing a pair of legs). The most obvious situation that comes to mind is a going from an upwards sloping edge to a downwards sloping edge. Without snapping to the ground (or some other trickery), the player will go airborne at some point.

Well, if you got massless boots on damped springs, not necessarily so - the spring could expand to keep in contact (and you expect character to go airborne if character is running fast enough and the edge angle is big enough).
My point is that walking character is very common, and engine should support that, even if as special case.
Quote:


Actually - I suppose something that didn't use a discrete timestep could do it. If you could adjust the velocity at the exact moment the player went over the apex... But - point me in the direction of a realtime C# physics engine than can do that? [smile]

Hmm, actually I've no idea about this specific engine - just the description (penetration-separation based and custom code required for walking) does not sound very nice.

BTW, for the example case. If collision is perfectly inelastic, you may want to attach the player as point mass onto the yellow box, updating box's mass, box's centre of mass, and box momentum of inertia accordingly.
The combined body's linear momentum, generally, is a sum of linear momentums of components, and combined body's angular momentum is
sum( angular_momentum + linear_momentum x (centre_of_mass_pos-new_centre_of_mass_pos) )
where x is cross product. (note that sign of the cross product here depends to convention used in engine, you might need to negate it)
(Then its up to physics engine to apply impulse from the edge of green box.)
Andrew Russell
Andrew Russell
OK, so I tried two different things based on your first post. I'll post some pseudo-code just in case I've made some mistake in that. The common stuff first:

VelocityLastFrame = VelocityThisFrame
VelocityThisFrame = PlayerVelocity + Ground.VelocityAtWorldPoint(PlayerFeet) // PlayerVelocity.Y is fixed at 0 when on the ground
deltaV = VelocityThisFrame - VelocityLastFrame

Just to note - I've ignored the X axis velocity for now, on the basis that it's zero for a lot of this testing.


The first thing I tried was a force like this:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y - Gravity) ), PlayerFeet)

Which I then realised isn't correct. I think it should be this:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y / deltaT - Gravity) ), PlayerFeet)

Which glitches pretty readily (sending things flying or getting things stuck). On a whim I tried this - which gives an "ok" result, and only rarely glitches:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y / (deltaT/3) - Gravity) ), PlayerFeet)


Anyway, then I tried using an impulse:

ImpulseY = -mass * (deltaV.Y - Gravity * deltaT)
ImpulseAt = Ground.GetLocalPosition(PlayerFeet)
Ground.ApplyImpulse((0, ImpulseY))
Ground.ApplyAngularImpulse(ImpulseAt.X * ImpulseY) // cross product with X=0

And this just sent things flying.


Anyway - I have more bad news about this (as you perhaps aptly call it) "garbage" engine. There is no "center of mass" to adjust. It seems it just puts it at (0, 0) in local co-ordinates - and translates the geometry back by its centroid during creation (there's an "offset" option, but that is only available during creation). Farseer has some weird system where a "Body" has one or more "Geometries". The Body holds the mass, position, velocity, MoI, etc. Each geometry has friction, coefficient of restitution, collision handling/events, as well as the actual shape.

Perhaps I should use a different engine? As I mentioned before - there's not a lot of choice. Physics2D.Net is a possibility, so is Box2DX. I hear they both have issues on Xbox, though, that I'd have to fix.

[Edited by - Andrew Russell on May 12, 2009 10:52:28 PM]
Andrew Russell
Andrew Russell
Quote:
Original post by Andrew Russell
On a whim I tried this - which gives an "ok" result, and only rarely glitches:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y / (deltaT/3) - Gravity) ), PlayerFeet)


And, joy of joys, it seems to have stopped working. Stupid mysterious bugs. I'm not sure if I have inadvertently fixed a bug that was fortuitously causing it to work, or inadvertently introduced a bug.
Dmytry
Dmytry
hmm, applying impulses fails in many engines. When you apply an impulse to yellow box, the engine must rather resolve constraint introduced by green box underneath and apply impulse from green box; that is not easy to do right. For better understanding maybe you can view it in slow motion, say, 60x slower? So you can see what happens in every sim frame. Maybe you can crank up simulation frame rate a lot (10x) and apply the force in 10 or 20 frames to apply impulse.

As for other engines on xbox... thats a bit strange, i'd think .net would be system independent enough.

BTW. Try negating this line
Ground.ApplyAngularImpulse(ImpulseAt.X * ImpulseY)
I don't know if angles are clockwise or counter-clockwise.
Andrew Russell
Andrew Russell
Quote:
Original post by Andrew Russell
Quote:
Original post by Andrew Russell
On a whim I tried this - which gives an "ok" result, and only rarely glitches:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y / (deltaT/3) - Gravity) ), PlayerFeet)

And, joy of joys, it seems to have stopped working. Stupid mysterious bugs. I'm not sure if I have inadvertently fixed a bug that was fortuitously causing it to work, or inadvertently introduced a bug.


OK - so two stuff-ups here on my part. First of all the line should be:

Ground.ApplyForceAtWorldPoint(( 0, -mass * (deltaV.Y / (deltaT*3) - Gravity) ), PlayerFeet)

That works (I made a mistake when converting to pseudo code - whoops). The bug I mentioned is now fixed - I had screwed up the deltaV calculation at some point (also whoops).



Anyway:

Further analysis seems to indicate that it's more-or-less a fluke that it works. Increasing the mass or decreasing the factor on deltaT breaks it. My guess is that it applies more force than the engine can handle in a frame. Even this "working" way sometimes breaks it.

The actual force being applied, when visualised, seems to oscillate up and down (around the Gravity force - sometimes being an outright upwards force). When it works, the oscillation reduces to a stop. When it fails, it seems to increase out of control - possibly because because the initial force pushes it too far into the ground below - but possibly some other reason.

Perhaps there is a way I can cap, dampen or perhaps even "store up" the force being applied so it never exceeds some threshold?



Regarding your latest reply, Dmytry:

I already have a frame-step function. Unfortunately it hasn't helped all that much with this issue. Perhaps your idea of applying the impulse over several frames is similar to the one above for capping / storing the force being applied.

The Xbox uses the .NET Compact Framework. It's missing a bunch of functionality of the full framework. More importantly - its garbage collector is non-generational. Creating garbage is a big no-no. I am under the impression that Farseer is the only one regularly tested on, and optimised for, Xbox. (I am yet to actually buy one and test on it myself.)

Also: I tried both angle directions already - they both break, unfortunately.
Dmytry
Dmytry
I think I see now why simulation explodes (is unstable).
Try using player's dV only on impact, and ignore it otherwise. Especially, ignore the component of dV caused by the floor object.

Say, player is significantly heavier than the floor and falls onto static floor. With those formulas, because mass of attached player is not considered, impulse from player is given to floor, and the floor velocity is given by ratio of masses; with your dt*3 trick, when floor is say 6 times lighter than player, it means floor will be moving 2 times faster than player. Then your snapping code could be snapping the player to floor, making player move 2x faster too. And make the floor go even faster. And kaboom. The correct solution would indeed require you to attach player's mass to the floor object and transfer impulse, as described...

regarding garbage collection: just wow.
Andrew Russell
Andrew Russell
So I've solved the problem!

It was simply a matter of buffering any upwards forces until a downwards force was available to cancel it out. This prevents any oscillation from growing out of control.

I'm not entirely sure about your new solution - only applying the deltaV force on impact - would it be correct for a platform that bounces up and down? Maybe it would, and all the cancellations I'm doing would add up to exactly that. But for the moment I'm going to run with what I have.

Thanks for your help!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.