Pinch-To-Zoom with Unity 5 UI - c#

I'm trying to re-implement a pinch-to-zoom system in a Unity UI-based app. About six months ago I was able to hack one together by making the UI canvas a child of a regular GameObject, and manipulating that object's transform, but since updating to Unity 5.5+ I find this doesn't work. The closest I can get allows the pinch gesture to change the canvas' scaleFactor, which a) can make images, panels, etc resize improperly depending on their alignments, and b) won't allow me to pan once zoomed.
What I have so far is this:
public class PinchToZoomScaler : MonoBehaviour {
public Canvas canvas; // The canvas
public float zoomSpeed = 0.5f; // The rate of change of the canvas scale factor
public float _resetDuration = 3.0f;
float _durationTimer = 0.0f;
float _startScale = 0.0f;
void Start() {
_startScale = canvas.scaleFactor;
}
void Update()
{
// If there are two touches on the device...
if (Input.touchCount == 2) {
// Store both touches.
Touch touchZero = Input.GetTouch (0);
Touch touchOne = Input.GetTouch (1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// ... change the canvas size based on the change in distance between the touches.
canvas.scaleFactor -= deltaMagnitudeDiff * zoomSpeed;
// Make sure the canvas size never drops below 0.1
canvas.scaleFactor = Mathf.Max (canvas.scaleFactor, _startScale);
canvas.scaleFactor = Mathf.Min (canvas.scaleFactor, _startScale * 3.0f);
_durationTimer = 0.0f;
} else {
_durationTimer += Time.deltaTime;
if (_durationTimer >= _resetDuration) {
canvas.scaleFactor = _startScale;
}
}
}
}
As I said, this works to a degree, but doesn't give me a nice uniform zooming, not does it allow me to pan the canvas. Thanks in advance for any help.

Attach this script in canvas object which you want to zoom in and zoom out by pinch
using UnityEngine;
using UnityEngine.EventSystems;
public class ObjectScalling : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
private bool _isDragging;
private float _currentScale;
public float minScale, maxScale;
private float _temp = 0;
private float _scalingRate = 2;
private void Start()
{
_currentScale = transform.localScale.x;
}
public void OnPointerDown(PointerEventData eventData)
{
if (Input.touchCount == 1)
{
_isDragging = true;
}
}
public void OnPointerUp(PointerEventData eventData)
{
_isDragging = false;
}
private void Update()
{
if (_isDragging)
if (Input.touchCount == 2)
{
transform.localScale = new Vector2(_currentScale, _currentScale);
float distance = Vector3.Distance(Input.GetTouch(0).position, Input.GetTouch(1).position);
if (_temp > distance)
{
if (_currentScale < minScale)
return;
_currentScale -= (Time.deltaTime) * _scalingRate;
}
else if (_temp < distance)
{
if (_currentScale > maxScale)
return;
_currentScale += (Time.deltaTime) * _scalingRate;
}
_temp = distance;
}
}
}
Reminder: This script only works in canvas objects

