I am trying to make a wall running script for my player and I was experimenting with OnCollisionStay The only problem I have is that it detects collisions with ground and things I don't want it to check. Is there any way to get it to only check walls or check things I want it too?
There are two ways to do this:
Use an if statement in your OnCollisionStay method and check the tag of the object you are colliding with:
void OnCollisionStay(Collision coll) {
if (coll.transform.tag.Equals("Wall")) {
// Do wall related stuff
}
}
Set up layer based collision detection. This documentation explains that: https://docs.unity3d.com/Manual/LayerBasedCollision.html
Depending on what it is that you're trying to achieve one or a mix of both of these approaches should get you the results you're looking for.
Related
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)
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.
If for instance I have a game which consists of just a single scene, and in that scene I make the user chose between the normal play mode or the tutorial play mode. The game has 4 different objects: a ball, 4 squares and a squarecontroller. In the tutorial mode i want to provide the user with a pointing arrow while pausing the game and continue after the user pressed the object being pointed at. Should I make a script for the normal mode and another one for the tutorial mode, make one script and check if a tutorial boolean is true or false in every function (boolean should be true if user pressed the tutorial button) or do some kind of preprocessing?
In the squarescript for example:
void OnCollisionEnter2D () {
if (isTutorial) {
PauseGame();
arrow.position = GetRelativePosition();
arrow.setActive(true);
} else {
if (canCollide) {
score++;
} else {
GameOver();
}
}
In the ballscript:
void OnMouseDown () {
if (!isTutorial) {
return;
}
ResumeGame();
}
We know nothing of your game so it's hard to answer. But as a rule of thumb: The less you have to type, the better. Also consider what will happen if you need to add new functionality to the game, will you have to go back and change tons in your code?
If so, you are most likely not writing good code.
As an attempt to give a concrete answer I'd say you should make an inheritance, create a class Level and make sub classes Tutorial and FreePlay or similar, that inherits from Level.
Then you can add all "general" functionality in the base class and the specific things goes in the sub classes
By structuring these behaviours inside if statements, it makes the code hard to understand and work with. Imagine what this will look like if you decide you want one of the squares to increase the players score AND show a tutorial arrow.
Split the behaviours into separate objects. For the square it could be something like a TutorialCollisionHandler, ScoreCollisionHandlerand HazardCollisionHandler. Then you can create different squares simply by changing which collision handlers are added to them, you don't even need to write any code!
Now depending on which mode the user picks, you can just use a different mix of squares. The same principle can be used with other tutorial or game specific behaviour.
Update
I've discovered that rigidbody doesn't cast to rigidbody2d. I've tried manually casting, but get errors saying that it can't be done... So my new question is, how can I get joints to work with rigidbody2D?
OP
Learning Unity and am now playing with physics. I've got a small game running and it's all working lovely, but what I'm having issues wrapping my head around is the 2D physics.
I've got various "crates" and each have their own physics values (gravity etc).I've also got a "crate mover" which will (in theory) move crates from one place to another.
However, due to my movers having defined positions they can move to in game space, I'm having terrible trouble getting the crates to "stick" to the movers when they're moved.
So, I did some digging around and found Joints - Specifically FixedJoint - I figured that would solve it, if I attached a joint to the mover and set it's body to the crate? Right? No.
I get this error:
NullReferenceException: Object reference not set to an instance of an object
RobertMover.OnCollisionEnter2D (UnityEngine.Collision2D coll) (at Assets/Scripts/RobertMover.cs:29)
Which relates to the following code:
void OnCollisionEnter2D(Collision2D coll)
{
Debug.Log (coll.gameObject);
FixedJoint j = (FixedJoint)this.gameObject.AddComponent ("FixedJoint");
j.connectedBody = coll.gameObject.rigidbody; // problem line
}
Seeing as I'm still learning Unity, I'm a little unsure as to what is null. My crate has a rigidbody. As does my mover, as I read that joints require them. The object isn't null (checked with the Debug.Log...). The only thing I can think is that the Joint itself is null, which is very odd!
Can anyone explain what I'm doing wrong?
2d physics only works with "2d" classes and methods, you should be using FixedJoint2D or a comparable "2D" joint class if that particular one doesn't exist
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.