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

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!
}
}

Related

how to detect which gameobject is clicked

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

How to create an object to follow my Player

I'm looking for a way to do something like this:
Gradius
In this game, orbs are following the player. How to do this in Unity where my orbs are following my Player.
Thanks!
A simple solution that i have thought is, lets say i have a player and it is holding it's previous position at start and whenever it moves lets say 5 units then we say to the object that will follow it to follow it's previous position and then we update player's previous position to it's current position and we follow same steps.
I have created a simple test scene and followed these steps :
I have created 2 game objects called Player and FollowPlayer
I have wrote the script below and attached it to the Player
public static event Action<Vector3> FollowMe;
[SerializeField] private float _followDistance;
private Vector3 _previousPosition;
private void Start()
{
_previousPosition = transform.position;
}
private void Update()
{
if(Vector3.Distance(transform.position,_previousPosition) > _followDistance)
{
if(FollowMe != null)
{
FollowMe.Invoke(_previousPosition);
}
_previousPosition = transform.position;
}
}
Then for the FollowPlayer object i have wrote the script below and attached to it
private void Start()
{
Player.FollowMe += OnFollowMe;
}
private void OnDestroy()
{
Player.FollowMe -= OnFollowMe;
}
private void OnFollowMe(Vector3 position)
{
transform.position = position;
}
Again, this is a simple follow script same logic in the Gladius game and i am sure you can use this idea and make it more generic and usable.

How to add collision physics in Unity Mapbox World Scale?

In World Scale AR, I want to move to an object I placed using SpawnOnMap. When I touch that object, I want that object to be erased. I made the Player have a script called PlayerController. I made a tag for the object so that when it touches, it should destroy or set it active. When I walk towards the object, nothing happens. I've tried OnTriggerEnter and OnCollisionEnter already. Is there some kind of method I'm missing that Mapbox provides?
public class PelletCollector : MonoBehaviour
{
public int count = 0;
// Start is called before the first frame update
void Start()
{
}
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Pellet")
{
count++;
Debug.Log("Collected Pellets: " + count);
other.gameObject.SetActive(false);
//Destroy(other.gameObject);
}
}
}

Destroy GameObject when its clicked on

I'm making a building mechanic in my game and I want to be able to clear out certain objects around the map (trees, other decor) so I have room to build. I've tried using a ray cast to find what object is being clicked on and destroy it but that doesn't work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectDestroy : MonoBehaviour {
// Start is called before the first frame update
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
Debug.Log (Input.mousePosition);
if (Physics.Raycast (ray, out hit)) {
if (hit.collider.gameObject == gameObject) Destroy (gameObject);
}
}
}
}
Here is a little example script:
public class Destroyable : MonoBehaviour
{
private void OnMouseDown()
{
Destroy(gameObject);
}
}
You can attach this script to the GameObject you want to destroy and then during Play-Mode you can click on it to destroy it. It is modifiable if you just need it in your In-Game-Editor.
Note: You need an active Collider on the same Gameobject.
Edit:
The following script shows an example for changing the color of the object:
public class Destroyable : MonoBehaviour
{
public Color mouseHoverColor = Color.green;
private Color previousColor;
private MeshRenderer meshRenderer;
private void Start()
{
meshRenderer = GetComponent<MeshRenderer>();
previousColor = meshRenderer.material.color;
}
private void OnMouseDown()
{
Destroy(gameObject);
}
private void OnMouseOver()
{
meshRenderer.material.color = mouseHoverColor;
}
private void OnMouseExit()
{
meshRenderer.material.color = previousColor;
}
}
You don't need to add this script on every object, just add it to a manager and also I think you are missing Raycast parameters.
To see where you ray is going you can use Debug.Ray()
Also, I would prefer you use #MSauer way since is much cleaner for what you want, just be sure the object contains a collider, I think they can be a trigger and the click will still happen.

Unity Jumping Issue - Character won't jump if it walks to the platform

