- Home /
Spinning an object using touch input
Hey guys and gals,
I have literally been pulling my hair out for a few days over this. I have done countless test builds to my iPad to try to fix the issue all to no avail. I feel that I can't move on until I solve this problem.
I am simply trying to spin an object smoothly based on user touches and swipes. The code (below) which I have cobbled together from various sources (based on mouse input) is largely working as expected. It is currently configured to spin an object using one finger. However it seems that if the user spins the object leaving the first finger on the screen and places another finger down. On the release of the second finger (with the first one still touching) the object jumps to an unexpected angle! I have a feeling this should be a fairly simple fix and that I am doing something rather silly. Any input would be seriously appreciated.
Here is my current code (just attached to any object, currently using a simple cube). There is some ugly stuff here (like using onMouseDown) this is mainly so that I can test the script to some extent without having to build it each time. The unity remote is practically useless when testing touches... so damn laggy and unresponsive.
//Start code
var rotationSpeed = 2.0;
var lerpSpeed = 1.0;
private var speed = new Vector3();
private var avgSpeed = new Vector3();
private var dragging = false;
private var targetSpeedX = new Vector3();
function OnMouseDown()
{
dragging = true;
}
function Update() {
if (Input.touchCount == 1)
{
var theTouch : Touch = Input.GetTouch(0);
if (theTouch.phase == TouchPhase.Moved)
{
OnMouseDown();
}
if ((theTouch.phase == TouchPhase.Ended) || (theTouch.phase == TouchPhase.Canceled))
{
dragging = false;
}
}
if (Input.touchCount > 1)
{
dragging = false;
}
//Debug.Log ("Speed = " + speed);
//Debug.Log ("Dragging = " + dragging);
if ((theTouch.phase == TouchPhase.Moved) && dragging)
{
speed = new Vector3(theTouch.position.x, theTouch.position.y, 0);
avgSpeed = Vector3.Lerp(avgSpeed,speed,Time.deltaTime * 5);
}
if (Input.GetMouseButton(0) && dragging)
{
speed = new Vector3(-Input.GetAxis ("Mouse X"), Input.GetAxis("Mouse Y"), 0);
avgSpeed = Vector3.Lerp(avgSpeed,speed,Time.deltaTime * 5);
}
else
{
if (dragging) {
speed = avgSpeed;
dragging = false;
}
var i = Time.deltaTime * lerpSpeed;
speed = Vector3.Lerp( speed, Vector3.zero, i);
}
transform.Rotate( Camera.main.transform.up * speed.x * rotationSpeed, Space.World );
transform.Rotate( Camera.main.transform.right * speed.y * rotationSpeed, Space.World );
}
//End code snippet
Answer by stek · Sep 08, 2015 at 01:37 PM
Hello!
I made this C# script. I think this is the easiest way to spin an object. Simply attach it to your object and it will spin while drag, and when you release the spin will slow down.
using UnityEngine;
using System.Collections;
public class SpinLogic : MonoBehaviour {
float f_lastX = 0.0f;
float f_difX = 0.5f;
float f_steps = 0.0f;
int i_direction = 1;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (Input.GetMouseButtonDown(0))
{
f_difX = 0.0f;
}
else if (Input.GetMouseButton(0))
{
f_difX = Mathf.Abs(f_lastX - Input.GetAxis ("Mouse X"));
if (f_lastX < Input.GetAxis ("Mouse X"))
{
i_direction = -1;
transform.Rotate(Vector3.up, -f_difX);
}
if (f_lastX > Input.GetAxis ("Mouse X"))
{
i_direction = 1;
transform.Rotate(Vector3.up, f_difX);
}
f_lastX = -Input.GetAxis ("Mouse X");
}
else
{
if (f_difX > 0.5f) f_difX -= 0.05f;
if (f_difX < 0.5f) f_difX += 0.05f;
transform.Rotate(Vector3.up, f_difX * i_direction);
}
}
}
I hope it helps!
Happy Coding!
Answer by martymcsly · Jul 01, 2012 at 05:47 PM
Ok I found the original clean and beautiful code (from user duck) that I brutally butchered with the finesse of a drunk rhino above. Thinking about it i really wish i posted this in response to the thread i found ducks example in:
http://answers.unity3d.com/questions/34317/rotate-object-with-mouse-cursor-that-slows-down-on.html
I'm basically trying to make this work as well as it does with a mouse but with fingers on a touchscreen with no unexpected issues with potentially multiple fingers touching and releasing in the heat of action. Should be a simple conversion job, but for me it is turning into a real nightmare. I feel so stupid right now. Please don't think I'm some script kiddie that wants something for free without working for it. I literally can't get my head around what may be wrong and have spent 2 full days on this tweaking/rebuilding/failing/starting over/coming back/swearing etc. This motion working properly and reliably is the only thing holding me back and it is the main action for my game so it needs to be as efficient as possible. Thanks so much for any help, suggestions or if you must negative comments ;-)
var rotationSpeed = 10.0;
var lerpSpeed = 1.0;
private var speed = new Vector3();
private var avgSpeed = new Vector3();
private var dragging = false;
private var targetSpeedX = new Vector3();
function OnMouseDown()
{
dragging = true;
}
function Update ()
{
if (Input.GetMouseButton(0) && dragging) {
speed = new Vector3(-Input.GetAxis ("Mouse X"), Input.GetAxis("Mouse Y"), 0);
avgSpeed = Vector3.Lerp(avgSpeed,speed,Time.deltaTime * 5);
} else {
if (dragging) {
speed = avgSpeed;
dragging = false;
}
var i = Time.deltaTime * lerpSpeed;
speed = Vector3.Lerp( speed, Vector3.zero, i);
}
transform.Rotate( Camera.main.transform.up * speed.x * rotationSpeed, Space.World );
transform.Rotate( Camera.main.transform.right * speed.y * rotationSpeed, Space.World );
}
Hey martymcsly, did you ever manage to find a solution to this? I'm hitting a similar issue heh.
Answer by HALXP · Mar 03, 2017 at 05:59 PM
Here's my version that uses physics and touch input from a mobile device if it can help anyone
using UnityEngine;
using System.Collections;
public class SpinWithTouch : MonoBehaviour
{
public Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
rb.AddTorque(Camera.main.transform.up * -touchDeltaPosition.x);
rb.AddTorque(Camera.main.transform.right * touchDeltaPosition.y);
}
}
}
This worked really well for my needs in an AR project. I modified it below so that only objects with a collider are rotated when the touch is over the object:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Rigidbody))]
public class SpinWithTouch : $$anonymous$$onoBehaviour
{
Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void On$$anonymous$$ouseOver()
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.$$anonymous$$oved)
{
// Get movement of the finger since last frame
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
rb.AddTorque(Camera.main.transform.up * -touchDeltaPosition.x);
rb.AddTorque(Camera.main.transform.right * touchDeltaPosition.y);
}
}
}
Answer by tmalhassan · Sep 16, 2017 at 03:35 AM
Following @stek very simple and very helpful example. I took his code and modified it so it can spin on both sides while accepting touch controls so it can work on mobile devices. Simply, attach the script to the spinning object. Bare with me, i am a beginner. The script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinWheelScript : MonoBehaviour {
float pointerY;
float f_lastX = 0.0f;
float f_difX = 0.5f;
float f_steps = 0.0f;
int i_direction = 1;
void Start()
{
pointerY = Input.GetAxis("Mouse Y");
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
f_difX = 0.0f;
}
else if (Input.touchCount > 0)
{
pointerY = Input.touches[0].deltaPosition.y;
f_difX = Mathf.Abs(f_lastX - pointerY);
var touch = Input.GetTouch(0);
if (touch.position.x > Screen.width / 2)
{
// Right
if (f_lastX < Input.GetAxis("Mouse Y"))
{
i_direction = 1;
transform.Rotate(Vector3.forward, f_difX * Time.deltaTime);
}
if (f_lastX > Input.GetAxis("Mouse Y"))
{
i_direction = -1;
transform.Rotate(Vector3.forward, -f_difX * Time.deltaTime);
}
}
else if (touch.position.x < Screen.width / 2)
{
// Left
if (f_lastX < Input.GetAxis("Mouse Y"))
{
i_direction = -1;
transform.Rotate(Vector3.forward, -f_difX * Time.deltaTime);
}
if (f_lastX > Input.GetAxis("Mouse Y"))
{
i_direction = 1;
transform.Rotate(Vector3.forward, f_difX * Time.deltaTime);
}
}
f_lastX = -pointerY;
f_difX = 500f;
Debug.Log(f_difX);
}
else
{
if (f_difX > 0.5f) f_difX -= 1f;
if (f_difX < 0.5f) f_difX += 1f;
transform.Rotate(Vector3.forward, f_difX * i_direction * Time.smoothDeltaTime);
}
}
}
Currently, the object will spin based on a swipe along the Y axis, you can simple change it by replacing the Y's with X's. Also, i've modified the speeds to fit my liking. Hope this helps.
Cheers!
Answer by tangowhiskeygirl · Oct 05, 2017 at 12:06 AM
I don't know if anyone else needs this. But in my case I didn't want the object to rotate, but rather rotate the camera around it, so this is the script I made (for mobile), in case anyone else wants something similar. (The swiper object is an empty gameobject which is the parent of the camera. I didn't include code to change the swiper transform to match the object since I only use one object, but that should be easy to add if you need to rotate many different things). cheers :)
using UnityEngine;
using UnityEngine.EventSystems;
public class Rotator : MonoBehaviour, IDragHandler
{
public Transform world;
public GameObject swiper;
public float zoomSpeed;
float direction;
public void OnDrag(PointerEventData eventData)
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
// Get movement of the finger since last frame
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
//actual touch
Touch touchZero = Input.GetTouch(0);
//current touch x position
float touchZeroCurrPos = touchZero.position.x;
//previous touch x position
float touchZeroPrevPos = touchZero.position.x - touchDeltaPosition.x;
//difference between previous and current finger position
float Diff = touchZeroPrevPos - touchZeroCurrPos;
if (touchZeroCurrPos < touchZeroPrevPos )
{
Debug.Log("right");
direction = -1;
swiper.transform.Rotate(Vector3.up, -Diff * Time.deltaTime * zoomSpeed);
}
if (touchZeroCurrPos > touchZeroPrevPos)
{
Debug.Log("left");
direction = 1;
swiper.transform.Rotate(Vector3.down, Diff * Time.deltaTime * zoomSpeed);
}
}
}
}