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. Dismiss Notice

Making an object move in a figure 8 programatically?

Discussion in 'Scripting' started by techmage, Dec 31, 2009.

  1. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    I can already think of a number of ways to do this, but would just like to get an opinion on this from someone more experienced before I delve too far into a certain method of accomplishing this.

    So option 1 is, do this entirely programmatically, bust out some code for bezier curves, iterate along the curve and whatnot.

    Option 2, use the animationCurve class. Is this what animationcurve class is meant to be used for? Moving single objects around programatically, or is it a character animation thing?

    I'd go through and make a new animation curve like so:
    var curve = new AnimationCurve(Keyframe(0, 0), Keyframe(1, 10))

    One for X and on for Y to have it go in a figure 8?

    The reason I want to do this programatically is because there is a bunch of objects that are going to follow this pattern, and each one is going to need to be on a slightly different figure 8 pathway. So it seems programatically would be the most proper.

    Also, when I use the command AnimationCurve(Keyframe(0, 0), Keyframe(1, 10))
    it seems to be adding the keys to curve with flat tangents, how do I make the tangents be set to 'auto'?

    Option 3 is, manually make a number of different animation curves, then randomly select which one to play.
     
  2. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    Just toyed with this idea. I just used trigonometry to get the effect:

    Code (csharp):
    1.  
    2. var m_Speed : float = 1;
    3. var m_XScale : float = 1;
    4. var m_YScale : float = 1;
    5.  
    6. private var m_Pivot : Vector3;
    7. private var m_PivotOffset : Vector3;
    8. private var m_Phase : float;
    9. private var m_Invert : boolean = false;
    10. private var m_2PI : float = Mathf.PI * 2;
    11.  
    12. function Start() {
    13.     m_Pivot = transform.position;
    14. }
    15.  
    16. function Update () {
    17.     m_PivotOffset = Vector3.up * 2 * m_YScale;
    18.    
    19.     m_Phase += m_Speed * Time.deltaTime;
    20.     if(m_Phase > m_2PI)
    21.     {
    22.         m_Invert = !m_Invert;
    23.         m_Phase -= m_2PI;
    24.     }
    25.     if(m_Phase < 0) m_Phase += m_2PI;
    26.    
    27.     transform.position = m_Pivot + (m_Invert ? m_PivotOffset : Vector3.zero);
    28.     transform.position.x += Mathf.Sin(m_Phase) * m_XScale;
    29.     transform.position.y += Mathf.Cos(m_Phase) * (m_Invert ? -1 : 1) * m_YScale;
    30. }
    31.  
    Basically, this uses Sin and Cos to move in a circle. Each time a full circle is reached (phase reaches 2PI) the direction flips and the circle movement is reversed. While this happens you also move the pivot point so the movement continues where it left off in the previous cycle.

    Speed is already controllable (though not for negative values), and you could add code to stretch the motion on either axis by just scaling the values you get from the Sin and Cos functions.

    Edit: Just built in the scaling.
     
  3. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    Awesome. Thank you very much.
     
  4. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    I modified your code to include a viewport gizmo for easier placement, a checkbox to make it go in figure 8 or a circle, and also I added offset parameters with 'link offsetscalex' booleans, so then you can justify the objects movement to the top,bottom,left of right of it's origin

    Code (csharp):
    1. var m_Speed : float = 1;
    2. var m_XScale : float = 1;
    3. var m_YScale : float = 1;
    4. var offsetX : float = 0;
    5. var offsetY : float = 0;
    6. var linkOffsetScalePositiveX : boolean = false;
    7. var linkOffsetScaleNegativeX : boolean = false;
    8. var linkOffsetScalePositiveY : boolean = false;
    9. var linkOffsetScaleNegativeY : boolean = false;
    10. var figure8 : boolean = false;
    11.  
    12. private var m_Phase : float;
    13. private var m_Pivot : Vector3;
    14. private var m_PivotOffset : Vector3;
    15. private var m_Invert : boolean = false;
    16. private var m_2PI : float = Mathf.PI * 2;
    17. private var originalPosition : Vector3;
    18. private var running : boolean = false;
    19.  
    20. function Start() {
    21.    m_Pivot = transform.position;
    22.    originalPosition = transform.position;
    23.    running = true;
    24.      
    25.     if (linkOffsetScalePositiveX)
    26.         m_Phase = 3.14 / 2 + 3.14;
    27.     else if (linkOffsetScaleNegativeX)
    28.         m_Phase = 3.14 / 2;
    29.     else if (linkOffsetScalePositiveY)
    30.         m_Phase = 3.14;
    31.     else
    32.         m_Phase = 0;  
    33. }
    34.  
    35. function Update () {
    36.    m_PivotOffset = Vector3.up * 2 * m_YScale;
    37.    
    38.    m_Phase += m_Speed * Time.deltaTime;
    39.    
    40.     if (figure8)
    41.     {
    42.         if(m_Phase > m_2PI)
    43.         {
    44.             m_Invert = !m_Invert;
    45.             m_Phase -= m_2PI;
    46.         }
    47.         if(m_Phase < 0) m_Phase += m_2PI;
    48.    }
    49.    
    50.    transform.position = m_Pivot + (m_Invert ? m_PivotOffset : Vector3.zero);
    51.    transform.position.x += Mathf.Sin(m_Phase) * m_XScale + offsetX;
    52.    transform.position.y += Mathf.Cos(m_Phase) * (m_Invert ? -1 : 1) * m_YScale + offsetY;
    53. }
    54.  
    55. function OnDrawGizmos()
    56. {
    57.     if (linkOffsetScalePositiveX)
    58.         offsetX = m_XScale;
    59.     else if (linkOffsetScaleNegativeX)
    60.         offsetX = m_XScale * -1;
    61.     else
    62.         offsetX = 0;
    63.        
    64.     if (linkOffsetScalePositiveY)
    65.         offsetY = m_YScale;
    66.     else if (linkOffsetScaleNegativeY)
    67.         offsetY = m_YScale * -1;   
    68.     else
    69.         offsetY = 0;
    70.        
    71.     if (running)
    72.     {
    73.         Gizmos.DrawLine(Vector3(originalPosition.x + offsetX, originalPosition.y + m_YScale + offsetY, originalPosition.z), Vector3(originalPosition.x + offsetX, originalPosition.y + offsetY, originalPosition.z));
    74.         Gizmos.DrawLine(Vector3(originalPosition.x + offsetX, originalPosition.y - m_YScale + offsetY, originalPosition.z), Vector3(originalPosition.x + offsetX, originalPosition.y + offsetY, originalPosition.z));
    75.         Gizmos.DrawLine(Vector3(originalPosition.x + m_XScale + offsetX, originalPosition.y + offsetY, originalPosition.z), Vector3(originalPosition.x + offsetX, originalPosition.y + offsetY, originalPosition.z));
    76.         Gizmos.DrawLine(Vector3(originalPosition.x - m_XScale + offsetX, originalPosition.y + offsetY, originalPosition.z), Vector3(originalPosition.x + offsetX, originalPosition.y + offsetY, originalPosition.z));
    77.     }
    78.     else
    79.     {
    80.         Gizmos.DrawLine(Vector3(transform.position.x + offsetX, transform.position.y + m_YScale + offsetY, transform.position.z), Vector3(transform.position.x + offsetX, transform.position.y + offsetY, transform.position.z));
    81.         Gizmos.DrawLine(Vector3(transform.position.x + offsetX, transform.position.y - m_YScale + offsetY, transform.position.z), Vector3(transform.position.x + offsetX, transform.position.y + offsetY, transform.position.z));
    82.         Gizmos.DrawLine(Vector3(transform.position.x + m_XScale + offsetX, transform.position.y + offsetY, transform.position.z), Vector3(transform.position.x + offsetX, transform.position.y + offsetY, transform.position.z));
    83.         Gizmos.DrawLine(Vector3(transform.position.x - m_XScale + offsetX, transform.position.y + offsetY, transform.position.z), Vector3(transform.position.x + offsetX, transform.position.y + offsetY, transform.position.z));  
    84.     }
    85. }
    86.  
     
  5. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    Ah! Some good additions there. 8)

    Quick note: The way you implemented the offset checkboxes means that once you turn them on the effect is permanent, you can't turn them back off. That's fine if you don't need to change them later on, but it doesn't make the code very reusable.
     
  6. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    oops

    fixed the code

    I also made it so that your object starts it's cycle from the side you justified it and doesn't always jump to the top
     
  7. ColossalDuck

    ColossalDuck

    Joined:
    Jun 6, 2009
    Posts:
    3,246
    That an interesting way to build scripting skill. Ask a question build upon the answer, submit and fix the mistakes told to you.
     
  8. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    Yeah, it seems that approach works well. Playing around with a given solution really helps you understand how things work and see what you can do with them. :)
     
  9. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Thanks, this code rocks, and the addition of the circle made the code I spent a few hours writing earlier look really bad :) Now I use it for both flight patterns!
     
  10. CodeBear

    CodeBear

    Joined:
    Jan 17, 2016
    Posts:
    4
    Here's the code translated to C#. Also, I needed the 8 on the "ground", so the axis is different. Note: if you want to make it vertical (or use another axis), make sure you adjust the axis in the pivotalOffset calculation at the start of update.

    One of my upcoming goals is to make it so the figure 8 "points" in whatever direction I want it to. If you, the reader, end up doing that, please let me know.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Figure8Movement : MonoBehaviour
    6. {
    7.     public float speed = 2;
    8.     public float scaleX = 2;
    9.     public float scaleZ = 3;
    10.     public float offsetX = 0;
    11.     public float offsetZ = 0;
    12.  
    13.     public bool isLinkOffsetScalePositiveX = false;
    14.     public bool isLinkOffsetScaleNegativeX = false;
    15.     public bool isLinkOffsetScalePositiveZ = false;
    16.     public bool isLinkOffsetScaleNegativeZ = false;
    17.     public bool isFigure8 = true;
    18.  
    19.     private float phase;
    20.     private float m_2PI = Mathf.PI * 2;
    21.     private Vector3 originalPosition;
    22.     private Vector3 pivot;
    23.     private Vector3 pivotOffset;
    24.     private bool isInverted = false;
    25.     private bool isRunning = false;
    26.  
    27.  
    28.     void Start ()
    29.     {
    30.         pivot = transform.position;
    31.         originalPosition = transform.position;
    32.         isRunning = true;
    33.  
    34.         if (isLinkOffsetScalePositiveX)
    35.             phase = 3.14f / 2f + 3.14f;
    36.         else if (isLinkOffsetScaleNegativeX)
    37.             phase = 3.14f / 2f;
    38.         else if (isLinkOffsetScalePositiveZ)
    39.             phase = 3.14f;
    40.         else
    41.             phase = 0;
    42.     }
    43.  
    44.     void Update ()
    45.     {
    46.         pivotOffset = Vector3.forward * 2 * scaleZ;
    47.  
    48.         phase += speed * Time.deltaTime;
    49.  
    50.         if (isFigure8) {
    51.             if (phase > m_2PI) {
    52.                 Debug.Log ("phase " + phase + " over 2pi: " + isInverted);
    53.                 isInverted = !isInverted;
    54.                 phase -= m_2PI;
    55.             }
    56.             if (phase < 0) {
    57.                 Debug.Log ("phase " + phase + " under 0");
    58.                 phase += m_2PI;
    59.             }
    60.         }
    61.  
    62.         Vector3 nextPosition = pivot + (isInverted ? pivotOffset : Vector3.zero);
    63.         transform.position = new Vector3 (nextPosition.x + Mathf.Sin (phase) * scaleX + offsetX, nextPosition.y, nextPosition.z + Mathf.Cos (phase) * (isInverted ? -1 : 1) * scaleZ + offsetZ);
    64.     }
    65.  
    66.     void OnDrawGizmos ()
    67.     {
    68.         if (isLinkOffsetScalePositiveX)
    69.             offsetX = scaleX;
    70.         else if (isLinkOffsetScaleNegativeX)
    71.             offsetX = scaleX * -1;
    72.         else
    73.             offsetX = 0;
    74.  
    75.         if (isLinkOffsetScalePositiveZ)
    76.             offsetZ = scaleZ;
    77.         else if (isLinkOffsetScaleNegativeZ)
    78.             offsetZ = scaleZ * -1;
    79.         else
    80.             offsetZ = 0;
    81.  
    82.         if (isRunning) {
    83.             Gizmos.DrawLine (new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z + scaleZ + offsetZ), new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z + offsetZ));
    84.             Gizmos.DrawLine (new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z - scaleZ + offsetZ), new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z + offsetZ));
    85.             Gizmos.DrawLine (new Vector3 (originalPosition.x + scaleX + offsetX, originalPosition.z + offsetZ, originalPosition.y), new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z + offsetZ));
    86.             Gizmos.DrawLine (new Vector3 (originalPosition.x - scaleX + offsetX, originalPosition.z + offsetZ, originalPosition.y), new Vector3 (originalPosition.x + offsetX, originalPosition.y, originalPosition.z + offsetZ));
    87.         } else {
    88.             Gizmos.DrawLine (new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z + scaleZ + offsetZ), new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z + offsetZ));
    89.             Gizmos.DrawLine (new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z - scaleZ + offsetZ), new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z + offsetZ));
    90.             Gizmos.DrawLine (new Vector3 (transform.position.x + scaleX + offsetX, transform.position.y, transform.position.z + offsetZ), new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z + offsetZ));
    91.             Gizmos.DrawLine (new Vector3 (transform.position.x - scaleX + offsetX, transform.position.y, transform.position.z + offsetZ), new Vector3 (transform.position.x + offsetX, transform.position.y, transform.position.z + offsetZ));  
    92.         }
    93.     }
    94. }
    95.  
     
  11. OlliTuu

    OlliTuu

    Joined:
    Nov 18, 2014
    Posts:
    4
    I like it simpler

    private Vector3 startPos;

    public float speed = 1;
    public float xScale = 1;
    public float yScale = 1;

    void Start () {
    startPos = transform.position;
    }

    void Update () {
    transform.position = startPos + (Vector3.right * Mathf.Sin(Time.timeSinceLevelLoad/2*speed)*xScale - Vector3.up * Mathf.Sin(Time.timeSinceLevelLoad * speed)*yScale);
    }
     
  12. CSGamesDev

    CSGamesDev

    Joined:
    Aug 9, 2016
    Posts:
    6
    Thanks a lot. This made my day.
     
  13. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    397
    I'm back here two years later just to say how much I appreciate your help! This was exactly what I was trying to do. Go forum go
     
  14. cloverme

    cloverme

    Joined:
    Apr 6, 2018
    Posts:
    177
    This is great... saved me a lot of work. Thanks, several years later in fact.

    Also, if anyone else needs to Figure 8 on the X & Z axis:

    Code (CSharp):
    1. public float zScale = 1;
    2.  
    3. transform.position = startPos + (Vector3.right * Mathf.Sin(Time.timeSinceLevelLoad/2*speed)*xScale - Vector3.forward* Mathf.Sin(Time.timeSinceLevelLoad * speed)*zScale);
     
  15. Rodlaiz

    Rodlaiz

    Joined:
    Jul 30, 2013
    Posts:
    38
    Thanks to all of you guys, you've saved me at least one hour of work today! ;)
     
  16. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
    Please don't necro old threads like this, it's much easier and preferred to just leave the user a Like for their work, instead of bringing the thread to the top of the forum with a new post.
     
    Rodlaiz likes this.