(This is a 2D project)
I have a jumping issue where if my character WALKS into an X platform, she won't jump, but when she JUMPS ONTO the X platform, she can perform a jump.
For the platforms I am currently using 2 Box Collider 2Ds (one with "is trigger" checked)
For the character I am currently using 2 Box Collider 2Ds (one with "is trigger" checked) and Rigidbody 2D.
Below is the code for jumping and grounded I am currently trying to use.
{
public float Speed;
public float Jump;
bool grounded = false;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (grounded)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, Jump);
}
}
}
void OnTriggerEnter2D()
{
grounded = true;
}
void OnTriggerExit2D()
{
grounded = false;
}
}
Issue arises on the same part of every platform. (Each square represents a single platform sprite and they have all the same exact characteristics, since I copy pasted each one of them). Please check the photo on this link: https://imgur.com/a/vTmHw
It happens because your squares have seperate colliders. Imagine this:
There are two blocks : A and B. You are standing on block A. Now you try to walk on block B. As soon as your Rigidbody2D collider touches block B, your character gets an event OnTriggerEnter2D(...). Now you claim, that you are grounded.
However, at this moment you are still colliding with block A. As soon as your Rigidbody2D no longer collides with block A, your character receives OnTriggerExit2D(...). Now you claim, that you are no longer grounded. But in fact, you are still colliding with block B.
Solution
Instead of having bool variable for checking if grounded, you could have byte type variable, called collisionsCounter:
Once you enter a trigger - increase the counter.
Once you exit a trigger - decrease the counter.
Do some checking to make sure you are actually above the collider!
Now, once you need to check if your character is grounded, you can just use
if (collisionsCounter > 0)
{
// I am grounded, allow me to jump
}
EDIT
Actually, after investingating question further, I've realized that you have totally unnecessary colliders (I'm talking about the trigger ones). Remove those. Now you have only one collider per object. But to get the calls for collision, you need to change:
OnTriggerEnter2D(...) to OnCollisionEnter2D(Collision2D)
OnTriggerExit2D(...) to OnCollisionExit2D(Collision2D)
Final code
[RequireComponent(typeof(Rigidbody2D))]
public sealed class Character : MonoBehaviour
{
// A constant with tag name to prevent typos in code
private const string TagName_Platform = "Platform";
public float Speed;
public float Jump;
private Rigidbody2D myRigidbody;
private byte platformCollisions;
// Check if the player can jump
private bool CanJump
{
get { return platformCollisions > 0; }
}
// Called once the script is started
private void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
platformCollisions = 0;
}
// Called every frame
private void Update()
{
// // // // // // // // // // // // // //
// Need to check for horizontal movement
// // // // // // // // // // // // // //
// Trying to jump
if (Input.GetKeyDown(KeyDode.UpArrow) && CanJump == true)
Jump();
}
// Called once Rigidbody2D starts colliding with something
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions++;
}
// Called once Rigidbody2D finishes colliding with something
private void OnCollisionExit2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions--;
}
// Makes Character jump
private void Jump()
{
Vector2 velocity = myRigidbody.velocity;
velocity.y = Jump;
myRigidbody.velocity = velocity;
}
}
Here can be minor typos as all the code was typed inside Notepad...
I think there are a couple of issues here.
Firstly, using Triggers to check this type of collision is probably not the best way forward. I would suggested not using triggers, and instead using OnCollisionEnter2D(). Triggers just detect if the collision space of two objects has overlapped each other, whereas normal collisions collide against each otehr as if they were two solid objects. Seen as though you are detecting to see if you have landed on the floor, you don't want to fall through the floor like Triggers behave.
Second, I would suggest using AddForce instead of GetComponent<Rigidbody2D>().velocity.
Your final script could look like something like this:
public class PlayerController : MonoBehaviour
{
public float jumpForce = 10.0f;
public bool isGrounded;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void OnCollisionEnter2D(Collision2D other)
{
// If we have collided with the platform
if (other.gameObject.tag == "YourPlatformTag")
{
// Then we must be on the ground
isGrounded = true;
}
}
void Update()
{
// If we press space and we are on the ground
if(Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
// Add some force to our Rigidbody
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
// We have jumped, we are no longer on the ground
isGrounded = false;
}
}
}