How to check if i`m touching something in Unity - c#

I've got OnCollisionEnter2D function, but it say's only once, when i`m landing on ground, i want function that will return object name(or smthng like this) if i'm touching it, so if i'll stay on it it will say me about it not only once but every frame.

It's as simple as using the correct method OnCollisionStay2D which is called every frame while you are colliding with another object
Sent each frame where a collider on another object is touching this object's collider (2D physics only).
To be fair: Their example in that link is bullshit since it is for OnTriggerStay2D ^^
It could look like
private void OnCollisionStay2D(Collision2D collision)
{
Debug.Log($"I am touching {collision.gameObject.name}", this);
}
If instead you want to keep track of every touching object I would rather use something like
private HashSet<GameObject> _currentlyTouching = new HashSet<GameObject>();
private void OnCollisionEnter2D(Collision2D collision)
{
if(!_currentlyTouching.Contains(collision.gameObject))
{
_currentlyTouching.Add(collision.gameObject);
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if(_currentlyTouching.Contains(collision.gameObject))
{
_currentlyTouching.Remove(collision.gameObject);
}
}
private void Update()
{
var logString = new StringBuilder("I am touching ");
foreach(var touchingObject in _currentlyTouching)
{
logString.Append(touchingObject.name).Append(" ");
}
Debug.Log(logString.ToString(), this);
}

Related

How can I input and destroy the other gameObject on Trigger

I want to destroy a GameObject when it hits the trigger but with an input. No errors, but its not working in unity
using UnityEngine;
public class DestroyTile : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Tile"))
{
if(Input.GetKeyDown(KeyCode.Space))
{
Destroy(other.gameObject);
}
}
}
}
what should I do to make this work??
Very unlikely that you manage to hit the key in exactly the same frame the collision happens ;)
As a quick fix rather use OnTriggerStay
OnTriggerStay is called once per physics update for every Collider other that is touching the trigger.
public class DestroyTile : MonoBehaviour
{
// Called once every FixedUpdate for each trigger you are touching
private void OnTriggerStay(Collider other)
{
if (other.CompareTag("Tile"))
{
if(Input.GetKeyDown(KeyCode.Space))
{
Destroy(other.gameObject);
}
}
}
}
With above way there is only one last issue: OnTriggerStay is not actually called every frame but rather every FixedUpdate so you might miss a key press if it happened in a frame where FixedUpdate isn't called. Therefore in general getting User input within FixedUpdate (or other physics based continuous methods) is unreliable and should always be done in Update.
So instead you could/should do something like e.g.
public class DestroyTile : MonoBehaviour
{
private readonly HashSet<GameObject> _currentlyTouching = new HashSet<GameObject>();
private void OnTriggerEnter(Collider other)
{
if (!other.CompareTag("Tile") return;
// For some reason we still have the other object stored
if(_currentlyTouching .Contains(other.gameObject)) return;
// Store the other object
_currentlyTouching.Add(other.gameObject);
}
private void OnTriggerExit(Collider other)
{
// We don't have this other object stored
if(!_currentlyTouching.Contains(other.gameObject) return;
// remove this other object
_currentlyTouching.Remove(other.gameObject);
}
private void Update()
{
// Are we touching anything?
// This check is cheaper than Input.GetKeyDown
if(_currentlyTouching.Count <= 0) return;
if(!Input.GetKeyDown(KeyCode.Space)) return;
// destroy (all) the touched object(s)
foreach(var obj in _currentlyTouching)
{
Destroy(obj);
}
// Don't forget to also clear the HashSet in that case
_currentlyTouching.Clear();
}
}
This way you have the User Input separated in Update (every frame) and only track the entering and exiting colliders via the Physcis based system.

How do I properly make an IgnoreCollision command?

I have the code to where is resets when my main object hits another object. However it hits every single object and resets the scene. I'm attempting to make my main abject ignore a certain object. Here is what I have so far.
void OnCollisionEnter(Collision collision)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
if (collision.gameObject.tag == "BlueLaunchpad")
{
Physics.IgnoreCollision(BlueLaunchpad.collider);
}
}
}
i think you should try something like thins:
void OnCollisionEnter(Collider collider )
{
if (collider.gameObject.tag == "BlueLaunchpad")
{
Physics.IgnoreCollision(collider);
}
else
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
this way it shouldn't load the scene when it hits the BlueLaunchpad

OnCollisionExit is not being called

Why is OnCollisionExit not being called? I am using both OnCollisionEnter and OnCollisionExit but unfortunately only OnCollisionEnter is being called.
public bool HandleCollided = false;
public void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == "RightHandAnchor")
{
HandleCollided = true;
}
}
public void OnCollisionExit(Collision col)
{
if(col.gameObject.name == "RightHandAnchor")
{
HandleCollided = false;
}
}
It's impossible to tell why your code isn't working based the given snippet - this code depends on the configuration of each of the GameObjects' inspector windows in the editor.
Both the GameObject that this script is attached to and the colliding GameObject must have one Collider component attached to each of them (for example, a BoxCollider component or a SphereCollider component). Both Colliders must have their isTrigger checkboxes disabled. The GameObject that this script is attached to must also have a Rigidbody component attached.
In order to debug this situation, add Debug.Log() statements in your functions. This is generally good practice, and it might be that the function is being called but the conditional statement is not true.
Here are some additional ideas on what might be going wrong:
You may be changing the name of the colliding GameObject somewhere else in your code.
You may be destroying the GameObject.
It might be that neither function was being called and HandleCollided was being changed elsewhere in your code.
It might be that the parameter, col, is not what you expect.
public void OnCollisionEnter(Collision col)
{
Debug.Log("Collision Enter!");
Debug.Log(col.gameObject.name);
}
public void OnCollisionExit(Collision col)
{
Debug.Log("Collision Exit!");
Debug.Log(col.gameObject.name);
}
So you said "There are two objects that collides with each other. One has sphere collider attached to it and another has box collider. One of the objects have rigidbody attached as well." On which one is the code? and yes this matters! only 1 of the objects will keep track of the exit meaning that if it's the non-kinematic it will not work.
Unfortunately using OnCollisionExit did not work so instead I used OnTriggerEnter and OnTriggerExit. I activated "isTrigger" for both objects.
public void OnTriggerEnter(Collider col)
{
Debug.Log("entered");
if (col.gameObject.name == "RightHandAnchor")
{
HandleCollided = true;
}
}
public void OnTriggerExit(Collider other)
{
Debug.Log("exit");
if (other.gameObject.name == "RightHandAnchor")
{
print("No longer in contact with " + other.transform.name);
HandleCollided = false;
}
}
You need a non-kinematic rigidbody attached to your object to get the event for OnCollisionExit
Try to use OnCollisionEnter() and OnTriggerExit() !!
Example:
void OnCollisionEnter(Collision collision)
{
if((collision.gameObject.GetComponent<AttributeManager>().attributes & doorType) != 0)
{
this.GetComponent<BoxCollider>().isTrigger = true;
}
}
private void OnTriggerExit(Collider other)
{
this.GetComponent<BoxCollider>().isTrigger = false;
}