You can use this function ( just pass to it negative deltaMagnitudeDiff )
Also it is good to multiplay deltaMagnitudeDiff with a ratio like ( 0.05 )
float currentScale = 1f;
void Zoom (float increment)
{
currentScale += increment;
if (currentScale >= maxScale)
{
currentScale = maxScale;
}
else if (currentScale <= minScale)
{
currentScale = minScale;
}
rectTransform.localScale = new Vector3 (currentScale, currentScale, 1);
pan.ValidatePosition ();
}
For Panning,
you can use something like this :
public class Pan : MonoBehaviour
{
public float Speed;
Vector3 startDragPosition;
public void BeginDrag ()
{
startDragPosition = Input.mousePosition;
}
public void Drag ()
{
transform.localPosition += (Input.mousePosition - startDragPosition) * Speed;
startDragPosition = Input.mousePosition;
ValidatePosition ();
}
public void ValidatePosition ()
{
var temp = transform.localPosition;
var width = ((RectTransform)transform).sizeDelta.x;
var height = ((RectTransform)transform).sizeDelta.y;
var MaxX = 0.5f * width * Mathf.Max (0, transform.localScale.x - 1);
var MaxY = 0.5f * height * Mathf.Max (0, transform.localScale.y - 1);
var offsetX = transform.localScale.x * width * (((RectTransform)transform).pivot.x - 0.5f);
var offsetY = transform.localScale.y * width * (((RectTransform)transform).pivot.y - 0.5f);
if (temp.x < -MaxX + offsetX)
temp.x = -MaxX + offsetX;
else if (temp.x > MaxX + offsetX)
temp.x = MaxX + offsetX;
if (temp.y < -MaxY + offsetY)
temp.y = -MaxY + offsetY;
else if (temp.y > MaxY + offsetY)
temp.y = MaxY + offsetY;
transform.localPosition = temp;
}
Just call the functions ( BeginDrag & Drag ) from the Events Trigger component.

what i did to scale an object using pinch was this, it works on any touch screen when the object is in the middle of the screen:
if (Input.touchCount == 2)
{
//The distance between the 2 touches is checked and subsequently used to scale the
//object by moving the 2 fingers further, or closer form eachother.
Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
if (isScaling)//this will only be done if scaling is true
{
float currentTouchDistance = getTouchDistance();
float deltaTouchDistance = currentTouchDistance - touchDistanceOrigin;
float scalePercentage = (deltaTouchDistance / 1200f) + 1f;
Vector3 scaleTemp = transform.localScale;
scaleTemp.x = scalePercentage * originalScale.x;
scaleTemp.y = scalePercentage * originalScale.y;
scaleTemp.z = scalePercentage * originalScale.z;
//to make the object snap to 100% a check is being done to see if the object scale is close to 100%,
//if it is the scale will be put back to 100% so it snaps to the normal scale.
//this is a quality of life feature, so its easy to get the original size of the object.
if (scaleTemp.x * 100 < 102 && scaleTemp.x * 100 > 98)
{
scaleTemp.x = 1;
scaleTemp.y = 1;
scaleTemp.z = 1;
}
//here we apply the calculation done above to actually make the object bigger/smaller.
transform.localScale = scaleTemp;
}
else
{
//if 2 fingers are touching the screen but isScaling is not true we are going to see if
//the middle of the screen is looking at the object and if it is set isScalinf to true;
Ray ray;
RaycastHit hitTouch;
ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out hitTouch, 100f))
{
if (hitTouch.transform == transform)
{
isScaling = true;
//make sure that the distance between the fingers on initial contact is used as the original distance
touchDistanceOrigin = getTouchDistance();
originalScale = transform.localScale;
}
}
}
}

Related

Zooming out the camera so that it can see all objects

