Can't Perform an event inside a Collider OnCollisionEnter - c#

I have a GameObject which when picked up and collides with a certain collider(detect) then it should drop the object and proceed to perform a specific event (Pouring a liquid) inside the collider.
The problem i have is that when on collision, the gameobject is not able to stay inside the collider. It gets thrown out when on Collision enter.
Both the collider and the picked up gameobject have rigidbodies because for the picked gameobject the isKinematic is set to true due to the liquid it holds/carries. Therefore for an OnCollisionEnter event to happen one of the colliders should have a non kinematic rigidbody as from the Unity Doc.
I tried changing the project settings from the phyiscs section to Enable contacts and see if the rigidbody maybe the cause, this is after removing a rigidbody from the detect collider but it was still a dead end. This came about from my research using this forum in unity.
So the question is what should i do to make the gameobject perform the event while inside the collider?
Does the Rigidbodies from the two colliders (picked gameobject and detect) make this not happen?
This is what the object should do on Collision enter!
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("FunnelPour"))
{
Debug.Log("Ready To Pour!");
if (interactor.currentlyPickedUpObject != null)
{
interactor.DropObject();
}
rb.useGravity = false;
StartCoroutine(startPour());
}
}
The action or event on how the object should work is under the PourFunnel method.
void PourFunnel()
{
RaycastHit raycastHit;
if (Physics.Raycast(transform.position, transform.forward, out raycastHit, distanceOfRaycast, detectPour))
{
Debug.Log("Pour the beaker to left");
isPouring = true;
transform.position = positionToPourLeft;
rb.constraints &= ~RigidbodyConstraints.FreezeRotationX;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(leftRotationAmount, transform.right), Time.deltaTime * 50);
}
else if (Physics.Raycast(transform.position, -transform.forward, out raycastHit, distanceOfRaycast, detectPour))
{
Debug.Log("Pour the beaker to Right");
isPouring = true;
transform.position = positionToPourRight;
rb.constraints &= ~RigidbodyConstraints.FreezeRotationX;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(rightRotationAmount, transform.right), Time.deltaTime * 50);
}
}
IEnumerator startPour()
{
yield return new WaitForSeconds(0);
Debug.Log("Pour the beaker!");
PourFunnel();
yield return new WaitForSeconds(timeToResume);
Debug.Log("Position the beaker to normal!");
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.identity, Time.deltaTime * 100);
yield return new WaitForSeconds(0.5f);
rb.constraints = RigidbodyConstraints.FreezeRotation;
isPouring = false;
rb.useGravity = true;
Debug.Log("Position the Beaker back to where it was picked up!");
transform.position = positionToDrop;
}

It sounds a bit like you would want to use the collider as a trigger (tick on IsTrigger) and then use the function OnTriggerEnter in stead of OnCollisionEnter.
When two objects hit each other (their colliders enter each other) AND none of the colliders are marked as triggers AND at least one of the objects have a (non-kinematic) RigidBody attached, we will get a 'collision'. This collision will trigger OnCollisionEnter but it will also make Unity's physics engine see this as a physical collision and move the involved non-kinematic RigidBodies according to the collision details. This is why your game object gets 'thrown' away when you collide with it.
If two objects hit each other AND at least one of their colliders is marked as trigger AND at least one of the objects has a RigidBody attached (can be kinematic or not) then the function OnTriggerEnter will be triggered. When one of the involved colliders is marked as trigger, Unity will not see it as a physical collision and they are free to move inside each other without the physics engine kicking in.

Related

Unity 2d player jumping infinitely

