Search Unity

Jumping off a moving surface...

Discussion in 'Physics' started by Chusuk, Jun 21, 2022.

  1. Chusuk

    Chusuk

    Joined:
    Apr 19, 2021
    Posts:
    43
    Forgive me, my understanding of vectors is new and limited with regard to game mechanics...

    In the real world, loosely speaking, if I were to jump off a moving object (say a train) then my jump motion is the result of my own generated motion combined with the existing motion of the train.

    If my character is standing on a moving object in Unity world (3D), say a train, I can detect I'm on the train, make the character a child of the train and have it move in sync by just being dragged around through inheritance. When my character jumps whilst on the train or jumps off it, I assume I need to add the vector movement of the train? (ignoring the real world situation of when leaving the surface of a train in real life, the person would likely be affected by wind resistance, especially at higher speeds).

    I can calculate a rough movement vector of the train as its current position minus its position last frame?

    If I have a third person player running around my world using the Unity 3rd person movement controller, the key movement line is:

    Code (CSharp):
    1. Vector3 movement = targetDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime;
    My poorly functioning brain is telling me just to add the trains rough motion vector to the movement vector of the character. Given the vector movement of the train is calculated every frame, I just tried to add that vector to the 'movement' vector described above, but my character behaves like he's been shot out of a cannon...

    Could someone explain the principles to solve this problem please?

    Many thanks
     
  2. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    First.
    Don't make character child of the train.

    Second.
    We do not know what character controller you are using so it is going to be hard to help.

    Third.
    Search for information about moving platforms.
     
  3. Chusuk

    Chusuk

    Joined:
    Apr 19, 2021
    Posts:
    43
    On your first point, thank you.
    On your second point, apologies I thought saying "using the third person controller from Unity" was clear but I should have expanded to say "Unity starter asset third person character controller as made available on the asset store".
    On your third point....I've been looking at a number of sources for moving platforms and they are swamped with limited tutorials on simply attaching a character as a child of a moving object. So thought it might be useful for others learning unity to see a discussion that has expert input on the concepts involved - please don't be that guy that just says "use google better".
     
  4. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    Sorry, you were clear about this, but I was blind :)

    Show us more of your method.
    Your calculation of movement equation looks correct on a first sight, but we need some more data.
     
  5. Chusuk

    Chusuk

    Joined:
    Apr 19, 2021
    Posts:
    43
    Ok, so my reasoning is as follows (and this includes making the player a child object of the train as it simplifies my code)...

    The "train" has a script attached that simply maintains a rough velocity value at any given moment...

    Code (CSharp):
    1.     public class ObjectMovementInfo : MonoBehaviour
    2.     {
    3.         public Vector3 objectMovement;
    4.         private Vector3 lastPosition;
    5.  
    6.         void Start()
    7.         {
    8.             lastPosition = transform.position;
    9.         }
    10.  
    11.         void Update()
    12.         {
    13.  
    14.             objectMovement = (transform.position - lastPosition) / Time.deltaTime;
    15.             lastPosition = gameObject.transform.position;
    16.         }
    17.     }
    In the Unity character controller script, at the point where the 'normal' movement is calculated:

    Code (CSharp):
    1. Vector3 movement = targetDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime;
    I feel like I need to add code which does the following:

    1. At the point I jump off the train, capture the trains movement vector as per the object movement script above.
    2. While I'm in the air (using !Grounded as per the Unity character controller), add the train movement like this:

    Code (CSharp):
    1. movement += finalMovingSurfaceVelocity * Time.deltaTime;
    where finalMovingSurfaceVelocity is the objectMovement vector captured from the train at the point of jumping off.

    When I implement this, I fly wildly greater distance than I should and exaggerated in the direction I'm jumping rather than in the direction the train was moving (ie if I jump off the side of the train, I have accelerated jump distance away from the train rather than normal jump distance with sideward motion inherited from the train).

    Clearly I'm conceptualising things incorrectly or have bad code somewhere...
     
  6. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    But you do de-parent character from the surface ?
     
  7. Chusuk

    Chusuk

    Joined:
    Apr 19, 2021
    Posts:
    43
    Yes, in my Character controller I do this:

    Code (CSharp):
    1.         private void OnTriggerExit(Collider collision)
    2.         {
    3.             if (collision.tag == "Movable" && transform.parent != null)
    4.             {
    5.                 transform.parent = null;
    6.                 finalMovingSurfaceVelocity = movingPlatformInfo.objectMovement;
    7.                 firstStepOffMovingSurface = true;
    8.             }
    9.         }
    10.  
    and in the move section of the character controller, I do this:

    Code (CSharp):
    1.             Vector3 movement = targetDirection.normalized * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime;
    2.  
    3.             if (firstStepOffMovingSurface)
    4.             {
    5.                 if (!Grounded)
    6.                     movement += finalMovingSurfaceVelocity * Time.deltaTime;
    7.                 else
    8.                     firstStepOffMovingSurface = false;
    9.             }
    The idea being to apply the platform movement to the jumping player, until that player hits a grounding object.
     
  8. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    First of all use debugger.
    Set breaking point at this places where calculations are done and see if the vales are correct.
     
  9. Chusuk

    Chusuk

    Joined:
    Apr 19, 2021
    Posts:
    43
    I think I've figured it out now. Just needed to spend more time looking through the code in detail and applying some real world logic to it. Here's what I've done if anyone finds this thread in the future...

    The controller is geared around speed in a given direction, therefore adding a velocity (the train velocity) to the movement may work for one frame, but subsequent frames will treat the velocity as a movement in the FORWARD direction. Which means your character will shoot off in the direction it faces.

    To cater for this, I created two booleans.

    firstStepOffMovingSurface - in order to identify the first frame at which the character leaves the train. This is set in OnTriggerExit.

    freeFallingFromAMovingSurface - in order to identify in each frame that I'm still in the air from jumping from the train. This is set to false at the point we detect we're grounded (in function GroundCheck) and true on the first frame that we've left the train.

    Calculating movement is now done like this:

    Code (CSharp):
    1.            
    2.             movement = (freefallingFromAMovingSurface ? fallingDirection : targetDirection.normalized) * (_speed * Time.deltaTime) + new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime;
    3.  
    4.             if (firstStepOffMovingSurface)
    5.             {
    6.                 movement += finalMovingSurfaceVelocity * Time.deltaTime;
    7.                 fallingDirection = movement.normalized;
    8.                 freefallingFromAMovingSurface = true;
    9.                 firstStepOffMovingSurface = false;
    10.             }
    Note that the train velocity (finalMovingSurfaceVelocity) is only added once and that we take a snapshot of the direction in which we're falling at that point, for future frame updates.

    It's also worth noting that where the existing controller script lerp's the movement speed while you're running (for smoother transition between animations), I've hijacked this in order to slow my movement whilst flying through the air after jumping off the train so that it looks a little like wind resistance. I did this by creating a variable for VelocityDecelerationRate and used it with the _speed calculation like this:

    Code (CSharp):
    1.                 _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude,
    2.                     Time.deltaTime * (freefallingFromAMovingSurface ? VelocityDecelerationRate : RunningSpeedChangeRate));
    Hope this helps someone in the future...