I have a list of objects, these are blue stickmen on the video, I need to make the camera move away by itself and all objects (blue stickmen) always fit into it, you need to take into account that there will be more and more objects each time, so the camera should be dynamic and adapt itself to all objects
https://youtube.com/shorts/x3uSO2L22Kc?feature=share
Practical solution for any camera angle and objects positions
The idea here is fairly simple.
At each step we check if each object is inside of camera view or is camera too far away, then we simply adjust the camera position towards a better one.
Step by step, our camera will follow target objects dynamically, and when stabilized, all target objects will be captured by camera.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraAutoFitSmooth : MonoBehaviour
{
public int maxNumberOfObjs = 100;
public GameObject objPrefab;
public float maxInitRange = 10f;
public float minCameraHeight = 1f;
public float maxCameraMoveSpeed = 9f;
public float marginalPos = 0.1f;
[HideInInspector]
public List<Transform> objs = new List<Transform>();
Camera cam;
void Start()
{
cam = Camera.main;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
RandomObjs();
}
// Randomly regenerate objects
void RandomObjs()
{
int nowNumberOfObjs = Random.Range(0, maxNumberOfObjs);
for (int i = objs.Count - 1; i > nowNumberOfObjs; i--)
{
Destroy(objs[i].gameObject);
objs.RemoveAt(i);
}
for (int i = objs.Count; i <= nowNumberOfObjs; i++)
objs.Add(Instantiate(objPrefab).transform);
foreach (var obj in objs)
obj.position = Random.insideUnitSphere * maxInitRange;
}
void LateUpdate()
{
SetCameraFitPosition(Time.deltaTime);
}
void SetCameraFitPosition(float deltaTime)
{
Vector3 targetCamPos = cam.transform.position;
if (objs.Count == 1)
{
targetCamPos = objs[0].position - minCameraHeight * cam.transform.forward;
}
else if (objs.Count > 1)
{
float minInsideDiff = 1f, maxOutsideDiff = 0f;
Vector3 center = Vector3.zero;
foreach (var obj in objs)
{
Vector3 screenPos = GetScreenPos(obj.position);
if (IsInsideView(screenPos))
minInsideDiff = Mathf.Min(minInsideDiff, CalculateInsideDiff(screenPos.x), CalculateInsideDiff(screenPos.y), CalculateInsideDiff(screenPos.z));
else
maxOutsideDiff = Mathf.Max(maxOutsideDiff, CalculateOutsideDiff(screenPos.x), CalculateOutsideDiff(screenPos.y), CalculateOutsideDiff(screenPos.z));
center += obj.position;
}
center /= objs.Count;
float nowHeight = Vector3.Project(cam.transform.position - center, cam.transform.forward).magnitude;
float maxDiff = maxOutsideDiff > 0f ? maxOutsideDiff : -minInsideDiff;
float finalHeight = Mathf.Max(nowHeight + maxDiff * maxCameraMoveSpeed, minCameraHeight);
targetCamPos = center - finalHeight * cam.transform.forward;
}
cam.transform.position = Vector3.MoveTowards(cam.transform.position, targetCamPos, maxCameraMoveSpeed * deltaTime);
}
Vector3 GetScreenPos(Vector3 pos)
{
return cam.WorldToViewportPoint(pos);
}
float CalculateOutsideDiff(float pos)
{
float diff = 0f;
if (pos > 1f + marginalPos)
diff = pos - 1f;
else if (pos < -marginalPos)
diff = -pos;
return diff;
}
float CalculateInsideDiff(float pos)
{
float diff = 0f;
if (pos < 1f - marginalPos && pos > marginalPos)
diff = Mathf.Min(1f - pos, pos);
return diff;
}
bool IsInsideView(Vector3 screenPoint)
{
return screenPoint.z > 0f && screenPoint.x > 0f && screenPoint.x < 1 && screenPoint.y > 0f && screenPoint.y < 1;
}
}
If you need more info feel free to contact me :) Cheers!
The following script will position a perspective camera in a top down view, so that all tracked GameObjects (objs) are visible.
It is assumed that the objects are on the zero xz plane and are points, so their actual dimensions are not taken into account. There must be at least one tracked object. The objects may not be spaced in such a way that would require the cameras height to exceed the maximum floating point value.
public GameObject[] objs;//objects that must be fitted
private Camera cam;
float recXmin, recXmax, recYmin, recYmax;
Vector3 center;
void Start()
{
cam = Camera.main;
cam.transform.rotation = Quaternion.Euler(90, 0, 0);
}
void LateUpdate()
{
recXmin = objs[0].transform.position.x;
recXmax = objs[0].transform.position.x;
recYmin = objs[0].transform.position.z;
recYmax = objs[0].transform.position.z;
center = Vector3.zero;
foreach (GameObject obj in objs)
{
if (obj.transform.position.x < recXmin)
{
recXmin = obj.transform.position.x;
}
if (obj.transform.position.x > recXmax)
{
recXmax = obj.transform.position.x;
}
if (obj.transform.position.z < recYmin)
{
recYmin = obj.transform.position.z;
}
if (obj.transform.position.z > recYmax)
{
recYmax = obj.transform.position.z;
}
}
float horizontalHeight = (recYmax - recYmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * cam.fieldOfView / 2);
float verticalHeight = (recXmax - recXmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * Camera.VerticalToHorizontalFieldOfView(cam.fieldOfView, cam.aspect) / 2);
float finalHeight = horizontalHeight > verticalHeight ? horizontalHeight : verticalHeight;
center = new Vector3(recXmin + (recXmax - recXmin) / 2, finalHeight, recYmin + (recYmax - recYmin) / 2);
cam.transform.position = center;
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmin, 0, recYmax));
Gizmos.DrawLine(new Vector3(recXmax, 0, recYmin), new Vector3(recXmax, 0, recYmax));
Gizmos.color = Color.green;
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmax, 0, recYmin));
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmax), new Vector3(recXmax, 0, recYmax));
Gizmos.color = Color.blue;
Gizmos.DrawSphere(center, 0.5f);
}
the script determines the "bounding square" formed by all tracked objects
the bounding square is assumed to be the basis of a pyramid with the camera at its peak
using trigonometry the height of the camera can be calculated, by taking into account the known length of the pyramid's base side and the cameras field of view
the calculation is made twice for the cameras horizontal and vertical field of view
the greater of these two values is then selected
lastly the camera is position into the middle of the pyramids base and at the determined height, so that it ends up at the pyramids peak

