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

Maintain velocity around a circle at different distances from center

Discussion in 'Scripting' started by danield, Oct 4, 2016.

  1. danield

    danield

    Joined:
    Sep 30, 2014
    Posts:
    40
    I'm trying to create a movement system for a character controller that move closer or further from a center point and around the center point. The forward and backward movement seems fine, but the code I have for going around either goes too fast or too slow. One thing that presents a problem is that being further from the center means a greater distance to travel. If the speed is the same, then the length of time to travel must increase. Any help would be much appreciated.

    Code (CSharp):
    1.     public static Vector3 center = Vector3.zero;
    2.     public static float distanceA = 3f;
    3.     public static float distanceB = 6f;
    4.     public static float distanceC = 12f;
    5.  
    6.     private CharacterController playerCC;
    7.     private Transform playerRot;
    8.     private float targetDist;
    9.     private Vector3 targetPos;
    10.     private Vector3 targetAngle = Vector3.back;
    11.     public float fwdSpeed = 5.0f;
    12.     public float backSpeed = 2.5f;
    13.     public float sideSpeed = 2.0f;
    14.     public Vector2 Moving = Vector2.zero;
    15.  
    16.     void Start()
    17.     {
    18.         playerCC = GetComponent<CharacterController>();
    19.         playerRot = transform.GetChild(0);
    20.         targetDist = Vector3.Distance(transform.position, center);
    21.         if (!playerCC)
    22.             playerCC = gameObject.AddComponent<CharacterController>() as CharacterController;          
    23.     }
    24.  
    25.     void Update()
    26.     {
    27.         // Move closer or further from enemies
    28.         float curDist = Vector3.Distance(transform.position, center);
    29.         Vector3 curAngle = transform.position - center;
    30.         transform.rotation = Quaternion.LookRotation(-curAngle, Vector3.up);
    31.         if (targetDist < curDist && Moving.y > 0f)
    32.         {
    33.             transform.position = Vector3.MoveTowards(transform.position, targetPos, fwdSpeed * Time.deltaTime);
    34.         }
    35.         else if (targetDist > curDist && Moving.y < 0f)
    36.         {
    37.             transform.position = Vector3.MoveTowards(transform.position, targetPos, backSpeed * Time.deltaTime);
    38.         }
    39.         else if(Moving.y != 0f)
    40.         {
    41.             Moving.y = 0f;
    42.         }
    43.         // Move around enemies
    44.         if(Vector3.Angle(curAngle, targetAngle) >= 0.01f)
    45.         {
    46.             transform.position = Vector3.RotateTowards(curAngle, targetAngle, ((2f * Mathf.PI * curDist)/sideSpeed) * Time.deltaTime, 0f);
    47.             playerRot.localRotation = Quaternion.Euler(0f, 90f * Moving.x, 0f);
    48.         }
    49.         else if(Moving.x != 0f)
    50.         {
    51.             Moving.x = 0f;
    52.             playerRot.LookAt(center);
    53.         }
    54.     }
    55.  
    56.     public void MoveFwd()
    57.     {
    58.         if(Moving.sqrMagnitude == 0f)
    59.         {
    60.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceC))
    61.             {
    62.                 targetDist = distanceB;
    63.                 targetPos = Vector3.Normalize(transform.position - center) * targetDist;
    64.                 Moving.y = 1;
    65.             }
    66.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceB))
    67.             {
    68.                 targetDist = distanceA;
    69.                 targetPos = Vector3.Normalize(transform.position - center) * targetDist;
    70.                 Moving.y = 1;
    71.             }
    72.         }
    73.     }
    74.  
    75.     public void MoveBack()
    76.     {
    77.         if(Moving.sqrMagnitude == 0f)
    78.         {
    79.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceA))
    80.             {
    81.                 targetDist = distanceB;
    82.                 targetPos = Vector3.Normalize(transform.position - center) * targetDist;
    83.                 Moving.y = -1f;
    84.             }
    85.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceB))
    86.             {
    87.                 targetDist = distanceC;
    88.                 targetPos = Vector3.Normalize(transform.position - center) * targetDist;
    89.                 Moving.y = -1f;
    90.             }
    91.         }
    92.     }
    93.  
    94.     public void MoveSide(float angle)
    95.     {
    96.         if(Moving.sqrMagnitude == 0f)
    97.         {
    98.             if(angle > 0f)
    99.             {
    100.                 targetAngle = Quaternion.AngleAxis(-90f, Vector3.up) * targetAngle;
    101.                 Moving.x = 1f;
    102.             }
    103.             if(angle < 0f)
    104.             {
    105.                 targetAngle = Quaternion.AngleAxis(90f, Vector3.up) * targetAngle;
    106.                 Moving.x = -1f;
    107.             }
    108.         }
    109.     }
     
  2. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    So thinking of this, I believe you are over-stressing a little. What you need is a simple way to move something around in a circle given some x/y movement.

    I didn't test this, however, this is probably more what you are looking for. I did comment it out for you, so hopefully the process will make some sense.
    Code (csharp):
    1. // localize the transform to the center
    2. var p = transform.position - center;
    3. var y = p.y; // hold this, for later
    4. p.y = 0; // set the y to zero to prevent odd distances
    5.  
    6. // find the distance we are going to
    7. float r = p.Magnitude + Moving.y * Time.deltaTime;
    8.  
    9. // find the circumferance of the new radius
    10. var circ = 2 * Mathf.PI * r;
    11.  
    12. // find the amount we move per frame normalized by the circumferance
    13. var f = (Moving.x * Time.deltaTime) / circ;
    14.  
    15. // get the angular movement in radians
    16. var radians = (f / 360) * Mathf.Deg2Rad;
    17.  
    18. // rotate the vector
    19. var x = p.x * Mathf.Cos(radians) - p.z * Mathf.Sin(radians);
    20. var z = p.x * Mathf.Sin(radians) + p.z * Mathf.Cos(radians);
    21.  
    22. // reconstruct the vector
    23. var output = new Vector3(x, y, z) + center;
     
  3. danield

    danield

    Joined:
    Sep 30, 2014
    Posts:
    40
    I realized that the word "velocity" might be misleading. Should have said speed. :oops:
    Anyways, this might be on the right track. I'm not sure what values you are using for Moving. I'm guessing you're using the values set by MoveFwd(), MoveBack(), and MoveSide(). Also, I'm guessing the code is for Update()? I used vector2 to localize the transform to center, since I'm not concerned with the the y-axis at this time. If I understood your code correctly, I think the code might look something like this.
    Code (CSharp):
    1.     void Update()
    2.     {
    3.         // localized transform to center
    4.         Vector2 playerPos = new Vector2((transform.position.x - center.x), (transform.position.z - center.z));
    5.         float curDist = playerPos.magnitude;
    6.         // get distance we are going to
    7.         float moveDist = curDist + ((Moving.y * fwdSpeed) * Time.deltaTime);
    8.         // get amount moved per frame normalized by circumferance
    9.         float moveSide = ((Moving.x * sideSpeed) * Time.deltaTime)/(2f * Mathf.PI * moveDist);
    10.         // get angular movement in radians
    11.         float radians = (moveSide/360f) * Mathf.Deg2Rad;
    12.         // rotate the vector
    13.         float xMove = playerPos.x * Mathf.Cos(radians) - playerPos.y * Mathf.Sin(radians);
    14.         float zMove = playerPos.x * Mathf.Sin(radians) + playerPos.y * Mathf.Cos(radians);
    15.         // create new vector and move player there
    16.         Vector3 targetPos = new Vector3(xMove, transform.position.y, zMove) + center;
    17.         transform.position = targetPos;
    18.     }
    I tried setting moving to (1, 1) in the inspector, but nothing visible happened.

    Allow me to elaborate on the behavior I'm trying to create. The character moves to a fixed distance from the center; close range, medium range, far range (move to closer position/ move to farther position). The player can also move to a fixed position around the center; front, right side, back, left side. (move to next position on the player's right/ move to the next position on the player's left). I hope this extra bit of info helps.
     
  4. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Change this line....

    Code (csharp):
    1. float radians = (moveSide / 360f) * Mathf.Deg2Rad;
    2.  
    3. to this....
    4.  
    5. float radians = (moveSide * 360f) * Mathf.Deg2Rad;
    (didnt test the code before, sorry)
     
  5. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    scratch that, you are also missing the radius change.

    Code (csharp):
    1.     // Update is called once per frame
    2.     void Update () {
    3.         // localized transform to center
    4.         Vector2 playerPos = new Vector2((transform.position.x - center.x), (transform.position.z - center.z));
    5.         float curDist = playerPos.magnitude;
    6.         // get distance we are going to limit to a distance
    7.         float moveDist = Mathf.Max(curDist + ((Moving.y * fwdSpeed) * Time.deltaTime), 5);
    8.         // get amount moved per frame normalized by circumferance
    9.         float moveSide = ((Moving.x * sideSpeed) * Time.deltaTime) / (2f * Mathf.PI * moveDist);
    10.         // get angular movement in radians
    11.         float radians = (moveSide * 360f) * Mathf.Deg2Rad;
    12.         // rotate the vector
    13.         float xMove = playerPos.x * Mathf.Cos(radians) - playerPos.y * Mathf.Sin(radians);
    14.         float zMove = playerPos.x * Mathf.Sin(radians) + playerPos.y * Mathf.Cos(radians);
    15.         // create the vector we need
    16.         Vector3 targetPos = new Vector3(xMove, 0, zMove) + center;
    17.         // move the transform out or in
    18.         targetPos = targetPos.normalized * moveDist;
    19.         // now set the y vector
    20.         targetPos.y = transform.position.y;
    21.  
    22.         transform.position = targetPos;
    23.         transform.forward = Vector3.Cross(Vector3.up, transform.position);
    24.     }
     
  6. danield

    danield

    Joined:
    Sep 30, 2014
    Posts:
    40
    The code is working very well. Thank you for the help.
    I have run into a small problem. Now I'm not sure why this is, but, if I change the center to something other than (0, 0, 0) via outside script, then the character goes crazy. For example, I have a cube I'm using as an enemy placeholder. I have this enemy standing off to the side of the center. What is supposed to happen is when I press a button, RunTo("Cube Transform", 2f) is called and the enemy becomes the new center and the player runs from where they were, to the enemy until they are distance of 2. Instead, the player warps to the right and then moves in a weird way.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Char_Actions : MonoBehaviour {
    5.  
    6.     public static Vector3 center = Vector3.zero;
    7.     public static float distanceA = 4f;
    8.     public static float distanceB = 8f;
    9.     public static float distanceC = 12f;
    10.  
    11.     private CharacterController playerCC;
    12.     private Transform playerRot;
    13.  
    14.     private float targetDist;
    15.     private Vector2 targetDir;
    16.     private Transform targetEnemy = null;
    17.     public float fwdSpeed = 5.0f;
    18.     public float backSpeed = 2.5f;
    19.     public float sideSpeed = 2.0f;
    20.     [HideInInspector]
    21.     public Vector2 Moving = Vector2.zero;
    22.  
    23.     void Start()
    24.     {
    25.         playerCC = GetComponent<CharacterController>();
    26.         playerRot = transform.GetChild(0);
    27.         targetDist = Vector3.Distance(transform.position, center);
    28.         targetDir = new Vector2(0f, -1f);
    29.         if (!playerCC)
    30.             playerCC = gameObject.AddComponent<CharacterController>() as CharacterController;          
    31.     }
    32.  
    33.     void Update()
    34.     {
    35.         Vector3 targetPos = (targetEnemy) ? targetEnemy.position : center;
    36.         Vector2 playerPos = new Vector2((transform.position.x - targetPos.x), (transform.position.z - targetPos.z));
    37.         float curDist = playerPos.magnitude;
    38.         float moveDist = curDist;
    39.         float moveSide = 0f;
    40.         float radians = Mathf.Sign(Vector3.Cross(playerPos, targetDir).z) * Vector2.Angle(playerPos.normalized, targetDir) * Mathf.Deg2Rad;
    41.         // moving toward and away from point
    42.         if(Mathf.Approximately(curDist, targetDist) == false)
    43.         {
    44.             if(Moving.y > 0f)
    45.             {
    46.                 moveDist = Mathf.Max(curDist - ((Moving.y * fwdSpeed) * Time.deltaTime), targetDist);
    47.             }
    48.             if(Moving.y < 0f)
    49.             {
    50.                 moveDist = Mathf.Min(curDist - ((Moving.y * backSpeed) * Time.deltaTime), targetDist);
    51.             }
    52.         }
    53.         else if(Moving.y != 0f)
    54.         {
    55.             Moving.y = 0f;
    56.         }
    57.         // moving around point
    58.         if(Mathf.Approximately(Vector2.Angle(playerPos, targetDir), 0f) == false)
    59.         {
    60.             moveSide = ((Moving.x * sideSpeed) * Time.deltaTime) / (2f * Mathf.PI * moveDist);
    61.             if(Moving.x > 0f)
    62.             {
    63.                 radians = Mathf.Min((moveSide * 360f), Vector2.Angle(playerPos.normalized, targetDir)) * Mathf.Deg2Rad;
    64.                 playerRot.localRotation = Quaternion.Euler(0f, 90f, 0f);
    65.             }
    66.             if(Moving.x < 0f)
    67.             {
    68.                 radians = Mathf.Max((moveSide * 360f), -Vector2.Angle(playerPos.normalized, targetDir)) * Mathf.Deg2Rad;
    69.                 playerRot.localRotation = Quaternion.Euler(0f, -90f, 0f);
    70.             }
    71.         }
    72.         else if (Moving.x != 0f)
    73.         {
    74.             Moving.x = 0f;
    75.             playerRot.localRotation = Quaternion.Euler(Vector3.zero);
    76.         }
    77.         float xMove = playerPos.x * Mathf.Cos(radians) - playerPos.y * Mathf.Sin(radians);
    78.         float zMove = playerPos.x * Mathf.Sin(radians) + playerPos.y * Mathf.Cos(radians);
    79.         Vector3 movePos = new Vector3(xMove + targetPos.x, 0f, zMove + targetPos.z);
    80.         movePos = movePos.normalized * moveDist;
    81.         movePos.y = transform.position.y;
    82.         Debug.DrawLine(transform.position, movePos, Color.black, 30f, false);
    83.  
    84.         transform.position = movePos;
    85.         transform.LookAt(targetPos, Vector3.up);
    86.     }
    87.  
    88.     public void MoveFwd()
    89.     {
    90.         if(Moving.sqrMagnitude == 0f)
    91.         {
    92.             if(Vector3.Distance(transform.position, center) > distanceC)
    93.             {
    94.                 targetDist = distanceC;
    95.                 Moving.y = 1;
    96.             }
    97.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceC))
    98.             {
    99.                 targetDist = distanceB;
    100.                 Moving.y = 1f;
    101.             }
    102.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceB))
    103.             {
    104.                 targetDist = distanceA;
    105.                 Moving.y = 1f;
    106.             }
    107.         }
    108.     }
    109.  
    110.     public void MoveBack()
    111.     {
    112.         if(Moving.sqrMagnitude == 0f)
    113.         {
    114.             if(Vector3.Distance(transform.position, center) < distanceA)
    115.             {
    116.                 targetDist = distanceA;
    117.                 Moving.y = -1f;
    118.             }
    119.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceA))
    120.             {
    121.                 targetDist = distanceB;
    122.                 Moving.y = -1f;
    123.             }
    124.             if(Mathf.Approximately(Vector3.Distance(transform.position, center), distanceB))
    125.             {
    126.                 targetDist = distanceC;
    127.                 Moving.y = -1f;
    128.             }
    129.         }
    130.     }
    131.  
    132.     public void MoveSide(float direction)
    133.     {
    134.         if(Moving.sqrMagnitude == 0f)
    135.         {
    136.             if(direction != 0f)
    137.             {
    138.                 targetDir = Quaternion.Euler(0f, 0f, 90f * direction) * targetDir;
    139.                 Moving.x = direction;
    140.             }
    141.         }
    142.     }
    143.  
    144.     protected void RunTo(Transform target=null, float dist=4f)
    145.     {
    146.         Vector2 playerPos = new Vector2(transform.position.x - target.position.x, transform.position.z - target.position.z);
    147.         targetEnemy = target;
    148.         targetDist = dist;
    149.         Moving.y = 1f;
    150.     }
    151.  
    152.     protected void RunTo(Vector3 target = default(Vector3), float dist=4f)
    153.     {
    154.         if(targetEnemy)
    155.             targetEnemy = null;
    156.         center = target;
    157.         targetDist = dist;
    158.         Moving.y = 1f;
    159.     }
    160.  
    161. }
    Here is an image the may help illustrate the goal.
     

    Attached Files: