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?
}
}
Related
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!
}
}
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;
}
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 make game with isometric view. When player comes into the house the roof of it hides and player can interact with NPCs, items, etc. But now it can interact with it even when roof is visible. How to detect that item is hidden by house roof or wall or another object?
void Update() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
if (Input.GetMouseButtonDown(0)) {
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
}
//Interact ...
}
}
}
You can simply check the distance between the hit wall/roof and NPC, from the ray-cast origin (camera). Like so:
private Camera cameraRef;
private void Awake() {
// P.S: Cache the 'Camera.main', calls to it can be expensive.
cameraRef = Camera.main;
}
void Update() {
if (Input.GetMouseButtonDown(0)) {
Ray ray = cameraRef.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
} else if (RaycastHitRoofOrWallFirst(hits, hit.collider.gameObject)) {
// This NPC is hidden behind a roof/wall.
continue;
}
// Interaction...
}
}
}
/// <summary>
/// Check if a target object is being hidden behind a roof/wall.
/// </summary>
/// <param name="hits">The hits that the raycast gotten.</param>
/// <param name="targetObject">The gameobject to check against.</param>
/// <returns>Return true if the target object is hidden, false if not.</returns>
private bool RaycastHitRoofOrWallFirst(RaycastHit[] hits, GameObject targetObject) {
foreach (RaycastHit hit in hits) {
if (hit.collider.CompareTag("roof") || hit.collider.CompareTag("wall")) {
float distanceFromCameraToObstacle = Vector3.Distance(cameraRef.transform.position, hit.collider.transform.position);
float distanceFromCameraToNPC = Vector3.Distance(cameraRef.transform.position, targetObject.transform.position);
// Check if the NPC is closer to the camera (raycast origin)
// compared to the roof or wall.
if (distanceFromCameraToObstacle < distanceFromCameraToNPC) {
// The roof/wall is closer to the camera (raycast origin)
// compared to the NPC, hence the NPC is blocked by the roof/wall
return true;
}
}
}
return false;
}
Here is a small visual diagram of what it should check for:
Or just use simple raycast...
If possible depending on the context, instead of using Physics.RaycastAll, you can use Physics.Raycast.
It returns the first object that the ray-cast hits.
Adding to this answer an alternative could maybe also be using OnBecameVisible
OnBecameVisible is called when the object became visible by any Camera.
This message is sent to all scripts attached to the Renderer.
and OnBecameInvisible
OnBecameInvisible is called when the Renderer is no longer visible by any Camera.
This message is sent to all scripts attached to the Renderer.
OnBecameVisible and OnBecameInvisible are useful to avoid computations that are only necessary when the object is visible.
For activating and deactivating the according NPC's colliders so the Raycast anyway will only work on visible objects in the first place.
Like on the NPCs have a script
public class InteractableController : MonoBehaviour
{
// you can also reference them via the Inspector
public Collider[] colliders;
private void Awake()
{
// pass in true to also get inactive components
if(colliders.Length = 0) colliders = GetComponentsInChildren<Collider>(true);
}
private void OnBecameInvisible()
{
foreach(var collider in colliders)
{
collider.enabled = false;
}
}
private void OnBecameVisible()
{
foreach(var collider in colliders)
{
collider.enabled = true;
}
}
}
However
Note that object is considered visible when it needs to be rendered in the Scene. It might not be actually visible by any camera, but still need to be rendered for shadows for example. Also, when running in the editor, the Scene view cameras will also cause this function to be called.
I am trying to instantiate a wall in front of a player when a button is clicked. I am using physics.checkbox for this because I want to check if there is any wall or gameObject already in front of my player. According to code, "Position not available" always gets printed in console ignoring the fact if there is any collider at the spawning position or not. I don't know if I am using it right or not. Help.
public class WallBuilder : MonoBehaviour
{
public GameObject horizontal, vertical;
public void BuildWallButton()
{
Vector3 verticalWallPosition = new Vector3(this.gameObject.transform.position.x, this.gameObject.transform.position.y + 3, this.transform.position.z+0.05f);
if (Physics.CheckBox(verticalWallPosition, vertical.GetComponent<Collider2D>().bounds.extents,Quaternion.identity))
{
Debug.Log("Available");
GameObject upWays = Instantiate(vertical, verticalWallPosition, Quaternion.identity);
}
else
{
Debug.Log(verticalWallPosition.y);
Debug.Log("Position not available");
}
//GameObject sideWays = Instantiate(horizontal, horizontalWallPosition);
}
}