Collisions between CharacterController and BoxCollider - c#

I'm trying to detect collision between the characterController and a platform (a rigidBody + boxCollider) in an Unity project.
I know that I can use this function at the characterController object:
void OnControllerColliderHit(ControllerColliderHit hit) {
// [...];
}
But I would strongly rather to detect it in the platform object, in order to try to maintain the code clearer. Something like this:
void OnCollisionEnter(Collision c) {
Debug.Log(c.gameObject.tag);
}
But it is not working! I searched in Unity forums and apparently the only way to detect a collision is to set the boxCollider's property isTrigger as True and using .OnTriggerEnter(Collider c) method instead. However, doing it will cause the player to fall through the platform, which obviously can't happen.
Alright, so my question is: is there another way to do it - whithout setting isTrigger as True - and detecting the collision in the platform object?
Thank you!

The way I handled a similar problem with a platform and a character controller, is by adding a child object to the platform with a trigger collider set to a larger size than the platform itself (think of it like an invisible box surrounding your platform). What this does is allow you to know if your player is going to hit the platform, the direction he's coming from etc. Then it's a simple matter of sending a message to the platform, with an necessary information parentPlatformObject.SendMessage(params)

I would like to suggest something very similar to what Steven Mills suggested, but may make things easier in the long run.
Add a child object to the player, that has a trigger collision box the size of the player (or just around it's feet if that's what you care about), but has a specific layer only for itself. In the project physics settings, make said layer only interact with the platform's layer. This means you won't trigger if this box hits anything else except for the platforms (like the rest of the player). Since the non triggers had not changed, the player and the platform will behave as you expect (just as with Steven's solution) but if you add new types of objects that you wish to land on/hit, and want them to work in a similar manner, you will not need double the prefabs/make 2 objects, just 1 with the correct layer assigned.

Related

Unity : is it possible to configure the scene for trigger colliders to only work with one layer?

Unity already manages the collisions and triggers well but I would like to know if I can optimise triggers to only react to a layer ?
Currently if I have many different objects with colliders in a scene I need to do :
void OnTriggerEnter (Collider other) {
if (other.gameObject.layer == myLayer) {
// do stuff here
}
}
But this means that the collisions where calculated with ALL of the colliders witch does not seem efficient.
If I want a trigger to react to the player and to the player only, is there a way to tell it to ignore all of the other layers to avoid unnecessary computing ?
Yes, using the Collision Matrix as described in the manual here.
Add a new layer mask (or just use the one you already have)
Uncheck all of the layers it can collide with except the "Player" layer
Change the game object's layer to the new modified layer
Now that object can only collide with the player, and only get triggered by the player (provided one of them has a rigid body component)

Where to attach collision detection script?

I watched Basic Platformer Game tutorial for Unity where presenter created coin pick-up script that he attached to Coin prefab. I want to know if the pick-up script should be attached to the Player or Coin GameObject.
Let's say we have a game with pick-upable objects. They do nothing more than incrementing the score (or affecting the player in another way) and destroy themselves on collision.
I was wondering that what is the preferred approach to this problem.
I've come up with two approaches:
Approach A
Have one ObjectPickup script on the player game object. This script would do whatever is required depending on the type of collided object.
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Coin"))
{
IncrementScore();
Destroy(other.gameObject);
}
else if (other.gameObject.CompareTag("SuperSpeed"))
{
IncreasePlayerSpeed();
Destroy(other.gameObject);
}
}
Approach B
Have CoinPickup script on every coin and SuperSpeedPickup script on every super speed powerup.
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
other.gameObject.IncrementScore();
Destroy(gameObject);
}
}
And other script:
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
other.gameObject.IncreasePlayerSpeed();
Destroy(gameObject);
}
}
Is Approach A more efficient then Approach B? If so what is the rough number of GameObjects when Approach A becomes preferable?
Or is it always better to work with Approach B as it seems to be cleaner (when we have a number of different pick-upable objects)?
In your example, "approach a" would be more efficient than the other because the OnTriggerEnter2D is called once. With "approach b", OnTriggerEnter2D is called twice on two different scripts. It's slower for Unity to make callback calls let alone on two different scripts + the player. Also, OnTriggerEnter2D is sent to disabled MonoBehaviours which makes "approach a" more efficient choice. The main reason for the slow event call is because the call is made from native C++ side of the code to the Unity's C# API side and this is costly.
Note that it really doesn't matter that much. This is just an attempt to answer your question for which one is more efficient but what you're doing is a micro-optimization.
The thing that should play big role in this decision is the behavior of the coin after the trigger.
If the coins behave the-same then use "approach a" and do the detection on the player's side.
If the coins have different behavior like coins with different score values, color and action(animation) when they are collected then do the detection on the coins side and handle each coin. This is the reason why scripts are attached to GameObjects in the first place. You don't need different tags for each different coin. Use enum to distinguish their action in the OnTriggerEnter2D function. It would be awful to use "approach a" to handle this in this case.
Considering you can create a prefab for every coin and for every super speed power up with the second approach script already attached to it AND considering you can spawn them very easily from anywhere in the code I'd really stick to the second approach, making it more sensible, clean and clearly understandable from everyone.
I don't like (and I wouldn't absolutely use) the first approach for this specific purpouse mainly because the OnTriggerEnter2D is very likely to be fired a very large number of times since it's put on the player and the player is moving around colliding with stuff everywhere.
I should use the B, cause it's more like how object-programming works (at least on my head!) even if it's more expensive.
But Method A do not split interactions, so imagine that you can collide with 2 different pickable objects, and you collide with bouth at the same time. If you use Method A, it will use allways the same collision-order, which can be desired or not!
But clearly with Method B you don't really know which object will be picked first (in this concrete case!)
Approach A sounds a bit more performant, also it would work with multiplayer. If you have n players, local or online, you'd need to check the player id on the coin. Otherwise you'd need a lot of tags. No need to do that if the players detect item collisions themselves.

