How to avoid placing object behind buttons? - c#

Google Tango and Unity related question.
Using
this tutorial we can place an object on the surface by tapping the screen.
Here is the code:
using UnityEngine;
using System.Collections;
public class KittyUIController : MonoBehaviour
{
public GameObject m_kitten;
private TangoPointCloud m_pointCloud;
void Start()
{
m_pointCloud = FindObjectOfType<TangoPointCloud>();
}
void Update ()
{
if (Input.touchCount == 1)
{
// Trigger place kitten function when single touch ended.
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Ended)
{
PlaceKitten(t.position);
}
}
}
void PlaceKitten(Vector2 touchPosition)
{
// Find the plane.
Camera cam = Camera.main;
Vector3 planeCenter;
Plane plane;
if (!m_pointCloud.FindPlane(cam, touchPosition, out planeCenter, out plane))
{
Debug.Log("cannot find plane.");
return;
}
// Place kitten on the surface, and make it always face the camera.
if (Vector3.Angle(plane.normal, Vector3.up) < 30.0f)
{
Vector3 up = plane.normal;
Vector3 right = Vector3.Cross(plane.normal, cam.transform.forward).normalized;
Vector3 forward = Vector3.Cross(right, plane.normal).normalized;
Instantiate(m_kitten, planeCenter, Quaternion.LookRotation(forward, up));
}
else
{
Debug.Log("surface is too steep for kitten to stand on.");
}
}
}
I have buttons on the screen, and when I press them I also place an object on the surface behind it.
How can I avoid it? So if I'm pressing a button I don't want to place an object at the same time.
If I have a function which executes when I tap on the screen, how can I prevent it from executing if I click on a button on the screen?

One way to accomplish this would be to add a tag to your buttons, cast a ray on touch, and see if the ray is colliding with an object with the button's tag.
if (Input.touchCount == 1)
{
// Trigger place kitten function when single touch ended.
Touch t = Input.GetTouch(0);
Ray ray = Camera.main.ScreenPointToRay(t.position);
RaycastHit hit;
if(t.phase == TouchPhase.Ended && Physics.Raycast(ray,out hit,100))
{
if(hit.collider.tag == "button")
{
//do button stuff
}
else
{
PlaceKitten(t.position);
}
}
}

I've answered this before but couldn't find the duplicate to close this question. When I do find it, I will close it.
You need to check if the mouse position is over the UI before considering that as a valid click.
This is done with the IsPointerOverGameObject function.
void Update()
{
if (Input.touchCount == 1)
{
// Trigger place kitten function when single touch ended.
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Ended)
{
//Make sure we are not over the UI
if (!UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
Debug.Log("Clicked outside the UI");
PlaceKitten(t.position);
}
}
}
}
For a touch screen devices, you have to pass the fingerId to the function:
void Update()
{
if (Input.touchCount == 1)
{
// Trigger place kitten function when single touch ended.
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Ended)
{
//Make sure we are not over the UI
if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
{
Debug.Log("Touched outside the UI");
PlaceKitten(t.position);
}
}
}
}

Related

Unity Trail Renderer is getting connected to non-existing point

I'm having an issue with Unity Trail Renderer. I'm expecting trail to appear only when moving finger, but instead I also get straight line connecting the end of last trail to the beginning of a new one. Is this a visual bug in simulation or expected result of my code?
First of all, I have Weapon object with corresponding script Weapon.cs, where the position of rigidbody of said object is dictated by touch.position:
public class Weapon : MonoBehaviour
{
private Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
rb.position = Camera.main.ScreenToWorldPoint(touch.position);
}
}
}
}
Then I have Trail Renderer Component on the Blade which is a child object of Weapon. In Blade.cs I use Trail.Clear() to remove all existing points of TrailRenderer when user ends Touch. I checked the size of trail array on Touch.Began but it was empty as expected so there is no reason for TrailRenderer to connect these points. I also tried to play around with Trail.emit but the result was the same.
Here's the Blade.cs script:
public class Blade : MonoBehaviour
{
private TrailRenderer tr;
void Start()
{
tr = GetComponent<TrailRenderer>();
}
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Ended)
{
tr.Clear();
}
}
}
}
Here's the result:
Also, worth mentioning that I am using Simulated Touchscreen from com.unity.inputsystem.
Thanks in advance!

How to check if clicked on UI object?

