Search Unity

Counteracting Gravity? Math help

Discussion in 'Scripting' started by Nanako, Aug 2, 2015.

  1. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I'm writing a manipulator for clicking and dragging ragdolls, and rigidbodies, and i'm trying to make the grabbed rigidbody hover at a certain height above the spot of ground where my cursor is. (about half a metre) by pushing it constantly towards the specified point

    While this works if the target is light enough, when i try to drag it horizontally, the target sinks downwards as it moves horizontally in a sort of inverse parabola, and doesn't climb back up until i stop horizontal movement.

    I can minimise this problem by increasing the force i move it with, but that causes other problems.

    What i'd like to do instead, is apply a constant secondary force that pushes it upwards. The math i'm trying to use for this is

    Physics.Gravity * targetrigidbody.mass * Time.DeltaTime

    This force is applied repeatedly every frame (is there a better way ? should i specify a forcemode?)

    It's worth noting that i'm NOT applying these forces directly to the rigidbody, but instead to a manipulator object which is linked to it with a fixedJoint. as far as i'm aware that shouldn't make a difference, but maybe i'm wrong

    as a far as i'm aware this should make the object float freely and act as if it's not obeying gravity, which should result in completely horizontal motion when i drag it around. But it doesn't. And that's just on single bodied objects.

    I also need to figure out how to apply this method to ragdolls, which are obviously made of several parts.

    I am well aware of the UseGravity flag on rigidbodies, however i'm opting not to use that if possible for two reasons:

    1. Ragdolls. Gravity is essential for making them flop around realistically
    2. Limiting. I'm putting an upper limit on this lifting force, so that it still won't work on exceptionally heavy objects

    I'm trying to implement this feature as a way to make the player feel more incontrol of dragging and manipulating objects. Without this force it's kind of lame



    Here's me attempting to drag a multipart ragdoll around. It's supposed to float horizontally though the air, not drag along the floor.

    And here's a demonstration on just a plain, single-rigidbody ball. The lifting force code is active here, but it doesn't seem to do anything


    As can be seen when i attempt to drag it horizontally, the ball still dips and moves in an arc. It also greatly overshoots the target, which is a result of me cranking up the basic force to try to eliminate this dipping

    I could do with some advice on making this work better
     
  2. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Also, here's my entire manipulator code: http://www.pasteall.org/60042/c

    The most relevant parts are the coroutine starting on line 139,
    And these selected lines from within it:

    Code (CSharp):
    1.             Vector3 tempLiftingForce = Physics.gravity * target.mass * Time.deltaTime;
    2.             Vector3 LiftingForce = Vector3.ClampMagnitude(tempLiftingForce, maxLiftingStrength * Time.deltaTime);
    Here i'm calculating the required lifting force, and clamping it to the maxLiftingStrength which is a value per second. I have a debug message after that indicating whether or not it was clamped, it is not being clamped indicating that the max strength i've set is sufficient for test cases.

    Also here, where i'm adding the lifting force to the total force which will be applied this frame, and then applying it to the manipulator object

    Code (CSharp):
    1. else if (movementMode == 1)
    2.             {
    3.                
    4.                 Vector3 force = targetPosition - transform.position;
    5.                 float dist = force.magnitude;
    6.                 strengthBonusMult = strengthBonusMaxMult * (Mathf.Min(dist, strengthBonusMaxDist) / strengthBonusMaxDist);
    7.                 force.Normalize();
    8.                 strength = (baseStrength * (1 + strengthBonusMult));
    9.                 force *= ( strength * Time.deltaTime);
    10.  
    11.  
    12.                 force += LiftingForce;
    13.                 body.AddForce(force);
    14.                 if (dist < stoppingZone)
    15.                 {
    16.                     body.position = targetPosition;
    17.                 }
    18.             }
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Force calculation typically don't use Time.deltaTime. The unit of force, Newtons is equivalent to kg m /s /s. So no need to use time.deltaTime to scale it.
     
  4. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    if you want to counter act gravity just do something like this.
    Code (csharp):
    1.  
    2. rigidBody.AddForce(-Physics.gravity * 2);
    3.  
    make sure you do that in the physics update, so FixedUpdate or a OnTrigger/CollsionStay
     
  5. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    What? noooo, hold on a second @passerbycmc

    Gravity works as a constant acceleration that is not dependant on mass. PhysX forces definitely do not, i'm screwing around with them right now, the effect is very noticeable if i increase the mass of an object, the same force produces less change in velocity, and doesn't move it as far.

    I don't see any logical way that could work on an object whose mass is not 1, unless the ForceMode were set to Acceleration
     
  6. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    ya sorry the force mode in my test was Acceleration, but simply adding the inverse of gravity, with ForceMode.Acceleration in the physics update will hold the object in place and nullify the effects of gravity on it.
     
  7. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Well that would certainly work, but it also doesn't allow me to limit the force applied, and would work equally on any object. That's not what i'm looking for D:

    A way to calculate a force i can clamp is preferable. how close was i originally?
     
  8. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    I must say, as someone who can contribute absolutely nothing to this conversation (as I've rarely worked directly with forces and physics), that your threads are always extremely fascinating Nanako. I have this feeling that we should bundle them all up in one place and submit them to the devs as an example of aspects of Unity where they could make things a bit easier to control.
     
    eisenpony and Nanako like this.
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Random thought, ever consider using a spring joint? A spring joint basically applies force based on the distance from a target point. Further you are away, the more force is applied. This may well reduce the amount of math you have to do.
     
  10. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I have, and i tried it. I didn't find it to offer a sufficient degree of control, and the force seems to scale up almost-infinitely. It also suffered the same problems mentioned here

    What i have now is basically my own remake of the spring joint, with more control i guess. Though i'm still polishing iot, clearly there are issues
     
  11. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Hah, thank you! I do tend to delve very deep into things that i'm working on, and often find shortcomings.
    You could subscribe to me, apparently someone else already did. A fan club would be fun :p
     
  12. novashot

    novashot

    Joined:
    Dec 12, 2009
    Posts:
    373
    I assume setting a height and turning off gravity while selected isn't a viable option?
     
  13. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    722
    But surely you can clamp that statement based off mass of the object? For instance, the higher the mass of the object, the less constant upwards force you apply. Also going through your code I'd recommend getting these parts out of the while() statement and into a FixedUpdate. It makes things simpler and more efficient without having to worry about framerate.
     
  14. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    As mentioned, i'm applying a limited strength to this functionality, turning off gravity is an indiscriminate, unlimited thing
     
  15. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    How can i clamp an acceleration? Any value less than gravity will be worthless. It's just coming at the problem from the wrong end of the equation. A maximum force magnitude seems better. That's what i was originally trying to do, but it didn't work for reasons unknown

    The manipulator is an object that spends a lot of time not doing things, and only gets forced around when the player is attempting to manipulate an object. Which is a common gameplay feature, but not constant.

    I used a coroutine because i can turn that on and off, whereas the Update/FixedUpdate monobehaviours can't be disabled and will continue firing every frame whether i need them or not. That concept has always annoyed me, and i try to simply not use them if i can help it. This seemed like an appropriate situation
     
  16. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    722
    While it's true that those functions can't be disabled, the performance you save from using FixedUpdate rather than adding force values every frame is much greater than the performance you lose from having the FixedUpdate fire all the time. Plus all you really need to do is if (doThing) for the entirety of the FixedUpdate and basically it's the same, the FixedUpdate call itself is nothing compared to the calculations in the script.

    However if your game requires extremely accurate physics, then doing it every frame would probably be worth the performance cost.

    Clamping acceleration? It's not a matter of clamping acceleration, but clamping the value that goes into that acceleration. Hmmmm.... So if the object moves down when moving from point A to B, have you considered that there is some downwards force when the object is moving? I see tempLiftingForce has already taken into account gravity, but it's added only after you do some more calculations with strength and the Normalize() function. So if there was any downwards force before hand, liftingForce would be only be adding to it. Have you tried just setting the force.y = LiftingForce.y? That would make sure there is no downwards force since we are just setting the value to be LiftingForce. I don't quite understand how you want to limit the force applied, but LiftingForce has the mass inside that calculation, so there wouldn't be any limit there. It would always be counter acting gravity correctly. Limits would have to be applied manually. Or, if you want the mass to be a factor in LiftingForce, then just remove mass from that statement and just add an upwards force based off a static variable. Since force up doesn't change but mass will change, that means you'll be limited by the weight of the object. If you want smaller objects to be the same height, just lower the force when the mass is really small.
     
  17. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Could i perhaps set the coroutine's yield time to Time.FixedDeltaTime in order to alleviate that? Extreme accuracy isn't an issue, no.

    Perhaps you're right there.

    The other calculations are for the main, mostly horizontal force that moves the object around. It is possible there's a downward force, but in that case I WOULD want it to be applied.

    There's lots of confusing modes in there i added while testing. But what it basically does right now is raycasts through the cursor, hits something, and pushes the manipulator to a point a flat distance above the hit location, so the higher the object the mouse is over, the higehr up the target would be, thats ok.

    I believe you missed something.
    Code (CSharp):
    1. Vector3 tempLiftingForce = Physics.gravity * target.mass * Time.deltaTime;
    2.             Vector3 LiftingForce = Vector3.ClampMagnitude(tempLiftingForce, maxLiftingStrength * Time.deltaTime);
    The force limiting is done by Vector3.ClampMagnitude. It clamps the magnitude of the already calculated force, to a predetermined maxLiftingStrength value. I first precalculate the infinitely scaling force required to counteract gravity, and then clamp it like so. If it was too high before the clamping, then it should fail to properly lift the object. If it wasn't too high, then it should suspend the object and perfectly counteract gravity.

    F = MA, right? As far as i'm aware, this code should work.
    I'm not really here for alternate suggestions, i created this thread to get help on why that exact code does not work, and how to fix it.
     
  18. Arcatus

    Arcatus

    Joined:
    May 27, 2015
    Posts:
    45
    Images in the OP doesn't work for me :(

    Anyway; I'll ask some stupid questions;

    On line 145 you do

    Code (CSharp):
    1.  
    2. if (tempLiftingForce.sqrMagnitude > LiftingForce.magnitude)
    3.  
    Should that be sqrMagnitudes on both sides?

    It also seems your lifting force is pointing in the same direction as the gravity. Is that intentional?

    Code (CSharp):
    1.  
    2. force += LiftingForce; //where liftingforce having a negative y component
    3.  
    4. body.AddForce(force); //the result here would be downwards movement, unless the y component of force is greater than LiftingForce
    5.  
     
    Last edited: Aug 4, 2015
    Nanako likes this.
  19. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    With regards to ragdoll, unless you simulate every body, it won't work without a high enough Solver Iteration Count and fixedupdate rate. Because joints won't resolve in the same frame, so it'll all lag behind.

    You will want to enable Preprocessing option on joints as well to deal with higher forces. Having a fixedjoint on the cursor location that connects to the grabbed limb's rigidbody will solve the rest and it should drag around as expected with AddForce upward equal to gravity on the limbs of your choice.

    And don't forget physics materials. I do this stuff in my game right now, which has a lot of dragging around of ragdolls, I didn't need any special maths, just the realisation that Physx joints are very conservative settings to begin with (obviously as they are quite expensive).

    The joint you use to drag also cannot be part of the same hierarchy.
     
  20. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Here's direct links to the two images, these might work for you:
    http://i.imgur.com/oLEgsaG.gif
    http://i.imgur.com/ftW9HmF.gif

    Ooh, you're right. That could be a part of the problem. I use that just for a debug line to tell me whether the clamping is being applied, but as i've written it, it's unlikely to be meaningful. So maybe i've set the force limit too low and didn't know it.
    Yikes ! That would also explain things. My lifting force is pushing down, and not up >.<

    Congratulations, you've seen two things that i, and everyone else seem to have missed. I nominate this for best post in the thread.
     
    Arcatus likes this.
  21. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Please explain this, i don't understand.

    Just so we're clear, my code does the following:
    1. User clicks a part of the object. Code finds the rigidbody of that object.
    2. Manipulator moves to the clicked point, attaches itself to the found rigidbody with a FixedJoint
    3. Forces are applied to the manipulator, to move it to target locations and drag the object(s) with it

    Is there a problem with this?
     
  22. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    No problem. That will work fine. It just has undesired behaviours if the joint is a child of whatever you're wanting to manipulate. You should probably put a sphere mesh on that joint or something, to ensure it's actually where the mouse cursor is, dumb stuff like that caught me out a few times.
     
  23. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Nanako,

    I can't get your code to work how I expect. I created a blank scene and dropped the (slightly modified so it would compile) Manipulator into a cube. When I attempt to drag the cube, it doesn't want to move. Seems like something is holding it in place since I've watched the force values change as I drag my mouse around. Is this the most up to date code for your manipulator?

    Anyways, I think you should probably try to follow the advice from hippocoder. You shouldn't need all of this math if you are able to get the joints working properly.
     
    Last edited: Aug 4, 2015
  24. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    if it's not moving at all then you've definitely messed something up. it works for me, just the upward force isn't working adequately.

    check if rigidbodies are kinematic maybe. they shouldn't be
     
  25. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    I can't, i tried.

    I spent hours tweaking and poking. Spring joints just can't do the job. Their pulling strength scales up seemingly infinitely with distance, negating attempts at limiting it. And their strength drops to an inadequate amount near the target, resulting in the grabbed object always dangling down a bit, and bouncing.

    The bouncing is the worst, i couldn't make it stabilise at all. with any speed it just bounces around endlessly.

    And if i add drag to prevent the bouncing, that just weakens its strength, causing it to sag down farther, and pull objects far too slowly.

    it's like playing whack-a-mole. and i wasn't making any progress.
     
  26. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Okay I got it working. My mistake was placing the manipulator script onto the object I was trying to grab -- that's not how your code was designed to work..

    To me, it feels like your upward force calculation is doing the job fine. The objects do not seem to dip much when moving through space. However, the problem I'm seeing is more fundamental to your design; it appears to be related to applying forces in order to move the object towards a position. The code is creating a compelling force which attracts the object to move towards the world point my mouse cursor is pointing at. That actually sounds a lot like gravity from an interplanetary point of view, which explains why I start seeing "orbiting" if I set the target point sufficiently far from the current position.

    I'm not sure you'll be able to solve this one using forces. You might want to try acting on the rigidbody's position directly, using an easing function to simulate physics. If you really want to stick with forces, you'll need to do some calculations to make sure your object doesn't overshoot the target by too much.

    maybe a combination of spring joints and code like your absolute positioning if within a certain range:
    Code (csharp):
    1.  
    2. if (dist < stoppingZone)
    3. {
    4.   body.position = targetPosition;
    5. }
    I'd need to play around with the spring joints a bit to better understand what you're seeing and disliking.
     
  27. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    722
    Yes that would actually be smarter xD Just remember that if you set yield to a fixed time rather than a single frame, it makes the Time.deltaTime calculations unnecessary.

    I did miss that-- I thought by limiting you meant that the mass is what limits it. And you are very right about the magnitude... since it's applied after the mass calculations it should still work...

    The last thing I can think of is maybe the you could temporarily add more force when the object is moving to counteract that dip in the arc... I know you said you want to fix that script specifically but I'm actually out of ideas haha D: Other than maybe some Debug.Log statements to figure out the actual velocity of the object. Maybe you could see if there is any unexpected forces happening on it? If the Logs look fine then it might be more reasonable to assume its a problem with the way physics are handled.
     
  28. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Hey Nanako, I was able to get decent looking results by using a ConfigurableJoint. Something like this:
    (Not the prettiest code, I admit)
    Code (csharp):
    1. connector = gameObject.AddComponent<ConfigurableJoint>();
    2. connector.angularXMotion = ConfigurableJointMotion.Locked;
    3. connector.angularYMotion = ConfigurableJointMotion.Locked;
    4. connector.angularZMotion = ConfigurableJointMotion.Locked;
    5. connector.xMotion = ConfigurableJointMotion.Limited;
    6. connector.yMotion = ConfigurableJointMotion.Limited;
    7. connector.zMotion = ConfigurableJointMotion.Limited;
    8. connector.xDrive = new JointDrive()
    9. {
    10.   mode = JointDriveMode.Position,
    11.   positionSpring = 1e3f,
    12.   maximumForce = 550
    13. };
    14. connector.yDrive = new JointDrive()
    15. {
    16.   mode = JointDriveMode.Position,
    17.   positionSpring = 1e3f,
    18.   maximumForce = 550
    19. };
    20. connector.zDrive = new JointDrive()
    21. {
    22.   mode = JointDriveMode.Position,
    23.   positionSpring = 1e3f,
    24.   maximumForce = 550
    25. };
    26. connector.breakForce = 500;
    27. connector.connectedBody = target;
    by setting my maximumForce just under my breakForce, it seemed to behave well on items that were "too heavy".

    Edit: I should add that I'm out of my field on this one.. I'm just learning about joints..

    Also, I used the same pattern as you did, with the manipulator being a separate object. I made the manipulator kinematic.
     
    Last edited: Aug 5, 2015
  29. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    By the way guys, thanks for all the replies so far, especially big thanks to @Arcatus. I believe i've now solved the original problem (of counteracting gravity)

    Two steps solved that;
    1. Inverting gravity in my original calculations. i forgot to do that, so my code was pushing the object downwards.
    2. Changing the ForceMode of my addForce statement to impulse.

    To this point, i'm still not clear on exactly what the default forcemode does, but it's clear to me now, it wasn't what i was expecting.

    The gravity calculating lines now;

    Code (CSharp):
    1. Vector3 tempLiftingForce = (Physics.gravity * -1f) * target.mass * Time.deltaTime;
    2. Vector3 LiftingForce = Vector3.ClampMagnitude(tempLiftingForce, maxLiftingStrength * Time.deltaTime);
    3. ....
    4. ...
    5. force += LiftingForce;
    6. body.AddForce(force, ForceMode.Impulse);
    Most of the last 8 ish posts in this thread have kind of moved onto a new topic, because the manipulator system is still not working as i'd hoped, but it's the horizontal motions i'm having issues with now
     
  30. Arcatus

    Arcatus

    Joined:
    May 27, 2015
    Posts:
    45
    Cool, nice I could help. :)

    One thing that is probably unrelated, but I dind't quite get;

    You are multiplying the normalized vector with a number that is the result of several multiplications and starts as force.magnitude
    This seems to me that you can be off by just a tiny bit on one axis but a huge distance at another, but since you base the applied force of the magnitude of the vector you end up with appyling a large force in both directions. Wouldnt it be better to treat forces on each direction seperatley?
     
  31. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Here's a few more points of interest -- sorry if they don't help directly.

    I would call what you are creating here is a "connection" between two GameObjects using forces to bring them together. There is a small risk here of recreating something the Unity framework provides to you: joints. That said, you are doing some pretty sophisticated things to get the behavior to "feel" the way you like. There may be no way to reproduce this exactly using the built-in joints, so if there is a very specific way you want the moving items to feel there is no point in looking into joints. However, if you just want something which works reasonably well, joints are going to be a better choice because...

    While I don't subscribe completely to the idea that code is our enemy, there is a truism in "you don't need to support code that doesn't exist".

    The reason you were seeing a dip when moving your objects is due to the way trigonometry works. When your manipulator is nearby the "grabbed" object, the entire magnitude of the manipulator's force is free to work against gravity. Once you start moving the object however, the manipulator needs to use some of it's strength to move the object in the x and z planes. If the forces required to move the object are great enough, they will diminish the manipulators ability to work against gravity. Another approach then, would be to set the forces on the x, y and z axes independently, or to make sure they never dip below a minimum threshold required to resist gravity.

    It's likely that the default forcemode does work how you expect, it's just that Impulse is giving a nicer feel to your custom joint. An impulse is the first derivative of a force over time. In other words, it's the effect a force has over a unit of time which, of course, is a change in velocity. In Unity, an impulse force will create an instant change in velocity, which should make for a snappier feel.