Im new to unity and this is my first game but my player is jumping infinitely and I've watched a lot of tutorials and still dont know how to fix it.
heres my code
public float moveSpeed = 5f;
void Update()
{
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
Vector3 characterScale = transform.localScale;
if (Input.GetAxis("Horizontal") < 0)
{
characterScale.x = 1;
}
if (Input.GetAxis("Horizontal") > 0)
{
characterScale.x = -1;
}
transform.localScale = characterScale;
}
void Jump()
{
if (Input.GetButtonDown("Jump"))
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 15f), ForceMode2D.Impulse);
}
}
}
There are a couple of things you have there. First you have Input.GetButtonDown("Jump") This means while the player is holding the button down it will execute that script inside the if statement, which applies a force. So this will run every frame and every frame while the player is holding down the button it will apply that force. You can try to do Input.GetButtonUp("Jump") which will be true when the player lets go of the button, then it will apply that force. Now you can keep the GetButtonDown its no problem if thats the feel you are going foor.
But the real problem and the second this is, you need to check if the player is touching the ground or not. If he is touching the ground then you can apply that force. If he is not touching the ground then dont apply the force.
There are couple of ways to go about this, the easiest way is to create a new Layer and call it Ground or something.
You click that drop down and click on Add layer .. then you can add a layer, then go back to ground gameobject and assign that layer to that. Now am assuming that the ground has a collider on it so the player doesnt go through it.
After that you need a reference to the player collider. In the Start() method you can add this:
private Collider2D myCollider;
void Start()
{
// This will get a reference to the collider 2d on the player
myCollider = GetComponent<Collider2D>();
}
void Update()
{
.
.
.
// This means the player is touching the ground layer.
if (myCollider.IsTouchingLayers(LayerMask.GetMask("Ground")))
{
if (Input.GetButtonDown("Jump"))
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 15f), ForceMode2D.Impulse);
}
}
}
So what this script does, it gets a reference to the player collider, and then checks if that player collider is touching the layer called ground. The ground needs to have a collider as well not just to prevent the player from fall through the level, but also to trigger this boolean to be true or false. True if they are touching each other, false if they are not. So, if they are not touching the ground then it doesnt matter how many times the player will press that button, he will not jump. Once they do then it applies the jump force if they are pressing Jump.

why my player rotates in unity

