Unity: How to destroy an Instantiated object in a different function - c#

I'm trying to make a game which is spawning an object and after the object is destroyed, another object spawns right away. But right now I'm trying to destroy an instantiate object in a different function, and it is not working.
`
public GameObject[] food;
public Vector3Int spawnPosition;
public void Start()
{
SpawnFood();
}
//Spawning food
public void SpawnFood()
{
int random = Random.Range(0, food.Length);
GameObject clone = (GameObject)Instantiate(food[random], this.spawnPosition, Quaternion.identity);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.C))
{
Destroy(this.gameObject);
}
}
`
I have tried to do some research on this and still, I can only find the solution for destroying an object inside the same function as the Instantiate.

When you call Destroy(this.gameObject), the game object the script attacting to will be destroyed, and after that you cannot call the script.
I guess what you want to destroy is the food game object, not the game object the script you showed here attacting to.
A quick adjustment to suit your need maybe :
...
private GameObject clone
...
private void Update()
{
if(Input.GetKeyDown(KeyCode.C))
{
if (clone != null)
Destroy(clone);
}
}
And as #Daniel seggested, if you will repeatedly instantiate/destroy food game object, the better way would be just use the same food object and change it's properties (e.g. location...) to create the new food game object pop-up illusion.
The key idea here is called Object Pooling.

Here is your answer :
declare a public Gameobject:
public GameObject clone;
and replace
GameObject clone = (GameObject)Instantiate(food[random], this.spawnPosition, Quaternion.identity);
with
GameObject clone = Instantiate(food[random], this.spawnPosition, Quaternion.identity);
and then you can destroy in another function or class
public void DestroyFood()
{
Destroy(clone);
//you can instantiate another gameobject here
}

Related

Finding a way for script to detect a child in a playerContainer game object

I'm currently making a 3D endless runner game with the choice to select different skins for the player. Everything's going smoothly until I come across a problem, in which I try to assign two collision game objects to each character prefab in the container that can detect the player's collision box when it collides with a powerup.
It only detects the first playerContainer's (Milo) 'Coin Detect' game object (eventhough it's been deactivated) and does not recognize the 'Coin Detect' collision game object in the Baddie playerContainer (which the player has chosen to play)
So my question is, how am I able to get the script to recognize the child that's active in the third playerContainer game object instead of it automatically detecting the first playerContainer game object's child?
From the Magnet powerup script I've made, the script detects the first playerContainer's 'Coin Detect' game object only. As shown in the attached picture below:
Here's my current script where the player is able to select their preferred characters.
using System.Collections.Generic;
using UnityEngine;
public class EnablePlayerSelector : MonoBehaviour
{
public GameObject[] players;
public int currPlayerCount;
void Start()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
foreach (GameObject player in players)
player.SetActive(false);
players[currPlayerCount].SetActive(true);
}
}
and here's the script where I deactivate the 'Coin Detect' collision game Object and activate it when the player collides with the powerup.
using System.Collections.Generic;
using UnityEngine;
public class Magnet : MonoBehaviour
{
public GameObject coinDetect;
void Start()
{
coinDetect = GameObject.FindGameObjectWithTag("CoinDetect");
coinDetect.SetActive(false);
Debug.Log("False");
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.tag == "Player")
{
StartCoroutine(coinActivate());
Destroy(transform.GetChild(0).gameObject);
}
}
IEnumerator coinActivate()
{
Debug.Log("Hi");
coinDetect.SetActive(true);
yield return new WaitForSeconds(5f);
coinDetect.SetActive(false);
}
}
Well your issue is rather that you are doing GameObject.FindGameObjectWithTag("CoinDetect") which will return whatever is the first encountered CoinDetect in your scene in this moment. So unless EnablePlayerSelector.Start is executed before the Magnet.Start it will always be the first one since they are still all active by then.
You could probably already solve your issue by following the general thumb-rule:
Do things where you do not depend on other components already in Awake
Do things where you o depend on other components in Start
This way you already cover most of the use cases and are sure that components are self-initialized (Awake) before anything is accessing them in Start.
So simply change the EnablePlayerSelector.Start into Awake and you should be fine.
In very specific cases you might still have to adjust the Script Execution Order or use events.
Instead of doing this at all though I would rather in the moment of the collision request the current one from the PlayerContainer directly:
// put this on each player root object
public class Player : MonoBehaviour
{
// reference these via the Inspector
[SerializeField] private GameObject coinDetector;
[SerializeField] private GameObject playerrange;
// ... and in general any information bout this Player that is relevant for other scripts
// Readonly accessor property
public GameObject CoinDetector => coinDetector;
}
and then rather have
public class EnablePlayerSelector : MonoBehaviour
{
[SerializeField] private Player[] players;
[SerializeField] private int currPlayerCount;
public Player CurrentPlayer => players[currPlayerCount];
// In general do things where you don't depend on other scripts already in Awake
// This way you could actually already solve your issue by keeping things where you do
// depend on others in Start
private void Awake()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
for(var i = 0; i < players.Length; i++)
{
players[i].gameObject.SetActive(i == currPlayerCount);
}
}
}
and then finally do e.g.
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
var playerSelector = other.GetComponentInParent<EnablePlayerSelector>();
if(!playerSelector) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = playerSelector.CurrentPlayer.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}
or alternatively if the other in this case refers to the object with the Player component anyway you could also directly do
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
if(!other.TryGetComponent<Player>(out var player)) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = player.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}

Create a reference to a gameobject based on a raycast hit

