unity2d prevent ui button from being clicked when raycast2d hit - c#

I have a gameobject with collider2d which was designed to be clickable, and I also Have an UI button which will follow the camera while player moving, the thing is they may overlap sometimes, and when they overlap, when user click the overlap area, I am sure that user want to click the object (which was do by raycast2d hit) so I should prevent the button to be clicked.
The script for the raycast implantation of clickable gameobject is as following:
private void checkTouch()
{
if (Input.touchCount > 0 || Input.GetMouseButtonDown(0))
{
Vector2 rayPos = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f);
if (hit)
{
Debug.Log(hit.collider.gameObject + "is hit");
IInputBehavior inputBehavior = hit.collider.gameObject.GetComponent<IInputBehavior>();
//IInputVehavior was a self-desgined C# interface which has a `OnClick()` method.
if (inputBehavior != null)
{
//here we should prevent the UIButton to be clicked, but how?
inputBehavior.OnClick();
}
}
}
}

Okay so I'm at work so I can't give you the exact code but I can point you in the right direction. When you made the canvas originally it should have generated an event system ( as a game object in the scene somewhere ), you need to reference this in your game object that you want to not click when the ui is over it.
In your game object it's something like this:
if(GetComponent<EventSystem>().IsPointerOverGameObject) {
// The mouse is obscured by a UI element
} else {
// The mouse is not over a UI element
}
https://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.IsPointerOverGameObject.html

Related

How to move a UI GameObject using Mouse Position in 3D World Space?

I have been trying to experiment with moving UI Elements using the position of the mouse, Trying to make it behave like a cursor. I was able to move the Element when the Canvas Render Mode was set to "Screen Space". I was able to clamp it within the Canvas as well.
But I want it to work when the Canvas Render Mode is "World Space" as well. If I use the same code that I used for the Screen Space, the Element leaves its boundaries and gets messed up as the angles are being varied.
I really need help. Any clue how to do it?
You can view how the scene looks in this image below. : https://img.techpowerup.org/200624/screen.png
If Raycasting is the solution, can someone please help me out and provide a snippet of code or something.
What you are looking for is probably RectTransformUtility.ScreenPointToWorldPointInRectangle
Transform a screen space point to a position in world space that is on
the plane of the given RectTransform.
The cam parameter should be the camera associated with the screen
point. For a RectTransform in a Canvas set to Screen Space - Overlay
mode, the cam parameter should be null.
When ScreenPointToWorldPointInRectangle is used from within an event
handler that provides a PointerEventData object, the correct camera
can be obtained by using PointerEventData.enterEventData (for hover
functionality) or PointerEventData.pressEventCamera (for click
functionality). This will automatically use the correct camera (or
null) for the given event.
You didn't post your code but you could probably use it in a GraphicRaycaster.Raycast. Something like
GraphicRaycaster m_Raycaster;
PointerEventData m_PointerEventData;
EventSystem m_EventSystem;
Canvas canvas;
void Start()
{
//Fetch the Raycaster from the GameObject (the Canvas)
m_Raycaster = GetComponent<GraphicRaycaster>();
//Fetch the Event System from the Scene
m_EventSystem = GetComponent<EventSystem>();
canvas = GetComponnet<Canvas>();
//Set up the new Pointer Event
m_PointerEventData = new PointerEventData(m_EventSystem);
}
void Update()
{
//Check if the left Mouse button is clicked
if (Input.GetKey(KeyCode.Mouse0))
{
//Set the Pointer Event Position to that of the mouse position
m_PointerEventData.position = Input.mousePosition;
//Create a list of Raycast Results
List<RaycastResult> results = new List<RaycastResult>();
//Raycast using the Graphics Raycaster and mouse click position
m_Raycaster.Raycast(m_PointerEventData, results);
//For every result returned, output the name of the GameObject on the Canvas hit by the Ray
foreach (RaycastResult result in results)
{
Debug.Log("Hit " + result.gameObject.name);
// until here it was the API example for GraphicRaycaster.Raycast
// Now get the first hit object
var rect = result.gameObject.GetComponent<RectTransform>();
if(rect)
{
// check which camera to get (main or null for ScreenSpace Overlay)
var camera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : Camera.main;
if(RectTransformUtility.ScreenPointToWorldPointInRectangle(rect, result.screenPosition, camera, out var worldPosition))
{
cursor3D.transform.position = worldPosition;
cursor3D.transform.rotation = result.gameObject.transform.rotation;
break;
}
}
}
}
}
Especially note also
Attach this script to your Canvas GameObject.
Also attach a GraphicsRaycaster component to your canvas by clicking the Add Component button in the Inspector window.
Also make sure you have an EventSystem in your hierarchy.

