I come here after some testing, and because after some googling, I was incapable of finding a straight answer.
Suppose I have a player character with a tag set to Player and a layer set to Humanoid. I also have a bunch of NPC performing patrol, wander, and other movement behaviors. Their tags are set to NPC and their layers to Humanoid. Since this is a 2D game using 2D physics, I used the Project Settings collision matrix to make it so that Humanoids do not collide with each other.
In other words, the NPCs and the player characters can pass through each other, while respecting gravity, force, impulse, etc... This is working fine. My problem arises when: I want some child GameObject of NPC which is in layer Default and has a CircleCollider2D of type Trigger and I want to detect that collision in order to perform some actions (e.g.: show dialogue, stop or switch AI Behaviour of the parent object, or others).
From my testing, this seems not to be working, but I might be doing something wrong. So my question is:
If a GameObject A ignores collisions against layer X, will collisions against a GameObject C in layer Y be ignored if C is a child of a GameObject P in layer X?
private void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.CompareTag("Player")) {
Debug.Log("Collided with player");
}
if (!eventConsumed && other.gameObject.CompareTag("Player")) {
var draw = Random.Range(0.001f, 1.0f);
if (draw > 1.0f - eventChance) {
dialogueEvent.Play();
dialogueEvent = null;
}
eventChance = Mathf.Clamp(eventChance * 1.33f, 0.0f, 1.0f);
}
}
In order to detect whether or not a physics collider that is set as a trigger has collided. You would need to use the OnTriggerEnter2D function instead of the OnCollisionEnter2D function.
Related
Well, while creating a kind of 2D platformer using Tilemaps for creating levels consisting of blocks I stumbled upon a problem with weird contact points I get on collision between my player's BoxCollider2D and TilemapCollider2D. I need to detect what tiles specifically the incoming collider is colliding with and because of this problem I often gain wrong tiles positions. Then I discovered that the very same problem of detecting contact points is actual for the collision of two BoxCollider2D so here is the test with them.
The last frame before the collision was detected. BoxCollider of a cube is perfectly aligned to the grid. Player's falling on collider from above (Collision Detection mode is Discrete).
The next frame. Player's collider changed it's position trying to get out of collider what is OK. Red dots are bottom corners of the collider from the previous frame and white dots are contact points returned at this frame from Collision2D.GetContacts(). We get two points, but the left one is out of collider bounds. I expected (and it seems quite logical) that all the points must be inside the colliders intersection area.
Here is the script attached to the Square that gets contacts and places dots in them:
public class Test : MonoBehaviour
{
[SerializeField] private BoxCollider2D _collider;
[SerializeField] private GameObject _contactDotWhite;
private ContactPoint2D[] _contacts = new ContactPoint2D[5];
private void OnCollisionEnter2D(Collision2D other)
{
other.GetContacts(_contacts);
for (var i = 0; i < other.contactCount; i++)
{
ContactPoint2D contact = _contacts[i];
GameObject go = Instantiate(_contactDotWhite);
go.transform.position = contact.point;
}
}
}
So, the questions are: how unity calculates these contact points? Is this a normal behaviour at all? If it's not, how do I fix it?
Basic Problem: Unity's physics engine produces weird collisions when a player is moving over a flat surface made of more than one Collider. The ghost collisions occur at the joints between Colliders, and express as two behaviors:
This seems to be a problem with physics engines in general, based on this talk by Bennett Foddy:
https://www.youtube.com/watch?v=NwPIoVW65pE&ab_channel=GDC
Game Specifics:
In my case, the player is moving through a procedurally generated wormhole, composed of Segment objects using a MeshCollider. The wormhole twists randomly through 3D space, while the width and height change dynamically. The player can strafe 360 degrees around the inside of the tunnel (direction of gravity is relative to position).
This makes the simpler solutions I've found impractical. Those include:
Using a single object instead of many
Placing redundant Colliders behind the joints
I've managed to flag these erroneous collisions in OnCollisionEnter(). This method on the PlayerController works fine to identify these erroneous collisions, and raise a flag.
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag != "Tunnel"){return;}
// Bit mask for tunnel layer.
int tunnelLayerMask = 1 << 10;
// Get the direction from the nearest Segment's origin to the collision point.
Vector3 toCollision = other.contacts[0].point - nearestSegment.transform.position;
if (Physics.Raycast(nearestSegment.transform.position, toCollision, out RaycastHit hit, 100f, tunnelLayerMask))
{
// Flag the collision if the detected surface normal
// isn't equal to the collision normal.
if (other.contacts[0].normal != hit.normal) { colFidelityFlag = true; }
}
}
But I'm at a complete loss when it comes to gracefully resolving the issue.
Currently, I'm just caching the player's velocity each frame. If a collision is flagged, I overwrite the resulting velocity with the cached velocity from the previous frame. This works for most conditions: the player ghosts imperceptibly into the floor for a frame, and gets past the offending joint.
But under high enough velocities or interactions with obstacles inside the tunnel, it is possible for the player to be ejected through the floor, and into space. I don't want to limit the velocity of the player too much, since the feel of the game partially relies on high velocity and hard collisions.
Does anyone know of a better way to resolve these erroneous collisions?
This is called "ghost vertices", and happens if you have like 2 box colliders (or something equivalent) and they are connected to each other. You could try to join the colliders so that instead of 2 separate connected colliders you have a single one.
Here ghost vertices are explained in more detail: https://www.iforce2d.net/b2dtut/ghost-vertices
My game (2D Top-Down view) is about a tank that fires bouncing bullets. Those bullets have infinite bounces and keep the same velocity after bouncing. It works fine with static objects, however it doesn't keep the same velocity with moving objects : the bullet can push things, but will slow and I don't know how to remove the slow.
Here is a tweet where you can see the bullets slow down after touching some objects :
https://twitter.com/TurboDevTeam/status/1350751139508215808?s=20
Edit :
Here are the parameters of the bullet :
derHugo's comment is on point: since the cars move when hit, they absorb part of the energy.
I've set up a minimal example to demonstrate.
The ball has a CircleCollider2D and a Rigidbody2D with Linear Drag = 0 and a material with Friction = 0 and Bounciness = 1.
The walls have a BoxCollider2D and a Rigidbody2D. Gravity is disabled.
For the first test, the wall Rigidbody2D is set to Static.
For the second test, the wall Rigidbody2D is set to Dynamic, with Mass = 10 and Linear Drag = 1.
In order to keep it bouncing forever in this case, you must code the bounciness by yourself. First remove the Physics Material or set the bounciness to 0, to make sure the Physics Engine is not simulating it.
Then, create a script for the bullet:
public class Bullet : MonoBehaviour
{
private Vector2 lastVelocity;
void FixedUpdate()
{
lastVelocity = GetComponent<Rigidbody2D>().velocity;
}
void OnCollisionEnter2D(Collision2D collision)
{
GetComponent<Rigidbody2D>().velocity = Vector2.Reflect(lastVelocity, collision.contacts[0].normal);
}
}
(Please note that you should use GetComponent carefully, maybe calling it only once on Start and saving a reference, I didn't do it to keep a minimal sample.)
The idea here is to take the velocity right before the collision and invert it. However, if you take the Rigidbody2D velocity on OnCollisionEnter, it's already the updated value. So you must store the value in FixedUpdate, since the next is only called after the OnCollisionEnter method.
Then, you use Vector2.Reflect to reflect the incoming velocity by the collision normal.
Vector2.Reflect results are valid in 2D:
I have a problem with physics. It is my first time doing in 3D, so it may be just a beginner mistake.
I just wanted to create a simple player controller and make it so that it can not pass trough cubes.
The problem is that when going straight into the cube, part of the player is in the cube itself. When stop moving, it pushes me, so they are not intersecting (that makes sense).
I tried moving the player using .Transalte, .MovePosition and by changing the velocity of rigidbody itself. None of it change anything. The player can always move a part of him into the cube.
Any ideas how to solve this?
My player controller:
(The 2 lines commented out in Move() are just other ways to move the player.)
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
private float movementSpeed;
private Vector3 input;
private void Update()
{
GetInput();
}
private void FixedUpdate()
{
Move();
}
private void GetInput()
{
float inputHorizontal = Input.GetAxisRaw("Horizontal");
float intputVertical = Input.GetAxisRaw("Vertical");
input = Vector3.ClampMagnitude(new Vector3(inputHorizontal, 0, intputVertical), 1);
}
private void Move()
{
GetComponent<Rigidbody>().velocity += input * movementSpeed;
//GetComponent<Rigidbody>().MovePosition(GetComponent<Rigidbody>().position + input * movementSpeed * Time.deltaTime);
//transform.Translate(input * movementSpeed * Time.deltaTime, Space.World);
}
}
Player is standing still
Player is moving towards cube
Settings of the Game Objects itself
Now I think I understand your problem.
The collider is a geometric shape that is checked but the outcome wont take place until the collision has actually taken place, this means, one geometric shape being inside the other. By this I mean, that what you are experiencing is the normal behaviour of the collision. If both elemnts are rigid bodies, both will move and your problem wont be perceivable, but if your cube is not a rigid body or is kinematic, will stand still in the same position, and depending on the other object speed, its normal that an invasion/superposition of the elements is perceivable, because that is the frame were the collision took place, and were your element needs to be moved back because it has collided.
Consider that if the speed is high enough, and the position from one frame to another varies enough, the collision might not even take place, because the geometric parts do not interfere between frames, as the position variation might be bigger than the bounds of the collider itself. The collision check at the end of the day, is dicrete, and not continuous (even you can set it as continuous to be as frecuent as possible).
To solve or improve that, you can adjust the speeds to avoid that being perceivable + adjust your collider to make it react before the graphic superposition occurs. This means making the capsule or the cube collider bigger than the graphic element itself. You can even calculate , to make it as bigger as much as your your speed * Time.deltaTime result or speed * FixedTimeStep result, depending on your safety distance needs. I think one of those should be the safety distance you need to take into account before the graphic collision occurs.
Another thing you can do is tight more the times physics calculations are done.
https://docs.unity3d.com/Manual/class-TimeManager.html
But need to be careful with this as this can be a performance devourer and need to be handled with care. You can make some trials with that and check your problem can improve.
Hope that helps
You can increase the scale of your player's collider from the Collider component attached to it. You can check how big the collider is from the editor scene view.
Edit: The issue might be that your movement or collision code is called in Update instead of FixedUpdate. When working with rigidbodies, you want to call the physics calculations inside FixedUpdate.
remove rigidbody from the cube, you can click on 'Gizmos' in the top right of the editor and make sure the colliders are at the edges of the objects.
So im creating a simple game and one component of the game is a greendot following the outside of the level. I got this working using a raycast in the middle which rotates and gives the position of collision the the gameobject.
game overview
The problem is that the speed is inconsistant at the moment since the distance between two collisions can be further distance if i have a slope. I also have the feeling that there should be a easier way to get the same result. What are your thoughts?
public class FollowPath : MonoBehaviour {
Vector3 collisionPos;
public GameObject greenDot;
void Update ()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, transform.up);
transform.Rotate(0.0f, 0.0f, 3);
if (hit.collider != null)
{
collisionPos = hit.point;
}
greenDot.transform.position = collisionPos;
}
}
I'm not sure if you will like this answer, as it suggests a complete departure from the way you were trying to do it.
A simple way to do this, would be to give your moving dot GameObject a Rigidbody2D component, AND a CircleCollider component.
Then, make your walls, and add to each an EdgeCollider component.
You'll probably also want to add a PhysicsMaterial2d to each GameObject with a Collider and set the friction and bounciness values for each.
Once this is setup, you can apply an initial force to the rigid body ball to get it moving, and it will bounce off the walls just like a ball does, using the Unity physics engine. No code would be needed in you update functions.