0
\$\begingroup\$

I'm creating a small endless runner where the player has to swipe up and down on the screen to move a fish. Imagine Subway Surfers where you travel smoothly as opposed to just jumping a foot to the left or right. Anyway, I am trying to make the fish rotate in the approximate direction of the player's finger and gradually rotate back when the player lets go of the screen. The program was working almost smoothly -- except for the fact that the fish was rotating back to the default position constantly, so it looked really glitchy.

The way this script works is that it takes in two Vector2 variables - the current and previous positions of the fish. It then subtracts these to get a Vector2 denoting the change in position since the last frame. I then feed it into Mathf.Atan to get the direction of this motion. Finally, I rotate the fish by this new amount - minus its current rotation - to get it going in the right direction. I'll show the code down below. Upon investigating why this was causing the fish to consistently rotate back to 0 degrees for a couple frames, over and over, I learned that the Unity thought that the change in y position was the thing switching to zero every other frame. All of my code is operating on FixedUpdate and I don't use Update anywhere, so I have no idea why it is switching so erratically. If user touch input is measured during Update, that might be the source of the problem, but it would be nice if I could find a solution. I'm just kind of confused, and would love if someone could clear this up for me as soon as possible.

Now here's my code for the class "RotateByTouch":

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateByTouch : MonoBehaviour
{
    public TrackObjectMotion trackObjectMotion;
    public GameObject thisObject;

    public float rotationValue;

    void FixedUpdate()
    {

        rotationValue = Mathf.Atan(trackObjectMotion.DeltaY / trackObjectMotion.DeltaX) * Mathf.Rad2Deg;

        thisObject.transform.Rotate(Vector3.forward, rotationValue - thisObject.transform.rotation.eulerAngles.z);

    }


}

And here's my code for the class "TrackObjectMotion":

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TrackObjectMotion : MonoBehaviour
{

    public Vector3 CurrentPosition;
    public Vector3 PreviousPosition;

    public float DeltaX;
    public float DeltaY;

    // Update is called once per frame
    void FixedUpdate()
    {
        PreviousPosition = CurrentPosition;
        CurrentPosition = this.gameObject.transform.position;

        DeltaX = CurrentPosition.x - PreviousPosition.x;
        DeltaY = CurrentPosition.y - PreviousPosition.y;
    }
}

And finally, here's the code I used for the actual moving of the fish:

public void FixedUpdate(){
    foreach(Touch touch in Input.touches){
        if(touch.Phase == TouchPhase.Moved){
            this.transform.Translate(0, touch.deltaPosition.y, 0);
        }
    }
}

That last bit is just leaving out a little bit about multiplying by constants to get the desired result, but that's not important.

Thanks in advance!

\$\endgroup\$
  • \$\begingroup\$ Unity runs fix update 50 times per second as default which is adjustable in the Time Settings. As you said all your code logic loop runs in FixedUpdate, may I know the reason? are you trying to move, rotate a rigid body object? \$\endgroup\$ – Emad Apr 28 at 14:34
  • \$\begingroup\$ I set FixedUpdate to run 60 times a second in the Time Settings. I had to convert everything to FixedUpdate because I wanted to make everything run smoothly and cleanly, as opposed to using the Update method. \$\endgroup\$ – Retro Vector Dev Team Apr 28 at 14:37
2
\$\begingroup\$

Whenever you have an actual target you want to eventually rotate towards, my suggestion would be to use either transform.LookAt(Transform target, Vector3 worldUp = Vector3.up) or Quaternion.LookRotation(Vector3 forward, Vector3 upwards = Vector3.up).

Meanwhile if you wouldn't know the exact rotation angle that you want your object to be rotated towards, just the angular speed (like if you'd move your characters by arrows, or D-pad), then I suggest you'd use transform.Rotate.

Since in your case you want the rotation to happen overtime, Quaternion.LookRotation would be the choice to get the desired final direction, and Quaternion.Lerp(currentAngle, targetAngle, step/totalsteps) to set the current rotation. Here OnTouch you'd set Quaternion targetAngle = Quaternion.LookRotation(touch.position, Vector3.Up), then Lerp towards that rotation via some speed.

In your actual code, I see that you use delta position for setting up the rotation. Remember, that you calculate your delta as previous vs current frame, therefore, right when the finger stops moving, or moves slower than frames register the delta, it becomes zero.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.