I spawn objects that have laser beam property. When I click on one of them(specific object), I want it to only show its laser beam not the others.
How can I prevent it? I have a static GameObject variable (touch_detect.clickedObject) by which I can determine which object is clicked.
using UnityEngine;
using System.Collections;
public class Laser : MonoBehaviour
{
private LineRenderer lr;
private bool clicked = false;
RaycastHit rh;
// Use this for initialization
void Start()
{
lr = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(ray, out rh, Mathf.Infinity))
{
if (rh.collider.gameObject.name == touch_detect.clickedObject.name)
{
Debug.Log(rh.collider.gameObject.name + " clicked.");
Debug.Log("static object name" + touch_detect.clickedObject.name + " clicked.");
clicked = true;
lr.enabled = true;
}
}
}
if (Input.GetMouseButtonUp(0))
{
if (Physics.Raycast(ray, out rh, Mathf.Infinity))
{
if (rh.collider.gameObject.name == touch_detect.clickedObject.name)
{
Debug.Log(rh.collider.gameObject.name + " clicked.");
Debug.Log("static object name" + touch_detect.clickedObject.name + " clicked.");
clicked = false;
lr.enabled = false;
}
}
}
if (clicked)
{
lr.SetPosition(0, transform.position + new Vector3(0, 0, 0));
RaycastHit hit;
if (Physics.Raycast(transform.position + new Vector3(0, 0, 0), -transform.up, out hit))
{
if (hit.collider)
{
lr.SetPosition(1, hit.point);
}
}
else lr.SetPosition(1, -transform.up * 5000);
}
}
}
The issue is that since this script is attached to both of your gameobjects, there are two rays being cast at the mouse position (one from each script). Because you are just looking to see that the raycollider matches the static object, this statement is true for both scripts no matter which you click on:
if (rh.collider.gameObject.name == touch_detect.clickedObject.name) // always true
To get an immediate fix, you should change the above statement to something like this to check that the ray is intersecting the same gameobject that the script is attached to:
if (rh.collider.gameObject.name == gameObject.name)
This really is not the best method though since you are still casting two rays and therefore doing all the logic twice (or more times if you spawn more cubes).
A better method would be to have one master gameobject that casts the ray. When this ray intersects a cube, you would then activate a method inside that cubes script to show the laser. So for example:
on the master object you would have:
if (Physics.Raycast(ray, out rh, Mathf.Infinity))
{
// add a tag to all objects with the laser script
if (rh.collider.gameObject.tag == "hasLaser") //verify object has laser script via tag
rh.collider.GetComponent<laser>().activateLaser(); // call public method in collider script
}
and then the cube would have the laser script with a public method:
public void activateLaser()
{
lr.enabled = true;
}
Related
So I have UI elements that can be dragged, so that a prefab can be instantiated OnDragEnd(). I am using below code attached to the UI buttons to achieve this
public class MyButton : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
..
..
..
public void OnDrag(PointerEventData eventData)
{
dragRect.anchoredPosition += eventData.delta;
}
public void OnBeginDrag(PointerEventData eventData)
{
// if we're on deploy cooldown, dont allow dragging this button
if (disableDrag )
{
eventData.pointerDrag = null;
}
}
public void OnEndDrag(PointerEventData eventData)
{
RaycastHit hit;
Ray ray = mainCamera.ScreenPointToRay(eventData.pointerCurrentRaycast.screenPosition);
Physics.Raycast(ray, out hit);
if (hit.collider.gameObject.tag == "Ground")
{
createUnit(hit);
disableDrag = true;
}
}
}
This works fine without any problems, but recently I also implemented the ability to drag the camera. I attached the following code to my camera to achieve this:
public class CameraController : MonoBehaviour
{
public float movementTime;
public Vector3 newPosition;
private Vector3 startPosition;
public Vector3 dragStartPosition;
public Vector3 dragCurrentPosition;
public float xLimit1, xLimit2;
float clampedX;
Camera mainCamera;
private void Start()
{
mainCamera = Camera.main;
newPosition = transform.position;
startPosition = transform.position;
}
// Update is called once per frame
void Update()
{
HandleMouseInput();
clampedX = Mathf.Clamp(newPosition.x, xLimit1, xLimit2);
transform.position = Vector3.Lerp(transform.position, new Vector3 (clampedX, startPosition.y, startPosition.z), Time.deltaTime * movementTime);
}
void HandleMouseInput()
{
// when mouse is pressed,
if (Input.GetMouseButtonDown(0))
{
if (!EventSystem.current.IsPointerOverGameObject())
{
Plane plane = new Plane(Vector3.up, Vector3.zero);
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
float entry;
if (plane.Raycast(ray, out entry))
{
dragStartPosition = ray.GetPoint(entry);
}
}
}
// if it is still held down
if (Input.GetMouseButton(0))
{
if (!EventSystem.current.IsPointerOverGameObject())
{
Plane plane = new Plane(Vector3.up, Vector3.zero);
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
float entry;
if (plane.Raycast(ray, out entry))
{
dragCurrentPosition = ray.GetPoint(entry);
newPosition = transform.position + dragStartPosition - dragCurrentPosition;
}
}
}
}
}
And the camera script works as well as it behaves as expected and the camera drags with mouse. But the problem is that when I drag my UI button onto screen that moves the camera also.
I tried to include the if condition of checking if its over a UI if (!EventSystem.current.IsPointerOverGameObject()) But this does not seem to work, the camera still drags when I drag the UI button.
How do I make them work together?
You are on the right path as
!EventSystem.current.IsPointerOverGameObject())
is the right way forward, but make sure to use it like this:
// Check if the left mouse button was clicked
if(Input.GetMouseButtonDown(0))
{
// Check if the mouse was clicked over a UI element
if(!EventSystem.current.IsPointerOverGameObject())
{
Debug.Log("Did not Click on the UI");
}
}
Make sure that the RaycastTarget toggle on your UI component is not enabled.
Also, check the Event Mask in the Physics Raycaster on your camera. IsPointerOverGameObject() will return true if the mouse is over any objects on those layers. If you only want to check for the UI layer, make sure that only UI is checked.
Here are some references:
EventSystem.IsPointerOverGameObject Docu.
EventSystem.IsPointerOverGameObject Manual
Thread that might help you
I have the following code that attempts to place a gameObject at the position of a left mouse button click on the scene view in unity. The code snippet is from a custom editor window. I can't figure out why when I click the gameobject gets instantiated to the left and above of where I click.
void CustomUpdate(UnityEditor.SceneView sv)
{
Event e = Event.current;
if ((e.type == EventType.MouseDrag || e.type == EventType.MouseDown) && e.button == 0)
{
if (PlaceObject)
{
RaycastHit hit;
Tools.current = Tool.Move;
if (Physics.Raycast(Camera.current.ScreenPointToRay(new Vector3(e.mousePosition.x, Camera.current.pixelHeight - e.mousePosition.y, 0)),
out hit, Mathf.Infinity, ~LayerMask.NameToLayer("Terrain")))
{
GameObject tee = AssetDatabase.LoadAssetAtPath("Assets/FSX Course Importer/Default Objects/Markers/TeeMarker/TeeMarker.prefab", typeof(GameObject)) as GameObject;
GameObject go = Instantiate(tee) as GameObject;
go.transform.position = hit.point;
go.transform.SetParent(TeeParent);
go.name = "Hole:" + HoleNumber + " Type:" + TeeType;
e.Use();
Undo.RegisterCreatedObjectUndo(go, "Undo placed Tee");
}
EditorWindow.GetWindow(typeof(CreateTeeWindow)).Close();
}
}
}
You Have To Replace
Physics.Raycast(Camera.current.ScreenPointToRay(new Vector3(e.mousePosition.x, Camera.current.pixelHeight - e.mousePosition.y, 0)
With
Physics.Raycast(Camera.current.ScreenPointToRay(Input.mousePosition))
And Unity Will Take Care About All The Matrix
Here's the updated code if anyone has a similar problem when working inside and editor window script
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, Mathf.Infinity, ~LayerMask.NameToLayer("Terrain")))
{
//code to place object at hit.point goes here
}
I'm currently making a sandbox game and it is able to create a object through left click. But, I am struggling to destroy a specific object when it is right clicked. I've looked on previous questions here, but they don't exactly answer my question.
using UnityEngine;
using System.Collections;
public class ControlObjects : MonoBehaviour
{
Vector3 mousePosition, targetPosition;
//To Instantiate TargetObject at mouse position
public Transform targetObject;
public GameObject Prefab;
float distance = 10f;
Ray ray;
RaycastHit hit;
//public int item_num = 1;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = targetPosition;
//To get the current mouse position
mousePosition = Input.mousePosition;
//Convert the mousePosition according to World position
targetPosition = Camera.main.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, distance));
//Set the position of targetObject
targetObject.position = targetPosition;
//Debug.Log(mousePosition+" "+targetPosition);
//If Left Button is clicked
if (Input.GetMouseButtonDown(0))
{
//create the instance of targetObject and place it at given position.
Instantiate(targetObject, targetObject.transform.position, targetObject.transform.rotation);
}
}
}
Implement what you need, but this is the base.
using UnityEngine;
public class Test : MonoBehaviour
{
private float distance = 10;
private float offset = -4;
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse1))
{
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
go.transform.position = new Vector3
{
x = offset += 1.5f,
y = 0,
z = 0
};
}
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if(Physics.Raycast(ray, out RaycastHit hit, distance))
{
Destroy(hit.transform.gameObject);
}
}
}
}
You should have a look at IPointerClickHandler. Attach this script to the objects you want to be able to click on
using UnityEngine;
using UnityEngine.EventSystems;
public class DestroyOnRightClick : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick (PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
{
Debug.Log ("Right Mouse Button Clicked on: " + name);
Destroy(gameObject);
}
}
}
Note
Ensure an EventSystem exists in the Scene to allow click detection. For click detection on non-UI GameObjects, ensure a PhysicsRaycaster is attached to the Camera.
How can I make a 3D object (a car) to move along the surface which was recognized by ARKit (Unity, C#)?
So that instead of finger tap I could use UI to manipulate the car, while the car will use the surface, recognized by ARKit, for movement and collide with it in case of any rigid body effects.
Is it possible to make it also collide with other surfaces (for example the walls)?
Something like this will allow you to tap on your object and drag to move it along a surface. In regards to collisions, etc - make sure your objects have appropriate colliders and rigidbody's. ARKit currently only provides horizontal surfaces, so not sure how to deal with wall collisions...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragAlongSurface : MonoBehaviour {
private bool holding;
void Start () {
holding = false;
}
void Update() {
if (holding) {
Move();
}
// One finger
if (Input.touchCount == 1) {
// Tap on Object
if (Input.GetTouch(0).phase == TouchPhase.Began ) {
Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch(0).position);
RaycastHit hit;
if (Physics.Raycast (ray, out hit, 100f)) {
if (hit.transform == transform) {
holding = true;
}
}
}
// Release
if (Input.GetTouch(0).phase == TouchPhase.Ended) {
holding = false;
}
}
}
void Move(){
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch (0).position);
// The GameObject this script attached should be on layer "Surface"
if(Physics.Raycast(ray, out hit, 30.0f, LayerMask.GetMask("Surface"))) {
transform.position = new Vector3(hit.point.x,
transform.position.y,
hit.point.z);
}
}
}
I have an empty gameobject on my scene with a component box collider 2D.
I attached a script to this game object with :
void OnMouseDown()
{
Debug.Log("clic");
}
But when i click on my gameobject, there is no effect. Do you have any ideas ? How can i detect the click on my box collider ?
Use ray cast. Check if left mouse button is pressed. If so, throw invisible ray from where the mouse click occurred to where to where the collision occurred.
For 3D Object, use:
3D Model:
void check3DObjectClicked ()
{
if (Input.GetMouseButtonDown (0)) {
Debug.Log ("Mouse is pressed down");
RaycastHit hitInfo = new RaycastHit ();
if (Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hitInfo)) {
Debug.Log ("Object Hit is " + hitInfo.collider.gameObject.name);
//If you want it to only detect some certain game object it hits, you can do that here
if (hitInfo.collider.gameObject.CompareTag ("Dog")) {
Debug.Log ("Dog hit");
//do something to dog here
} else if (hitInfo.collider.gameObject.CompareTag ("Cat")) {
Debug.Log ("Cat hit");
//do something to cat here
}
}
}
}
2D Sprite:
The solution above would work for 3D. If you want it to work for 2D, replace Physics.Raycast with Physics2D.Raycast. For example:
void check2DObjectClicked()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Mouse is pressed down");
Camera cam = Camera.main;
//Raycast depends on camera projection mode
Vector2 origin = Vector2.zero;
Vector2 dir = Vector2.zero;
if (cam.orthographic)
{
origin = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
else
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
origin = ray.origin;
dir = ray.direction;
}
RaycastHit2D hit = Physics2D.Raycast(origin, dir);
//Check if we hit anything
if (hit)
{
Debug.Log("We hit " + hit.collider.name);
}
}
}