0
\$\begingroup\$

i am currently trying left & right, up & down swipe motion in unity C#.

I have done the scripting, it all working,

but after i debug the script,

the object's position suddenly changed to a very weird number:

from (-3, 0, 3) changed to (-3.000004, 0.00000464, 2.99999).

And i intend to move just 1 unit every time i swipe, but the unit seems different,

it always move around 1 but not exact 1 (Ex: maybe like 0.9954, or like 1.002931).

How i set the initial position to be exact integer number,

and also move only 1 unit every time i swipe? Thanks

  using UnityEngine;

    public class Player : MonoBehaviour
    {
        private Vector3 startTouchPosition, endTouchPosition;
        private Vector3 startRocketPosition, endRocketPosition;
        private float flyTime;
        public float speed = 1f;
        bool canMove;

        void Start()
        {
            transform.position = new Vector3(-3, 0, 3);
        }

        void Update()
        {
            if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
            {
                startTouchPosition = Input.GetTouch(0).position;
            }
            if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
            {
                endTouchPosition = Input.GetTouch(0).position;

                float xDifference = Mathf.Abs(endTouchPosition.x - startTouchPosition.x);
                float yDifference = Mathf.Abs(endTouchPosition.y - startTouchPosition.y);

                if ((endTouchPosition.x < startTouchPosition.x) && xDifference >= yDifference && transform.position.x > -5.5f)
                {
                    //StartCoroutine(Fly("left"));
                    MoveLeft();
                }
                else if ((endTouchPosition.x > startTouchPosition.x) && xDifference >= yDifference && transform.position.x < -0.5f)
                {
                    //StartCoroutine(Fly("right"));
                    MoveRight();
                }
                else if ((endTouchPosition.y > startTouchPosition.y) && yDifference >= xDifference && transform.position.z < 5.5f)
                {
                    //StartCoroutine(Fly("up"));
                    MoveUp();
                }
                else if ((endTouchPosition.y < startTouchPosition.y) && yDifference >= xDifference && transform.position.z > 0.5f)
                {
                    //StartCoroutine(Fly("down"));
                    MoveDown();
                }
            }
            if (canMove)
            {
                transform.position = Vector3.Lerp(startRocketPosition, endRocketPosition, speed * Time.deltaTime);
                canMove = false;
            }
        }

        {
            startRocketPosition = transform.position;
            endRocketPosition = new Vector3(startRocketPosition.x - 10f, 0, transform.position.z);
            canMove = true;
        }
        void MoveRight()
        {
            startRocketPosition = transform.position;
            endRocketPosition = new Vector3(startRocketPosition.x + 10f, 0, transform.position.z);
            canMove = true;
        }
        void MoveUp()
        {
            startRocketPosition = transform.position;
            endRocketPosition = new Vector3(startRocketPosition.x, 0, transform.position.z + 10f);
            canMove = true;
        }
        void MoveDown()
        { 
            startRocketPosition = transform.position;
            endRocketPosition = new Vector3(startRocketPosition.x, 0, transform.position.z -10f);
            anMove = true;
        }
\$\endgroup\$
0
\$\begingroup\$

This code:

if (canMove)
{
     transform.position = Vector3.Lerp(startRocketPosition, endRocketPosition, speed * Time.deltaTime);
     canMove = false;
}

...tries to move your object in a single frame. After it's run once, it sets canMove to false so now it doesn't continue the motion next frame. This doesn't make sense when using Lerp and deltaTime.

In this single frame, it tries to move to some ratio of the way between start and end, given by speed * Time.deltaTime. But if speed is in m/s then this is m/s * s = m, ie. you're calculating a distance here, where you're meant to provide a ratio, so your units don't make sense.

If your speed is close to 3 and your game is running at about 30 fps, then this will come out to some number close to 0.1, but not exactly. So when you try to move a fraction of 0.1 of the way between start and end (which are 10 units apart), you get a travel of almost 1 unit, but not exactly. On longer frames you'll overshoot, and on shorter frames you'll undershoot.

Basically: the code you've written does not make any logical sense for the behaviour you want from it. I would rewrite this from scratch, to reduce repetition and improve the behaviour.

public float travelDistance = 1f;
public float speed = 1f;

Coroutine movementInProgress;

void Update()
{
    if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
    {
        startTouchPosition = Input.GetTouch(0).position;
    }
    if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
    {
        endTouchPosition = Input.GetTouch(0).position;
        movementInProgress = StartCoroutine(Slide(endTouchPosition - startTouchPosition));
    }
}

IEnumerator Slide(Vector3 swipe) {
    Vector3 startPosition = transform.position;
    Vector3 endPosition = transform.position;

    if(Mathf.Abs(swipe.y) > Mathf.Abs(swipe.x)) {
        endPosition.z += Mathf.Sign(swipe.y) * travelDistance;
    } else {
        endPosition.x += Mathf.Sign(swipe.x) * travelDistance;
    }

    // m / m/s = s, so our unit cancellation confirms we've calculated a duration.
    float slideDurationSeconds = travelDistance / speed;

    // Now our increment's units are s / s = 1:
    // all units cancel out, and we're left with a radio, as Lerp expects.
    for(float t = 0f; t < 1f; t += Time.deltaTime / slideDurationSeconds) {
        transform.position = Vector3.Lerp(startPosition, endPosition, t);
        yield return null;
    }

    transform.position = endPosition;
    movementInProgress = null;
}

If you want to inhibit a new slide from starting until the previous slide has finished, you can add a check if(movementInProgress == null) before starting a new slide.

| improve this answer | |
\$\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.