0
\$\begingroup\$
private void Update()
{
  targetAngles = transform.eulerAngles + 180f * Vector3.up;
  transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, targetAngles, 3f * Time.deltaTime);
}

I guess because it's rotating by 180 degrees all the time.

How can I make the player rotate by 180 degrees only once, without using a coroutine?

Before that, I used in the Update with a coroutine and a flag to start the coroutine only once and it did rotate fine, but with stuttering. I guess the coroutine didn't sync good enough with the player movement.

While the player is rotating I also make the player start moving.

This is the coroutine I used before but then when the player starts moving and rotating he was stuttering.

public IEnumerator DoRotate(float time)
    {
        var startRotation = transform.rotation;
        var newRotation = Quaternion.AngleAxis(180, Vector3.up) * transform.rotation;
        var progress = 0f;

        while (progress < 1)
        {
            progress += Time.deltaTime / time;
            transform.rotation = Quaternion.Slerp(startRotation, newRotation, progress);

            yield return null;
        }

        transform.rotation = newRotation;
    }

This is the full script the rotating part is at lines 56-57.

At the bottom is the coroutine I used to make the rotation, but because of the stuttering I want to try to do it without coroutine to see if it will fix the stuttering problem.

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.Characters.ThirdPerson;
using Cinemachine;

public class DistanceCheck : MonoBehaviour
{
    public Transform objectToRotatetowards;
    public float slowDownLerpDuration;
    public float speedUpLerpDuration;
    public float rotationSpeed;
    public GameObject descriptionTextImage;
    public TextMeshProUGUI text;
    public ThirdPersonUserControl thirdPersonUserControl;

    private Animator anim;
    private float timeElapsed = 0;
    private float startValue = 1;
    private float endValue = 0;
    private float valueToLerp = 0;
    private bool startRotating = false;
    private bool slowOnBack = true;
    private bool exited = false;
    private Vector3 exitPosition;
    private float distance;
    private bool rotateTowards = false;
    private bool rotateBackwards = false;
    private bool rotateTowardsObject = false;
    private Vector3 targetAngles;


    void Start()
    {
        anim = transform.GetComponent<Animator>();
    }

    private void FixedUpdate()
    {
        if (startRotating)
        {
            if (rotateTowards)
            {
                if (rotateTowardsObject)
                {
                    Vector3 targetDirection = objectToRotatetowards.position - transform.position;
                    float singleStep = 3f * Time.deltaTime;
                    Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f);
                    transform.rotation = Quaternion.LookRotation(newDirection);
                }

                if (rotateBackwards)
                {
                    targetAngles = transform.eulerAngles + 180f * Vector3.up;
                    transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, targetAngles, 3f * Time.deltaTime);
                    //StartCoroutine(DoRotate(2f));
                    //rotateBackwards = false;
                }
            }
        }

        if (exitPosition != new Vector3(0, 0, 0) && slowOnBack)
        {
            distance = Vector3.Distance(transform.position, exitPosition);
        }

        if (distance > 5 && slowOnBack)
        {
            slowOnBack = false;
            StartCoroutine(SlowDown());
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "NoExit")
        {
            rotateTowardsObject = true;
            rotateBackwards = false;
            descriptionTextImage.SetActive(true);
            if (other.TryGetComponent(out ColliderInfo info))
            {
                text.text = info.onExitText;
                rotateTowards = info.rotateTowards;
            }
            RepositionPlayer();
        }
        else if (other.tag == "NoEntry")
        {
            OnPlayerRepositioned();
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "NoExit")
        {
            OnPlayerRepositioned();
        }
        else if (other.tag == "NoEntry")
        {
            rotateTowardsObject = false;
            rotateBackwards = true;
            descriptionTextImage.SetActive(true);
            if (other.TryGetComponent(out ColliderInfo info))
            {
                text.text = info.onEnterText;
                rotateTowards = info.rotateTowards;
            }
            RepositionPlayer();
        }
    }

    private void RepositionPlayer()
    {
        exited = true;
        slowOnBack = true;
        exitPosition = transform.position;
        thirdPersonUserControl.enabled = false;
        StartCoroutine(SlowDown());
    }

    private void OnPlayerRepositioned()
    {
        exited = false;
        startRotating = false;
        text.text = "";
        descriptionTextImage.SetActive(false);
    }

    IEnumerator SlowDown()
    {
        timeElapsed = 0;

        while (timeElapsed < slowDownLerpDuration)
        {
            valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / slowDownLerpDuration);
            anim.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;

            yield return null;
        }

        if (exited)
        {
            yield return new WaitForSeconds(1.5f);

            startRotating = true;
            StartCoroutine(SpeedUp());
        }

        if (slowOnBack == false)
        {
            thirdPersonUserControl.enabled = true;
        }
    }

    IEnumerator SpeedUp()
    {
        timeElapsed = 0;

        while (timeElapsed < speedUpLerpDuration)
        {
            valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / speedUpLerpDuration);
            anim.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;

            yield return null;
        }
    }

    public IEnumerator DoRotate(float time)
    {
        var startRotation = transform.rotation;
        var newRotation = Quaternion.AngleAxis(180, Vector3.up) * transform.rotation;
        var progress = 0f;

        while (progress < 1)
        {
            progress += Time.deltaTime / time;
            transform.rotation = Quaternion.Slerp(startRotation, newRotation, progress);

            yield return null;
        }

        transform.rotation = newRotation;
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ So what do you expect when you want to do a 180 rotation that takes some time and the player decides in the mean time to rotate by himself against your current rotation? Or switching directions, whatever is affecting the current rotation \$\endgroup\$
    – Zibelas
    Jun 12 at 22:44
  • \$\begingroup\$ @Zibelas switch directions. but i'm not sure this is the problem thatcause the stuttering. here is a very short video clip i recorded of the editor showing the stuttering starting from 1:20 youtube.com/watch?v=-WjWebb_6kY \$\endgroup\$
    – Sharo SA
    Jun 13 at 1:45
  • \$\begingroup\$ No, the coroutine does not cause stutter. That's a bug in your code, not anything fundamental to coroutines. Likely that you have multiple pieces of code or coroutine instances all trying to set the rotation at once. \$\endgroup\$
    – DMGregory
    Jun 13 at 11:22

1 Answer 1

1
\$\begingroup\$

My usual solution to this problem is:

  1. Introduce a new variable Quaternion desiredRotation which represents the direction where the object should look but might or might not be looking right now because it's still in the progress of turning around.

  2. When I give the object the command to turn, I set the desiredRotation to where it is supposed to look, not its actual rotation. (You seem to have a variable targetAngle which seems to do the same thing, but you are setting its value in Update. So you are changing the value again and again, which is probably not what you want. I would usually do that once in response to Input.GetKeyDown or whatever other situation triggers the rotation).

  3. In FixedUpdate I use Quaternion.RotateTowards. This methods returns a quaternion which represents a rotation from from into the direction of to, but limited to an angular change of maxDegreeDelta. So you can use it for things like this:

    transform.rotation = Quaternion.RotateTowards(transform.rotation, desiredRotation, turnSpeed * Time.deltaTime);
    
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .