Collision triggers without direct attachment to object - c#

This is a very amateur question and I'm not entirely sure how to title it. Essentially I want to use the OnCollisionEnter2D method for multiple (while separate) game objects in the same script, rather than a script for each separate game object.
I've tried the following with no success:
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.name == "Ballone")
{
charge += 1;
}
}
I believe this isn't functioning properly because it's attached to a script for my event system, but I'm not sure how to change the syntax.

OnCollisionEnter2D is an event handler.
During FixedUpdate when Unity performs collision calculations, it notifies colliders about whether they have collided and passes them the relevant Collision2D object. If that collider is attached to an object with a script component that has a OnCollisionEnter2D method, then the Collision2D object is passed into that method and the collision is handled.
In that context, it doesn't make sense for an unrelated object to handle OnCollisionEnter2D events that belong to other objects. However, you can capture the events on each object and then pass those events (or data from those events) to another more "general purpose" method in another script if you want.
E.g
Create a class and method to handle your collisions then attach it to a GameObject in the scene such as an empty GameObject named CollisionManager
class CollisionManagerScr : MonoBehaviour {
public void HandleCollision(GameObject collidingObject, Collision2D col) {
Debug.Log("I've managed this collision with " + collidingObject.name);
Debug.Log(col) // Some info about the Collision2D object
}
}
Then, attach a script to the objects that you want to check for collision on:
public class MyCollisionScript : MonoBehaviour {
private CollisionManagerScr colManager;
void Start() {
colManager = GameObject.Find("CollisionManager").GetComponent<CollisionManagerScr>();
}
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.name.equals("Ballone")) {
if (colManager != null) {
colManager.HandleCollision(this, col); // Passes in the object that detected the collision
colManager.HandleCollision(col.gameObject, col) // Passes in the Ballone object
}
}
}
}
Now, whenever OnCollisionEnter2D is triggered for those objects, it calls HandleCollision. HandleCollision can then do whatever you want it to do. There undoubtedly is a use case for something like this but it smells a lot like unnecessary abstraction to me.
Please bear in mind that I wrote this on the fly so there could be problems/typos etc. Also, this is just one potential approach. You could also implement an approach that is effectively the same using EventHandlers etc. but that is just adding an additional event handling layer to the existing event handling layer.

Related

Child Objects Not Following Parent Object On Frequent Teleportation

Game Screen
Teleportation Code
Child Objects And Their Original Location
I'm new to Unity, and so after taking just some regular online courses for Unity2D I wanted to mess around with adding in different features, the first of which I decided to do was something like in Portal, where a projectile spawns two connected portals you can teleport between. However, I've run into an issue. When I'm teleporting my character, sometimes, usually when I'm teleporting too quickly but can happen at any time, the child objects to the Player game object tend to shift, and I don't understand why. **I'd like to:
Know why the offset between the parent and child objects are changing through teleportation.
Know how to fix this issue, preferably in code I can easily understand as a beginner to Unity. Also preferably in a way that doesn't involve me constantly appending the child objects to the parent object through transform position with the added offset, though if it's the simplest solution I'm not against trying it.**
Something worth noting is that the offset change is different as well, I have a Child Object called Feet which detect the Ground for jumping, which seems to remain at the location of the previous portal when it first breaks. However, another child object called Gun which is where the projectiles spawn from seem to only move down a little bit, meaning there's inconsistency in how they are offset when they break. It might be because the Feet has a collider, but I'm unsure, don't know enough, and only felt it was worth mentioning.
[SerializeField] GameObject otherPortal;
Portal otherPortalComponent;
BoxCollider2D boxCollider2D;
bool firstEntered = true;
// Start is called before the first frame update
void Start()
{
boxCollider2D = GetComponent<BoxCollider2D>();
otherPortalComponent = otherPortal.GetComponent<Portal>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
private void Teleport(GameObject obj)
{
obj.transform.position = otherPortal.transform.GetChild(0).transform.position;
}
public void SetFirstEnteredFalse()
{
this.firstEntered = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
this.firstEntered = true;
}
To simplify the question, the position of the child objects relative to the parent changes when I instantly change the parents position sometimes, why does this happen and how do I fix the issue without simply using transform.position in an Update method to constantly append the child to the parent, if possible.
I would look at your OnTriggerEnter2D method's if statement for a solution.
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Because you are using OnTriggerEnter this method will only be called with your feet since your player's main collider isn't a Trigger. This means that your collision variable that you call your Teleport method on is actually your feet object, not your player's body object. So you are changing the offset of your feet from your player in your Teleport method.
I would try changing your collision method to OnCollisionEnter2D(Collision2D)
which would pick up your player's base collider when it enters and not the feet collider.
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.GetComponent<Player>() && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Since the Player script is on the same object as your base player collider you can do a simple GetComponent<Player>() call to check if its the player object. Though you could still use the Layer to check if you want.

I'm studying the C# constructor and destructor with Unity, but I don't know why it works like this

I'm studying C#'s Constructer and destructor with unity component system.
I'm sorry if the English of this question is weird. I used a translator cause I am not good at English.
The log output came out like this.
The Constructor log was displayed without pressing the play button. Why?
When I pressed the play button, I saw a log of something being created and immediately disappearing. I didn't write a code to create an object after the game started, where does this phrase run?
This is my code.
project working structure
Pressing the space bar brings the pre-made pre-fab to the game world,
and Prefab has a component that attached to test the constructor and
destructor.
CubeFactory.cs / when Press the space bar, It creates Prefab.
this component was attached to "GameObject" Gameobject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeFactory : MonoBehaviour
{
public GameObject obj;
private int pos = 1;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(obj,new Vector3(pos,0,0),Quaternion.identity);
pos++;
}
}
}
ClassTest.cs / Component for Testing Constructor and Destructor. It attached Cube prefab.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClassTest : MonoBehaviour
{
public ClassTest(){
Debug.Log("I was born!");
}
~ClassTest(){
Debug.Log("I Died x0x");
}
}
This is my project file. (I'm sorry to compress it up. I haven't learned how to use Git yet.)<
https://github.com/Scincy/UnityStudy
Please change your ClassTest.cs script to use simpler functions. Never use constructors and finalizers in Unity. What I understood from your logic is that when you press Spacebar, an object is instantiated. The ClassTest.cs is attached to this object. Better use:
void Start()
{
Debug.Log("I was born!"); //Will be called when the gameObject is active for the first time
}
Now you also want to know if the gameObject is destroyed, you can do this with another script attached to an always-active gameObject such as:
public GameObject cube;
void Update()
{
cube= GameObject.Find("Cube(Clone)"); //Depending on how your object is called
if(cube == null)
{
Debug.Log("Either not created or is destroyed");
}
}
I have used Update() which is called every frame. You could use a different function that you would like to call from another script only once.
Ye olde wisdom says that you should only use C# finalizers if your class contains a handle (IntPtr) to an object in unmanaged memory. If you're unclear what that means, then you probably don't need to be using finalizers; either way, you should never need to use them in a UnityEngine.Object-derived class.
Instead, prefer either of these Unity messages:
void OnDestroy()
{
// (1) called when the Object is about to be fully destroyed.
}
void OnDisable()
{
// (1) called when the Object is disabled in the Scene hierarchy.
// OR
// (2) called right before OnDestroy() would be called.
}
These Unity messages are of course available on MonoBehaviours, but also on all ScriptableObjects too! (For the latter, OnDisable essentially resolves to the same thing as OnDestroy since they are not Scene-bound Objects.)
Similarly, you should also avoid using C# constructors on MonoBehaviours and ScriptableObjects. The messages to prefer instead are:
void Awake()
void Start()
void OnEnable()
Each has its own quirks, so you may want to familiarize yourself with the differences and write some tests.
You can refer to the relevant documentation here: (see section header "Messages")
MonoBehaviour docs
ScriptableObject docs

What is "Messages" in Unity's MonoBehaviour class? (Start,Update,Awake...etc)

What is the "Messages" of this page? (Start,Update,Awake...etc)
Is is a something like virtual method or event?
Or "Messages" is one of the C# syntax?
https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
The Unity engine basically calls these methods on MonoBehaviours if they are defined, depending on Engine events.
For example:
Awake is called when the script instance is being loaded.
Start is called in the first frame when the script is being enabled, before every Update method and after Awake
Update is being called in every frame
There are numerous messages as you can see it int the DOCs, and they are being called depending on engine events.
You can not call these events they are being handled by the engine!
Most commons are:
Update
Start
Awake
OnDestroy
OnEnable
But please note that using these methods(messages) while they are empty have a small overhead because the engine will call them, even if they are empty.
Another advanced thing is that some of these messages can be Coroutines. And you can give them some advanced functionality.
IEnumerator Start()
{
Debug.Log("First frame i'm being enabled! yeee");
// After 2 seconds i'm gonna blink
yield return new WaitForSeconds(2.0f);
Debug.Log("I'm going to blink");
Blink();
}
'Message' here is a synonymous for Function/Method, which are just Automatically called functions by unity engine, for any script inheriting from MonoBehaviour and attached to an ACTIVE gameobject in your scene.
Consider an animal script
public class Animal : MonoBehaviour
{
void Awake()
{
Debug.Log("Code here in awake is executed by unity the first time this object is activated, and never again in the lifetime of this object.");
}
void Start()
{
Debug.Log("Start is similar to awake but is executed after 'Awake' is executed on all game objects.");
}
void OnEnable()
{
Debug.Log("Code executed EVERYTIME your object is activated, including the first time you enter playmode, provided this object is active.");
}
void OnDisable()
{
Debug.Log("Code executed EVERYTIME your object is deactivated, does not include the first time you enter playmode if the object was disabled before playing.");
}
}
And so on, every Message/Function/Method has its use case and time, you'll get the hang of it when you start using them, they are the core of the engine.

How to make a trigger only for one of objects?

I have a car in my game with 4 wheels(Unity3D):
Also i have a trigger of EndLevel:
But after when im going throght the trigger its trying to work 4th times
How can i change it?
I tried to add my "Player(car)" inside EndGame object but its didnt fix my problem.
using UnityEngine;
public class EndTrigger : MonoBehaviour
{
public GameManager gameManager;
void OnTriggerEnter()
{
gameManager.CompleteLevel();
}
}
First of all note that OnTriggerEnter(Collider other) requires a parameter of type Collider otherwise it wouldn't get called at all.
Flag
The simplest solution might be adding a bool flag as already mentioned by Eric Warburton's answer.
Layers
I would prefer to rather tackle the origin of the issue and suggest using different Layers and then configure the Layer-based collision detection via the Edit&rightarrow;ProjectSettings&rightarrow;Physics&rightarrow; Layer Collision Matrix.
Create a Layer e.g. END and assign it to your goal collider object. Make this object not Is Trigger and rather attach your script checking for OnTriggerEnter here.
Create a Layer e.g. Player create a new dedicated invisible object with a collider and enable Is Trigger here. This object has the only purpose of colliding with the goal collider nothing else. Assign the Player layer here.
Configure the collision matrix thus that END only collides with Player and nothing else. And Player only collides with END and nothing else - or maybe later another effect layer like e.g. PowerUps ;)
You can create up to 24 custom layers and make use of the already existing ones so this should hold up a while
Tags
Another alternative to the Layers is using Tags
As previously I would make the END object not a trigger but rather use one on the Player.
Then you can simply compare the tag using CompareTag
void OnTriggerEnter(Collider other)
{
if (!other.CompareTag("Player")) return;
gameManager.CompleteLevel();
}
in very very complex games this might be sometimes better since you can create a lot more Tags than Layers.
Well there are a few things that I can think of trying.
You can make sure that only one of your colliders is a trigger. There should be a bool check in the properties to uncheck for your wheels.
You can also do something like creating a counter or a bool that prevents the OnTriggerEnter() from firing multiple times if you only want it to fire once. You can reset it at the start of levels if needs be.
Something like
void OnTriggerEnter()
{
if (!gameManager.IsLevelComplete)
gameManager.CompleteLevel();
}
Inside the gameManager script
public bool IsLevelComplete { get; set; }
public void CompleteLevel()
{
IsLevelComplete = true;
//Do stuff
}

if (!this) { return; } makes sense in C#?

I'm coding in Unity and I've found that sometimes a method can be called by an Invoke function even if the game object has been destroyed.
That's why I've added this check, and it seems to work correctly, but it's quite weird.
Let's say I have an object that I want to self destroy in one second.
I add an Invoke at the Awake to self destroy it.
But also, I want to destroy the game object instantly if the game is over. The object is subscribed to a Gameover event.
void Awake ()
{
Invoke ("_destroy", 1);
GameMachine.Gameover += _destroy;
}
The _destroy method is like this:
void _destroy()
{
if (!this) {
return;
}
GameMachine.Gameover -= _destroy;
Destroy (this.gameObject);
}
I've added that if (!this) check because I've found that the Invoke could reach the _destroy method, even if the object was already destroyed by the game over event.
This makes any sense, is it possible to happen?
Thanks
As far as I can tell, It will work, BUT, it's not a great idea to do so.
Unity's MonoBehaviour overrides the bool operator. Therefore, when you check
!this
you ARE checking whether the Object (and thereby the script) exists.
As a side note, if you want to unsubscribe events, use the OnDisable function instead. Code example
void OnDisable () {
GameMachine.Gameover -= _destroy;
}

Categories