Is there a way to drag and drop UI buttons in unity 3d? - c#

Is there a way to drag and drop UI buttons in unity 3d?
Can I add two functions to a button?
The first when it is normally clicked it does something.
The second when it is clicked and held, it can be dragged and dropped somewhere else on the screen?
This is the code I have so far but it's not working on the button.
void Update()
{
if (isDragged)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
}
public void OnMouseOver()
{
if (Input.GetMouseButtonDown(0))
{
isDragged = true;
}
}
public void OnMouseUp()
{
isDragged = false;
}

You can use unity built-in interfaces,
Use IPointerClickHandler to do something when it is clicked and implement another three interface for handling dragging which is IBeginDragHandler, IDragHandler, IEndDragHandler
public class Example : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
//Detect if a click occurs
public void OnPointerClick(PointerEventData pointerEventData)
{
Debug.Log(name + " Game Object Clicked!");
}
public void OnBeginDrag(PointerEventData eventData)
{
}
public void OnDrag(PointerEventData data)
{
}
public void OnEndDrag(PointerEventData eventData)
{
}
}
References : https://docs.unity3d.com/2018.1/Documentation/ScriptReference/EventSystems.IDragHandler.html

Related

Character jumps only when player touch and release finger Unity 2D

So I have a 2D platformer mobile game I'm about to release soon but there is this issue bothers me and appearently my testers.
In the game, you must touch screen to jump. But character only jumps when you touch and release your finger. This causes delay and affect gameplay badly. I need it to jump immediately when player touch. Not after releasing her/his finger.
Here is the code block jumps the character:
public void Jump(){
if (currentJumpRights > 0){
gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up*jumpForce,ForceMode2D.Impulse);
soundManager.PlaySound("jumpSound");
currentJumpRights--;
}
}
I have an invisible jump button that covers most of the screen to prevent jumping when player clicks pause button. And this function is assigned to that invisible button.
How can I achieve this? Thanks for help!
Instead of using Button (since you don't want any visual effects anyway) you can use IPointerDownHandler interface and do e.g.
public class MyButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public UnityEvent onClick;
public void OnPointerDown(PointerEventData pointerEventData)
{
onClick.Invoke();
}
public void OnPointerUp(PointerEventData pointerEventData)
{
// not doing anything but from the Documentation I'm never sure if needed
// "Note: In order to receive OnPointerUp callbacks, you must also implement the IPointerDownHandler interface"
// Not sure if this also goes for the other direction ;)
}
}
This way you receive the callback immediately when pressing down instead of "clicking".
If you rather want the button to continuously trigger as long as it stays pressed you can extend this and do e.g.
public class MyButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
public UnityEvent onPointerDown;
public UnityEvent onPointerUp;
public UnityEvent whilePressed;
public void OnPointerDown(PointerEventData pointerEventData)
{
onPointerDown.Invoke();
StartCoroutine(PressedRoutine);
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
onPointerUp.Invoke();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// nothing but needed for exit event
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
onPointerUp.Invoke();
}
private IEnumerator PressedRoutine()
{
while(true)
{
yield return null;
whilePressed.Invoke();
}
}
}

Unity - One Button for all Clone Objects

