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();
}
}
}
Related
I'm making some on-screen controls for a mobile game. Movement is handled by an on-screen joystick. Attacking can be done via an on-screen button. I want it so that while I'm holding down the attack button that the player attacks for as long as the button is held.
This works however if I'm holding down the joystick simultaneously then the hold is canceled prematurely (before the attack button is released). I see that the context did in fact get canceled - likely the Joystick is triggering OnPointerUp.
My expectation is that no other input should be able to interrupt/break the HoldInteraction
This issue doesn't happen on PC so it may be because both the on-screen joystick/button use the same OnPointerDown/OnPointerUp Events.
The closest to this issue I've found is: https://forum.unity.com/threads/on-screen-button-holds-inadvertently-calling-cancel.998021/
But creating my own on-screen button and adding an OnDrag handler doesn't solve the issue.
So how is one supposed to handle multiple on-screen buttons being pressed more or less at the same time? Shouldn't each button react independently to being pressed down/unpressed?
MyControllerScript
public void OnAttack(InputAction.CallbackContext value)
{
// BasicAttackSideEffects(0);
if(value.interaction is HoldInteraction)
{
Debug.Log(value.interaction);
basicAttackHeld = true;
}
else {
Debug.Log("Press only " + value.interaction);
StartCoroutine(mPlayerAnimOrchestrator.AttackAnim());
}
if(value.canceled) {
Debug.Log("Hold canceled");
basicAttackHeld = false;
// BasicAttackSideEffects();
}
}
void Update()
{
CalculateMovementInputSmoothing();
UpdatePlayerMovement();
UpdatePlayerAnimationMovement();
if(basicAttackHeld)
{
StartCoroutine(mPlayerAnimOrchestrator.AttackAnim());
}
}
MyButtonScript
namespace UnityEngine.InputSystem.OnScreen
{
public class Action : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
public void OnPointerUp(PointerEventData eventData)
{
SendValueToControl(0.0f);
}
public void OnPointerDown(PointerEventData eventData)
{
SendValueToControl(1.0f);
}
public void OnDrag(PointerEventData eventData)
{
Debug.Log("Dragging " + gameObject.name);
}
[InputControl(layout = "Button")]
[SerializeField]
private string m_ControlPath;
protected override string controlPathInternal
{
get => m_ControlPath;
set => m_ControlPath = value;
}
}
}
I am creating a game with unity and I have a question.
I have 5 game objects + player. And they always rotate when the game has started. I want that if the player collides with the object that is tagged snowflake, the other 4 objects pause the rotation.
Have a static event on your player script, when player collide invoke that event. On other scripts subscribe to that event.
For example in your PlayerScript should be something like
public class PlayerScript : MonoBehaviour {
public static UnityAction OnPlayerCollidedWithSnowFlakes;
private void OnCollisionEnter(Collision other) {
if (other.gameObject.tag.Equals("snowflake")) {
OnPlayerCollidedWithSnowFlakes?.Invoke();
}
}
}
And your RotatingObjectScript should be something like
public class RotatingObjectScript : MonoBehaviour {
private void Awake() {
PlayerScript.OnPlayerCollidedWithSnowFlakes += CollidedWithSnowflakeEventHandler;
}
private void OnDestroy() {
PlayerScript.OnPlayerCollidedWithSnowFlakes -= CollidedWithSnowflakeEventHandler;
}
private void CollidedWithSnowflakeEventHandler() {
.
.
// Stop rotating
.
.
}
}
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
The game I'm making is meant for mobile devices.
I have a PlayerInput class in which I check for mouse events in Update():
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//hide UI elements
}
}
I have a button which I hide when I detect mouse input in PlayerInput class but I don't want to hide it if the player presses the button.
I've managed to solve this issue by adding this component to my UI elements:
public class UiPointerHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
//disable mouse checks
}
public void OnPointerExit(PointerEventData eventData)
{
//enable mouse checks
}
}
It allows me to disable processing mouse events in PlayerInput's Update() to interact with certain UI elements.
This does its job fairly well on PC when I'm testing/prototyping the game but when I build the game for mobile it doesn't work at all and I can't press the buttons.
I'm looking for a solution that would work on mobile as well.
You can solve this fairly easily by adding the following check
void Update()
{
if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject())
{
//hide UI elements
}
}
What this does is it checks if your pointer (mouse or finger) is over an UI element. By checking if it's not, you get your desired behaviour.
See "Input.GetTouch" on 'Input' class on Unity, GetMouseButtonDown doesn't work on mobile. Unity designed 'Touch' event on mobile instead of mouse event so try it. it has to work with your code.
Thanks to user Immorality I've managed to solve my problem.
I've added the EventSystem.current.IsPointerOverGameObject() check to the Update() inside of my PlayerInput class.
void Update()
{
if(EventSystem.current.IsPointerOverGameObject()) return;
if (Input.GetMouseButtonDown(0))
{
//hide UI elements
}
}
I've tested this solution on its own and it did not help in my case, I still wasn't able to press the buttons.
I've introduced a bool in PlayerInput which controls whether the mouse input is being processed and 2 static methods allowing to change this variable.
public class PlayerInput : MonoBehaviour
{
private static bool _transmitInput;
void Update()
{
if(!_transmitInput || EventSystem.current.IsPointerOverGameObject()) return;
if (Input.GetMouseButtonDown(0))
{
//hide UI elements
}
}
public static void DisableInput()
{
if (!_transmitInput) return;
_transmitInput = false;
}
public static void EnableInput()
{
if (_transmitInput) return;
_transmitInput = true;
}
}
These are called from the UiPointerHandler:
public class UiPointerHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
PlayerInput.DisableInput();
}
public void OnPointerExit(PointerEventData eventData)
{
PlayerInput.EnableInput();
}
}
At this point I was still having problems on mobile. When pressing the button, the OnPointerEnter() would be called but OnPointerExit() would not.
I assume it is because of how my scene is set up. The 2 buttons I'm working with are in the same position and are the same size. I just disable one and enable the other.
I solved this issue by calling PlayerInput.EnableInput() in the onClick of the buttons.
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.