how to detect which gameobject is clicked - unity3d

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.)

Related

How to show a trigger only when pointing to it directly?

I created a transparent cube trigger and I placed it in front of closed doors, so whenever the player walks near the door a message appears saying "this door is locked".
However, I want the message to be gone whenever the player is Not pointing to the door. currently, it shows even when I turn around, the player needs to walk away from the door to make the message disappear.
Here is my code:
public class DoorsTrigger : MonoBehaviour
{
public GameObject partNameText;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
partNameText.SetActive(true);
}
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player"))
{
partNameText.SetActive(false);
}
}
}
How can I modify it to achieve my goal?
Here is a simple example using Vector3.Angle() to get the direction the player is facing relative to that trigger.
public class DoorsTrigger : MonoBehaviour
{
public GameObject partNameText;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
//Assuming 'other' is the top level gameobject of the player,
// or a child of the player and facing the same direction
Vector3 dir = (this.transform.position - other.gameObject.transform.position);
//Angle will be how far to the left OR right you can look before it doesn't register
float angle = 40f;
if (Vector3.Angle(other.gameObject.transform.forward, dir) < angle) {
partNameText.SetActive(true);
}
}
}
private void OnTriggerExit(Collider other)
{
//Make sure the text is actually showing before trying to disable it again
if (other.CompareTag("Player") && partNameText.activeSelf)
{
partNameText.SetActive(false);
}
}
}
Alternatively, you can keep your trigger mostly as is, and do a ray cast from the player to see if they are looking at the door.
//Put this snippet in a function and call it inside the player's Update() loop
RaycastHit hit;
Ray ray = new Ray(this.transform.position, this.transform.forward);
if(Physics.Raycast(ray, out hit))
{
//Tag the gameObject the trigger is on with "Door"
if(hit.collider.isTrigger && hit.collider.CompareTag("Door"))
{
//Here is where you would call a function on the door trigger to activate the text.
// Or better would be to just have that code be on the player.
// That way you can avoid unnecessary calls to another gameObject just for a UI element!
}
}

Physics OverlapSphere does not detect Collision

I wanted to create an collider on my players sword, that if he attacks that he would detect and via an animation event turn off/on an gameobject called (Damage Point) who was an script attached that would subtract the enemys health.
But somewhere it does not detect correctly. I tried to add the OnDrawGizmos function to see my sphere collider but even after making it bigger it does not detect.
The most strange thing about my issue is that im using the same code for my monster chest and my fantasy player, but for the chest it works but for the player it does not.
I created a class called PlayerDamage that is attached on an empty gameobject to the tip of my sword.
{
public class PlayerDamage : MonoBehaviour
public int damageAmount = 2;
public LayerMask enemyLayer;
void Update()
{
Collider[] hits = Physics.OverlapSphere(transform.position, 1.7f, enemyLayer);
if (hits.Length > 0)
{
if (hits[0].gameObject.tag == MyTags.ENEMY_TAG)
{
print("COLLIDED WITH ENEMY");
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, 1.7f);
}
In another script called PlayerScript that is attached directly to the player I have a function called Attack:
void Attack()
{
if (Input.GetKeyDown(KeyCode.K))
{
if (!anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.ATTACK_ANIMATION) || !anim.GetCurrentAnimatorStateInfo(0).IsName(MyTags.RUN_ATTACK_ANIMATION))
{
anim.SetTrigger(MyTags.ATTACK_TRIGGER);
}
}
}
Also in the PlayerScript class there are two functions called ActivateDamagePoint and DeactivateDamagePoint, these are assigned to animation events for the attack animations.
void ActivateDamagePoint()
{
damagePoint.SetActive(true);
}
void DeactivateDamagePoint()
{
damagePoint.SetActive(false);
}
I double checked that everything is on his layer and that the tags are okay, but that did not solve my problem.
Like I said before for the chest I use the same code and it works, but unfortunately it does not work with my player. I also have the Activate and Deactivate DamagePoint functions in there and these are also called by the animation event for my chest attack animation.
Okay after 1hour of debugging I finnaly found the solution:
It was in the MyTags class (a helper class for all the string variables), the error was:
public static string ENEMY_TAG = "Enemey";
instead of:
public static string ENEMY_TAG = "Enemy";

How to move a gameobject in Unity with the click on UI button when otherwise I would need the Update function to check for new inputs?

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.

Click and object, then click the ground, make object move to that position

Hi there I'd like to be able to click on an object then click a position on a plane and would like my object to lerp to that mouse clicked position. The problem is doing this for more than one object becomes tricky. Anybody got any ideas? so far I have followed the tutorial on Coroutines on unity3D's website under the advanced scripting tutorials. here is the code:
attached to the game object:
private Vector3 target;
public float smoothing = 7f;
public Vector3 Target
{
get{return target;}
set
{
target = value;
StopCoroutine("Movement");
StartCoroutine("Movement",target);
}
}
IEnumerator Movement(Vector3 target)
{
while (Vector3.Distance(transform.position,target) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target, smoothing* Time.deltaTime);
yield return null;
}
}
attached to the plane:
public propertiesandcoroutines coroutinescript;
private float Deltapos = 0.5f;
private GameObject collobj;
public void OnMouseDown()
{
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
Physics.Raycast (ray, out hit);
if (hit.collider.gameObject == gameObject)
{
coroutinescript.Target = new Vector3( hit.point.x,hit.point.y + Deltapos,hit.point.z);
}
}
this code works perfectly for one game object. how can I change this to work for a game object that I click on?
You just need to add a couple of lines to each script to get the result you want.
To the script attached to your moveable GameObjects
//This is the same type as the script attached to your plane. Rename it
//to whatever it's actually called (I've just called it PlaneScript). Also
//drag the plane into this field for each of the moveable GameObjects in the inspector
public PlaneScript planeScript;
//Paste this method anywhere inside the script attached to the GameObjects
void OnMouseUpAsButton () {
planeScript.SetObjectToMove(this);
}
Now, in the script attached to your plane, add the following
public void SetObjectToMove (propertiesandcoroutines script) {
coroutinescript = script;
}
How it works
Basically, what we're doing here is when one of your GameObjects are clicked, we reassign the "coroutinescript" variable in the script attached to the plane.
So, when the plane is clicked, the last GameObject that was clicked on before clicking the plane is moved.

Turn Sub-Object Code (raycast & mouse click & sub-objects)

Game characters are going somewhere that is clicked with the mouse
My problem : the code only works over a GameObject. I want to use one in the ground in the box, it can not.
I use a line of code :
public GameObject obj;
void Update() {
if(Input.GetMouseButton(0)) {
RaycastHit rayHit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (obj.collider.Raycast (ray, out rayHit, Mathf.Infinity)) {
transform.position = rayHit.point;
renderer.material.color = Color.green;
}
}
}
Map as the only model so I need to do. prevents me from making changes to my map
I need to call the individual sub-objects. but I could not.
Translating google translate :)
Try using Physics.Raycast(ray, out rayHit). This will raycast from your screen into the world. Then you can check the type of the object:
if (rayHit.collider.GetType() == typeof(YourTypeHere))
{
doSomething();
}