Instantiate objects in a list

I want to write a simple "point painting" code.
I have created a script which was supposed to instantiate gameobject (point) according to mouse position, but there is a 5 clone instead of 1, in a one mouse click.
void Update()
{
if (Input.GetMouseButton(0))
{
Vector3 newPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
newPos.z = 0;
Instantiate(prefab, newPos, Quaternion.identity);
}
}
how can I play with instantiate speed in update method?
or
how can I make list (ArrayList) of instantiated objects and then destroy objects (points) which has a same position?
The problem here is that u are using Input.GetMouseButton(0) which will return true in every frame while the left mouse button is held down(your machine basically is so fast that while you as a human only did a simple mouse press, your computer actually rendered 5 frames thus the 5 instatiation), in order to prevent this use Input.GetMouseButtonDown(0), which will only return true in one frame specifically when the left mouse button is pressed.
Here are the mouse button events that might be useful for you:
Returns true during the frame the user pressed the given mouse button.
Input.GetMouseButtonDown(int button);
Returns whether the given mouse button is held down or not.
Input.GetMouseButton(int button);
Returns true during the frame the user releases the given mouse button.
Input.GetMouseButtonUp(int button);
You can try this: Paint when the mouse cursor travels a certain amount of distance, like so:
public class MyPainter : MonoBehaviour {
[SerializeField, Tooltip("The prefab to paint")]
private GameObject toPaint;
[SerializeField, Tooltip("How far does the cursor have to move in order to paint the next object?")]
private float paintDistanceThreshold;
private bool isPainting;
/// <summary>
/// How much distance did the cursor travelled since last paint.
/// </summary>
private float cursorDistanceTravelledSinceLastPaint;
private Vector2 lastCursorPosition;
private Camera mainCameraRef;
private void Awake() {
isPainting = false;
// Btw, cache the main camera. Repeated calls to 'Camera.main' is expensive.
mainCameraRef = Camera.main;
cursorDistanceTravelledSinceLastPaint = 0f;
}
private void Update() {
// Mouse button pressed, start painting.
if (Input.GetMouseButtonDown(0)) {
PaintAtCurrentCursorPosition();
isPainting = true;
}
if (isPainting) {
cursorDistanceTravelledSinceLastPaint += Vector2.Distance(Input.mousePosition, lastCursorPosition);
if (cursorDistanceTravelledSinceLastPaint >= paintDistanceThreshold) {
PaintAtCurrentCursorPosition();
}
}
// Mouse button lifted, stop painting.
if (Input.GetMouseButtonUp(0)) {
isPainting = false;
}
lastCursorPosition = Input.mousePosition;
}
private void PaintAtCurrentCursorPosition() {
Vector3 newPos = mainCameraRef.ScreenToWorldPoint(Input.mousePosition);
newPos.z = 0;
Instantiate(toPaint, newPos, Quaternion.identity);
cursorDistanceTravelledSinceLastPaint = 0f;
}
}
Basically, it starts painting OnMouseDown.
When the mouse cursor travels a certain amount of distance (paintDistanceThreshold), it will paint another object.
Stops painting OnMouseUp.
Though one issue you might encounter is that when your mouse cursor intercepts a point where you have painted before, it will paint on that point again.
can I make list (ArrayList) of instantiated objects and then destroy objects (points) which has a same position?
It will mostly work if your painter is pixel/grid based. Otherwise, the coordinates needs to be rounded off for it to work accurately.
That said, most painting applications today uses pixel-based. I would highly suggest that you make your painter paint by pixel/grid rather than world coordinates.
This way, you can avoid a lot of the issues that I mentioned earlier.

Can't solve hitbox issues in mobile game made in Unity

