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.
Related
I am new to unity and want to make a card - board game. I want to be able to place a card on a particular tile on the board. However, my draggable script that is attached to my card seems to not interact with the world space tile (it will interact with other UI). The world space tile has a droppable script, that currently doesn't have any real functionality, but should at least output a message to the console when a user hover overs it with their cursor. It doesn't even do that. (Again, droppable works with other UI elements) I thought that maybe because when the user is selecting their cards from their hand, and the canvas is in render mode - screen space camera - that had an effect, so I wrote a script that changes the render mode of the canvas to world space and it still seems to not interact. I will attach as much as I can for a clearer explanation, but at this point all I wish is for the board or tile to be able to recognize the user is hovering over it with the cursor.
Any help or information is appreciated, I am trying to absorb as much knowledge as I can, so even if you don't have a solution, advice is useful!
Dropzone.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Dropzone : MonoBehaviour, IDropHandler, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("OPEnt");
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("OPExt");
}
public void OnDrop(PointerEventData eventData)
{
Debug.Log("on drop " + gameObject.name);
}
}
Draggable.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Draggable : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
Transform originalParent = null;
private RectTransform RectTransform;
private void Awake()
{
RectTransform = GetComponent<RectTransform>();
}
public void OnBeginDrag(PointerEventData eventData)
{
originalParent = this.transform.parent;
this.transform.SetParent(this.transform.root);
GetComponent<CanvasGroup>().blocksRaycasts = false;
this.transform.parent.gameObject.GetComponent<CameraRender>().ChangeState();
}
public void OnDrag(PointerEventData eventData)
{
RectTransform.anchoredPosition += eventData.delta;
}
public void OnEndDrag(PointerEventData eventData)
{
this.transform.parent.gameObject.GetComponent<CameraRender>().ChangeState();
this.transform.SetParent(originalParent);
GetComponent<CanvasGroup>().blocksRaycasts = true;
}
}
CameraRender.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraRender : MonoBehaviour
{
enum RenderModeStates { camera, overlay, world };
RenderModeStates m_RenderModeStates;
Canvas m_Canvas;
// Use this for initialization
public void Start()
{
m_Canvas = GetComponent<Canvas>();
}
// Update is called once per frame
public void Update()
{
//Press the space key to switch between render mode states
if (Input.GetKeyDown(KeyCode.Space))
{
ChangeState();
}
}
public void ChangeState()
{
switch (m_RenderModeStates)
{
case RenderModeStates.camera:
m_Canvas.renderMode = RenderMode.WorldSpace;
m_RenderModeStates = RenderModeStates.world;
break;
case RenderModeStates.world:
m_Canvas.renderMode = RenderMode.ScreenSpaceCamera;
m_RenderModeStates = RenderModeStates.camera;
break;
}
}
}
I have tried to add a script that transitions render modes from Screen Space to World Space, I have attached box colliders to the world space objects.
For non-UI 3D objects in order to receive the IPointerXYHandler etc messages you need
Collider on the according object(s)
a PhysicsRaycaster component attached to your Camera (and make sure the according Layers are selected as "Event Mask" in its Inspector)
I have a game that is similar to "Flappy Bird" and I have main menu where I can start game and change skin of a pigeon. My skin collection is implemented with scroll rect and in the center there is a trigger which starts an animation of scaling a pigeon, it works fine until I click "start" and the scene changes to game and when I return to my main menu and click "skins" this trigger doesn't work anymore.
Script what is attached to all scroll rect elements to detect collisions with trigger:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ResizeFieldScript : MonoBehaviour
{
private Animator _anim;
private void Start()
{
_anim = GetComponent<Animator>();
}
public void OnTriggerEnter2D(Collider2D collider)
{
Debug.Log("Trigger is working");
if(collider.tag == "ResizeField")
{
Debug.Log("Condition is working");
_anim.SetBool("isInTrigger", true);
}
}
public void OnTriggerExit2D(Collider2D collider)
{
_anim.SetBool("isInTrigger", false);
}
}
Script what is attached to an empty object to change scenes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
public class UIController : MonoBehaviour
{
[SerializeField] private List<string> sceneNameList;
private string sceneToFind;
private int index = 0;
public void SceneChanger()
{
sceneToFind = EventSystem.current.currentSelectedGameObject.name;
foreach(string str in sceneNameList)
{
if(str == sceneToFind)
{
SceneManager.LoadScene(index);
index = 0;
break;
}
index++;
}
}
public void Exit()
{
Application.Quit();
}
public void BackMenu()
{
SceneManager.LoadScene(4);
}
}
OnTriggerEnter2D doesn't work after switching scenes. We could use OnTriggerEnter2D to jump scenes.
code show as below:
private void Update() {
// If E is pressed
if (Input. GetKeyDown(KeyCode. E)) {
// scene switching
SceneManager.LoadScene(4);
}
}
private void OnTriggerEnter2D(Collider collision) {
if (collision. tag == "ResizeField") {
// The UI prompts the user to press E to jump
EnterDialog.SetActive(true);
Debug.Log("Condition is working");
// _anim.SetBool("isInTrigger", true);
}
}
Hope it helps you.
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");
}
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
I have this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
public class start : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public GameObject musica;
public bool pulsado;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnPointerDown(PointerEventData eventData)
{
pulsado = true;
audio();
SceneManager.LoadScene("pajaro");
}
public void OnPointerUp(PointerEventData eventData)
{
pulsado = false;
}
void audio()
{
musica.SetActive(true);
}
}
When I export it to Android, touch does not work, but if it works in unity
You're using OnPointerDown, this is specific to the mouse, this event does not fire for Touch input (touch input does not have a concept of "up" and "down").
In order to have touch input, you have to use Input.touches during the Update() loop.