Unity spawning repeatedly, unused spawn positions, problems thereof - c#

I want to enable and disable instead of instantiating and destroying everytime. It's pooling system that i seek.

instead of destroying it i want to disable it and instead of instantiating it again i want to enable it in a random position and type
You can't do that.
But the solution is incredibly simple. When you destroy it, call to the manager for a new one.
Exactly as you do here:
public void OnMouseDown()
{
manager.SpawnNewObstacle(transform.position);
Destroy(gameObject);
}
You're done!
Let's say you want (for example) the same type to spawn. Or, one green produces three gold, for example. Just do this
public void OnMouseDown()
{
if (myType == .Green)
manager.SpawnThreeGold(transform.position);
if (myType == .Gold)
manager.SpawnOneBlack(transform.position);
Destroy(gameObject);
}
.. or whatever the case may be. It's that easy.

Related

Is it better practice to keep a static list of every game object as they are instantiated, or to use raycasts to find every gameobject periodically?

If I want to check for the closest gameObject with the class "Enemy", I have two options:
I can build a static list of every Enemy gameObject that is instantiated, and find the closest one using the list, or
I can use physics raycasts to find every Enemy gameObject, put them into a list, and find the closest one like that.
They both seem about equal; if anything, it seems like building a static list would have better performance since there's no raycasting involved, but on the other hand using approach 2 seems like it is more extensible for further changes.
This sounds quite opinion based but here is what I would do
public class Enemy : MonoBehaviour
{
private static readonly List<Enemy> _instances = new ();
public static IReadOnlyList<Enemy> Instances => _instances;
private void Awake()
{
_instances.Add(this);
}
private void OnDestroy()
{
_instances.Remove(this);
}
}
This way you always have the actual list of existing instances without needing to poll them all the time.
Now you can get the closest one e.g. using Linq
var closestEnemy = Enemy.Instances.OrderBy(e => (e.transform.position - transform.position).sqrMagnitude).FirstOrDefault();
Using physics is no good approach if you need to find all the Enemy instances anyway.
Depends on your use case though. You might only want to get those within a certain range. In that case you could again either use above approach and filter on a specific distance or you could use e.g. an OverlapSphere. Performance wise I claim that Physics will always be worse.

How can I destroy multiple game objects at once?

I am trying to figure out how I can basically stand on a pressure plate and have something that's in the way disappear. I have it working to destroy the object you interact with but not finding any solution to make another object be destroyed.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("PressurePlate"))
{
Destroy(collision.gameObject.CompareTag("tree"));
}
}
Destroy(Object) takes in an Object, more specifically, a component or a game-object;(Though nothing would happen if you attempt to destroy a boolean, like what you did in this case; Someone can correct me on that though.)
If you want to destroy the game-object that it has collided with, Destroy(collision.gameObject) would do it.
If you want to destroy all GameObjects with a specific tag, you can do GameObject.FindGameObjectsWithTag(tag), like so:
foreach (var gameObj in GameObject.FindGameObjectsWithTag("Your target tag")){
Destroy(gameObj);
}
There are a few other methods like Object.FindObjectsOfType(Type) which you can use to fetch all game-objects with a certain type.
But since they are generally slow, I do not highly recommend using those unless you need to;You can consider caching the respective game-objects somewhere first, and destroying them later.

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
}

How do I make the program do something when an object is destroyed?

I have plane(as in air plane) objects in my program, they get destroyed when bullets hit them but they also get destroyed after 5 seconds, when they exit the screen.
I also have a health script that resets the whole thing when it goes down to 0, and I want to remove a point every time the object is destroyed, but only when off screen. So I keep the scripts separate.
I use this in the ships spawn script to destroy them after 5 seconds, simple enough.
Destroy(spawnedPlane, 5f);
It would be perfect if I could just have some code that does "Destroy this object after X seconds AND add this to this value". Because as I understand it, "destroy" only accepts 2 parameters and nothing else.
Surely it is possible but I am at a loss. Still very new to this. Sorry if this is very unclear but I barely know what I'm doing myself.
You can use events to cleanly achieve what you are after. Below is an example of an event you might find useful. Other objects can listen to the event and once it is triggered, they will be notified.
[Serializable]
public class PlaneEvent : UnityEvent<Plane> { }
Once you have defined your event, you can then add it as a field in your Plane. Once your plane has been destroyed, you can fire the event and it will in turn notify anyone who is listening!
public class Plane : MonoBehaviour {
public PlaneEvent OnDestroyed;
public void Destroy () {
Destroy(gameObject);
OnDestroyed.Invoke(this);
OnDestroyed.RemoveAllListeners();
}
}
Now in our score class, we add a method that will be called once the OnDestroyed plane event is triggered.
public class Score : MonoBehaviour {
public void AddPointsFor (Plane plane) {
Debug.Log("A Plane was destroyed!");
//Tick a counter, add points, do whatever you want!
}
}
Once we have these pieces, it is trivial to make them work together. We take the plane and we add the score as a listener to the OnDestroyed event. Then once the plane is destroyed, the event is fired and score is told to add points.
public class Game : MonoBehaviour {
[SerializeField]
private Score _score;
[SerializeField]
private Plane _plane;
public void Start () {
// When you are destroyed let me know so I can add some points.
_plane.OnDestroyed.AddListener(_score.AddPointsFor);
_plane.Destroy();
}
}
Another big advantage in using events is your plane has no idea that a score even exists, it will let anyone who cares know that it has been destroyed. In the same way this event could also be used to trigger particle effects, animations and sound effects when the plane is destroyed and all you need to do is add more listeners.
Just use a coroutine to wait and then subtract a point and destroy the object at the same time.
void Start()
{
// your startup script
StartCoroutine(DestroyAfterSeconds(5f));
}
IEnumerator DestroyAfterSeconds(float seconds)
{
// wait for X amount of seconds before continuing on
yield return new WaitForSeconds(seconds);
/*
* this runs after the wait.
* if the coroutine is on the same gameobject that you are
* destroying, it will stop after you run Destroy(), so subtract
* the point first.
* */
points--;
Destroy(spawnedPlane);
}
If it was me I would surely go with events as suggested by CaTs.
Coroutine are another way to do that, but events are better in general at least in this case. Also using a Coroutine for just one Invoke is a bit an overkill (and unity Coroutines are a bit not performant.) Also the coroutine must be outside of the object you want to destroy because Unity Coroutines die when their MonoBehaviour is destroyed.
If you are still uncomfortable with events you...
well you should overcome it and try them anyway.
You could take a shortcut you can use More Effective Coroutine - Free.
And launch this code:
Timing.CallDelayed(5f, DestroyAndRemoveHP());
Basically this will run your logic with the delay you want to apply.
DestroyAndRemoveHP will be your method to destroy the platform and do anything else you like.
Full method description.
On the plus side you will start using MEC that are better than unity coroutine, but learning events makes you also a better programmer. You might do both.