I am developing a game where objects fall from the top of the screen, and you try to and tap on them before they reach the bottom of the screen. The code works fine when I played the game in the editor (with the touch controls swapped to mouse controls), except when I run the game on a phone, the game only seems to register a successful hit if you tap slightly in front of the object in the direction that it is traveling, and does not register a hit if you tap towards the back end or center of the object. I have built and ran the game over 10 times now, each time trying to fix this issue but nothing seems to help. My theory at the moment is that my code for the touch controls have too much going on and/ or have redundancies and by the time it checks whether or not an object is at the position of the touch, the object has moved to a different location. Any thoughts on why the hit boxes are off, and is there a better way to do hit detection with touch screen?
void FixedUpdate()
{
if (IsTouch())
{
CheckTouch(GetTouchPosition());
}
}
// Returns true if the screen is touched
public static bool IsTouch()
{
if (Input.touchCount > 0)
{
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
return true;
}
}
return false;
}
// Gets the position the touch
private Vector2 GetTouchPosition()
{
Vector2 touchPos = new Vector2(0f, 0f);
Touch touch = Input.GetTouch(0);
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
touchPos = touch.position;
}
return touchPos;
}
// Checks the position of the touch and destroys the ball if the
ball is touched
private void CheckTouch(Vector2 touchPos)
{
RaycastHit2D hit =
Physics2D.Raycast(Camera.main.ScreenToWorldPoint(
(Input.GetTouch(0).position)), Vector2.zero);
if (hit.collider != null)
{
destroy(hit.collider.gameObject);
}
}

Changing sprite after mouse click on collider (working with many colliders and many different sprites)

I won't hide that I am new to the Unity and C#. I am trying to make mini escape game.
My problem: Changing sprites using colliders works only on one object. After clicking second object it works one time and then either the first and the second object don't work.
Description:
On the main screen I will have many items that are "clickable" and some that are "pickable". I created 2 scripts- one that close up to the clicked item, and second that return to the main view.
Main view looks like that: they are 3 colliders and each one close-up to different view. Colliders are the children of the Background. After close-up I don't want the child colliders of background to be working. Only the collider of the close-up should work.
So the question: Am I doing anything wrong? Is there any better method to change sprites after mouse click?
My code:
First script:
public class GetCloser : MonoBehaviour // shows close-up of clicked object
{
public GameObject Actual, Background;
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit.collider != null)
{
Background.SetActive(false);
Actual.GetComponent<Collider2D>().enabled = true;
Actual.GetComponent<SpriteRenderer>().enabled = true;
}
}
}
Second script:
public class ReturnTo : MonoBehaviour //hide the close-up image and return to the background
{
public GameObject Actual, Background;
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit.collider != null)
{
Background.SetActive(true);
Actual.GetComponent<Collider2D>().enabled = false;
Actual.GetComponent<SpriteRenderer>().enabled = false;
}
}
}
Last script:
public class PickUp : MonoBehaviour { //hide clicked object- works every time
// Use this for initialization
void Start () {
gameObject.GetComponent<SpriteRenderer>().enabled = true;
}
// Update is called once per frame
void OnMouseDown()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up);
if (hit == true && hit.collider != null)
{
hit.collider.gameObject.GetComponent<SpriteRenderer>().enabled = false;
Destroy(hit.collider);
}
}
}
So if the issue is the colliders for your main screen still being active after zoom that's simply adding in your first script "zoom script" deactivating the colliders for all background or even just simply turning the gameobjects in the background off, then when you zoom back out simply turn the objects back on or colliders back on depending on which you decided to turn off.
Wait I just reread and realize after you click the first object you are no longer able to get the other objects to react any longer. Looking over your code it is probably because you are destroying the collider in your third script therefore you are no longer able to "hit" the other objects to trigger the code of a collision.

How to activate a button only when the player releases from within button boundary?

I want my button to work only when a player releases from within the button boundary. If the player slides his/her finger over and exits the boundary of the button, I don't want it to do anything. This is for a touch based interface.
What can I add to the following code to make it work:
void OnMouseUp(){
Application.LoadLevel ("MoonWalker");
}
First of all, for touch based interface you should use TouchPhase instead. (I've posted the link in my comment)
To make the button work only when touch released within its boundary, you'll need Raycast to detect if the position of touch release is within the button or not. If it is then do something, if it is not then do nothing.
void Update(){
foreach(Touch touch in Input.touches) {
if(touch.phase == TouchPhase.Ended) {
Ray ray = Camera.main.ScreenPointToRay(touch.Position);
if (Physics.Raycast(ray, out hit)) {
if(hit.GameObject.name == "Button")
//do something
}
}
}
}

Categories

Resources