So, I'm making a coin system. When a player collides with trigger collider, i want it to disable only the object it collided with.
this.SetActive(false);
Finding object by name
void OnTriggerEnter (Collider other)
{
if (other.tag == "Coin")
{
this.SetActive(false);
}
}
/
You were very close with your original solution, but you may be misunderstanding what is actually happening here. So I've documented my solution to show the difference.
/* The following script is called when a Rigidbody detects a collider that is
* is set to be a trigger. It then passes that collider as a parameter, to this
* function.
*/
void OnTriggerEnter (Collider other)
{
// So here we have the other / coin collider
// If the gameObject that the collider belongs to has a tag of coin
if (other.gameObject.CompareTag("Coin"))
{
// Set the gameObject that the collider belongs to as SetActive(false)
other.gameObject.SetActive(false);
}
}
If you want the coin to be removed from the scene, because you don't expect it to ever be reactivated, then you can amend this code to the following:
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag("Coin"))
{
// Removes the coin from your scene instead
Destroy(other.gameObject);
}
}
Related
I am using a Player rigid body object and there are walls around the Player. These walls are restricting the Player to go through. The Player gets collided with these walls and then falls. The Player uses teleport function to jump from one area to next. Is there a way to make the Player jump to a position just outside the collision area after the Player is collided with these walls?
That is, Player A gets collided with the wall and does not jump to last position, but the position before the collision happened?
public GameObject Player;
public Vector3 PlayerPos;
public bool RecordPos = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(RecordPos == true)
{
PlayerPos = Player.transform.position;
}
}
public void OnTriggerEnter(Collider col)
{
if(col.gameObject.name == "Cube(3)" )
{
RecordPos = false;
Player.transform.position = PlayerPos;
}
}
In this script, the Player moves to last position it teleported from.
The "last" position before colliding is 1 frame before the collision. If you capture this position, the character will probably just fall on the obstacle again. Imagine you have a platform ___...___ and an obstacle. The easiest solution is to have 1 trigger at the left side of the obstacles and 1 trigger at the right side. If the player hasn't overcome the obstacle yet, he will be teleported to a chosen destination by you (before the obstacle) and if he's already overcome the obstacle, he will be teleported at the right side. __S_..._S__ (S stands for save/checkpoint trigger)
You need the following script on the gameobject with the Trigger collider. You also need to create a child object to the gameobject with the trigger:
private void OnTriggerEnter2D(Collider2D collision)
{
SaveManager.Instance.LastCheckpointOnHit = transform.GetChild(0).position;
}
And I suppose you have some sort of a singleton for data persistance. Now you can move the child gameobject whereever you want to teleport the player. And BTW I named the property LastCheckpointOnHit, because I was thinking of Holow Knight where if you get hit by spikes it instantly teleports you.
Then you just move the player: Player.transform.position = SaveManager.Instance.LastCheckpointOnHit;
In general when dealing with Rigidbody you shouldn't apply positions through the Transform component but rather through the Rigidbody.
I could imagine something like if you collide with a wall you get pushed away from the wall a bit in the direction where you came from like e.g.
[SerializeField] private float someDistanceThreshold = 0.01f;
[SerializeField] private Rigidbody _rigidbody;
private void Start()
{
if(!_rigidbody) _rigidbody = Player.GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
PlayerPos = _rigidbody.position;
}
public void OnTriggerEnter(Collider col)
{
// instead of the name I would rather use a tag later to cover all obstacles
if(col.gameObject.name == "Cube(3)")
{
_rigidbody.position -= (_rigidbody.position - PlayerPos).normalized * someDistanceThreshold;
// For stopping the player here
_rigidbody.velocity = Vector3.zero;
}
}
I am working on a simple game where the goal is to help a Player catch specific objects with a tag "Present".
After taking care of the models, animations I am now working on the collisions and counting UI.
For the collisions, on my Player Controller (I am using the ThirdPersonUserController from the player Unity Standard Assets - which simplified the whole animation process), I added the following functions:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count == 0)
{
winText.text = "Thanks to you the Christmas will be memorable!";
}
}
However, like this, when the Player hits an object with the tag "Present", even though the counting is working properly, the player disappears.
I have tried to change the OnCollisionEnter to an OnTriggerEnter, like this:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
However when the Player hits the objects with the tag "Present", they don't disappear.
My Player has a Capsule Collider and a Rigidbody.
The objects with the tag "Present" have a Box Collider and a Rigidbody.
Any guidance on how to make my Player stay in scene while removing the other objetcs and reducing the count is appreciated.
Few things. You are destroying the incorrect game object:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject); // this is destroying the current gameobject i.e. the player
count = count - 1;
SetCountText();
}
}
Update to:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject); // this is destroying the other gameobject
count = count - 1;
SetCountText();
}
}
Always use CompareTag() which is optimized for performance.
Setting the Collider IsTrigger property will then make use of the OnTriggerEnter events and not the OnCollisionEnter anymore.
On the first physics update where the collision is detected, the
OnCollisionEnter function is called. During updates where contact is
maintained, OnCollisionStay is called and finally, OnCollisionExit
indicates that contact has been broken. Trigger colliders call the
analogous OnTriggerEnter, OnTriggerStay and OnTriggerExit functions.
See the docs
Your present object is using a non-trigger collider, so you should use OnCollisionEnter. The CompareTag call you tried in OnTriggerEnter is preferable. You should use that.
Also, you are currently trying to destroy the gameobject that the ThirdPersonUserController is attached to (gameObject). Instead, destroy the gameobject of the colliding collider (other.gameObject) with Destroy(other.gameObject);:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
In my platform game I have just added some checkpoints, so that if the player dies doesn't necessarily spawn at the beginning of the track.
ghfdghdggfhfg
using UnityEngine;
public class CheckPoints : MonoBehaviour
{
[SerializeField] private Grounded game;
void Update()
{
transform.Rotate(0, 0, 5);
}
private void OnTriggerEnter() {
game.updatedCheckPointPosition = transform.position;
Destroy(this);
}
}
What I unsuccessfully tried to do is to set the public float variable of the Grounded script to the current position of the CheckPoint itself, which should be destroyed after doing that.
Any information or help on how to do this is really appreciated.
From Destroy
The object obj will be destroyed now or if a time is specified t seconds from now.
If obj is a Component it will remove the component from the GameObject and destroy it. [But keep the rest of the GameObject intact!]
If obj is a GameObject it will destroy the GameObject, all its components and all transform children of the GameObject.
this refers to the according component instance. What you want is rather
Destroy(gameObject);
OnTriggerEnter requires a parameter of type Collider in order to work
private void OnTriggerEnter(Collider other)
{
game.updatedCheckPointPosition = transform.position;
Destroy(this);
}
Note however that this way round the player has to be a trigger while the checkpoint a non-trigger! I would actually rather do it the other way round and make the chackpoint a trigger and rather let the player object check for OnTriggerEnter.
Is there a way to detect if the collider on the player (with a rigidbody) object is not colliding with any other collider in a 2D environment?
Not per se, but there are two ways you can get the information. It's easiest to keep an int counter, and increment it in OnCollisionEnter and decrement in OnCollisionExit. When the counter is 0, there are no collisions.
Another way, which will tell you which colliders are overlapping but not necessarily if they are touching, is to use a physics function. Note which type of collider you have--sphere, capsule, box. Knowing the size/shape/position of the collider, you can call Physics.OverlapBox, Physics.OverlapCapsule, or Physics.OverlapSphere. Give the function the same shape as your collider, and it will return the colliders that overlap that space. (For 2D colliders, you can use the Physics2D.Overlap* functions.)
/edit - actually piojo's idea with counters is better, just use int instead of bool.
One way would be to add an int to your script called collisions and set it to 0. Then if at any point the collider fires OnCollisionEnter just increment it, and in OnCollisionExit decrement it.
Something like this should work for 3D:
public int collisions = 0;
void OnCollisionEnter(Collision collision)
{
collisions++;
}
void OnCollisionExit(Collision collision)
{
collisions--;
}
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionExit.html
I don't understand what's with all the number keeping. I just did this for when my character jumps or falls off the edge and it works great.
Both my player and terrain have colliders. I tagged my terrain with a "Ground" tag.
Then check if I am currently in contact with the collider with OnCollisionStay()
void OnCollisionStay(Collision collision)
{
if (collision.collider.tag == "Ground")
{
if(animator.GetBool("falling") == true)
{
//If I am colliding with the Ground, and if falling is set to true
//set falling to false.
//In my Animator, I have a transition back to walking when falling = false.
animator.SetBool("falling", false);
falling = false;
}
}
}
void OnCollisionExit(Collision collision)
{
if (collision.collider.tag == "Ground")
{
//If I exit the Ground Collider, set falling to True.
//In my animator, I have a transition that changes the animation
//to Falling if falling is true.
animator.SetBool("falling", true);
falling = true;
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "Obstacle")
{
//If I collide with a wall, I want to fall backwards.
//In my animator controller, if ranIntoWall = true it plays a fall-
//backwards animation and has an exit time.
animator.SetBool("ranIntoWall", true);
falling = true;
//All player movement is inside of an if(!falling){} block
}
}
I cast Raycast to only one existing Box collider in scene
if (Physics.Raycast(mousePositionInWorld, transform.forward, 10))
{
Debug.Log("Ray hit something");
}
I get message Ray hit something
But i never get trigger on the box collider
void OnTriggerEnter(Collider other) {
Debug.Log("Menu hit");
}
Target object is gameObject only with Box collider, and script for trigger checking
OnTriggerEnter (and other collider event methods) are only called if a collision actually takes place but not by casting a ray. To solve your problem it depends on the your use case.
If you want to react just before the real collision, you can enlarge your collider to be for example 1.5 in size of the mesh
If you need both cases i.e. react on direct collisions and in some other situations need to take some actions before, you should split your code, e.g.:
if (Physics.Raycast(mousePositionInWorld, transform.forward, 10)) {
doSomething ();
}
void OnTriggerEnter(Collider other) {
doSomething ();
}
void doSomething () {
}