I am new to Unity and trying to make a basic minesweeper game. I have a square prefab and I want to learn if it is clicked. But I can not do it listening because I need both left click and right click. How can I do that?
The absolute quickest way should be to implement something like this on the prefab you wish to listen for clicks on:
void OnMouseOver()
{
if (Input.GetMouseDown(0)) {
// Left click
}
else if (Input.GetMouseDown(1)) {
// Right click
}
}
If you instead want to detect mouseclicks from a more central position (as in, not distributed to each GameObject) you will need to create a component that fires Raycasts depending on left & right-clicks and look for specific objects, and then do you logic
Short example:
if (Input.GetMouseDown(0)) {
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100)) {
// something was hit
}
}
https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
You can try usign the Input.GetKeyDown function in the Update:
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
//print("Right click");
}
else if (Input.GetKeyDown(KeyCode.Mouse1))
{
//print("Left click");
}
}
For detecting if the mouse is over the spesific game object you can use:
void OnMouseOver()
{
///
}
Or
Ray ray;
RaycastHit hit;
void Update()
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out hit))
{
print (hit.collider.name);
}
}

Block the background when I make scroll in a UI text in device mobiles

I have a map in the background and I can move it with the finger. When I tap in some objects of screen it appears a UI text in scroll with information but when I touch the screen to do scroll in the text, the map placed in the background moves in UI devices. In Unity editor works fine. How can I get the map doesn't move in mobile devices?
This is the code to move the background map:
if(dragToPan){
if(!mapping && ready){
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved) {
if(Input.GetTouch(0).position.y > screenY/12){
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
//Check if any of the tile borders has been reached
CheckBorders();
//Translate the camera
cam.Translate(-touchDeltaPosition.x*dragSpeed*Time.deltaTime, -touchDeltaPosition.y*dragSpeed*Time.deltaTime, 0);
//Clamp the camera position to avoid displaying any off the map areas
ClampCam();
}
}
}
}
void Update(){

if (EventSystem.current.IsPointerOverGameObject() || EventSystem.current.currentSelectedGameObject != null) {
 return;
 }
And this is how I enable the scroll text:
public class SeleccionarTesoro_LIST : MonoBehaviour {
void Start()
{
GameObject[] hitObject = GameObject.FindGameObjectsWithTag("TESOROS");
}
public void SetHitObjectToActive(GameObject hitObject)
{
hitObject.transform.GetChild(0).GetChild(0).gameObject.SetActive (true);
hitObject.transform.GetChild(0).GetChild(2).gameObject.SetActive (true);
}
void Update() {
if (Input.GetMouseButtonDown (0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit)) {
SetHitObjectToActive (hit.collider.gameObject);
}
}
}
}
I would do something in the lines of:
1 - Create bool isSeleccionarTesoroScrollUp.
2 - Set it to true whenever the scroll text is up, and to false when it is dismissed.
3 - In the map script, you can change if(dragToPan) to
if(dragToPan && !isSeleccionarTesoroScrollUp), or whatever you find it more convenient.
That way the map won't move when you scroll.
Good luck and keep up the good work :)

Unity how to scale gameobjects using mouseclick?

