Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Help "visualizing"/comprehending how Mathf.Sin/other trig functions work to affect movement

Discussion in 'Scripting' started by tytbone, Aug 13, 2015.

  1. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
    EDIT: Found some good info on YouTube. Maybe I jumped the gun on making this thread.

    (I certainly regret not taking more math classes in college. Of course I've been doing a little reading on trig but I wanted to see if the fine folks here could help clue me in on a specific issue. Preemptive apology about all the text, too.)

    Like the title says, I'm trying to understand in visual terms how Mathf.Sin and co. work to move gameobjects so that I can make use of them in a variety of ways. I got an enemy easing in/out thanks to to https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/, but I don't really understand why it works, and I'd like to get a better understanding preferably with pictures indicating how each variable affects the movement.

    I tried to cut the code down to just the important elements.
    Code (csharp):
    1.  
    2. float lerpFull = 3f;
    3. float currentLerpTime;
    4.  
    5.     void Update ()
    6.         currentLerpTime += Time.deltaTime;
    7.         float lerpFraction = currentLerpTime / lerpFull;
    8.         float sinerp = Mathf.Sin(lerpFraction * Mathf.PI);
    9.         transform.position = Vector3.Lerp(start.position, stop.position, sinerp);
    My understanding of Lerp is like this: lerpFull acts as the "100%" point of the movement (number of seconds to completion?) and acts as a constant speed, larger nums slower (taking longer to "reach" or "fill-up" percentage wise) or smaller nums faster (the opposite).

    currentLerpTime snags and adds up the (constant?) time since the previous frame happened (like 0.0002+0.0002+etc.). lerpFraction gets the current fraction/decimal of the "100%".

    transform.position = Vector3.Lerp(start.position, stop.position, lerpFraction) works as a straight, constant point-to-point, adding up the time until the "final time"/100% is reached and movement stops).

    Course I'm really wondering about
    Code (csharp):
    1. sinerp = Mathf.Sin(lerpFraction * Mathf.PI);
    It seems to adjust the movement to that of a Sine wave (or half a sine wave/half circle), except in this case there's no circular movement, just straight point-to-point.

    Is Mathf.Sin getting the Opposite length of the triangle each frame, and factoring in the lerpFraction percentage? How does .Sin sort of "keep track" of how much to ease in and ease out at which lerpFraction percentage (or should I just accept that "it just does")? Is Math.PI necessary for Mathf.Sin to "think" the gameobject is moving on a circle (thus have the different lengths that the "spokes" of a circle can provide, like I've tried to show in the image below)?

    Anything right here in this image?



    Also, sorry for the weird way of trying to understand this. It's probably confusing in its own right.

    In trying to more fully understand trig for other enemies, I'm wondering, could I apply Sin or another Mathf trig function to have an enemy jump/"bounce" along (jump in a sine wave motion (half-circle), then go again once it contacts the ground)? I assume I'd get the Mathf.Sin info but instead of Vector3.Lerp I'd want a different function(s). Or perhaps I should stick with AddForce for something like this?

    Thanks in advance anyone who read my bigass wall of text and tries to assist me. ;) I'll definitely continue to read about geometry/trig in the context of games, but I felt the need to get this curiosity out of my system.
     
    Last edited: Aug 13, 2015
    bowserscastle likes this.
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Ok... we're going to go to your piece of code:

    Code (csharp):
    1.  
    2.         currentLerpTime += Time.deltaTime;
    3.         float lerpFraction = currentLerpTime / lerpFull;
    4.         float sinerp = Mathf.Sin(lerpFraction * Mathf.PI);
    5.         transform.position = Vector3.Lerp(start.position, stop.position, sinerp);
    6.  
    In this code the first two lines are merely scale the frequency of our period. Increasing 'lerpFull' will make the frequency less often, reducing lerpFull will increase the frequency.

    The 3rd line, this is where the frequency is going to occur. Note... Sine is an infinite wave with a period of 2pi. By multiplying by the frequency calculated, you're saying we should travel pi distance down the sine curve every 'lerpFull' seconds.

    Check out in this image, note how the 0.5x causes the curve to be longer. But 2x makes it shorter. This is what you're doing... you're stretching the curve out so that it crosses the x-axis every 'lerpFull' seconds.


    So sine function will take that input and give you back a value from -1 to 1. What will happen is you'll start out getting 0, and as time proceeds it'll climb towards 1, then back to 0, in 'lerpFull' seconds. It'll go down to -1 and back up to 0 at 2x 'lerpFull' seconds. And repeat this every 2x lerpFull seconds.


    Lastly we take this value and we feed it into a Vector3.Lerp function. What this is going to do is move a vector from start to end the percentage of t (sinerp in your case). If you put 2 in their it'd travel in the direction of end from start, just twice the distance. SO if you put in -1, it'll move the opposite direction of end from start, but the distance of them apart.

    Imagine here O is start, and X is end. The ----> will be the result:

    Code (csharp):
    1.  
    2. 0:
    3.       O      X
    4. 0.5:
    5.       O-->   X
    6. 1:
    7.       O----->X
    8. 0.5:
    9.       O-->   X
    10. 0:
    11.       O      X
    12. -0.5:
    13.    <--O      X
    14. -1:
    15. <-----O      X
    16. -0.5:
    17.    <--O      X
    18. 0:
    19.       O      X
    20. 0.5:
    21.       O-->   X
    22. 1:
    23.       O----->X
    24. 0.5:
    25.       O-->   X
    26. 0:
    27.       O      X
    28. -0.5:
    29.    <--O      X
    30. -1:
    31. <-----O      X
    32. -0.5:
    33.    <--O      X
    34. 0:
    35.       O      X
    36.  
    So you're oscilating from start, towards end, and back to start in 'lerpFull' seconds. Then for the next lerpFull seconds you're moving away from both start and end, to a point that is just as far from start as end is, just in the opposite direction.
     
    bowserscastle and tytbone like this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Technically yes.

    But how you have your code here is not how you'd go about doing it.

    Furthermore, trig methods might not actually be the best method of going about doing what you want. You could just apply a forward speed, and a jump force, and allow some simulated gravity to take care of everything. You'd essentially end up with a curve that could be described with trig... but didn't require trig to create. Sort of how bouncing a ball in the real world works... or you hopping like a bunny rabbit. You don't actually do trig in your head to do it... you just determine a force, and the curve falls out as a result.
     
    tytbone likes this.
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This actually might be a good time to use my trender to visualise the math as it's happening. Or it might not, it's not overly battle tested, but there is a link in my signature.

    Thinking of the sin curve as projecting a circle onto a flat plane certainly is a valid way to conceptualise it.

    Now to the actual parameters. A sin equation is typically expressed like this.

    y = amplitude * sin (frequency * angle + phase) + offset

    Amplitude represents the height of the wave, and is equivalent to the radius of the circle.

    Frequency relates to the time between peaks, and is equivalent to the speed you move around the circle

    Phase relates to your starting point.

    Offset basically sets the zero value. In other words the centre of your circle.
     
    tytbone likes this.
  5. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
    Thanks @lordofduct , some great info there; I still haven't put all the pieces of the puzzles together mentally, but it definitely helped and I've bookmarked this page for future reference. :) @BoredMormon , can you expand on how I might use the sin equation you posted above, a game example? Is the example above meant to move a gameobject in an actual sin wave or semi-circular motion, or in a way outside of a standard Lerp? It's late so I won't test it right now but I'm wondering if it could be used something like, maybe,
    Code (csharp):
    1. transform.position.y = 2f * Mathf.Sin(addedTime/CompletedTime * Mathf.PI + 0) + 2f;
    Thanks again guys.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    A sin curve is often used to make enemies move in a wave pattern. Its especially common in space invaders type games.

    The code you have posted will make the object bounce up and down between 0 and 4.

    Its also worth noting that most games don't use a sin curve for jumping. Jumping and gravity in the real world can be better represented by a parabolic curve.
     
    tytbone likes this.
  7. tytbone

    tytbone

    Joined:
    Jul 16, 2013
    Posts:
    18
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You are assigning it to y, so on the y axis.
     
    tytbone likes this.