Offscreen target indicators - Unity 2D

I'm trying to point to an object when it's off-screen. The objects are static.
public class SpawnIndicator : MonoBehaviour
{
public Camera UIcamera;
public GameObject point;
public Canvas canvas;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (point.GetComponent<Renderer>().isVisible)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.clear;
}
else
{
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
//POSITION
Vector3 pointPos = UIcamera.WorldToScreenPoint(point.transform.position);
pointPos.z = 0;
pointPos.x = Mathf.Clamp(pointPos.x, (Screen.width * 0.01f), (Screen.width * 0.99f));
pointPos.y = Mathf.Clamp(pointPos.y, (Screen.height * 0.01f), (Screen.height * 0.99f));
pointPos -= new Vector3((Screen.width/2), (Screen.height/2), 0);
gameObject.transform.localPosition = pointPos;
//ROTATION
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3 vectorToTarget = point.transform.position - gameObject.transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
angle -= 90;
Quaternion newRotation = Quaternion.AngleAxis(angle, Vector3.forward);
gameObject.transform.rotation = newRotation;
}
}
}
This works fine when the screen size is 1920x1080 (the reference size for my canvas). However at lower sizes, the objects sit awawy from the edges, and in larger sizes they sit outside of the edges.
Figured it out.
WorldToScreenPoint was returning a value based on the reference resolution (1920x1080) so different resolutions ended up mismatched. I think that's what was going wrong, regardless, I've got the solution.
I found this method to convert a world position to canvas position.
public static Vector3 WorldToScreenSpace(Vector3 worldPos, Camera cam, RectTransform area)
{
Vector3 screenPoint = cam.WorldToScreenPoint(worldPos);
screenPoint.z = 0;
Vector2 screenPos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(area, screenPoint, cam, out screenPos))
{
return screenPos;
}
return screenPoint;
}
I'd also changed the rest of the code a bit while trying to fix this before finding that solution (and coincidentally was using the canvas size) so here's the full script:
public class SpawnIndicator : MonoBehaviour
{
public Camera UIcamera;
public GameObject point;
public Canvas canvas;
Vector3 pointPos;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (point.GetComponent<Renderer>().isVisible)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.clear;
}
else
{
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3[] canvasPoints = new Vector3[4];
canvas.GetComponent<RectTransform>().GetLocalCorners(canvasPoints);
Vector3 pointPos = WorldToScreenSpace(point.transform.position, UIcamera, canvas.GetComponent<RectTransform>());
float xMin = canvasPoints[0].x * 0.98f;
float xMax = canvasPoints[2].x * 0.98f;
float yMin = canvasPoints[0].y * 0.8f;
float yMax = canvasPoints[2].y * 0.98f;
//POSITION
if (pointPos.x <= xMin) pointPos.x = xMin;
if (pointPos.x >= xMax) pointPos.x = xMax;
if (pointPos.y <= yMin) pointPos.y = yMin;
if (pointPos.y >= yMax) pointPos.y = yMax;
pointPos.z = 0f;
gameObject.transform.localPosition = pointPos;
//ROTATION
gameObject.GetComponent<SpriteRenderer>().color = Color.white;
Vector3 vectorToTarget = point.transform.position - gameObject.transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
angle -= 90;
Quaternion newRotation = Quaternion.AngleAxis(angle, Vector3.forward);
gameObject.transform.rotation = newRotation;
}
}
public static Vector3 WorldToScreenSpace(Vector3 worldPos, Camera cam, RectTransform area)
{
Vector3 screenPoint = cam.WorldToScreenPoint(worldPos);
screenPoint.z = 0;
Vector2 screenPos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(area, screenPoint, cam, out screenPos))
{
return screenPos;
}
return screenPoint;
}
}