I attach a movement script to the player. with reference to the roller ball tutorial and modified it for jump. the problem is that when may player moves in any direction the player starts to rotate in that direction and even if i am standing still at the one position it starts to rotate and fall down the platform. the player has a rigidbody, boxcollider components.
void Awake ()
{
playerRigidbody = GetComponent<Rigidbody>();
Coll = GetComponent<CapsuleCollider>();
}
/*private void Update()
{
}*/
// Update is called once per frame
void FixedUpdate ()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool down = Input.GetKeyDown(KeyCode.Space);
if (down)
{
playerRigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Move(h, v);
}
private bool IsGrounded()
{
return Physics.CheckCapsule(Coll.bounds.center, new Vector3(Coll.bounds.center.x,Coll.bounds.min.y,Coll.bounds.center.z),Coll.height * 9f,Ground);
}
void Move (float h, float v)
{
movement.Set(h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
In your code,you use AddForce function,so I confirm your player rigidbody doesn't set Kinematic to true.
When your charactor collision with any other objects ,such as ground,wall, your charactor maybe rotate.
You could freeze rigidbody's rotation via inspector like Bijan says.
I don't recommand your use AddForce and MovePosition together.It may has other problem.
like this
This is due to the nature of the Rigidbody component. This component is very lifelike and when force is added and removed, the object still has inertia and therefore the object is still moving.
When it comes to the rotation it is due to the fact that when a force is applied to an object, the object also acquires rotational motion (also known as angular motion).
You can freeze the rotation using the constraints on the Rigidbody component.
Have a deeper look into this component [here].1
In my opinion, it is one of the core components of Unity and it is definitely worth looking into.
I hope I answered your question!

Unity multiple enemies, raycast dedection?

I got a script like this to create a raycast from maincamera and hit the enemy.
void FixedUpdate(){
if (fire) {
fire = false;
RaycastHit hit;
if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit, range)){
if (Enemy.distance < 80) {
if (hit.collider.tag == "body") {
Debug.Log ("Bullet in the body.");
Enemy.bodyshot = true; //Changing other script variable.
} else if (hit.collider.tag == "head") {
Debug.Log ("Bullet in the head.");
Enemy.headshot = true; //Changing other script variable.
}
}
}
}
}
Enemy Script in Update;
if (headshot) { //headshot variable is static to reach from other script.
anim.SetTrigger ("isDying");
speed = 0;
death = true;
}
if (bodyshot) { //bodyshot variable is static to reach from other script.
anim.SetTrigger ("isSore");
}
So, when I shoot an enemy, all enemies are dying at the same time. Because these scripts are attached to all enemies. I need to change bodyshot and headshot variables without using static. What can I do to separate them ?
When it comes to raycast, you only need the raycast script attached to an empty GameObject. It does not have to be attached to each individual Object that you want to perform the raycast on.
The only time you have to attach script to a GameObject you want to detect clicks on is when using the Unity EventSystem to detect clicks on the objects.
So, remove this script from other GameObjects and just attach it to one GameObject.
Note:
If you want to access or do something to the GameObject that the ray just hit, the GameObject is stored in the hit variable.
RaycastHit hit;...
Destroy(hit.collider.gameObject);
You can also access scripts attached to the Object hit:
hit.collider.gameObject.GetComponent<YourComponent>().doSomething();
Instead of checking to see what the tag of the collider is, you can first check to make sure the collider is one of the colliders attached to your object, and then check the tag for body location. For example:
public Collider[] coll;
void Start() {
coll = GetComponents<Collider>();
}
void FixedUpdate(){
if (fire) {
fire = false;
RaycastHit hit;
if (Physics.Raycast (fpsCam.transform.position, fpsCam.transform.forward, out hit, range)){
if (Enemy.distance < 80) {
if (hit.collider == coll[0] || hit.collider == coll[1]) {
if (hit.collider.tag == "head"){
or to simplify you can make coll[0] the head and [1] the body and ignore checking for a tag.
edit: As Programmer mentioned, this is not an efficient way to do this since you will be casting a ray for every object that has this script and you really only want to be casting a single ray.

Unity 2D C# - Collider not working

I am working on a 2D TopDown game in Unity 5. The collision is not working at all.
The player and the obstacle both have a 2D Collider and they are not Trigger. The player has a 2D Rigidbody with Kinematic set to false, gravity scale is equal to zero. Maybe the movement code has something to do with it.
The code is a bit long so I'll just show you the code for moving up:
if (Input.GetAxis ("Up") > 0) {
if (movingDown == false) {
posY += speed * Time.deltaTime;
movingUp = true;
}
} else {
movingUp = false;
}
/.../
transform.position = new Vector3 (posX, posY, 0);
It is always setting the value of the position as long as you are pressing the up button. Is there a way to fix this?
I think that the problem is that you are setting the position directly. So at each frame, you are telling unity exactly were the object should be, which overrides the position that would be computed from collision.
To fix this, you need to modify your movement code to add a force to your rigidbody and leave the position untouched ( see rigidbody doc, and function AddForce (https://docs.unity3d.com/ScriptReference/Rigidbody.html)
Try using
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
This replaces your system with a velocity-based system as updating the transform.postition of a rigidbody is not recommended. With the system you have, the collision is not being detected because the rigidbody isn't being updated.

Raycast can not detect collider

My bullet can not detect the collider and raycast can not detect the collision. It's very weird since the only way to get a message on the console is whenever I shoot bullets within the range of my terrain(either on or above), I instantly get "Terrain" printed on my console, but the raycast cannot detect any other objects and print anything, and if I go out of the range and shoot at a sphere, nothing gets printed. Everything in my game has a collider except the bullet.
Thanks!
Here's an image of my game .
void Update () {
if (Input.GetKey(KeyCode.KeypadEnter) && counter > delayTime)
{
Instantiate(bullet, transform.position, transform.rotation);
counter = 0;
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit))
{
Debug.Log(hit.collider.gameObject.name);
}
}
counter += Time.deltaTime;
}
Add a collider to your bullet prefab. You should have tags on your enemies or other destructible objects. Use OnCollisionEnter OR OnTriggerEnter. For enemies, I prefer to use OnCollisionEnter for the most part.
void OnCollisionEnter(Collision collision){//Assuming bullet touches enemy
if(collision.tag=="Bullet"){
// insert your code here for damage
}
}
As far as your RayCast, I'd do something like this:
Vector3 fwd = transform.TransformDirection(Vector3.forward) * 3; // length of ray
//forward-facing. ( * 3 is equal to 3 units/Meters )
Debug.DrawRay(transform.position, fwd, Color.red); // Can Make any color
if (Physics.Raycast(transform.position, fwd, out hit))
{
print(hit.collider.gameObject.name);
}
See, the reason I'd use collision detection is that as long as your enemy is tagged you'll make contact. Otherwise, your raycast should detect contact and you can still set collision damage or whatever.

Categories