Why are transforms typically passed when working with in-game objects rather than game objects?

I am well along in learning Unity basics but would like to nail down my understanding of the relation between components and the objects that own them. In the tutorials I've been watching typically use or pass the Transform component when working with objects pulled in code. For example:
void Explode ()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, explosionRadius);
foreach (Collider collider in colliders)
{
if (collider.tag == "Enemy")
{
Damage(collider.transform);
}
}
}
which calls "Damage" with the transform on the colliders it finds:
void Damage (Transform enemy)
{
Enemy e = enemy.GetComponent<Enemy>();
if (e != null)
{
e.TakeDamage(damage);
}
}
Looking at this, it is clear that it is pulling a Component found on all game objects and then using "GetComponent" to pull another component instance by name since the "Enemy" component isn't going to have its own method. Why not just pass collider.gameObject though? I tried this (after changing the Damage to expect a GameObject) and it worked fine.
This seems more intuitive to me but all of the tutorials I've seen use the transform component instead. Does anyone have any insight into why this is so? It would help me deepen my understanding of how Unity structures its code.
I'm not sure what tutorials you are following, but the ones I followed when I first started with Unity used GameObject instead of Transform.
I agree that it is more intuitive to use GameObject. Transform is a part or Component of a GameObject, but since it's mandatory it will always be there, hence the public field .transform. But since a GameObject is technically the holder of all its Componenents, it would be most logical, architecture wise, to pass that as a parameter instead.
At the end of the day, it makes little difference in your examples since you can call a lot of the same methods on both Transform as GameObject.
TLDR:
Use whatever you feel makes most sense.
Short answer
GameObject is more intuitive as you say.
Long answer
I think (my opinion) it's best to pass the most specific and relevant component.
For example, assume you have a function for a character to pick up a crate:
public void PickUp(Transform crate)
{
crate.SetParent(this.transform);
crate.localPosition = Vector3.zero; // or whatever your attachment point is
}
Here it makes sense in my mind that the Transform is passed, because it's the most specific component that will be needed.By passing GameObject here you are only delaying the inevitable GetComponent<Transform> or go.transform.
On the other hand, if you have a function to hide a create then passing the game object would be the bare minimum you need to achieve this:
public void Hide(GameObject crate)
{
crate.SetActive(false);
}
Passing anything else just delays the inevitable x.gameObject
In the explosion example I don't think that I would pass either to be honest. I would probably do this:
void Explode ()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, explosionRadius);
var enemies = colliders
.Select(x => x.GetComponent<Enemy>())
.OfType<Enemy>(); // Or IHealth, IDamageable, ITarget, etc if you have other things that can take damage.
foreach (var enemy in enemies) // If empty, nothing will happen by default
{
enemy.TakeDamage(damage);
}
}
With this approach you can see that there is no need to check tags or nulls. The enemies enumerable is guaranteed to contain either enemies or nothing at all.
By always passing gameObject/transforms you will always have to worry about what it is that you are really receiving at the destination component. You will also open yourself to situations where you are not sure anymore where certain changes to you gameObjects are being made because it can be anything in the system that's making those changes. Your ColorChangerComponent could actually also be moving the object around, destroying some other components, etc. By giving the component a Renderer, it more naturally limits the component to changes on the Renderer only (although you could obviously violate this limitation unless you perform actions against appropriate interfaces).
The only time it really makes sense to pass a generic component is when you are broadcasting this 'event' to a bunch of possible receivers that will each to a different thing with the object. I.e. you can't be certain at compile time what the gameobject will be used for, or you know that it will be used for many different things.
The fact that Unity itself returns Collider[] for the collision check sort of supports this line of thinking, otherwise it would've just returns GameObject[]. The same goes for OnTriggerEnter(Collider collider) or OnCollisionEnter(Collision collision) etc.

Categories