Destroy particle system when initalised programmatically

I have a game with a player and an enemy.
I also have a particle system for when the player dies, like an explosion.
I have made this particle system a prefab so I can use it multiple times per level as someone might die a lot.
So in my enemy.cs script, attached to my enemy, I have:
public GameObject deathParticle;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player" && !player.dead){
player.dead = true;
Instantiate(deathParticle, player.transform.position, player.transform.rotation);
player.animator.SetTrigger("Death");
}
}
So this plays my particle system when the player gets killed by an enemy. Now on my player script, I have this. This specific function gets played after the death animation:
public void RespawnPlayer()
{
Rigidbody2D playerBody = GetComponent<Rigidbody2D>();
playerBody.transform.position = spawnLocation.transform.position;
dead = false;
animator.Play("Idle");
Enemy enemy = FindObjectOfType<Enemy>();
Destroy(enemy.deathParticle);
}
This respawns the player like normal but in my project each time I die I have a death (clone) object which I don't want. The last 2 lines are meant to delete this but it doesn't.
I have also tried this which didn't work:
Enemy enemy = FindObjectOfType<Enemy>();
ParticleSystem deathParticles = enemy.GetComponent<ParticleSystem>();
Destroy(deathParticles);
There is no need to Instantiate and Destroy the death particle, that will create a lot of overhead, you can simply replay it when you want it to start and stop it, when you dont need it
ParticleSystem deathParticleSystem;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticleSystem.time = 0;
deathParticleSystem.Play();
}
public void RespawnPlayer()
{
//rest of the code
deathParticleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
}
or you can enable and disable the gameObject associated with your particle prefab
public GameObject deathParticlePrefab;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticlePrefab.SetActive(true);
}
public void RespawnPlayer()
{
//rest of the code
deathParticlePrefab.SetActive(false);
}
You could always create a new script and assign it to the prefab that destroys it after a certain amount of time:
using UnityEngine;
using System.Collections;
public class destroyOverTime : MonoBehaviour {
public float lifeTime;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
lifeTime -= Time.deltaTime;
if(lifeTime <= 0f)
{
Destroy(gameObject);
}
}
}
So in this case you would assign this to your deathParticle. This script is useful in a multitude of scenarios if you're instantiating objects so that you don't have a load of unnecessary objects.

