I have the following code:
void Start()
{
gameObject.SetActive(false);
StartCoroutine(Load());
}
IEnumerator Load()
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(true);
}
This gives me an error that says:
Coroutine couldn't be started because the the game object 'NameOfObj'
is inactive!
This makes sense, since the game object has been set to deactivate before running the script. Even still, what is it that I'm supposed to do then? I tried moving gameObject.SetActive(false) to the coroutine, before WaitForSeconds(). Doing this stopped the game object from loading at all.
From my understanding, when the line gameObject.SetActive(false) is executed, the script stops running until the game object is reactivated. However, if this is the case, would it not be impossible to then reactivate the game object (as the script is disabled)?
Regardless, how would I delay my game object from loading until 2-3 (or any arbitrary length of time) after the game has started?
You cannot start a coroutine function from a script that has its GameObject de-activated.
The StartCoroutine function is a function under the MonoBehaviour class. When you have to start a coroutine on a deactivated GameObject, you need a reference to a MonoBehaviour object that has an active GameObject.
Two ways to do this:
1. Use an already existing GameObject that's unlikely to be deactivated. In my case, I usually use the camera. I access the camera's MonoBehaviour since it's likely to be activated then use it to start the coroutine function.
I suggest you use this method.
Replace the code in your Start function with the one below:
//De-activate this GameObject
gameObject.SetActive(false);
//Get camera's MonoBehaviour
MonoBehaviour camMono = Camera.main.GetComponent<MonoBehaviour>();
//Use it to start your coroutine function
camMono.StartCoroutine(Load());
2. Attach the script to an empty GameObject and the script on the empty GameObject will control or be able to activate/de-activate the other GameObject.
The script with the coroutine function you expect to run on a de-activated GameObject (Attach it to the GameObject you wish to de-activate):
public class YourDeactivatableScript: MonoBehaviour
{
public IEnumerator Load()
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(true);
}
}
Now, let's say that you want to deactivate a GameObject named "Cube" that has the YourDeactivatableScript script attached to it but still be able to start its Load coroutine function, create an empty GameObject with a new script, then start the Load function from it.
Create an empty GameObject then attach this script to it:
public class LoadFuncCallerScript: MonoBehaviour
{
GameObject targetObject;
public void Start()
{
//Find the GameObject you want to de-activate
targetObject = GameObject.Find("Cube");
//De-activate it
targetObject.SetActive(false);
//Get it's component/script
YourDeactivatableScript script = targetObject.GetComponent<YourDeactivatableScript>();
//Start coroutine on the other script with this MonoBehaviour
StartCoroutine(script.Load());
}
}
The coroutine is now started from another script named LoadFuncCallerScript.
What I do to avoid stopping coroutine is have a game object that does not get deactivated.
public class CoroutineHandler : MonoBehaviour
{
private static CoroutineHandler instance = null;
public static CoroutineHandler Instance
{
get
{
if(instance == null)
{
GameObject inst = new GameObject("CoroutineHandler");
DontDestroyOnLoad(inst);
instance = inst.AddComponent<CoroutineHandler>();
}
return instance;
}
}
}
Then use this for such coroutines by using CoroutineHandler.Instance.StartCoroutine(RoutineMethodHere());.
If you do not want something like this, since it can go wrong or cause leaks if not handled correctly, you can try using Invoke("MethodName", delay);
Related
I'm making a game on unity where the user selects a character and the character spawns into the game world. The world consists of different doors. I want to add a transition animation (just a regular fade) between scenes in the game world but because the character is instantiated during runtime, I'm not sure how to attach the animator to the character. I also want the animation to trigger upon collision of the player with a door. I know how to create the animation clips and the animator but I need help on knowing when and how to attach the animator to an object that's going to be instantiated during runtime.
Will I attach the animator in OnCollisionEnter() function? If so, how do I reference that animator through code?
Here is my code for OnCollisionEnter in a script that is attached to the player during runtime. (this works fine)
private void OnCollisionEnter(Collision collision)
{
GameObject door = collision.gameObject;
if (door.CompareTag("ExitDoor"))
SceneManager.LoadScene(0); // spawn back at main lobby
else if (door.CompareTag("RoomDoor"))
{
GameObject Room = door.transform.parent.gameObject;
if (Room.name.Equals("Room1Door"))
SceneManager.LoadScene(1); // go to first room
if (Room.name.Equals("Room2Door"))
SceneManager.LoadScene(2); // go to second room
if (Room.name.Equals("Room3Door"))
SceneManager.LoadScene(3); // go to third room
}
}
And here is the script of instantiating the player during runtime when the scene is loaded (this is in another script)
public GameObject InstantiatePlayer()
{
characterIndex = PlayerPrefs.GetInt(playerprefkey);
selectedChar = characters[characterIndex];
selectedChar.tag = "Player";
selectedChar.AddComponent<MoveRooms>(); //attaches the script where OnCollisionEnter is
return Instantiate(selectedChar, spawnPoint.transform.position, spawnPoint.transform.rotation);
}
to get the Animator , you can do this if the script is attached
Animator anim;
void Start()
{
anim = gameObject.GetComponent<Animator>();
}
else if it is not attached you can try something like this
GameObject object;
Animator anim;
void Start()
{
object = GameObject.Find("objectsname");
anim = object.GetComponent<Animator>();
}
For further details how to use Animator
https://docs.unity3d.com/ScriptReference/Animator.html
https://docs.unity3d.com/ScriptReference/Animator.Play.html
https://docs.unity3d.com/ScriptReference/Animator.SetTrigger.html
As for a transition, you can try to use Caroutines. What Caroutines are is it executes a piece of code 'on the side' whie the other codes continue. It can be used to "wait" for a few seconds (or more). Its very useful and you can do alot with it.
some examplee
public class ExampleClass : MonoBehaviour
{
void Start()
{
// Start function as a coroutine.
StartCoroutine(waitForSeconds);
}
private IEnumerator waitForSeconds()
{
//do something
yield return new WaitForSeconds(1f); // the number is in seconds.
//do something else
}
}
https://docs.unity3d.com/ScriptReference/Coroutine.html
In my platform game I have just added some checkpoints, so that if the player dies doesn't necessarily spawn at the beginning of the track.
ghfdghdggfhfg
using UnityEngine;
public class CheckPoints : MonoBehaviour
{
[SerializeField] private Grounded game;
void Update()
{
transform.Rotate(0, 0, 5);
}
private void OnTriggerEnter() {
game.updatedCheckPointPosition = transform.position;
Destroy(this);
}
}
What I unsuccessfully tried to do is to set the public float variable of the Grounded script to the current position of the CheckPoint itself, which should be destroyed after doing that.
Any information or help on how to do this is really appreciated.
From Destroy
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. [But keep the rest of the GameObject intact!]
If obj is a GameObject it will destroy the GameObject, all its components and all transform children of the GameObject.
this refers to the according component instance. What you want is rather
Destroy(gameObject);
OnTriggerEnter requires a parameter of type Collider in order to work
private void OnTriggerEnter(Collider other)
{
game.updatedCheckPointPosition = transform.position;
Destroy(this);
}
Note however that this way round the player has to be a trigger while the checkpoint a non-trigger! I would actually rather do it the other way round and make the chackpoint a trigger and rather let the player object check for OnTriggerEnter.
I am making a shooting game using Raycasts in Unity. I have a gameObject which is a cube (The ground for my game). That cube can be destroyed by using Destroy() method. How do I make it indestructible so that it doesn't get destroyed even after using the Destroy() method?
This my FPS gun script so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public float damage = 20f;
public float range = 150f;
//public GameObject ground;
public Camera fpsCamera;
void Start()
{
//DontDestroyOnLoad(ground);
}
void Update()
{
if (Input.GetButtonDown("Fire1")) {
Shoot();
}
}
void Shoot()
{
RaycastHit hitObject;
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
Destroy(hitObject.transform.gameObject);
}
}
It sounds like you want to sort which objects are destroyed somehow. The most simple way is of course to not call the Destroy method on that object in the first place.
However since you explain in the comments you wish to shoot and destroy some objects but not all. I would in your case make a script which either checks the tag, layer or calls a method in a script on each object.
For example, you try something similar to this:
ObjectProperties : MonoBehaviour{
public boolean CanBeDestroyed = true;
}
Add that script to the objects you shoot at.
And then when your raycast hits an object, use GetComponent on the object shot to try get ObjectProperties from that object. If the object hit has a ObjectProperties that isn't null, and that ObjectProperties has CanBeDestroyed set to be true. Then you may destroy the object.
The most simple way of achieving this, is using tags. Create a tag by highlighting the object. In the Inspector open the tags tab. Then create a tag and assign it to your object. The last thing you need to do is to check in code which tag the object you hit has.
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
switch (hitObject.tag)
{
case "immortal": //Nothing happens
break;
case "veryweak": //Objects with this tag get destroyed
Destroy(hitObject.transform.gameObject);
break;
default: //Objects that do not have a tag and do not match any of the above get destroyed
Destroy(hitObject.transform.gameObject);
break;
}
}
Hope that helps.
Using Unity, I'm working on a game where all Gameobjects with a certain tag vanish/reappear fairly regularly (every 10 seconds on average). I use GameObject.FindGameObjectsWithTag() to create a Gameobject[] through which I enumerate every time that the objects need to be made visible/invisible. I cannot call it once, on Start, as new Gameobjects are created while playing. I thought that it would be worse to access and change the Gameobject[] every time something got created/destroyed. Is there a better way to handle this. I know how bad of an impact on performance the GameObject.Find methods make...
Yes, there is a better way to do this. Have a script with List variable that can store GameObjects. Making it a singleton is better but not necessary.
This singleton script should be attached to an empty GameObject:
public class GameObjectManager : MonoBehaviour
{
//Holds instance of GameObjectManager
public static GameObjectManager instance = null;
public List<GameObject> allObjects = new List<GameObject>();
void Awake()
{
//If this script does not exit already, use this current instance
if (instance == null)
instance = this;
//If this script already exit, DESTROY this current instance
else if (instance != this)
Destroy(gameObject);
}
}
Now, write a script that registers itself to the List in the GameObjectManager script. It should register itself in the Awake function and un-register itself in the OnDestroy function.
This script must be attached to each prefab you want to add to the List. Simply do that from the Editor:
public class GameObjectAutoAdd : MonoBehaviour
{
void Awake()
{
//Add this GameObject to List when it is created
GameObjectManager.instance.allObjects.Add(gameObject);
}
void OnDestroy()
{
//Remove this GameObject from the List when it is about to be destroyed
GameObjectManager.instance.allObjects.Remove(gameObject);
}
}
If the GameObjects are not prefabs but GameObjects created through code, simply attach the GameObjectAutoAdd script to them once they are created:
GameObject obj = new GameObject("Player");
obj.AddComponent<GameObjectAutoAdd>();
You can now access your GameObjects in the List with GameObjectManager.instance.allObjects[n]; where n is the index number and you don't have to use any of the Find functions to find the GameObject anymore.
It's true that calling to GameObject.Find consumes a lot of recourses.
The point should be to save the result and use it always but I understand that you can't do that.
Other option is to have all this gameobjects as a child of one gameobject let's call it handlerGameobject. It will have a script that doesn't use GameObeject.Find it could getChild or transform.Find that uses less resources.
Hope it helps!
I am trying to make my player hit an object, destroying the object and triggering an animation, but everything I try causes an error. I am relatively new at c# so the answer may be obvious but I need help. How can I set it up so that the collision will cause the object to disappear and the player to play an animation? Here is the script I am currently trying.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class succ : MonoBehaviour
{
public float speed = .15f;
public static float jumpSpeed = 170f;
void Start()
{
GetComponent<ConstantForce2D>().enabled = false;
GameObject.Find("goal");
}
public bool animation_bool;
private object coll;
private object other;
void Update()
{
OnCollisionStay2D(Collision2D coll);
{
if (coll.gameObject.tag == "succ") ;
{
animation_bool = true;
GetComponent<Animator>().SetBool("succ", animation_bool);
GetComponent<ConstantForce2D>().enabled = true;
Destroy(other.object);
}
}
}
private void Destroy(object gameObject)
{
throw new NotImplementedException();
}
private void OnCollisionStay2D(Collision2D collision2D, object coll)
{
throw new NotImplementedException();
}
}
There are a few things I can see that are wrong, but I'll start by answering your question.
I suggest you change your MonoBehaviour method OnCollisionStay2D to OnCollisionEnter2D. OnCollisionStay2D is "sent each frame where a collider on another object is touching this object's collider". OnCollisionEnter2D is "sent when an incoming collider makes contact with this object's collider".
I believe you are looking for the latter since you only want to trigger this once during the collision. You are also destroying the other object, making it impossible to call OnCollisionStay2D anymore even if you wanted to do so.
You should also remove your Update method. I honestly do not understand what you are trying to achieve there now. All of the OnCollision methods get called automatically; you do not have to call them yourself.
Then you can use the Awake and OnCollisionEnter2D methods as follows
public class Succ : MonoBehaviour
{
private Animator animator;
private void Awake()
{
// You can already get a reference to the Animator on Awake
// This way you do not have to do it on every collision
animator = GetComponent<Animator>();
}
// Use OnCollisionEnter2D instead since the code
// needs to be excecuted only once during the collision
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("succ")
{
// Assuming that you only want to trigger an animation once
// to reflect attacking or colliding, you could use SetTrigger
// instead. Otherwise you need to use SetBool again to set it
// back to false. You should then change the Animator parameter
// accordingly, from a bool to a trigger.
animator.SetTrigger("succ");
Destroy(collision.gameObject);
}
}
}
Apart from this, I have a few things I would like to comment on:
I am not sure what you are trying to achieve by setting your ConstantForce2D component to false on Start and then setting it to true on collision.
You seem to be using GameObject.Find on Start. GameObject.Find is something that should be very rarely used. It can be extremely expensive, especially if your Scene has a lot of GameObjects in it; this is because it simply goes through the Hiearchy, comparing the parameter string to names of GameObjects until it either finds a match or runs out of GameObjects.
Moreover, you are using GameObject.Find on Start to look for a GameObject, but then you do not store that anywhere, making the whole finding process completely pointless.
Overall, I recommend you to take a look at all of the different learning resources offered by Unity themselves. Your question is about fairly basic functionality that is certainly covered during all of the different tutorials.