2

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

headkit
  • 3,307
  • 4
  • 53
  • 99
  • By any chance are there some images or diagrams you can use to illustrate an example of the concept? – Tom 'Blue' Piddock Aug 15 '13 at 15:55
  • @Blue Added requested image. – headkit Aug 19 '13 at 07:55
  • Simple but explanatory image - just what we needed! In these cases there are a couple of approaches including snapping and tweening your input. @Heisenbug's answer is fantastic, take a look at that for a starting point and see if it helps you out. – Tom 'Blue' Piddock Aug 19 '13 at 08:14

2 Answers2

4

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.

Bart
  • 19,692
  • 7
  • 68
  • 77
Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • hey, thanx! its a good hint, I will go through it and try some tests. Maybe 'Slerp' could help here, too. – headkit Aug 16 '13 at 10:22
  • +1 I like answers like this where you get a hint of what to do instead of "paste this code in". It promotes thinking in the right directions and helps the user solve the problem instead of paste in an answer. – Tom 'Blue' Piddock Aug 19 '13 at 08:15
  • This is helpful, but the diagram shows movement in all directions... how would any arbitrary movement be mapped to t? – davidkomer Oct 07 '15 at 15:11
0

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);
    }
}