I have an InputAction callback where I am recording the position where the player clicks the screen, but only if the click is not over a UI element. Here's my code
private void OnPress(InputAction.CallbackContext context)
{
if (!EventSystem.current.IsPointerOverGameObject())
{
this.pressPosition = Mouse.current.position.ReadValue();
}
}
This has been working correctly. However, I recently updated my version of Unity, and now I'm getting this warning every time I click somewhere in my game:
Calling IsPointerOverGameObject() from within event processing (such as from InputAction callbacks)
will not work as expected; it will query UI state from the last frame
According to the changelog, this warning was added with an update to the input system.
Is there a way to figure out whether the mouse was over the UI when the player clicks the screen without getting this warning?
how I solved it was by moving just that piece of logic to an Unity's Update function:
private void Update()
{
if (Mouse.current.leftButton.wasPressedThisFrame)
{
if (EventSystem.current.IsPointerOverGameObject(PointerInputModule.kMouseLeftId))
// was pressed on GUI
else
// was pressed outside GUI
}
}
You can still keep using the inputsystem, i.e. when cancelled:
private void OnPress(InputAction.CallbackContext context)
{
if (context.canceled)
// Left mouse is no longer pressed. You can do something within the input system notification.
}
private bool pressControl = false;
private void Update()
{
if (Mouse.current.leftButton.wasPressedThisFrame)
pressControl =!EventSystem.current.IsPointerOverGameObject(PointerInputModule.kMouseLeftId);
}
void selector(InputAction.CallbackContext context)
{
if (!pressControl) return;
pressControl = false;
Vector3 position = new Vector3(mausePositionEvent.ReadValue<Vector2>().x, mausePositionEvent.ReadValue<Vector2>().y, 0);
Ray ray = Camera.main.ScreenPointToRay(position);
RaycastHit hit;
if (!Physics.Raycast(ray, out hit)) return;
}
Related
I want to generate a description label on an object when the mouse enters it. However, the generated description blocked the mouse and immediately triggered the OnPointerExit event for the object. For some reason, I don't want to turn off the raycast function in prefab, so how can I turn it off in code?
public void OnPointerEnter(PointerEventData eventData)
{
_desc = Instantiate(prefab, this.transform);
var bg = _desc.transform.Find("BG");
if (bg != null)
{
//What should I do?
}
}
I have a UI Image GameObject and i want to make it so that if you hover over it for a second it zooms but if at any point the cursor leaves the GameObject stays the same size. The way i wanted to do it was to check for first Pointer enter with OnPointerEnter and after a second elapses to check again if the cursor is on the Image but i had no luck with trying to find a way of checking if mouse is over the Image. Another thing i tried to find was if there is any way to delay OnPointerEnter by one second but most of the stuff i found made it delayed but if i moved my cursor away from the Image it would still zoom after a second.
You can set flag when PointerEtner and PointerExit, so that you can get the cursor isHovered by that flag.
public bool isHovered = false;
public void DoWhenHover()
{
isHovered = true;
StartCoroutine(DelayZoom());
}
IEnumerator DelayZoom()
{
yield return new WaitForSeconds(1);
print("Zoom the image");
}
public void DoWhenExit()
{
isHovered = false;
StartCoroutine(DelayRecover());
}
IEnumerator DelayRecover()
{
yield return new WaitForSeconds(1);
print("Reset the image");
}
First of all, please don't get too technical: I've only been studying Unity coding for two months and I'm not a programmer in general! I'm a total beginner! :-)
So, I'm building a game where you control a ball on a treadmill avoiding endless spawning obstacles by shifting left-right and jumping.
I set up a restart function after game over with SceneManager.LoadScene method, it works, but after reloading the jumping function gets compromised: it looks like there's an invisible wall above the player blocking its jumps. And it also looks like it's an "additive" issue: if I die and restart again, the player jumps even less, until it just doesn't jump at all. The other functions seem fine.
Any idea why that happens?
public class PlayerController : MonoBehaviour
//Jump code:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && isOnLane)
{
Jump();
}
}
void Jump()
{
playerRb.AddForce(Vector3.up * forceMult, ForceMode.Impulse);
isOnLane = false;
}
private void OnCollisionEnter(Collision other)
{
//checks if player is touching the ground
if (other.gameObject.CompareTag("Lane"))
{
isOnLane = true;
}
}
Restart code:
public class GameManager : MonoBehaviour
//restart game by clicking Restart Button
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
Thanks!
The problem is that additive scene loading is being used when there is only a single scene. It would be good to break these up into 2 scenes:
A scene for GameManager & the Player object
A scene for the level itself which is being reloaded
This way the GameManager is not being reloaded and the player is not being reloaded. Upon reload you need to "reset" variables for the player back to 0 and move him to a starting position.
RestartGame needs some logic to reset the player and his physics.
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
playerController.playerRb.velocity = Vector3.zero;
playerController.playerRb.angularVelocity = Vector3.zero;
playerTransform.position = spawnLocation.tranform.position;
}
You probably want to unload the scene, wait for 1 frame, and then load the scene. This way variables in the scene get reset properly.
public void RestartGame()
{
SceneManager.UnloadScene(scene);
playerController.playerRb.isKinematic = true; // Try setting kinematic between scenes
StartCoroutine(RestartLoad());
}
private IEnumerator RestartLoad()
{
yield return new WaitForEndOfFrame();
// Reset the player here
SceneManager.LoadScene(scene);
playerController.playerRb.isKinematic = false; // Player can use physics again
}
I want to know which gameobject is clicked with mouse on a 2D project
I used
void Update()
{
if (Input.GetMouseButtonDown(0))
{
clickTime = DateTime.Now;
mousePosition = Input.mousePosition;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit != null && hit.collider != null)
{
}
}
}
but it never goes in the second if condition
EDIT: I am working on a single script and access all gameobject from there using GameObject.FindGameObjectWithTag() and as I understand thats why the collider code in main script doesnt triggered.
I added a screenshot my code is in GameObject
This method works perfect on both desktop and mobile apps:
Add a collider component to each object you want to detect its click event.
Add a script to your project (let's name it MyObject.cs).
This script must implement the IPointerDownHandler interface and its method. And this script must add the Physics2DRaycaster to the camera. The whole body of this script could be like this:
public class MyObject : MonoBehaviour, IPointerDownHandler
{
private void Start()
{
AddPhysics2DRaycaster();
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
}
private void AddPhysics2DRaycaster()
{
Physics2DRaycaster physicsRaycaster = FindObjectOfType<Physics2DRaycaster>();
if (physicsRaycaster == null)
{
Camera.main.gameObject.AddComponent<Physics2DRaycaster>();
}
}
}
Add the MyObject.cs script to every object you want to detect click on it. After that, when you click on those objects, their name will display on Console.
Make sure that EventSystem exists in your project's Hierarchy. If not, add it by right click.
P.S. The MyObject.cs script now has IPointerDownHandler. This detects click as soon as you touch the objects. You also can use IPointerUpHandler and IDragHandler for different purposes!
and sorry for my bad English.
When you use a raycast you should set a collider on your sprite
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null) {
Debug.Log ("CLICKED " + hit.collider.name);
}
}
}
This is working for me in unity 5.6
Note: "LeftClick" is just a "GameObject" nothing else, I called it like this for better identification :)
EDITED
I test this method for the UI button but it's not working; so I used a different approach. For the UI button you can add a listener like this:
GameObject.Find ("YourButtonName").GetComponent<Button> ().onClick.AddListener (() => {
});
Consider using OnMouseOver() method (docs):
private void OnMouseOver() {
if (!Input.GetMouseButtonDown(0)) return;
// Your logic here.
}
This method only works with collider attached to gameobject, just like Raycast.
It would be cleaner and slightly more performant because there are no multiple Update() methods to iterate each frame. Something to keep in mind if you have dozens or hundreds of clickable objects.
(Although there are reports that this method doesn't work with multiple cameras one of which is ortographic one, in which case it is not an option for you.)
I am having a little trouble understanding the following. I am working on a simple game where the player can move a game object around by clicking left or right on the keyboard. Simple enough:
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
transform.position += Vector3.left * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.RightArrow))
{
transform.position += Vector3.right * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.UpArrow))
{
transform.position += Vector3.up * jumpHeight * Time.deltaTime;
}
if (Input.GetKey(KeyCode.DownArrow))
{
transform.position += Vector3.down * movementSpeed * Time.deltaTime;
}
}
However, I also have two buttons on screen, that are also supposed to move the player just as the keyevents do. What I dont understand is, that I need an update function to check every frame if a new button is pressed, right? So now I have my button and assign a script to it, where I can attach the button to a certain function of that script. But I cannot simply assign the button to a new "update" function that checks for different inputs of that button. How do I get my Ui Button to also controll the player JUST like the Update function does here?
Thank you!
Check out the Screenshot of the Scene. Just create an UI with an EventSystem. Create a button within your UI. Attach an EventTrigger to your button. Now create two Events on your EventTrigger:
(1) Pointer Down (will be called, when the button get's pressed down.)
(2) Pointer Up (will be called when the button is released.)
Attach the script at the end of this answer as a component to your player or cube or whatever you wanna move.
Now you can see on the screenshot, that the public method MoveCube.MoveLeft get's called whenever the MoveLeftButton is pressed. Whenever it is released, the MoveCube.StopMovingLeft gets called. The joke is about the bool, that will be switched from false to true and back. The actual movement happens in the Update, that essentially follows the same logic as the script you have provided. Hope this helps.
private bool moveLeft, moveRight;
void Start ()
{
// Get Rigidbody or CC or whatever you use to move your object.
}
void Update ()
{
if (moveLeft)
{
// Move Left
Debug.Log("Moving Left");
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
EDIT: Very important - I just saw that you are using transform.position += Vector3 to move the object. You will have big troubles with accurate collision, as your function is literally teleporting your gameobject to the new position. To avoid bad colliding you should use Rigidbody.AddForce or if you want to use transform you can easily use transform.Translate:
From Unity Docs:
transform.Translate(Vector3.forward * Time.deltaTime);
EDIT 2: Here is the code you have requested in the comment to this answer. Just copy the script, should work fine.
private bool moveLeft, moveRight;
// Create the rigidbody and the collider
private Rigidbody rb;
private Collider col;
// Create a variable you can change in inspector window for speed
[SerializeField]
private float speed;
void Start ()
{
// You can either use a CC (Character Controller) or a Rigidbody. Doesn't make that much of a difference.
// Important thing is, if you use a rigidbody, you will need a collider as well. Collider detect collision
// but rigidbodies do the actual work with regards to physics. In this case we use rigidbody/collider
// Get the rigidbody
// If you are making a 2D game, you should use Rigidbody2D
// The rigidbody will simulate actual physics. I tested this script, the
// rigibody will accelerate and will need time to slow down upon breaking
// You can change it's mass and stuff
rb = GetComponent<Rigidbody>();
// Now in this case we just get any collider. You can be more specific, if you know which collider your gameobjects has
// e.g. BoxCollider or SphereCollider
// If you are making a 2D game, you should use Collider2D
col = GetComponent<Collider>();
}
void Update ()
{
if (moveLeft)
{
// If you make a 2D game, use Vector2
rb.AddForce(Vector3.left * speed);
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
You could do something like this. In your script, attached to a button:
using UnityEngine.EventSystems; // Required when using Event data.
public class ExampleClass : MonoBehaviour, IPointerDownHandler // required interface when using the OnPointerDown method.
{
bool moveLeft = false;
// Do this when the mouse is clicked over the selectable object this script is attached to.
public void OnPointerDown(PointerEventData eventData)
{
moveLeft = true;
Debug.Log(this.gameObject.name + " was clicked.");
}
public void OnPointerUp(PointerEventData eventData)
{
moveLeft = false;
Debug.Log(this.gameObject.name + " was released.");
}
void Update()
{
if (moveLeft)
// Move left code
}
}
This is a fairly dumb example, but should be at least something you can expand on.