Unity 3D: How can I have directional light on for one camera and off for another?

I need to show two camera display side by side. One will have the sun(directional light) on and one will have the sun off while rendering the same scene at the same time. I have tried to use sun.SetActive() inside of OnPreRender() method, for one Camera I set it sun.SetActive(false) and sun.SetActive(true) for another. It does not work, sun remains off for both camera. How can I achieve the desired output?
You need to add OnPreCull too to your initial try for versions highers than Unity 4.6.
I preserve OnPrerender for older versions, you got an advice on your log but ignore it.
Add this script to your camera:
function OnPreCull () {
if (sun != null)
sun.enabled = false;
}
function OnPreRender() {
if (sun != null)
sun.enabled = false;
}
function OnPostRender() {
if (sun != null)
sun.enabled = true;
}
Another way to do this would be to have all gameobjects duplicated and set to different layers - e.g.: 'Sunlit' and 'Shadowed'. The duplicate object needs to be nested within the original object, which can be quite messy, you'd need a script to duplicate the scene's objects at runtime.
The hierarchy would look something like this:
Your directional light's culling mask (on the light's inspector) would be set to everything but to the objects on the 'Shadowed' layer.
Then 2 cameras, culling everything but one of the layers:
I'm unsure if this is better or worse than joreldraw's solution in terms of performance. But I would guess it would depend on the amount and complexity of the models. On joreldraw's solution we're enabling/disabling the lightsource several times per frame. With this solution we have twice the gameobjects. If you don't have a lot of objects on your scene, then perhaps this solution is better.

Unity. Attempting fake internal ship gravity. Rigid body children of rotating object keep sliding around

