hey does anyone knows how to make a C# script for object to re spawn after getting destroyed by a bullet
There are multible ways to archive this.
Note that you can also work completely without Coroutine but this usually sucks
private float timer;
private bool isRespawning;
private GameObject clone;
private void DestroyObject(GameObject obj)
{
isRespawning = true;
timer = 3f;
// first make a clone of the current Object
var clone = Instantiate(obj, obj.transform.position, obj.transform.rotation, objtransform.parent);
// disable the clone
clone.SetActive(false);
// Destroy the original
Destroy(obj);
}
private void Update()
{
if(Mathf.Approximately(timer,0f)) return;
timer += Time.deltaTime;
if(timer > 0) return;
// Set the clone active
clone.SetActive(true);
timer = 0;
}
Simplier would be a Coroutine:
private void DestroyAndRespawn(GameObject obj)
{
StartCoroutine(DestroyAndRespawnRoutine(obj));
}
private IEnumerator DestroyAndRespawnRoutine(GameObject obj)
{
// first make a clone of the current Object
var clone = Instantiate(obj, obj.transform.position, obj.transform.rotation, objtransform.parent);
// disable the clone
clone.SetActive(false);
// Destroy the original
Destroy(obj);
// Wait 3 seconds
yield return new WaitForSeconds(3f);
// Set the clone active
clone.SetActive(true);
}
But why should you destroy the object and respawn it? You can simply only disable and later re enable it
private void DestroyAndRespawn(GameObject obj)
{
StartCoroutine(DestroyAndRespawnRoutine(obj));
}
private IEnumerator DestroyAndRespawnRoutine(GameObject obj)
{
// disable the obj
obj.SetActive(false);
// Wait 3 seconds
yield return new WaitForSeconds(3f);
// Set the objactive
obj.SetActive(true);
}
Update
Since you want to call this on collision:
You will need another GameObject that works as a Manager for the respawning / enabling, disabling since you can not just attach that script to the object you disable -> would also disable the script and never re-enable the object.
Create a new empty GameObject e.g. called "RespawnManager"
Create a new Script RespawnManager.cs:
public class RespawnManager : MonoBehaviour
{
public static RespawnManager Singleton;
private void Awake()
{
if(Singleton)
{
enabled = false;
return;
}
Singleton = this;
}
private void DestroyAndRespawn(GameObject obj)
{
StartCoroutine(DestroyAndRespawnRoutine(obj));
}
private IEnumerator DestroyAndRespawnRoutine(GameObject obj)
{
// disable the obj
obj.SetActive(false);
// Wait 3 seconds
yield return new WaitForSeconds(3f);
// Set the objactive
obj.SetActive(true);
}
}
Than you can call it on collision like
OnCollisionEnter(Collision obj)
{
if (obj.gameObject.name != "bullet") return;
// pass this GameObject to the manager
RespawnManager.Singleton.DestroyAndRespawn(gameObject);
}
Set object active to False. using
gameobject.SetActive(false);
And you can use Coroutine to do this.
If you use Destroy(yourObject);, the object does get removed and you loose access.
You could make your Object into a prefab and spawn it where you want by using GameObject obj = Instantiate(...);
Obj contains the reference to the new spawned object and not to the prefab, which means you can later again use Destroy(obj);
Related
I'm sorry for any messy code, I'm relatively new to this. I made a working teleport in Unity but whenever I teleport from one of the teleports to the other, I wanna make it so there's a 5 second cooldown before you can use the teleporter again. So I used IEnumerator, added 5 seconds before "justTeleported" became false again, but when I teleported, I instantly got teleported back, and had to wait 5 seconds before I could try again. So my though was maybe I'm touching the trigger too quickly, before it can become false, that's why I added the two seconds. But now, whenever I get on the teleporter, it goes from true to false to true a couple times, and then I eventually get teleported back to where I came from. If anyone could help, I would be very thankful. Thank you.
{
public Transform Destination;
bool justTeleported;
public GameObject Player = GameObject.FindGameObjectWithTag("Player");
// Start is called before the first frame update
void Start()
{
justTeleported = false;
}
private void Update()
{
print(justTeleported)
}
private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.tag == "Player" && justTeleported == false)
{
StartCoroutine("Cooldown");
}
}
IEnumerator Cooldown()
{
justTeleported = true;
yield return new WaitForSeconds(2f);
Player.transform.position = Destination.transform.position;
yield return new WaitForSecondsRealtime(5f);
justTeleported = false;
}
Because each of the teleports has its own bool justTeleported, setting the first true doesn't automatically set the other true, so you need some way to tell the Destination teleport script to start it's own cooldown coroutine. Here's my test script, it works for me.
using System.Collections;
using UnityEngine;
public class Teleport : MonoBehaviour
{
public Teleport Destination = null;
public float CooldownTime = 5f;
private bool justTeleported = false;
public void StartCoolDown()
{
StartCoroutine(Cooldown());
}
private void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.CompareTag("Player") && !justTeleported) //CompareTag("tag") is better than .tag == "tag"
{
TeleportPlayer(coll.gameObject);
}
}
private void TeleportPlayer(GameObject player)
{
if (Destination) //if Destination is not null
{
StartCoolDown();
Destination.StartCoolDown(); //tell Destination to cooldown
player.transform.position = Destination.transform.position;
}
}
private IEnumerator Cooldown()
{
justTeleported = true;
yield return new WaitForSeconds(CooldownTime);
justTeleported = false;
}
}
Teleport1 (Destination: Teleport2)
Teleport2 (Destination: Teleport1)
I'm developing a simple VR shooter in unity, i use object pooling for the bullets (Lasers here)
this is the ObjectPool script, here i istantiate a list of bullets in the start() method and disable them.
public class ObjectPool : MonoBehaviour
{
public static ObjectPool instance;
private List<GameObject> pooledObject = new List<GameObject>();
private int amountToPool = 20;
[SerializeField] private GameObject laserPrefab;
private void Awake()
{
if (instance == null)
{
instance = this;
}
}
// Start is called before the first frame update
void Start()
{
for (int i = 0; i < amountToPool; i++)
{
GameObject obj = Instantiate(laserPrefab);
obj.SetActive(false);
pooledObject.Add(obj);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObject.Count; i++)
{
if (!pooledObject[i].activeInHierarchy)
{
return pooledObject[i];
}
}
return null;
}
}
This is the Script attached to the gun where i get the pooled bullet and set it to active
public class FireLaserGun : MonoBehaviour
{
public GameObject laserBeamModel;
public Transform laserSpawnPoint;
// Start is called before the first frame update
public void FireGun()
{
GameObject laser = ObjectPool.instance.GetPooledObject();
if (laser != null)
{
laser.transform.position = laserSpawnPoint.position;
laser.transform.rotation = laserSpawnPoint.rotation;
laser.SetActive(true);
Debug.Log("BOOM");
}
else
{
Debug.Log("Laser is null");
}
}
}
I'm trying to disable the bullet after two seconds was fired using a coroutine in the script that moves the bullets:
public class LaserBeamMove : MonoBehaviour
{
private Rigidbody rb;
public float thrust = 10.0f;
float waitTime = 2.0f;
private IEnumerator coroutine;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
coroutine = WaitToDisable(waitTime);
StartCoroutine(coroutine);
}
void FixedUpdate()
{
rb.velocity = transform.forward * thrust;
}
private IEnumerator WaitToDisable(float waitTime)
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(false);
Debug.Log("bullet disabled after " + waitTime + "seconds");
}
}
Strangely first seconds of the game everything seems fine, all the bullets start as inactive and every bullet becomes active when fired and inactive after two seconds.
After some seconds bullets dont become inactive anymore (actually only some of them do).
this is a screenshot of the console log, when i fire i print "BOOM" and when a bullet becomes inactive i print "bullet disabled after 2 seconds"
As you can see this down't work for every bullet and i don't understand why.
Am i doing something wrong with the courutine?
https://i.stack.imgur.com/rMJgg.png
Take the code you have in void Start() and move it to a new void OnEnable. The reason is that Start is only called once, the first time the object holding the script is enabled (e.g when the scene loads, when you instantiate a bullet etc), OnEnable is called every time the object is enabled, which is what you want here. You could also use Awake but IMHO that's best kept separately for initialisation stuff only so you can keep the code tidy and separate out game logic from init logic.
I am trying to set a gameobject to active on collision, wait a second and then set it to inactive again, but after the WaitForSeconds() line the execution seems to stop. I have no experience with C# and Unity so this may be a beginner mistake. Any idea why this could be happening?
private IEnumerator OnCollisionEnter2D(Collision2D collidedWith) {
if (collidedWith.gameObject.tag == "Shape") {
collidedWith.transform.GetChild(0).gameObject.SetActive(true);
Object.Destroy(this.gameObject);
yield return new WaitForSeconds(1);
Debug.Log("Should have waited for 1 second");
collidedWith.transform.GetChild(0).gameObject.SetActive(false);
}
}
You do
Object.Destroy(this.gameObject);
on this object which is running the Coroutine -> The routine is interrupted in that very same moment (when it reaches the first yield statement) -> it never actually starts to wait ;)
You should rather have a component on the object you collide with and make sure the Coroutine is run on that one instead.
E.g. like
// Have this on your Shape objects
public class Shape : MonoBehaviour
{
private bool isCollided;
public void Collided()
{
if(!isCollided) StartCoroutine(Routine());
}
private IEnumerator Routine()
{
if(isCollided) yield break;
isCollided = true;
var child = transform.GetChild(0).gameObject;
child .SetActive(true);
yield return new WaitForSeconds(1);
child.SetActive(false);
isCollided = false;
}
}
and then rather do e.g.
private void OnCollisionEnter2D(Collision2D collidedWith)
{
if (collidedWith.gameObject.TryGetComponent<Shape>(out var shape))
{
shape.Collided();
Destroy(gameObject);
}
}
I have a singleton LevelManager loading a level, waiting for a script from the newly-loaded level to assign a GameObject to the LevelManager to then do stuff with it.
I have the following code:
// some GameObject calls the loadLevel coroutine
void somefunction(sceneToLoad){
StartCoroutine(LevelManager.Instance.loadLevel (sceneToLoad));
}
// snippet of LevelManager.cs
public GameObject levelPrepper = null;
public IEnumerator loadLevel(string levelName){
Application.LoadLevel (levelName);
while (!levelPrepper)
yield return null;
yield return StartCoroutine (waitForLevelPrepper());
print("yay");
//do stuff
}
//snippet of the levelPrep.cs:
void Awake(){
LevelManager.Instance.levelPrepper = gameobject;
}
The problem is that "yay" never gets printed.
I've done some reading and found that this might happen when the GameObject carrying the coroutine is destroyed. However, LevelManager is definitely never destroyed during the process, so I'm at a loss.
The issue is that you start the Coroutine not on the LevelManager, but on "some gameObject", that most likely will be destroyed and its coroutine will stop being executed.
You could fix that by moving the call StartCoroutine into a new method, like this :
void somefunction(sceneToLoad)
{
LevelManager.Instance.LoadLevel(sceneToLoad));
}
public class LevelManager
{
public void LoadLevel(string levelName)
{
StartCoroutine(LoadLevelCoroutine);
}
private GameObject levelPrepper = null;
private IEnumerator LoadLevelCoroutine(string levelName){
Application.LoadLevel (levelName);
while (!levelPrepper)
yield return null;
yield return StartCoroutine (waitForLevelPrepper());
print("yay");
//do stuff
}
}
or calling the StartCoroutine of LevelManager directly
void somefunction(sceneToLoad){
LevelManager.Instance.StartCoroutine(LevelManager.Instance.loadLevel(sceneToLoad));
}
I want simply to destroy a deactivated instance of a Quad prefab (hp bar) , am able to destroy activated ones with :
private GameObject correspondingHpBar;
private string correspondingHpBarName;
void Start()
{
correspondingHpBarName = "hpBar1"
}
void Update()
{
correspondingHpBar = GameObject.Find (correspondingHpBarName);
if (shipHp <= 0)
{
Destroy (correspondingHpBar);
Destroy (gameObject);
}
}
This doesn't work with the deactivated objects, i googled hard but failed to find an answer.
Deactivated object don't have their Start or Update method called (nor any coroutine for that matter). In fact when an object is deactivated it is like its own time is frozen.
What you could do is create a method that does the destruction and find a way to call it from another script (for example a kind of controller that keeps reference to all HP bars in your scene).
The following is some pseudo-code (didn't check if it compiles, but you should adapt it anyway):
// in script for HP bar
public Boolean TryDestroy()
{
if (shipHp <= 0)
{
Destroy (correspondingHpBar);
Destroy (gameObject);
return true;
}
return false;
}
// in another script
private List<HPBar> _allHPBars;
void Awake()
{
_allHPBars = new List<HPBar>(FindObjectsOfType(typeof(HPBar)));
}
void Update()
{
var destroyedHPBars = new List<HPBar>();
foreach (var hpBar in _allHPBars)
{
if (hpBar.TryDestroy())
{
destroyedHPBars .Add(hpBar);
}
}
foreach (var destroyedBar in destroyedHPBars)
{
_allHPBars.Remove(destroyedBar);
}
}