I have two objects in my scene, one which only has a BoxCollider2D (the Column class) and the second object has a Rigibody2D as well as its own BoxCollider2D (Player class).
I added a script to the first object to have an OnCollisionEnter2D. I see it gets triggered when my second object collides with it, and it bounces my 2nd object back when it tries to enter.
I do see my OnCollisionEnter2D method getting called. But if I move my 2nd object again to my first object it gets bounced back again, however I don't see my OnCollisionEnter2D method getting called the 2nd time.
Is this intended behavior? If so, what method would get called every time a collision occurs between these two objects?
Note: I saw OnCollisionStay2D get called a few more times and then it stopped. I assume this is when it's bouncing my 2nd object out. I also see that OnCollisionExit2D never got called. I zoomed into the editor and saw clearly the green lines of the BoxCollider2D did not overlap between my objects so it should've exited the collision when it bounces it back.
public class Column : MonoBehaviour
{
private BoxCollider2D columnColl;
// Start is called before the first frame update
void Start()
{
columnColl = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
}
private void OnCollisionExit2D(Collision2D collision)
{
Debug.Log("Collision Exit");
}
void OnCollisionStay2D(Collision2D collision)
{
//Debug.Log("Collision Stayed");
}
void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Collision Happened");
}
}
and
public class Player : MonoBehaviour
{
public float xMoveSpeed = 1f;
private Rigidbody2D rbody;
// Start is called before the first frame update
void Start()
{
rbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("d"))
{
rbody.position = rbody.position + new Vector2(xMoveSpeed, 0);
}
}
}
You shouldn't "manually" change rbody.position.
Rather use Rigidbody.MovePosition
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
You also should rather do it in FixedUpdate which is used for the Physics instead of Update
void FixedUpdate()
{
if (Input.GetKeyDown("d"))
{
rbody.MovePosition(rbody.position + new Vector2(xMoveSpeed, 0);
}
}
Also make sure at least one of the objects is not kinematic as to OnCollisionEnter:
Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached.
Related
In my game I've a case that when the player is touching the collider of a specific GameObject and the player press the X key I want to change his position to the position of another GameObject.
To do that I use the following code
void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.X))
{
// Check Colliders collition
// End of Check Colliders collition
var playerRigidBody = GetComponent<Rigidbody2D>();
playerRigidBody.position = AnotherObject.transform.position;
}
}
I've realized that very often when I press the X key the player don't change his position to the position of the other GameObject. However if I move the code to the Update() method it works as expected (with no delay).
But I've read in the official documentation and in a lot of blogs that the code related to physics and Rigidbodies must be put in the FixedUpdate instead of Update
So the question is the following:
For this case putting the code in the Update() method is not recommended? (Note that I'm not using forces nor torques)
If it's not recommended then how can I assure that every time the player press the X key he will change his position to the other GameObject position in FixedUpdate()?
In general if you want a smooth movement you should rather use MovePosition in FixedUpdate otherwise everything regarding Physics (so also RigidBody) you should always do in FixedUpdate (as explained in the API there as well).
You should however catch the GetKeyDown in Update and split those two things:
bool wasClicked;
RigidBody playerRigidBody;
void Start()
{
// do this only once!
playerRigidBody = GetComponent<Rigidbody2D>();
}
void Update()
{
// Catch user input here
if (Input.GetKeyDown(KeyCode.X))
{
wasClicked = true;
}
}
void FixedUpdate()
{
// Handle physics here
if(!wasClicked) return;
playerRigidBody.position = AnotherObject.transform.position;
wasClicked = false;
}
I have a script that is a attached to a box, the box has two colliders on for physics and one (slightly bigger) for detecting OntriggerEnters when my player walks over it. I have a script attached to the box that does the following:
public class ColorChangeCollision : MonoBehaviour {
private GameObject Element;
private Color32 BaseColor, CollisionColor;
private Material ElementMaterial;
void Start () {
Element = this.gameObject;
BaseColor = new Color32(255,255,255,255);
CollisionColor = new Color32((byte)Random.Range(0, 255),(byte)Random.Range(0, 255),(byte)Random.Range(0, 255), 255);
ElementMaterial = Element.gameObject.GetComponent<Renderer>().material;
}
private void OnControllerColliderHit(ControllerColliderHit other)
{
Debug.Log("collision...");
ElementMaterial.color = CollisionColor;
}
private void OnTriggerEnter(Collider other)
{
Debug.Log("enter");
}
private void OnTriggerStay(Collider other)
{
Debug.Log("staying..");
}
private void OnTriggerExit(Collider other)
{
Debug.Log("left...");
ElementMaterial.color = BaseColor;
}
}
Main problem OnTriggerEnter or OnControllerColliderHit is never called nor are the other trigger events..
See below for an image of the setup of the box, and its components:
See here for my player who should be calling the OntriggerEnter or OnControllerColliderHit function of the box:
EDIT I modified all the elements to the suggestions of #Programmer. But the OnControllerColliderHit event is still not being called.. (note this function is attachted on the box )
There are 3 issues on how you setup your scene and your code:
1. You are missing a Rigidbody. This should be attached to the "Cube" GameObject since it's not ok to attach Rigidbody to GameObject with a CharacterController.
2. You have two BoxColliders attached to one GameObject ("Cube"). Do not do this. This can cause many issues including a callback function being called multiple times or non at-all.
What to do when you need both trigger and non trigger collider?
Create an empty child GameObject put the trigger there. You can do that.
3. You are using CharacterController so you should remove the OnTriggerXXX callback functions. When using CharacterController, it recommended to use it's callback function OnControllerColliderHit.
void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody body = hit.collider.attachedRigidbody;
......
}
4. CharacterController is not mean to be used as triggers. They are used to detect collisions.
You have two options to make it detect triggers:
A. Remove the isTrigger from the "Cube" Object
Or
B. Add a child empty GameObject under the CharacterController then add the CapsuleCollider or any other collider to it. You don't have to mark it as isTrigger since the "Cube" is already marked as so. You also shouldn't have Rigidbody attached to this child collider.
Create a new layer named "CC" and make the CharacterController and the new child GameObject to be in the "CC" layer. Go to your physics collision matrix settings and make it so that "CC" layer cannot collide with "CC" layer. This will make sure that the CharacterController doesn't detect collsion from tis child object.
You can now use your OnTriggerXXX callback functions to detect collision between the CharacterController and the "Cube" object.
One of them needs to have rigidbody to detect collisions.
Already 2 days I'm trying to solve this problem but I can't.I'm trying to delete the unlimited spawning barriers consistently when they are collide with the boundary.The barriers are made from 5 cubes.Any ideas?
public List<GameObject> spawning=new List<GameObject>();
public Vector3[] positions = new Vector3[5];
public GameObject barrier;
public GameObject boundary;
void Start()
{
StartCoroutine (SpawnBarrier());
}
void Update()
{
if(true)
{
foreach (GameObject move in spawning)
move.transform.Translate (0f, 0f, -0.1f);
}
}
IEnumerator SpawnBarrier(){
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
yield return new WaitForSeconds (3f);
}
}
Your barriers are marked as isTrigger on their colliders therefore you can use OnTriggerEnter to detect when any of the barriers collides with the boundary.
You need to create new script, lets call that BoundaryDetector and attach it to the barrier prefab so that every instance of the barrier will have this script attached to it.
When OnTriggerEnter is called, check if the trigger is made by the boundary. This can be done by checking for the "Player" tag since boundary is tagged as Player in your screenshot. If the detected trigger tag is "Player", first remove the spawning from the spawning List then Destroy it.
The BoundaryDetector script is as below (Must be attached to the barrier prefab):
ScriptFromYourQuestion yourInstancingSript;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectScriptInYourQuestionIsAttachedTo");
yourInstancingSript = obj.GetComponent<ScriptFromYourQuestion>();
}
void OnTriggerEnter(Collider other)
{
//Detect if we collided with the boundary
if (other.CompareTag("Player"))
{
//Remove Self/barrier from the List
yourInstancingSript.spawning.Remove(this.gameObject);
//Delete Self/barrier
Destroy(this.gameObject);
}
}
Note: The ScriptFromYourQuestion should be replaced with the name of the script in your question.
MUST DO:
To get the code above working, the following changes must be made in the Update function of the script from your question:
1.You must attach Rigidbody to at-least one of the Objects (boundary or barrier). In this case, I suggest you do so to the barrier prefab.
2.You must remove move.transform.Translate (0f, 0f, -0.1f); and replace it with Rigidbody.MovePosition and use that to move your barries because you have now attached Rigidbody to your barries and this is the proper way to move a Rigidbody Object.
Maybe something like this:
Your Update function in the code form your quesion should looks something like this:
public float speed = 100.0f;
void Update()
{
if (true)
{
foreach (GameObject move in spawning)
{
//Get Rigidbody component
Rigidbody rb = move.GetComponent<Rigidbody>();
//Calculate Z-axis pos to move to
Vector3 pos = new Vector3(0, 0, 1);
pos = pos.normalized * speed * Time.deltaTime;
//Move with Rigidbody
rb.MovePosition(rb.transform.position + pos);
}
}
}
Your problem is that you have an infinite loop while true that you're not breaking out of. Instead you should add a counter. You haven't explained what you want to do but my example is that it will breakout once it completes 10 instantiations of 5 blocks.
IEnumerator SpawnBarrier(){
spawnCount=0
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
if (++spawnCount==10)
{
break;
}
yield return new WaitForSeconds (3f);
}
}
If I understood corrently, you want to destroy barriers that collided with some kind of boundary?
If the 'boundary' is the visible screen space
In this case, we can assume that, once the object is invisible to MainCamera it can be counted as out of the boundary. Keeping that in mind, we can use OnBecameInvisible() method, which is method (or message, as stated in Unity Scripting API) called by MonoBehaviour class:
// A piece of barrier that gets destroyed once out of screen
public sealed class BarrierPiece : MonoBehaviour
{
// Called once the object is no longer visible to any (scene editor included) camera
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
NOTE: if you have more than one Camera in your scene, the object must be invisible to all of the cameras in order for the OnBecameInvisible() to be called.
If the 'boundary' is other object
In this case, there are many approaches based on your game:
Create GameObject with Rigidbody component and add a script which would have OnTriggerEnter(...) method and destroy colliding barriers in it.
(Inefficient) have two static Vector2 variables, one for pivotPosition, other for maximumOffset (this one should be squared for faster calculations). Then, have script on each of the barriers and in Update() checks whether the object's squared distance to the pivotPosition is greater than maximumOffset and call Destroy(gameObject) if the condition is true.
(others) ...
Those are two that came up quickly in my mind.
Hope this helps!
EDIT
Didn't look at the images...
So for your solution, you would need to assign Rigidbody component to the Boundary and add BoxCollider component on all of the barriers. Now, create a tag, called Barrier and tag all the barriers with it. Create a script:
// Destroys barriers on collision
public sealed class BarrierBoundary : MonoBehaviour
{
// Called once the script is created
// Checks if the object has Rigidbody component attached
private void Awake()
{
Debug.Assert(GetComponent<Rigidbody>() != null);
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Barrier")
Destroy(collision.collider.gameObject);
}
}
(The code wasn't tested, so it can have some typos.)
Now, assign the script to the Boundary. And voila! Barriers are destroyed once hitting the boundary.
Hope (again) this helps!
This code is for an elevator-type platform, where once the player stands on it, it 'takes' the player up by adding force onto it.
The thing is, while the force is created, the rigidbody (the player) does not move when the elevator moves. The code was written in C#, using Unity 5. In the code, the player is assigned the public 'rb', and contains a rigidbody.
The animation is a simple animation clip that moves the elevator up. Any ideas? Thank you for your time and answers in advance.
The elevator is Kinematic, the Player is not.
using UnityEngine;
using System.Collections;
/*This script activates when the player steps on the elevator, as it takes them up a floor.*/
public class ElevatorMovementScript : MonoBehaviour
{
private bool elevatorUp = false;
public Animation anim;
public int elevatorDelay = 5;
public int force = 800;
public Rigidbody rb;
// Use this for initialization
void Start ()
{
anim = GetComponent<Animation>();
}
// Update is called once per frame
void Update ()
{
}
/*Checks if the player has stepped onto the elevator. If the player has, it waits five seconds, and then pushes the player up.*/
void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player" && !elevatorUp)
{
Invoke("AnimationPlay",elevatorDelay);
elevatorUp = true;
}
}
/*Plays the animation of the player going up. Used for the 'Invoke' method.*/
void AnimationPlay()
{
rb.AddForce(transform.up * force);
Debug.Log (transform.up * force);
anim.Play ("Up");
}
}
It looks like this script is on your elevator's game object, in which case this line:
rb.AddForce(transform.up * force);
Will try to apply a force to the elevator, not the player. You've got to keep track of the player's rigidbody or somehow get it on demand in AnimationPlay.
You said that
the player is assigned the public 'rb'
But rb = GetComponent<Rigidbody>(); will ignore this and use the rigidbody attached to the gameobject that ElevatorMovementScript is attached to.
I have a invicible GameObject called trigger, and when my Hero collides with it, a chandelier falls.
I want to give the chandelier a Rigidbody so it falls, and you can collide with it and maybe use it.
If you can explain to me how collision works and show how to do something if to gameObject collides its would realy cool still new at Unity.
using UnityEngine;
using System.Collections;
public class Collider : MonoBehaviour {
public GameObject chandelier;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
//When my hero collides with trigger go to the fall function
void OnTriggerEnter (Collider other) {
if (other.tag == "Trigger")
{
fall();
}
}
//Add Rigidbody to the GameObject called chandelier
void fall ()
{
chandelier.rigidbody.AddForce(1, 1, 1);
}
}
For collision to work at least one of your colliding gameObjects must have a rigidbody attached to it, since you using OnTriggerEnter method, the following setup should work:
First object:
-collider (Marked as trigger)
-rigidbody (Marked as kinematic)
Second object
-collider (Marked as trigger)
Then when the objects collide, "OnTriggerEnter" method will get called and you can add the physical
rigidbody to the second object, please notice that you cannot add a rigid body as you mentioned in your post
// This will cause to Exception if there is no rigidbody attached
chandelier.rigidbody.AddForce(1,1,1);
So basically you have to options:
-Add a rigidbody to your game object through Unity editor and set it to kinematic, then remove "Is kinematic" flag in your collision method, like this:
rigidbody.isKinematic = false;
-Add the rigidbody after collision using the following code:
gameObject.AddComponent< Rigidbody >();
Afterwards you can add the required force or if you only want it to fall, simply add gravity to the rigidbody:
rigidbody.useGravity = true;