I'm attempting to simulate a ship/space station with internal gravity.
To accomplish this, I'm making the player and all contents of the ship children of the ship. The ship itself has colliders, but no rigid body components. The idea is that as the ship moves, so will all of its contents. Pretty straightforward so far.
To simulate gravity in the ship, the player controller and all rigid bodies have the default gravity turned off. Instead of the standard, each frame a force is applied along the negative up vector of the parent ship.
This sort of works, but there is one major problem that I have to sort out before this thing is solid. All rigid bodies slide around the interior of the ship very slowly.
I'm aware that this is probably due to the updated position of the floor combined with the gravity force resulting in some kind of shear force. The objects always slide against the rotation of the ship.
I've tried mucking around with all of the physics properties from Physic materials to drag to mass, etc. None of these have worked, and I'm pretty sure it's due to the fundamental fact that the floor is moving, even though the RBs are children of the object that the floor is a part of.
Anyone have a solution to this that isn't some kind of duct tape? I could try to make everything kinematic and only "wake up" when certain external collisions occur or something, but that could get very cumbersome. I need for this to work in as much of a general purpose way as possible.
Some code:
On the ship
void Update ()
{
transform.Rotate(new Vector3(Time.deltaTime * 0.125f,Time.deltaTime*0.5f,0));
}
void FixedUpdate()
{
Vector3 tempVec;
foreach(Rigidbody rb in rigidBodies)
{
//Gravity!!
tempVec = transform.up * -9.81f * rb.mass * Time.deltaTime;
rb.AddForce(tempVec, ForceMode.Acceleration);
}
}
I've also worked on a version where the ship was following the movements of a rigid body. I couldn't do direct parenting, so I had to simply set the transform manually each frame to match the physics proxy. This still had the same effect as above, though it's probably ultimately how I want to move the ship, since that will tie into the flight mechanics more properly.
If you equate this to a real world scenario, the only thing that stops us from sliding around on the floor is friction.
Does the Physics library correctly apply friction based on the contacting materials? If not applying a certain amount of friction (or a minimum amount of force applied required to overcome it) should have the effect of preventing you from sliding around on the floor.
Although this is pretty much "duct tape" as above, it could neatly fit in and expand your physics engine if it doesn't already contain a way to enforce it.
As suggested above, the issue is because of how the physics engine applies friction. If I'm not mistaken, there will be some other forces acting on objects in a rotating frame (some of which are very unintuitive - check out this video: https://www.youtube.com/watch?v=bJ_seXo-Enc). However, despite all that (plus likely rounding errors arising from the engine itself and the joys of floating-point mathematics), in the real world, static friction is greater than moving (kinetic) friction. I don't think this is often implemented in game physics engines, which is why we so often see "wobbly" near-static objects. Also, you may run into the issue that even if this is implemented, the physics engine may be interpreting two contacting rotating bodies as non-static (even though their contact surfaces are static from a local perspective, the engine may be thinking globally)... [Insert joke about Newton and Einstein arguing].
https://i.stack.imgur.com/AMDr2.gif shows an idealised version of what friction actually looks like in the real world: until you overcome the static friction, nothing moves.
One way you would implement this (if you can access the physics engine that low-level) would be to round all movement forces below a certain threshold to zero - i.e. force < 0.001* is set to 0 (or possibly velocity < 0.001 is set to zero - whichever is easier).
*Some threshold - you'll have to work out what this is.
Otherwise, maybe you could instruct those objects to stop calculating physics and "stick" them to the parent surface, until such time as you want to do something with them? (This is probably a bad solution, but most of the other ideas above rely on hacking the underlying physics code).

2D collision resolution system

I am making a simple 2D game in the console in C# as a learning project. However I seem to be unable to come up with a decent design for a collision system.
The resolution of a collision presents the biggest problem for me.
Basically there can be any combination of the following 2 sets of effects:
1. The triggering object or tile is removed;
2. The remote object or tile is removed;
Or nothing can happen - the objects just stop moving(i.e. when both are invulnerable or something).
Any ideas towards that effect would be greatly appreciated.
How to detect a collision and how to respond to it are two totally different concerns and you should separate them.
The simplest way would be to have a virtual method in your base game object responding to collisions:
class GameObject
{
virtual protected void OnCollision(GameObject withObject) { }
}
In your collision detection system, whenever two objects collide, simply raise the collision event on the two objects: object1.OnCollision(object2); and object2.OnCollision(object1).
You will soon realise that this might become a mess at some point and will want to learn about multiple dispatch and how to emulate it on languages that don't support it.

Categories

Resources