headkit
headkit

Reputation: 3327

How to control an animation by touch position along a path in Unity3D?

I have a GameObject that I want to animate along a specific path/curve, but the animation should be controlled by mouse/touch position. So when I touch/click on the GameObject and move the finger/mouse on/near the path (or maybe its easier to just move down) the GameObject should follow its defined path.

I like iTween, but I think it is not possible to find a solution using it here, right?

edit: added image: enter image description here

Upvotes: 2

Views: 2985

Answers (2)

gaurav garg
gaurav garg

Reputation: 1

I tried with keyboard and its working fine, but not with mouse or touch

using System;
using UnityEngine;

public class Collector : MonoBehaviour
{
    public Transform startPoint;
    public Transform middlePoint;
    public Transform endPoint;

    public float curveSpeed = 0.5f;
    //public float speed = 0f;
    private int _direction = 1;

    private bool _isObjectSelected;
    private Vector3 _mouseLastPosition;
    private float _journeyLength;
    private Vector3 _offsetPos;

    private float _currentTime = 0;

    private void Start()
    {
        _journeyLength = Vector3.Distance(startPoint.position,
                                            endPoint.position);

        UpdateJourney(0);
    }

    private void OnMouseDown()
    {
        if (_isObjectSelected)
            return;

        _offsetPos = Vector3.zero;
        _mouseLastPosition = Input.mousePosition;
        _isObjectSelected = true;
    }

    private void OnMouseUp()
    {
        _isObjectSelected = false;
    }

    private void OnMouseExit()
    {
        _isObjectSelected = false;
    }

    private void OnMouseDrag()
    {
        if (_isObjectSelected)
        {
            Debug.LogError("Mouse drag");
            Vector3 currentPosition = Input.mousePosition;
            _offsetPos += currentPosition - _mouseLastPosition;

            float distCovered = _offsetPos.y / _journeyLength;
            UpdateJourney(distCovered);
            _mouseLastPosition = currentPosition;
        }
    }

    private void UpdateJourney(float time)
    {
        if (time < 0)
            time = 0;
        else if (time > 1)
            time = 1;

        _currentTime = time;

        transform.position = 
            QuadraticCurve(startPoint.position, 
                            middlePoint.position, 
                            endPoint.position, 
                            _currentTime);

        transform.rotation = Quaternion.Euler(
                            new Vector3(0, 0, 
                                QuadraticCurve(0, 45, 90, _currentTime)));
    }

    private void Update()
    {
        // moving on path using keyboard input
        float direction = Input.GetAxisRaw("Horizontal");
        if (Math.Abs(direction) > 0.1f)
        {
            _currentTime += Time.deltaTime * curveSpeed * direction;
            UpdateJourney(_currentTime);
        }
    }

    private static Vector3 Lerp(Vector3 start, Vector3 end, float time)
    {
        return start + (end - start) * time;
    }

    private static Vector3 QuadraticCurve(Vector3 start, Vector3 middle, Vector3 end, float time)
    {
        Vector3 point0 = Lerp(start, middle, time);
        Vector3 point1 = Lerp(middle, end, time);
        return Lerp(point0, point1, time);
    }

    private static float QuadraticCurve(float start, float middle, float end, float time)
    {
        float point0 = Mathf.Lerp(start, middle, time);
        float point1 = Mathf.Lerp(middle, end, time);
        return Mathf.Lerp(point0, point1, time);
    }
}

Upvotes: 0

Heisenbug
Heisenbug

Reputation: 39174

It's quite a simpler task than what you might think.

Basically it's a question of remapping a function (that takes the input as parameter) to another function (that express a position along a path). There are several ways of doing that, depending on the precise effect you want to implement.

The most important choices you have to take are:

  • How the describe the path/curve
  • How to handle input

Example

For the path an easy and flexible way is to use some sort of spline curves, such as cubic Bézier curve. It's easy to implement and Unity3D provides built-in functions to draw them. Have a look at Handles.DrawBezier.

Basically a Bézier function takes as input a parameter t in the domain [0,1] and return as a result a point in the space (2D or 3D as you prefer). B(0) gives the point at the begin of the curve, B(1) the end point. (Side note: the function is not linear so in the general case incrementing at a constant rate t doesn't produce a movement at constant speed along the curve. This paper might be useful).

For what concern the input the simpler solution that comes up to my mind is the following:

  • Accumulate somewhere the vector describing the offset from the position when the touch started to the current touch position. (Here's how to handle touches, have a look at deltaPosition).

Something like:

if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) 
{
  offsetFromStartPos += Input.GetTouch(0).deltaPosition;
}

Let's say you want to swipe up/down your finger for moving forward/back an object along a path.Choose a "travel" distance (the domain of the input function) for your finger in order to complete the movement along the curve and normalize the offset using such distance in order to remap the input into the [0,1] domain.

float t = offsetFromStartPos.y / maxDistanceAlongYAxis;
Vector3 pos = CalculateBezier(t);
transform.position = pos;

It's just an hint to put you in the right direction.

Upvotes: 4

Related Questions