I want to create a reference to an instance of a game object based on if a raycast hits a specific game object. In this case, its called "resource". Once I have click the gameobject I want to create an object of type ResourceSource that holds all variables and methods of that specific gameObject.
E.g i click the gameobject. A varaible is created
ResourceSource resource = hit.collider.gameObject.GetComponent<ResourceSource>();
Obviously, I cant just create a new object as that will just be a copy so if I make any changes to the gameobject the original won't be affected.
ResourceSource class
public class ResourceSource : MonoBehaviour
{
public ResourceType type;
public int quantity;
public UnityEvent onQuantityChange;
Cooldown cooldown;
public Vector3 GetPositon()
{
return gameObject.transform.position;
}
}
The method i want to create the reference
void CheckIfResource(RaycastHit hit)
{
if (hit.collider.gameObject.tag == "Resource")
{
//Want to create something like this
ResourceSource resourcePosition = hit.collider.gameObject.GetComponent<ResourceSource>();
}
}
Still somewhat new to unity so any help is appreciated :)
ResourceSource resource;
resource = hit.collider.gameObject.GetComponent<ResourceSource>();

Can someone explain how i can just call for ridgedbody2d with this code?

so I have a script calling from another class, I'm wondering how I can write this to only destroy the ridgedbody 2d. I know that it will keep the sprite in scene which is what I'm looking for.
private void OnTriggerEnter2D(Collider2D other)
{
DamageDealer damageDealer = other.gameObject.GetComponent<DamageDealer>();
health -= damageDealer.GetDamage();
}
If you pass in a Component reference to Destroy it will only destroy the according component but keep the rest of the GameObject untouched
The object obj will be destroyed now or if a time is specified t seconds from now. If obj is a Component it will remove the component from the GameObject and destroy it. If obj is a GameObject it will destroy the GameObject, all its components and all transform children of the GameObject.
Destroy(damageDealer.GetComponnet<Rigidbody2D>());
If you do this quite often it might be better to store this reference already in Awake of the DamageDealer component and later pass it on like
public class DamageDealer : MonoBehaviour
{
// if possible already reference this via the Inspector
[SerializeField] private Rigidbody2D rigidbody;
// This is a read-only property returning the value of rigidbody
public Rigidbody2D Rigidbody => rigidbody;
private void Awake()
{
if(!rigidbody) rigidbody = GetComponnet<Rigidbody2D>();
...
}
...
}
then later you can simply do
Destroy(damageDealer.Rigidbody);

How to fix "the object of type 'GameObject' has been destroyed, but you are still trying to access it" error in Unity?

I'm creating a 2.5D fighting game in Unity with C#. Currently, I'm trying to make a bumper appear around the player and disappear after a set amount of time. I've managed to make the bumper appear and disappear once, but after that, when I try to make the bumper appear again, Unity has an error for me: "The object of type 'GameObject' has been destroyed but you are still trying to access it."
I've tried using the "instantiate" and "destroy" commands, following a tutorial by "Brackeys" on 2D shooting. After also following some questions on forums about the same issue, I've altered my code again, but the problem persists.
The firePoint is an empty object, from where the BumperPrefab is instantiated.
using UnityEngine;
public class weapon: MonoBehaviour
{
public Transform firePoint;
public GameObject BumperPrefab;
public float lifetime = 0.2f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
attack();
}
}
void attack()
{
BumperPrefab = (GameObject) Instantiate(BumperPrefab, firePoint.position, firePoint.rotation);
Destroy(BumperPrefab, lifetime);
}
}
I expect the GameObject "BumperPrefab" to appear, stick around for 0.2 seconds and disappear. I should be able to repeat that as many times as I want, but what actually happens is that I can do this only once and then the error "The object of type 'GameObject' has been destroyed but you are still trying to access it" shows up and I can't make the BumperPrefab appear again.
Any help is much appreciated!
using UnityEngine;
public class weapon: MonoBehaviour
{
public Transform firePoint;
public GameObject BumperPrefab;
public float lifetime = 0.2f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
attack();
}
}
void attack()
{
var bumper = (GameObject) Instantiate(BumperPrefab, firePoint.position, firePoint.rotation);
Destroy(bumper, lifetime);
}
Right now you're overwriting your public field containing the prefab object with your instantiated object, then destroying it. Set the instantiated object as a variable and you should be fine.
The problem is that in your code you don't care about is your GameObject exist. So for example if (for some reason) the object BumperPrefab will not be created, Destory() will try to act on null.
You can try add to BumperPrefab script bumper.cs with:
float lifetime = 0.2f;
private void OnEnable()
{
Desroy(this, lifetime)
}
problem is you are destroying BumperPrefab
when you Instantiate a new GameObject you should add it to the local variable like this
var newbumper = (GameObject) Instantiate(BumperPrefab, firePoint.position,firePoint.rotation);
and you must destroy your local variable which contains newly created gameObject
Destroy(newbumper , lifetime);

Instantiated prefab should call a function from the main script?

I want something like this.
ComponentTest.cs - This script is attached to a empty game object in the scene.
public class ComponentTest : MonoBehaviour {
public GameObject boxPrefab;
void Start () {
GameObject temp = Instantiate(boxPrefab, new Vector3(0,0,0), Quaternion.identity) as GameObject;
temp.GetComponent<PrefabBox> ().go = gameObject.GetComponent<ComponentTest>().Yes();
}
public void Yes(){
print("yes");
}
}
and PrefabBox.cs - This script is attached to the prefab.
public class PrefabBox : MonoBehaviour {
public GameObject go;
void Start () {
go ();
}
}
I get this as error:
The member `PrefabBox.go' cannot be used as method or delegate
First of all, the go object is declared as a GameObject but then you assign a function to it and then call it like a function.
It looks like you want Closures in which you can store functions to variables and call them in the way you do.
This post covers it: http://answers.unity3d.com/questions/494640/capturing-a-variable-in-a-closure-behaves-differen.html
Seems the data type needs to be Action but best to read up on it to grasp it properly.
Good luck.

Categories