Repeat function (unity3d/c#)

First of all, here's the code:
using UnityEngine;
using System.Collections;
namespace UltimateSurvival
{
public class Radiation : MonoBehaviour
{
public GameObject radiationEffect;
public EntityVitals Vitals { get { return m_Vitals; } }
private EntityVitals m_Vitals;
// Use this for initialization
void Start() {
InvokeRepeating ("OnTriggerEnter", 1.5f, 3.5f);
}
// Update is called once per frame
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
radiationEffect.SetActive(true);
//yield return new WaitForSeconds(5);
var entity = other.GetComponent<EntityEventHandler>();
if(entity)
{
var healthEventData = new HealthEventData(-Random.Range(7.0f, 23.0f));
entity.ChangeHealth.Try(healthEventData);
}
//yield return new WaitForSeconds(5);
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
radiationEffect.SetActive(false);
}
}
}
}
What I'm trying to do is that I want this script to execute OnTriggerEnter every 3.5 seconds. As you can see, I'm using InvokeRepeating but it seems like it doesnt work. I've also tried changing void OnTriggerEnter on IENumerator OntriggerEnter and then yield return new WaitForSeconds(5); - It didn't work either. I'm really confused D: Please help!
It seems you're trying to solve the problem of draining HP from the player if player is inside the area of radiation. This is a solution that will use most of your current code, but is not neccesarily the best code. I'd also like to inform you of OnTriggerStay, which
is called once per frame for every Collider other that is touching the trigger.
and can also be used to solve this problem. I'm going to use your already declared OnTriggerEnter and OnTriggerExit to damage every player inside area every 3.5 seconds.
public GameObject radiationEffect;
public EntityVitals Vitals { get { return m_Vitals; } }
private EntityVitals m_Vitals;
// Declare a list that will contain the players.
List<GameObject> playersInArea = new List<GameObject>();
// Use this for initialization
void Start() {
InvokeRepeating ("DamagePlayers", 1.5f, 3.5f);
}
void DamagePlayers() {
foreach (var player in playersInArea) {
var entity = player.GetComponent<EntityEventHandler>();
if(entity)
{
var healthEventData = new HealthEventData(-Random.Range(7.0f, 23.0f));
entity.ChangeHealth.Try(healthEventData);
}
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
playersInArea.Add(other.gameObject);
radiationEffect.SetActive(true);
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
playersInArea.Remove(other.gameObject);
if (playersInArea.Count == 0) {
radiationEffect.SetActive(false);
}
}
}
Something like that should work. If you only have 1 player it should work all the same, but this supports multiple players. Let me know if you have any further issues.
You have problems calling your method using InvokeRepeating because of two reasons:
InvokeRepeating can not be used with method that have parameters: you probably have the Trying to Invoke method: [...] couldn't be called. message in your console.
OnTriggerEnter is a method that is automatically called by Unity when the gameobject's collider is set as Trigger and another collider enters it: as #Hellium said, calling such methods manually is a bad idea (same as calling Start or Update manually: this can happen but really doesn't make sense in most of the scenarios).
Hope this helps,

Categories