Ok, so I have a standard joystick downloaded from Unity asset store, script here (isn't that long):
// Implement IPointerDownHandler, IPointerUpHandler, IDragHandler to subscribe to pointer events
public class Joystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler {
public delegate void JoystickAction(Vector2 joystickAxes);
public static event JoystickAction JoystickMoved;
public Vector2 joystickAxes;
public bool usingJoystick;
private Image bgImg;
private Image stickImg;
private RectTransform bgTransform;
private RectTransform stickTransform;
// The first tap is treated similar to any following detection of a drag
public virtual void OnPointerDown(PointerEventData ped) {
usingJoystick = true;
OnDrag (ped);
}
public virtual void OnPointerUp(PointerEventData ped) {
usingJoystick = false;
joystickAxes = stickImg.GetComponent<RectTransform> ().anchoredPosition = Vector2.zero;
//joystickAxes = new Vector2(Screen.height / 2, Screen.width/2);
if (JoystickMoved != null) JoystickMoved (Vector2.zero);
}
public virtual void OnDrag(PointerEventData ped) {
Vector2 rectPos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
bgImg.rectTransform,
ped.position,
ped.enterEventCamera,
out rectPos)) { // Check if the pointer is positioned on the joystick
// Clamp the position to be inside the image bounds, prevents dragging beyond the image to get higher values.
Vector2 clampedPos = GetClampedPosition (rectPos);
// Normalize the joystick axes to be between -1 and 1.
joystickAxes = new Vector2 (
clampedPos.x / (bgTransform.rect.width / 2f),
clampedPos.y / (bgTransform.rect.height / 2f));
// Set the position of the inner joystick.
if (joystickAxes.magnitude > 1f) { // Normalizing the clampedPos constrains the joystick positions to a circle
stickImg.GetComponent<RectTransform> ().anchoredPosition = clampedPos.normalized * stickTransform.rect.width;
} else {
stickImg.GetComponent<RectTransform> ().anchoredPosition = clampedPos;
}
}
}
private Vector2 GetClampedPosition(Vector2 pos) {
Vector2 bgMin = bgTransform.rect.min;
Vector2 bgMax = bgTransform.rect.max;
return new Vector2 (
Mathf.Clamp (pos.x, bgMin.x, bgMax.x),
Mathf.Clamp (pos.y, bgMin.y, bgMax.y));
}
// Use this for initialization
void Start () {
joystickAxes = new Vector2 ();
bgImg = GetComponent<Image> ();
stickImg = transform.GetChild (0).GetComponent<Image> ();
bgTransform = gameObject.GetComponent<RectTransform> ();
stickTransform = transform.GetChild (0).GetComponent<RectTransform> ();
}
void Update() {
if (usingJoystick && JoystickMoved != null) {
JoystickMoved (joystickAxes);
}
}
}
This relies on an image object on a canvas and another image for the knob. as pictured:
This all works well, however I want the joystick to BE the screen, meaning the outside bounds for the knob aren't the background image but the screen bounds itself.
Ive tried scaling the background image to the screen using rh canvas, etc but this makes the inputAxes values way off. How can I do this?
I want to keep this script, just tweak it so the background "image"/bounds are the screen, whatever size that is.
Related
Goal: Make a general component that supports pinching to zoom on device and scroll mouse to zoom in editor. While pinching, set pivot to the middle point of two fingers, so it scales around where you pinch.
I provided my scripts in the followings. However it does not work well in scroll view on devices with two fingers pinching, when it "jumps" or jitters a lot. It works with mouse scroll in the Editor though. I think the reason is perhaps related with how the scroll view updates the layout internally. In a single frame my scripts changed the pivot and position of the content however the scroll view can not handle these changes properly. That's why I also tested my scripts using 1 LateUpdate, 2 yield WaitForEndOfFrame, 3 register call back in scroll view's OnValueChanged event, but all failed.
Does anyone know how to fix the problem in my script or any other new solutions how to make a scroll view to support pinch to zoom? Thanks!
My codes are something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PinchZoom : MonoBehaviour {
public float zoomSpeedPinch = 0.001f;
public float zoomSpeedMouseScrollWheel = 0.05f;
public float zoomMin = 0.1f;
public float zoomMax = 1f;
RectTransform rectTransform;
public int type = 1; // for device testing type 1 use LateUpdate; type 2 use Update
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
}
//public void OnValueChanged(Vector2 v) // test failed: called by scroll view event
//{
// //Zoom();
//}
void Update()
{
//Zoom();
if (type == 2)
{
if (Input.touchCount == 2)
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, Input.GetTouch(0), Input.GetTouch(1), Input.mousePosition));
else
StartCoroutine(ZoomInTheEndOfFrame(Input.mouseScrollDelta.y, Input.touchCount, default(Touch), default(Touch), Input.mousePosition));
}
}
private void LateUpdate()
{
if (type == 1) Zoom();
}
void Zoom()
{
var mouseScrollWheel = Input.mouseScrollDelta.y;
float scaleChange = 0f;
Vector2 midPoint = Vector2.zero;
if (Input.touchCount == 2)
{
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = touchDeltaMag - prevTouchDeltaMag;
scaleChange = deltaMagnitudeDiff * zoomSpeedPinch;
midPoint = (touchOne.position + touchZero.position) / 2;
}
if (mouseScrollWheel != 0)
{
scaleChange = mouseScrollWheel * zoomSpeedMouseScrollWheel;
midPoint = Input.mousePosition;
}
if (scaleChange != 0)
{
var scaleX = transform.localScale.x;
scaleX += scaleChange;
scaleX = Mathf.Clamp(scaleX, zoomMin, zoomMax);
var size = rectTransform.rect.size;
size.Scale(rectTransform.localScale);
var parentRect = ((RectTransform)rectTransform.parent);
var parentSize = parentRect.rect.size;
parentSize.Scale(parentRect.localScale);
if (size.x > parentSize.x && size.y > parentSize.y)
{
var p1 = Camera.main.ScreenToWorldPoint(midPoint);
var p2 = transform.InverseTransformPoint(p1);
var pivotP = rectTransform.pivot * rectTransform.rect.size;
var p3 = (Vector2)p2 + pivotP;
var newPivot = p3 / rectTransform.rect.size;
newPivot = new Vector2(Mathf.Clamp01(newPivot.x), Mathf.Clamp01(newPivot.y));
rectTransform.SetPivot(newPivot);
}
else
{
rectTransform.SetPivot(new Vector2(0.5f, 0.5f));
}
transform.localScale = new Vector3(scaleX, scaleX, transform.localScale.z);
}
}
//private IEnumerator ZoomInTheEndOfFrame(float mouseScrollWheel, int touchCount, Touch touchZero, Touch touchOne, Vector3 mousePosition) // testing failed
//{
// yield return new WaitForEndOfFrame();
// ZoomWithData(mouseScrollWheel, touchCount, touchZero, touchOne, mousePosition);
//}
}
For changing pivot without get the image "jumped" I used an extension script:
using UnityEngine;
public static class RectTranformExtension
{
/// <summary>
/// Set pivot without changing the position of the element
/// </summary>
public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
Vector3 deltaPosition = rectTransform.pivot - pivot; // get change in pivot
deltaPosition.Scale(rectTransform.rect.size); // apply sizing
deltaPosition.Scale(rectTransform.localScale); // apply scaling
deltaPosition = rectTransform.rotation * deltaPosition; // apply rotation
rectTransform.pivot = pivot; // change the pivot
rectTransform.localPosition -= deltaPosition; // reverse the position change
}
}
After working on this for days, I finally solved it.
The solution it is to call Zoom() in OnDrag(), and block the scollView component to receive dragging related events while zooming.
If you want to use it, just copy my codes under, do not do any changes. This is what I got after tons of testing on device. There're some minor potential gotcha with too many details that I don't want to explain, just copy and use it. And also I suggest to use a speed of current content.scale.x * 0.001
Codes:
// please note that scrollRect is the component on the scroll view game object, not where this script is
public void OnDrag(PointerEventData eventData)
{
Zoom();
if (Input.touchCount <= 1) scrollRect.OnDrag(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
scrollRect.OnEndDrag(eventData);
}
public void OnBeginDrag(PointerEventData eventData)
{
if (Input.touchCount <= 1) scrollRect.OnBeginDrag(eventData);
}
P.S. In the original script, there's a part that if a check for boundary is not passed then set pivot to (0.5,0.5). This part should be commented out(do not need to change pivot if the check is not passed ).
One more thing, you can use whatever parent-children relations you want, but you must make sure the object that receive the onDrag events has a whole image to catch the events.
I want to move an image UI when Canvas is set on Screen Space - Camera
However, nothing seems to work. I've tried the following things:
public void OnDrag(PointerEventData eventData)
{
Vector3 screenPoint = Input.mousePosition;
screenPoint.z = 0.13f; //distance of the plane from the camera
icon.transform.position = Camera.main.ScreenToWorldPoint(screenPoint);
}
Makes the image move out of the screen instantly.
public GameObject Target;
private EventTrigger _eventTrigger;
void Start ()
{
_eventTrigger = GetComponent<EventTrigger>();
_eventTrigger.AddEventTrigger(OnDrag, EventTriggerType.Drag);
}
void OnDrag(BaseEventData data)
{
PointerEventData ped = (PointerEventData) data;
Target.transform.Translate(ped.delta);
}
same story, image dissapires and moves out of the screen.
public void OnDrag(PointerEventData eventData)
{
Vector3 clickedPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
InventoryParent.transform.position = clickedPosition;
}
as the above.
Anyone has idea on how can I do accurate mouse dragging UI with Screen-Space Camera? Doing it with overlay works fine.
Change your UI element RectTransform anchors to:
and add this script to your UI element object:
RectTransform rectT;
void Start()
{
EventTrigger trigger = GetComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.Drag;
entry.callback.AddListener((data) => { OnDragDelegate((PointerEventData)data); });
trigger.triggers.Add(entry);
rectT = GetComponent<RectTransform>();
}
public void OnDragDelegate(PointerEventData data)
{
rectT.anchoredPosition = data.position;
}
You also need the EventTrigger component on your UI element object.
I am trying to show a text say "Scan for a surface" until it scans a plane surface.Once the device picks up a plane surface the message should disappear.Where should I write code for this purpose.I found two instances where prefab plane is initialised,one in UnityARGeneratePlane.cs and UnityARUtility.cs .I added a Gameobject as Text in UnityARGeneratePlane.cs and used setactive to true and false to show and hide the text.It is showing when i used it in the start method.How to hide the text is where i struggle.Where to use the code to hide the text when a plane is detected?Is it in UnityARGeneratePlane.cs or UnityARUtility.cs.
public class UnityARGeneratePlane : MonoBehaviour
{
public GameObject planePrefab;
public GameObject scantxt;
private UnityARAnchorManager unityARAnchorManager;
// Use this for initialization
void Start () {
unityARAnchorManager = new UnityARAnchorManager();
UnityARUtility.InitializePlanePrefab (planePrefab);
scantxt.SetActive(true); //Tried to show the text working when app opens
}
void OnDestroy()
{
unityARAnchorManager.Destroy ();
scantxt.SetActive(false); //Here is where I tried to hide the text until a Vertical or Horizontal plane is detected
}
void OnGUI()
{
IEnumerable<ARPlaneAnchorGameObject> arpags = unityARAnchorManager.GetCurrentPlaneAnchors ();
foreach(var planeAnchor in arpags)
{
//ARPlaneAnchor ap = planeAnchor;
//GUI.Box (new Rect (100, 100, 800, 60), string.Format ("Center: x:{0}, y:{1}, z:{2}", ap.center.x, ap.center.y, ap.center.z));
//GUI.Box(new Rect(100, 200, 800, 60), string.Format ("Extent: x:{0}, y:{1}, z:{2}", ap.extent.x, ap.extent.y, ap.extent.z));
}
}
}
The code below is UnityARUtility.cs
public class UnityARUtility
{
private MeshCollider meshCollider; //declared to avoid code stripping of class
private MeshFilter meshFilter; //declared to avoid code stripping of class
public static GameObject planePrefab = null;
// public Text text1;
public static void InitializePlanePrefab(GameObject go)
{
planePrefab = go;
}
public static GameObject CreatePlaneInScene(ARPlaneAnchor arPlaneAnchor)
{
GameObject plane;
if (planePrefab != null)
{
plane = GameObject.Instantiate(planePrefab); //I dont understand why again Plane prefab is initialized.Other than Generate planes.
// text1.text = "Select Any Painting from panel";
}
else {
plane = new GameObject (); //put in a blank gameObject to get at least a transform to manipulate
}
//plane.name = arPlaneAnchor.identifier;
ARKitPlaneMeshRender apmr = plane.GetComponent<ARKitPlaneMeshRender> ();
if (apmr != null) {
apmr.InitiliazeMesh (arPlaneAnchor);
}
return UpdatePlaneWithAnchorTransform(plane, arPlaneAnchor);
}
public static GameObject UpdatePlaneWithAnchorTransform(GameObject plane, ARPlaneAnchor arPlaneAnchor)
{
//do coordinate conversion from ARKit to Unity
plane.transform.position = UnityARMatrixOps.GetPosition (arPlaneAnchor.transform);
plane.transform.rotation = UnityARMatrixOps.GetRotation (arPlaneAnchor.transform);
ARKitPlaneMeshRender apmr = plane.GetComponent<ARKitPlaneMeshRender> ();
if (apmr != null) {
apmr.UpdateMesh (arPlaneAnchor);
}
MeshFilter mf = plane.GetComponentInChildren<MeshFilter> ();
if (mf != null) {
if (apmr == null) {
//since our plane mesh is actually 10mx10m in the world, we scale it here by 0.1f
mf.gameObject.transform.localScale = new Vector3 (arPlaneAnchor.extent.x * 0.1f, arPlaneAnchor.extent.y * 0.1f, arPlaneAnchor.extent.z * 0.1f);
//convert our center position to unity coords
mf.gameObject.transform.localPosition = new Vector3(arPlaneAnchor.center.x,arPlaneAnchor.center.y, -arPlaneAnchor.center.z);
}
}
return plane;
}
}
}
I have created a project where a cube is rotated when we touch on it. I want the cube to return back to its original position when the user stops touching the cube. Below I have added the source code of rotating a cube:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(MeshRenderer))]
public class dr : MonoBehaviour
{
#region ROTATE
private float _sensitivity = 1f;
private Vector3 _mouseReference;
private Vector3 _mouseOffset;
private Vector3 _rotation = Vector3.zero;
private bool _isRotating;
#endregion
void Update()
{
if(_isRotating)
{
// offset
_mouseOffset = (Input.mousePosition - _mouseReference); // apply rotation
_rotation.y = -(_mouseOffset.x + _mouseOffset.y) * _sensitivity; // rotate
gameObject.transform.Rotate(_rotation); // store new mouse position
_mouseReference = Input.mousePosition;
}
}
void OnMouseDown()
{
// rotating flag
_isRotating = true;
// store mouse position
_mouseReference = Input.mousePosition;
}
void OnMouseUp()
{
// rotating flag
_isRotating = false;
}
}
edited code:-
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(MeshRenderer))]
public class pt : MonoBehaviour
{
#region ROTATE
private float _sensitivity = 1f;
private Vector3 _mouseReference;
private Vector3 _mouseOffset;
private Vector3 _rotation = Vector3.zero;
private bool _isRotating;
private Quaternion original;
#endregion
void start(){
original = transform.rotation;
}
void Update()
{
if(_isRotating)
{
// offset
_mouseOffset = (Input.mousePosition - _mouseReference); // apply rotation
_rotation.y = -(_mouseOffset.x + _mouseOffset.y) * _sensitivity; // rotate
gameObject.transform.Rotate(_rotation); // store new mouse position
_mouseReference = Input.mousePosition;
}
}
public void OnMouseDown()
{
// rotating flag
_isRotating = true;
// store mouse position
_mouseReference = Input.mousePosition;
}
public void OnMouseUp()
{
// rotating flag
_isRotating = false;
transform.rotation = original;
}
}
"im trying to rotate a 3d model of a sofa and return to its starting rotation.but if i used this code " whenever if i stopped touching the sofa , it turns to **backside of sofa"**
i want it to return to initial rotation.u can see initally this is how the sofa looks like and if i stopped touching it returns to its backside of sofa. i want it to return to its front side again if i stopped rotation
I want the cube to return back to its original position when the user
stopped touching the cube
I can't exactly tell which part of this you are struggling with but you can simply get the position of the GameObject in the Start or Awake function then set the transform.position to that value when OnMouseUp is called.
private Vector3 originalPos;
void Start()
{
//Get the original position
originalPos = transform.position;
}
void OnMouseUp()
{
_isRotating = false;
//Reset GameObject to the original position
transform.position = originalPos;
}
EDIT:
For rotation, it is also the-same thing. Just use Quaternion and transform.rotation instead of Vector3 and transform.position.
private Quaternion originalPos;
void Start()
{
//Get the original rotation
originalPos = transform.rotation;
}
void OnMouseUp()
{
_isRotating = false;
//Reset GameObject to the original rotation
transform.rotation = originalPos;
}
You still have to incorporate that into the original code from your answer. If this is something you can't do then consider watching Unity's scripting tutorial here.
Hoping someone has a quick answer for this one as I haven't been able to figure this out. Rather than have the image jumping to center itself on the mouse cursor, I'd like to be able to drag the image from any place on the image without the jump. I know it has something to do with referencing the mouse position against the image or reseting the origin point of the image to the mouse location, but I don't know how to code it. Has anyone done this already? Using C#.
Vector3 partsPanelScale;
public Vector3 buildPanelScale;
public Transform placeholderParent = null;
public Transform parentToReturnTo = null;
GameObject placeholder = null;
public GameObject animalPart;
public GameObject trashCan;
public GameObject partsPanel;
public GameObject partsWindow;
GameObject buildBoard;
GameObject dragLayer;
private float _mX; // holds current eventData.position.x
private float _mY; // holds current eventData.position.y
private float _pmX;// holds previous eventData.position.x
private float _pmY;// holds previous eventData.position.y
void Start ()
{
dragLayer = GameObject.FindGameObjectWithTag("DragLayer");
buildBoard = GameObject.FindGameObjectWithTag("Board");
partsPanel = GameObject.FindGameObjectWithTag("Parts");
partsWindow = GameObject.FindGameObjectWithTag("PartsWindow");
trashCan = GameObject.FindGameObjectWithTag("Trash");
}
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
if(transform.parent.gameObject == buildBoard)
{
transform.SetAsLastSibling();
}
}
#endregion
#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
// each frame updates the current position of the mouse.
_mX = eventData.position.x;
_mY = eventData.position.y;
// create placeholder gap and hold correct position in layout
placeholder = new GameObject();
placeholder.transform.SetParent(transform.parent);
placeholder.transform.SetSiblingIndex(transform.GetSiblingIndex());
if(transform.parent.gameObject == partsPanel)
{
partsPanelScale = transform.localScale;
}
parentToReturnTo = transform.parent; // store current parent location
placeholderParent = parentToReturnTo; // set placeholder gameobject transform
GetComponent<CanvasGroup>().blocksRaycasts = false; // turn off image raycasting when dragging image in order to see what's behind the image
}
#endregion
#region IDragHandler implementation
float distance = 0;
public void OnDrag (PointerEventData eventData)
{
// Divided the difference by 6 to reduce the speed of dragging.
transform.position = new Vector3
(
(_pmX - _mX)/6 + transform.position.x,
(_pmY - _mY)/6 + transform.position.y,
distance
);
// Vector3 mousePosition = new Vector3(eventData.position.x, eventData.position.y, distance);
// Vector3 objPosition = Camera.main.ViewportToScreenPoint(mousePosition);
// transform.position = mousePosition; // set object coordinates to mouse coordinates
if(transform.parent.gameObject == partsPanel)
{
transform.SetParent(dragLayer.transform); // pop object to draglayer to move object out of partsPnael
}
if(transform.parent.gameObject == buildBoard)
{
// Constrain drag to boundaries of buildBoard Code
}
}
#endregion
#region IEndDragHandler implementation
public void OnEndDrag (PointerEventData eventData)
{
// end of the drag. set the previous position.
_pmX = _mX;
_pmY = _mY;
transform.SetParent(parentToReturnTo); // Snaps object back to orginal parent if dropped outside of a dropzone
transform.SetSiblingIndex(placeholder.transform.GetSiblingIndex()); // Returns card back to placeholder location
GetComponent<CanvasGroup>().blocksRaycasts = true; // turn Raycast back on
Destroy(placeholder); // kill the placeholder if object hits a drop zone or returns to parts panel
if(transform.parent.gameObject == buildBoard)
{
// Debug.Log ("Your sprite is now on the " + transform.parent.name);
transform.localScale = buildPanelScale;
transform.SetAsLastSibling(); // always place last piece on top
}
if(transform.parent.gameObject == partsPanel)
{
transform.localScale = partsPanelScale;
}
}
#endregion
To drag correctly you need both Previous and Current position of the mouse.
Because each time you move mouse the Image should Move a bit (at x and y coordinate) from its previous position.
If you just use the current position and directly set it to image the resault will be jump over the screen.
You have to Transform the transformation of image by getting differences of previous and current position (x2 - x1 , y2 - y1).Then set the Final transformation to the Image transform.
You also need MouseOver event to update the positions. (orMouseMove)
private double _mX; // holds current eventData.position.x
private double _mY; // holds current eventData.position.y
private double _pmX;// holds previous eventData.position.x
private double _pmY;// holds previous eventData.position.y
// each frame updates the current position of the mouse.
private void MouseOver(PointerEventData eventData)
{
_mX = eventData.position.x;
_mY = eventData.position.y;
}
public void OnDrag (PointerEventData eventData)
{
transform.position = new Vector3D((_pmX - _mX)/6 + transform.position.x,
(_pmY - _mY)/6 + transform.position.y, distance);
// Divided the difference by 6 to reduce the speed of dragging.
//...
// end of the drag. set the previous position.
_pmX = _mX;
_pmY = _mY;
}
Note that class names and events i have used may differ from unity3d.
If you drag from right to left but image goes from left to right replace _pmX - mX with _mX - _pmX.
If you drag from up to down but image goes from down to up replace _pmY - mY with _mY - _pmY.