does anyone know how to scale up different game objects using mouse click and after doing the scaling, the game object should not be able to scale up again. Currently my code is able to scale up but its not what i wanted.
Heres my current code:
public void ScaleForRuler()
{
transform.localScale += new Vector3 (3.0F, 0, 0.1F);
}
void OnMouseDown()
{
if (touch == false)
{
if(ruler.GetComponent<Collider>().name == "Ruler")
{
ScaleForRuler ();
touch = true;
Debug.Log (touch);
}
if(TriangleThingy.GetComponent<Collider>().name == "Triangle_Set_Square_Ruler")
{
ScaleForTriangleThingy ();
touch = true;
Debug.Log (touch);
}
if (Tsquare.GetComponent<Collider> ().name == "Tsquare")
{
if (LineR.GetComponent<Collider> ().name == "LineRight" || LineL.GetComponent<Collider> ().name == "LineLeft")
{
TsquareScaleForTB ();
touch = true;
Debug.Log (touch);
}
else if (LineB.GetComponent<Collider> ().name == "LineBottom" || LineT.GetComponent<Collider> ().name == "LineTop")
{
TsquareScaleForTB ();
touch = true;
Debug.Log (touch);
}
}
if (protractor.GetComponent<Collider> ().name == "Protractor")
{
ScaleForProtractor ();
touch = true;
Debug.Log (touch);
Debug.Log ("protractor scale");
}
}
}
Stop using OnMouseDown(), Unity has introduced since a while some new handler interfaces like IPointerDownHandler, IDragHandler, etc. within the EventSystem class, which works perfectly with mouse AND touch inputs.
It's not really clear what you want to achieve, if you want to scale every single object separately when it's clicked or just scale all together regardless which one is clicked.
I'll assume you want to be able to scale all objects separately. The steps to do that are the following:
1) Add the Physics 2D Raycaster component to your scene camera.
2) To every single object you want to scale, add this simple script:
using UnityEngine;
using UnityEngine.EventSystems;
public class ScaleExample : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
private Vector2 startPosition;
private float scalingFactor = .01f;
private bool isObjectAlreadyScaled = false;
public void OnBeginDrag(PointerEventData eventData) {
startPosition = Vector2.zero;
}
public void OnDrag(PointerEventData eventData) {
if (!isObjectAlreadyScaled) {
startPosition += eventData.delta;
var scalingAmount = new Vector2(Mathf.Abs(startPosition.x), Mathf.Abs(startPosition.y));
transform.localScale = (Vector3)scalingAmount * scalingFactor;
}
}
public void OnEndDrag(PointerEventData eventData) {
isObjectAlreadyScaled = true;
}
}
The example works with 2D objects (i.e.: scales up the X and Y of the object), you can ofc change this very easily in order to accomodate different coordinates scaling.
The use of the interfaces should be clear by example: OnBeginDrag is called once as soon as the dragging starts, OnDrag is called repeatedly every time there's a change in the position of the pointer during the drag, and OnEndDrag is called as soon as the drag is over (i.e.: user releases the button/finger).
In a comment to your question, you wrote
So i wanted to ask if i could just use a single script to control scaling for many diff objects
The answer is yes (and it is a really good idea also)
Here is an idea on how to do it:
From the doc of OnMouseDown:
OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.
This means that if your script contains the OnMouseDown function:
private void OnMouseDown()
{
}
you can add this script to several different objects. If you click on object1, the function will be called only for object1. If you click on object2, the function will be called for object2. So you don't need to check which object is clicked.
Your script should look like this:
private void OnMouseDown()
{
// touch is a boolean, you don't need to write touch == true, it will do it automatically
// Also, instead of nesting all the code if touch == false, we just stop if it is true, so the code is easier to read
if (touch)
return;
touch = true;
Debug.Log (touch);
// transform is the transform of the object that has the script
// you don't need any check
transform.localScale += new Vector3 (3.0f, 0f, 0.1f);
}
I hope this helps
Good luck

Destroying game object

I have created a simple 2D touch game in Unity and I have spikes spawning at the top of the scene every 3 seconds and when the spikes fall the player will tap the spikes before they hit the characters below, however, when I test the game, when I tap the spike (game object) it destroys all of the spikes (game objects) in the scene. Here is the code:
public GameObject spike;
public float spawnTime1 = 1f;
// Use this for initialization
void Start () {
InvokeRepeating ("SpawnSpike", spawnTime1, spawnTime1);
}
// Update is called once per frame
void Update () {
for (var i = 0; i < Input.touchCount; ++i) {
if (Input.GetTouch(i).phase == TouchPhase.Began) {
Destroy (gameObject);
}
}
}
void SpawnSpike () {
var newSpike = GameObject.Instantiate (spike);
newSpike.transform.position = new Vector3 (Random.Range(-3, 3), 11f, 0f);
It looks like you're destroying this game-manager-like script itself when you run
void Update () {
for (var i = 0; i < Input.touchCount; ++i) {
if (Input.GetTouch(i).phase == TouchPhase.Began) {
Destroy (gameObject);
}
}
}
The line Destroy (gameObject); is destroying its own game-object, which I'm guessing is parented to the spikes that are instantiated(?); hence destroying all the spikes at once.
I suggest using raycasts to find out if any spike is tapped on and/or which spike is tapped on, and then destroying only the game-object of that spike. Here's a good reference. I also suggest looking for tutorials regarding the same if you're still finding it hard to understand.
I hope that helps!
UPDATE:
Here's some sample code to get you going:
if (Input.GetTouch(i).phase == TouchPhase.Began) {
Vector3 pos = Camera.main.ScreenToWorldPoint (Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(pos, Vector2.zero);
if (hit != null && hit.gameObject.CompareTag("spike") && hit.collider != null) {
Debug.Log ("I'm tapping "+hit.gameObject.name);
Destroy(hit.gameObject);
}
}
This specific code would require that your spike prefab (template) has the tag "spike". I also suggest you place this code in some kind of a global "game-manager" script's Update() (rather than in the Spike script), so that you don't over-process the touches.

Categories