Search Unity

Move and rotate an object along a quarter curve

Discussion in 'Scripting' started by studiostartunity, May 23, 2019.

  1. studiostartunity

    studiostartunity

    Joined:
    May 3, 2019
    Posts:
    20
    Hello,
    I'm trying to solve this problem, but I'm having a lot of difficulties. I prepared this image to make my question clearer:



    I have an object that goes straight (it simply moves on X,Z axis) towards the A point. At the moment, I'm able to know when the object position is in A. What I want is that the object continues to move and roate (90 degrees) towards the point B (I know the B coordinates). From the point B the object has to continue to go straight.

    I tryed with this code:
    Code (CSharp):
    1. // targetWayPoint is the B position.
    2. transform.forward = Vector3.RotateTowards(transform.forward, targetWayPoint - transform.position, speed * Time.deltaTime, 0.0f);
    3. transform.position = Vector3.MoveTowards(transform.position, targetWayPoint, speed * Time.deltaTime);
    4.  
    5. if (transform.position == targetWayPoint) // go simply straight
    The problem is that the object doesn't rotate by 90°, so basically it goes diagonally.

    Can anyone help me?
    Thanx!
     
  2. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    505
    Simply rotate the object continually through 90 degrees while continuing to move forward. Use Lerp for this. If you are plotting on the xz plane, rotate around y, if you are plotting on the xy plane, rotate around z.
     
  3. studiostartunity

    studiostartunity

    Joined:
    May 3, 2019
    Posts:
    20
    Thanx for you fast reply. Can I ask you a code example?

    I tryed with this:
    Code (CSharp):
    1. transform.rotation = Quaternion.Euler(0f, Mathf.Lerp(transform.rotation.eulerAngles.y, 0, Time.deltaTime * speed), 0f);
    The object rotate, but the effect is weird.
    1) The movement is in diagonal and not in a curve
    2) I have to adjust the speed during rotation or the object reach the B point before the 90° rotation.
     
    Last edited: May 23, 2019
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    4,626
    Try slerp instead lerp.
    But wont guarantee it will work.

    Otherwise, you may want use some trigonometry calcs.
     
  5. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    505
    Here's the general idea: when you want to initiate the turn, you pre-calculate once start- and end rotation values, then run a countdown (meaning the turn time is pre-determined) and lerp the rotation between start end end rotation values.

    I've written, from the top of my head, a small bit of sample code that should work in principle:

    Code (CSharp):
    1. public float turnTime = 2f; // two seconds for 90 degrees
    2. public float turnAmount = 90f; // 90 degree turn
    3. private float countdown = 0;
    4. private Quaternion startRotation;
    5. private Quaternion endRotation;
    6. private bool turning = false;
    7.  
    8. public void startTurn(){
    9.    turning = true;
    10.    vector3 currentHeading = transform.forward;
    11.    vector3 newHeading = currentHeading * Quaternion(turnAmount, transform.up);
    12.    startRotation = transform.rotation; // this is where we start
    13.    endRotation = Quaternion.LookRotation(newHeading, transform.up);
    14.    countDown = turnTime;
    15. }
    16.  
    17.     If (startTurning) startTurn(); // initiate turning;
    18.  
    19. void Update(){
    20.    …
    21.  
    22.    if (turning) {
    23.     countdown -= Time.deltaTime;
    24.     float percent = 1-countDown/turnTime;
    25.     transform.rotation = Quaternion.Lerp(startRotation, endRotation, percent)
    26.     if (countdown < 0) {
    27.         turning = false;
    28.         transform.rotation = endRotation;
    29. }
    30.    }
    31.   // now do the move here
    32.  
    33. }
    34.  
    During update, the turning part is only executed when the 'turning' bool is set, and the code requires that you only ever execute startTurn once per turn, when you determine it is time to turn.

    This code assumes you are lookg down on the xz axis, with the y axis being the one poking in your eye, and being the one the object turn around.
     
  6. studiostartunity

    studiostartunity

    Joined:
    May 3, 2019
    Posts:
    20
    Thanx for all your replies.
    I found a solution for my specific need. I share the code I'm working on, so it's just a prototype. Obviously, it's just a solution, not "the" solution and it's designed to work in specific conditions and for specific purposes.

    Basically, the idea is to calculate a predefined number of points (with X and Z position) along this imaginary "quarter curve" simply using sin and cosin methods and then use these points to calculate the points to which the object has to move and rotate.

    All calculations are done in the Start() function:
    Code (CSharp):
    1.     void Start()
    2.     {
    3.         index = 1;
    4.         float density = 2;
    5.         float percX = 0;
    6.         float percZ = 0;
    7.  
    8.         float offsetX = 0;
    9.         float offsetZ = 0;
    10.  
    11.         float newX = 0;
    12.         float newZ = 0;
    13.  
    14.         startX = PLAYER.transform.position.x;
    15.         startZ = PLAYER.transform.position.z;
    16.         distX = Mathf.Abs(UP.transform.position.x - PLAYER.transform.position.x);
    17.         distZ = Mathf.Abs(UP.transform.position.z - PLAYER.transform.position.z);
    18.  
    19.         curve.Add(new Vector3(startX, 0, startZ));
    20.         for (float angle = 1; angle < 90; angle += density)
    21.         {
    22.             percX = Mathf.Sin(Mathf.Deg2Rad * angle);
    23.             percZ = 1 - Mathf.Cos(Mathf.Deg2Rad * angle);
    24.  
    25.             offsetX = distX * percX;
    26.             offsetZ = distZ * percZ;
    27.  
    28.             newX = startX + offsetX;
    29.             newZ = startZ + offsetZ;
    30.  
    31.             curve.Add(new Vector3(newX, 0, newZ));        
    32.         }
    33.         curve.Add(new Vector3(UP.transform.position.x, 0, UP.transform.position.z));
    34.  
    35.     }
    Where PLAYER is the object to move and roate, UP is the final point to reach, curve is the global array of the points that describes the curve. With the density variable I can define how "good" this curve has to be.
    Now, in the Update() function I have just to move and rotate PLAYER towards the precalculated points:
    Code (CSharp):
    1.     void Update()
    2.     {
    3.         if (index < curve.Count)
    4.         {
    5.             PLAYER.transform.position = Vector3.MoveTowards(PLAYER.transform.position, curve[index], speed * Time.deltaTime);
    6.  
    7.             Vector3 targetDir = curve[index] - PLAYER.transform.position;
    8.             Vector3 newDir = Vector3.RotateTowards(PLAYER.transform.forward, targetDir, speed * Time.deltaTime * 4, 0.0f);
    9.             PLAYER.transform.rotation = Quaternion.LookRotation(newDir);
    10.  
    11.             if (PLAYER.transform.position == curve[index]) index++;
    12.         }
    13.     }
    This is just a prototype and it works with the PLAYER located in the A point, rotated to that direction, with the aim of reaching the B point (see my first post image).
    The quality of the movement basically depends on density, speed, size of the path, etc. All parameters that can be easily adjusted.
     
    Antypodish likes this.
  7. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    4,626
    Well done, for applying trigonometry :)