Moving the player on the y-axis

I am working on a Pong-Clone for Android right now and have watched a tutorial about it. It works perfectly with the keyboard, but how do I get a similar control on the mobile phone. My current code looks like this:
private void Start()
{
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Ball>();
col = GetComponent<BoxCollider2D>();
if (side == Side.Left)
forwardDirection = Vector2.right;
else if (side == Side.Right)
forwardDirection = Vector2.left;
}
private void Update()
{
if (!overridePosition)
MovePaddle();
}
private void MovePaddle()
{
float targetYPosition = GetNewYPosition();
ClampPosition(ref targetYPosition);
transform.position = new Vector3(transform.position.x, targetYPosition, transform.position.z);
}
private void ClampPosition(ref float yPosition)
{
float minY = Camera.main.ScreenToWorldPoint(new Vector3(0, 0)).y;
float maxY = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height)).y;
yPosition = Mathf.Clamp(yPosition, minY, maxY);
}
private float GetNewYPosition()
{
float result = transform.position.y;
if (isAI)
{
if (BallIncoming())
{
if (firstIncoming)
{
firstIncoming = false;
randomYOffset = GetRandomOffset();
}
result = Mathf.MoveTowards(transform.position.y, ball.transform.position.y + randomYOffset, moveSpeed * Time.deltaTime);
}
else
{
firstIncoming = true;
}
}
else
{
float movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
result = transform.position.y + movement;
}
return result;
}
I want the player to move on the y Axis, but I have no idea how I can do it with touch controls (I am new to games). I would really appreciate any help, so I can continue programming :)
I'm going to assume what you want isove the padel by touch and drag up or down.
I would keep your code for debugging and add a switch to only use touch if touch is supported and your code otherwise.
// Adjust via the Inspector how much the user has to drag in order to move the padel
[SerializeField] private float touchDragSensitivity = 1;
private float lastTouchPosY;
// Make sure to store the camera
// Using Camera.main is quite expensive!
[SerializeField] private Camera _mainCamera;
private void Awake()
{
if(!_mainCamera) _mainCamera = GetComponent<Camera>();
...
}
private float GetNewPosition()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = _mainCamera.ScreenToWorldPoint(touch.position).y;
if(touch.phase = TouchPhase.Moved)
{
var delta = touchPositionY - lastTouchPosY;
movement = delta * touchDragSensitivity;
}
lastTouchPosY = touchPositionY;
}
}
result = transform.position.y + movement;
}
}
Or alternatively if you rather wanted a similar experience as for the button input you could also only check whether the touch is on the bottom or top half of the screen and move the padel constantly like with your original code before:
private void Update()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = touch.position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
result = transform.position.y + movement;
}
}
Update
As to your question
is there some way to split the screen so two players can play on the same screen
Yes: You can add a check on which side a touch is using touch.position.x >= screen.width / 2f => right side.
You could e.g. filter for valid touches like
if(Input.touchCount > 0)
{
if(side == Side.Right)
{
var validTouches = Input.touches.Where(t => t.position >= Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
else
{
var validTouches = Input.touches.Where(t => t.position < Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
}
If you want to press and hold make the object move you can make two buttons one for +y and the other for -y. then use Input.GetTouch to register the players input.

UNITY - Camera Orbit, move camera to some fixed positions

i have a scene in which i have a camera that is orbiting around a pivot in a 180° semisphere. All is working like intended but now i would like to point the camera to some fixed coordinates of the semisphere (possibily in a smooth way). Can someone give me some hints? I'll post the code i'm using right now.
public class CameraOrbitTouch : MonoBehaviour {
protected Transform _XForm_Camera;
protected Transform _XForm_Parent;
protected Vector3 _LocalRotation;
protected float _CameraDistance = 10f;
// The rate of change of the field of view in perspective mode.
public float perspectiveZoomSpeed = 0.2f;
public float OrbitDampening = 30f;
public float ScrollDampening = 18f;
public bool CameraDisabled = false;
public bool moving = false;
// Use this for initialization
void Start() {
this._XForm_Camera = this.transform;
this._XForm_Parent = this.transform.parent;
}
void LateUpdate() {
if (Input.GetKeyDown(KeyCode.LeftShift))
CameraDisabled = !CameraDisabled;
if (!CameraDisabled)
{
//Rotation of the Camera based on Mouse Coordinates
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
moving = true;
_LocalRotation.x += Input.touches[0].deltaPosition.x ;
_LocalRotation.y += Input.touches[0].deltaPosition.y;
//Clamp the y Rotation to horizon and not flipping over at the top
if (_LocalRotation.y < 0f)
_LocalRotation.y = 0f;
else if (_LocalRotation.y > 90f)
_LocalRotation.y = 90f;
}
//Zooming Input from our Mouse Scroll Wheel
if ((Input.touchCount == 2)&& (Input.GetTouch(0).phase == TouchPhase.Moved) && (Input.GetTouch(1).phase == TouchPhase.Moved))
{
moving = true;
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = (prevTouchDeltaMag - touchDeltaMag);
//this._CameraDistance += deltaMagnitudeDiff * -1f;
this._CameraDistance += deltaMagnitudeDiff * perspectiveZoomSpeed;
this._CameraDistance = Mathf.Clamp(this._CameraDistance, 1.5f, 100f);
}
}
if (moving)
{
//Actual Camera Rig Transformations
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if (this._XForm_Camera.localPosition.z != this._CameraDistance * -1f)
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f,Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
moving = false;
}
}
}
I tought i could use somethig like this
string selectedObj = "CameraPivot";
camera = GameObject.Find(selectedObj);
camera.transform.rotation = Quaternion.Euler(90.0f, 0.0f, 100.0f);
But i think is not the right waty to make it work...

Rotate 2D Sprite with Virtual Joystick

I am trying to rotate a GameObject along z-axis using Joystick, but its rotating y-axis. I might missed some math calculation. Any Help??
Reference Image
void FixedUpdate()
{
// get input from joystick
// get input from joystick
rightJoystickInput = rightJoystick.GetInputDirection();
float xMovementRightJoystick = rightJoystickInput.x; // The horizontal movement from joystick 02
float zMovementRightJoystick = rightJoystickInput.y; // The vertical movement from joystick 02
// if there is only input from the right joystick
if (rightJoystickInput != Vector3.zero)
{
// calculate the player's direction based on angle
float tempAngle = Mathf.Atan2(zMovementRightJoystick, xMovementRightJoystick);
xMovementRightJoystick *= Mathf.Abs(Mathf.Cos(tempAngle));
zMovementRightJoystick *= Mathf.Abs(Mathf.Sin(tempAngle));
// rotate the player to face the direction of input
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
if (lookDirection != Vector3.zero)
{
rotationTarget.localRotation = Quaternion.Slerp(rotationTarget.localRotation, Quaternion.LookRotation(lookDirection) * Quaternion.Euler(0, 45f, 0), rotationSpeed * Time.deltaTime);
}
}
}
You don't need most of the code in your question and this is really simple.
1.Find the angle with Mathf.Atan2 then multiple it with Mathf.Rad2Deg.
2.Use Quaternion.Euler(new Vector3(0, 0, angle)) to get the rotation then apply it to the Object.
This should be one in the Update function not FixedUpdate because FixedUpdate is used to move Rigidbody Objects.
public Transform rotationTarget;
public bool flipRot = true;
void Update()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rotationTarget.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
}
If using Rigidbody2D then use Rigidbody2D.MoveRotation in the FixedUpdate function. The rest of the code stays the-same.
public Rigidbody2D rg2d;
public bool flipRot = true;
void FixedUpdate()
{
rightJoystickInput = rightJoystick.GetInputDirection();
float horizontal = rightJoystickInput.x;
float vertical = rightJoystickInput.y;
float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
EDIT:
But only the problem is when i leave joystick its rotation is setting
to 0 instantly which looks too odd. How can i fix it?
You have to detect when you release the joystick in OnPointerUp then slowly lerp the joystick thump back to the Zero position. You also have to lerp the current target object angle to zero or to its default value and this should be done in a coroutine function. When OnPointerDown is called, stop the current coroutine function. Prevent the code in FixedUpdate from running when finger is released so that it won't interfere with the coroutine function.
For the sake of completeness, below is the combination of a Joystick code and the Rigidbody answer above:
public class VirtualJoystickController : MonoBehaviour,
IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
public float mas_distance = 7f;
void Start()
{
bgImg = GameObject.Find("JoystickBGImage").GetComponent<Image>(); // the joysticks background
joystickImg = GameObject.Find("Joystickthumb").GetComponent<Image>(); // the joystick object to use
}
private Vector3 _inputDirection = Vector3.zero;
//the movementDirection
public Vector3 joystickInputDirection
{
set
{
//Change only if value is different from old one
if (_inputDirection != value)
{
_inputDirection = value;
Debug.Log("Dir: " + _inputDirection);
}
}
get
{
return _inputDirection;
}
}
public void OnDrag(PointerEventData eventData)
{
dragJoyStick(eventData);
}
void dragJoyStick(PointerEventData eventData)
{
Vector3 tempDir = Vector3.zero;
Vector2 pos = Vector2.zero;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle
(bgImg.rectTransform,
eventData.position,
eventData.pressEventCamera,
out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
float x = (bgImg.rectTransform.pivot.x == 1) ? pos.x * 2 + 1 : pos.x * 2 - 1;
float y = (bgImg.rectTransform.pivot.y == 1) ? pos.y * 2 + 1 : pos.y * 2 - 1;
tempDir = new Vector3(x, y, 0);
if (tempDir.magnitude > 1)
{
tempDir = tempDir.normalized;
}
joystickImg.rectTransform.anchoredPosition = new Vector3(
tempDir.x * (bgImg.rectTransform.sizeDelta.x / mas_distance),
tempDir.y * (bgImg.rectTransform.sizeDelta.y / mas_distance));
joystickInputDirection = tempDir;
}
}
public void OnPointerDown(PointerEventData eventData)
{
released = false;
//Stop current coroutine
if (retCoroutine != null)
StopCoroutine(retCoroutine);
if (eventData.pointerCurrentRaycast.gameObject == bgImg.gameObject ||
eventData.pointerCurrentRaycast.gameObject == joystickImg.gameObject)
{
OnDrag(eventData);
}
}
public void OnPointerUp(PointerEventData eventData)
{
released = true;
//Stop current coroutine then start a new one
if (retCoroutine != null)
StopCoroutine(retCoroutine);
retCoroutine = StartCoroutine(SlowReturn(returnTime));
}
IEnumerator SlowReturn(float duration)
{
RectTransform thumbstickTransform = joystickImg.rectTransform;
Vector3 toPosition = Vector3.zero;
float counter = 0;
//Get the current position of the object to be moved
Vector2 currentThumb = thumbstickTransform.anchoredPosition;
while (counter < duration)
{
counter += Time.deltaTime;
//Slowly returns thumbstick
Vector2 tempThumbStickVal = Vector2.Lerp(currentThumb, toPosition, counter / duration);
joystickInputDirection = tempThumbStickVal;
thumbstickTransform.anchoredPosition = tempThumbStickVal;
//Slowly returns the target Object to original pos
float tempTargetObjAngle = Mathf.Lerp(angle, originalAngle, counter / duration);
rg2d.MoveRotation(tempTargetObjAngle);
yield return null;
}
}
public float returnTime = 1.0f;
public Rigidbody2D rg2d;
public bool flipRot = true;
const float originalAngle = 0;
bool released = true;
float angle;
Coroutine retCoroutine;
void FixedUpdate()
{
if (released)
return;
float horizontal = joystickInputDirection.x;
float vertical = joystickInputDirection.y;
angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
angle = flipRot ? -angle : angle;
rg2d.MoveRotation(angle);
}
}
your function calculates a point where you want to look at:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.z += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
this point is on the XZ plane, that is why the car rotates on Y axis
if you want to rotate on Z axis, calculate a point on XY plane like this:
Vector3 temp = transform.position;
temp.x += xMovementRightJoystick;
temp.y += zMovementRightJoystick;
Vector3 lookDirection = temp - transform.position;
PS: i don't know why you multiply with Quaternion.Euler(0, 45f, 0) - that is a constant angle on Y axis, it just means that each lookDirection will be rotated for 45 degrees - i would have to see your scene to know why you need this...

Categories