Hallo,
I followed this tutorial: https://www.youtube.com/watch?v=dMROaOQs7z8&t=7s
Selecting cars in the game using the Instantiate () function changes the prefabricated cars and I want to control them (throttle, brake, etc.) with using the UI buttons
I have a problem that the button does not find the event trigger (pointerDown, pointerUp) on newly created cloned objects, because the prefab is still changing
I have buttons and not keys (as in the video), the buttons do not find inputs to newly created objects.
I tried to do this using a script pinned to a button, with OnPointerEnter () and OnPointerExit () but it doesn't work, how should I do it? I will be grateful for any answer, thanks.
unity version 2019.4.14f1
public class GasButton : MonoBehaviour
{
public CarController controller;
private void Update()
{
controller = GameObject.FindGameObjectWithTag("Player").GetComponent<CarController>();
}
public void OnPointerEnter(PointerEventData eventData)
{
controller.GasPressed();
Debug.Log("Gas Pressed");
}
public void OnPointerExit(PointerEventData eventData)
{
controller.GasReleased();
Debug.Log("Gas Released");
}
OnPointerEnter and OnPointerExit should implement the IPointerEnterHandler and IPointerExitHandler.
To do this append interface in the monobehavior like this.
public class GasButton : MonoBehaviour,IPointerEnterHandler, IPointerExitHandler
{
public CarController controller;
private void Update()
{
controller = GameObject.FindGameObjectWithTag("Player").GetComponent<CarController>();
}
public void OnPointerEnter(PointerEventData eventData)
{
controller.GasPressed();
Debug.Log("Gas Pressed");
}
public void OnPointerExit(PointerEventData eventData)
{
controller.GasReleased();
Debug.Log("Gas Released");
}

Refresh ScrollView In Unity3D

does someone know how to detect if the UI Component "ScrollView" has been pulled up?.
This is planned to be like, when you refresh the feed in youtube or facebook, with the swipe up gesture.
It should detect the gesture and then, called a method that I have : refresh();
Thanks.
ScrollRect:
You can subscribe to ScrollRect's onValueChanged event and it will be called when there are changes on the ScrollRect.
public ScrollRect scrollRect;
void OnEnable()
{
scrollRect.onValueChanged.AddListener(ScrollChanged);
}
void OnDisable()
{
scrollRect.onValueChanged.RemoveAllListeners();
}
void ScrollChanged(Vector2 pos)
{
Debug.Log("Scroll changed pos to: " + pos);
}
or you can implement the IScrollHandler interface and use the OnScroll function. The script will have to be attached to the GameObject that has the ScrollRect component for it to work.
public class ScrollDetector : MonoBehaviour, IScrollHandler
{
public void OnScroll(PointerEventData eventData)
{
}
}
Scrollbar:
If using Scrollbar, then the callback function you register with the onValueChanged event should use float instead vector2 as param.
void ScrollChanged(float val)
{
}
or implement the IMoveHandler interface and use the OnMove function
public class ScrollDetector : MonoBehaviour, IMoveHandler
{
public void OnMove(AxisEventData eventData)
{
}
}

Unity3D, have BOTH 3D and UI Raycaster simultaneously respond to touch?

These days in Unity you very easily detect touch on objects using a raycaster...
public class DragThreeDee:MonoBehaviour,IPointerDownHandler
IPointerUpHandler, IBeginDragHandler,
IDragHandler, IEndDragHandler
{
public void OnPointerDown (PointerEventData data)
{ etc
So, regarding your UI layer, it correctly ignores touch on the UI layer and so on.
What if you want the following to happen,
you want to collect (simultaneously, in the same frame, both) a touch which is over both a 3D object, and, a UI item?
(Imagine you have, say, a few transparent UI buttons on the screen; you also have ordinary 3D robots running around. User clicks on a point on the screen where there's a UI button, and "underneath", a robot. BOTH the robot's script as above, and the button, should respond.)
How would you do that? I tried extensively using separate cameras, separate EventSystem, adjusting the layers, blocking masks and so on. I can't find a way to do it.
How to do this in Unity?
Looks complicated but can be done.
1.Route the data from PointerEventData in all of the UI components you want to unblock 3D GameObjects raycast on.
2.Get all the instance of PhysicsRaycaster with FindObjectsOfType<PhysicsRaycaster>(). In terms of performance, it would make sense to cache this.
3.Perform a Raycast with PhysicsRaycaster.Raycast which will return all GameObjects with Collider attached to it.
4.Use ExecuteEvents.Execute to send the appropriate event to the result stored in the RaycastResult.
RaycastForwarder script:
public class RaycastForwarder : MonoBehaviour
{
List<PhysicsRaycaster> rayCast3D = new List<PhysicsRaycaster>();
List<RaycastResult> rayCast3DResult = new List<RaycastResult>();
private static RaycastForwarder localInstance;
public static RaycastForwarder Instance { get { return localInstance; } }
private void Awake()
{
if (localInstance != null && localInstance != this)
{
Destroy(this.gameObject);
}
else
{
localInstance = this;
}
}
public void notifyPointerDown(PointerEventData eventData)
{
findColliders(eventData, PointerEventType.Down);
}
public void notifyPointerUp(PointerEventData eventData)
{
findColliders(eventData, PointerEventType.Up);
}
public void notifyPointerDrag(PointerEventData eventData)
{
findColliders(eventData, PointerEventType.Drag);
}
private void findColliders(PointerEventData eventData, PointerEventType evType)
{
UpdateRaycaster();
//Loop Through All Normal Collider(3D/Mesh Renderer) and throw Raycast to each one
for (int i = 0; i < rayCast3D.Count; i++)
{
//Send Raycast to all GameObject with 3D Collider
rayCast3D[i].Raycast(eventData, rayCast3DResult);
sendRayCast(eventData, evType);
}
//Reset Result
rayCast3DResult.Clear();
}
private void sendRayCast(PointerEventData eventData, PointerEventType evType)
{
//Loop over the RaycastResult and simulate the pointer event
for (int i = 0; i < rayCast3DResult.Count; i++)
{
GameObject target = rayCast3DResult[i].gameObject;
PointerEventData evData = createEventData(rayCast3DResult[i]);
if (evType == PointerEventType.Drag)
{
ExecuteEvents.Execute<IDragHandler>(target,
evData,
ExecuteEvents.dragHandler);
}
if (evType == PointerEventType.Down)
{
ExecuteEvents.Execute<IPointerDownHandler>(target,
evData,
ExecuteEvents.pointerDownHandler);
}
if (evType == PointerEventType.Up)
{
ExecuteEvents.Execute<IPointerUpHandler>(target,
evData,
ExecuteEvents.pointerUpHandler);
}
}
}
private PointerEventData createEventData(RaycastResult rayResult)
{
PointerEventData evData = new PointerEventData(EventSystem.current);
evData.pointerCurrentRaycast = rayResult;
return evData;
}
//Get all PhysicsRaycaster in the scene
private void UpdateRaycaster()
{
convertToList(FindObjectsOfType<PhysicsRaycaster>(), rayCast3D);
}
private void convertToList(PhysicsRaycaster[] fromComponent, List<PhysicsRaycaster> toComponent)
{
//Clear and copy new Data
toComponent.Clear();
for (int i = 0; i < fromComponent.Length; i++)
{
toComponent.Add(fromComponent[i]);
}
}
public enum PointerEventType
{
Drag, Down, Up
}
}
RayCastRouter script:
public class RayCastRouter : MonoBehaviour, IPointerDownHandler,
IPointerUpHandler,
IDragHandler
{
public void OnDrag(PointerEventData eventData)
{
RaycastForwarder.Instance.notifyPointerDrag(eventData);
}
public void OnPointerDown(PointerEventData eventData)
{
RaycastForwarder.Instance.notifyPointerDown(eventData);
}
public void OnPointerUp(PointerEventData eventData)
{
RaycastForwarder.Instance.notifyPointerUp(eventData);
}
}
Usage:
A.Attach RaycastForwarder to an empty GameObject.
B.Attach RayCastRouter to any UI component such as Image that you don't want to block a 3D GameObject. That's it. Any UI component with RayCastRouter attached to it will be able to allow a 3D GameObject behind it receive a raycast.
An event will now be sent to 3D Object that has a script that implements functions from IPointerDownHandler, IPointerUpHandler and IDragHandler interface.
Don't forget to attach Physics Raycaster to the camera.

Buttons in Unity, without using UI?

Is there a way in Unity to create a simple 2D button without using the UI layer. I have a game with a lot of buttons and I don't want to make the whole app in the UI. Some more controls are welcome too: switches, sliders etc.
PS. I saw NGUI and I don't like it so far. Anything else?
Is there a way in Unity to create a simple 2D button without using the
UI layer
You can use Sprite/Sprite Render as a Button.First Create a GameObject and attach EventSystem and StandaloneInputModule to it. Attach Physics2DRaycaster to the Camera, implement IPointerClickHandler and override OnPointerClick function. Create a 2D Sprite by going to GameObject->2D Object->Sprite then attach your script to the Sprite. Here is a complete code to do that:
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
public class SPRITEBUTTON: MonoBehaviour, IPointerClickHandler,
IPointerDownHandler, IPointerEnterHandler,
IPointerUpHandler, IPointerExitHandler
{
void Start()
{
//Attach Physics2DRaycaster to the Camera
Camera.main.gameObject.AddComponent<Physics2DRaycaster>();
addEventSystem();
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Mouse Clicked!");
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Mouse Down!");
}
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("Mouse Enter!");
}
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Mouse Up!");
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("Mouse Exit!");
}
//Add Event System to the Camera
void addEventSystem()
{
GameObject eventSystem = null;
GameObject tempObj = GameObject.Find("EventSystem");
if (tempObj == null)
{
eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
}
else
{
if ((tempObj.GetComponent<EventSystem>()) == null)
{
tempObj.AddComponent<EventSystem>();
}
if ((tempObj.GetComponent<StandaloneInputModule>()) == null)
{
tempObj.AddComponent<StandaloneInputModule>();
}
}
}
}
EDIT:
If this is a 3D GameObject/Mesh, then you need to add a simple collider to it. If it is just Sprite then you must add a 2D collider to the sprite.
Another approach that is even simpler, is to just add a BoxCollider2D component, then add the following methods to the a new componenent, say UIButton, where you will to perform the button actions :
void OnMouseOver()
void OnMouseDown()
void OnMouseUp()
This avoids the use of an EventSystem, StandaloneInputModule and Physics2DRaycaster.
Example :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class UIButton : MonoBehaviour {
public Sprite regular;
public Sprite mouseOver;
public Sprite mouseClicked;
public TextMeshPro buttonText;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
private void OnMouseDown()
{
}
private void OnMouseEnter()
{
}
private void OnMouseExit()
{
}
private void OnMouseUpAsButton()
{
}
}
Tested in unity 2018.1. One difference I initially noticed to this and the above approach is that the right mouse button click is not detected in this model, but is detected in the EventSystemModel.

Categories