I'm developing an open source editor tool for Unity3D https://github.com/JAFS6/BoxStairsTool and I'm writting a CustomEditor.
I create a main GameObject and I attach my script BoxStairs to it. This script attachs to the same GameObject a BoxCollider.
On my CustomEditor code, I have a method which is in charge of removing both two components attached before to finalize editing.
This is the code:
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
This method is called on the method OnInspectorGUI after a button has been pressed
public override void OnInspectorGUI ()
{
...
if (GUILayout.Button("Finalize stairs"))
{
FinalizeStairs();
}
}
Both two methods are on the class
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
It actually removes the two components but, once the BoxCollider has been removed the following error appears:
MissingReferenceException: The object of type 'BoxCollider' has been
destroyed but you are still trying to access it.
I try to locate where the error is ocurring by looking at the trace:
Your script should either check if it is null or you should not destroy the object.
UnityEditor.Editor.IsEnabled () (at C:/buildslave/unity/build/Editor/Mono/Inspector/Editor.cs:590)
UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1154)
UnityEditor.InspectorWindow.DrawEditors (UnityEditor.Editor[] editors) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1030)
UnityEditor.InspectorWindow.OnGUI () (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:352)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
But none of my scripts appears on it.
I've been looking on the code where I'm referencing the BoxCollider and the only place is where it is created, when the stairs are created which is triggered once a change has happened on the inspector.
It is in the class:
[ExecuteInEditMode]
[SelectionBase]
public sealed class BoxStairs : MonoBehaviour
This is the code:
/*
* This method creates a disabled BoxCollider which marks the volume defined by
* StairsWidth, StairsHeight, StairsDepth.
*/
private void AddSelectionBox ()
{
BoxCollider VolumeBox = Root.GetComponent<BoxCollider>();
if (VolumeBox == null)
{
VolumeBox = Root.AddComponent<BoxCollider>();
}
if (Pivot == PivotType.Downstairs)
{
VolumeBox.center = new Vector3(0, StairsHeight * 0.5f, StairsDepth * 0.5f);
}
else
{
VolumeBox.center = new Vector3(0, -StairsHeight * 0.5f, -StairsDepth * 0.5f);
}
VolumeBox.size = new Vector3(StairsWidth, StairsHeight, StairsDepth);
VolumeBox.enabled = false;
}
I've tried to comment this method's body to allow removing the BoxCollider without this "reference" and the error still appears, so, I guess this method is not the problem.
Also, I've removed the BoxCollider manually, without clicking the Finalize button to trigger this code, via right click on the component on the inspector "Remove Component" option and the error not appears and after that, click over finalize stairs and no problem shows up.
As #JoeBlow mentioned in the comments I've checked that the FinalizeStairs method is called just once.
Also I've checked that the process of creation with the call to AddSelectionBox method it is not happening on the moment of clicking finalize button.
So, please I need a hand on this. This is the link to the development branch https://github.com/JAFS6/BoxStairsTool/tree/feature/BoxStairsTool, here you will find that the above mentioned method FinalizeStairs has the code wich removes the BoxStairs script only and on that moment it throws no errors.
Any idea or advice on this will be very helpful. Thanks in advance.
Edit:
a Minimal, Complete, and Verifiable example:
Asset/BoxStairs.cs
using UnityEngine;
using System.Collections.Generic;
namespace BoxStairsTool
{
[ExecuteInEditMode]
[SelectionBase]
public sealed class BoxStairs : MonoBehaviour
{
private GameObject Root;
private void Start ()
{
Root = this.gameObject;
this.AddSelectionBox();
}
private void AddSelectionBox()
{
BoxCollider VolumeBox = Root.GetComponent<BoxCollider>();
if (VolumeBox == null)
{
VolumeBox = Root.AddComponent<BoxCollider>();
}
VolumeBox.size = new Vector3(20, 20, 20);
VolumeBox.enabled = false;
}
}
}
Asset\Editor\BoxStairsEditor.cs
using UnityEngine;
using UnityEditor;
namespace BoxStairsTool
{
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
{
private const string DefaultName = "BoxStairs";
[MenuItem("GameObject/3D Object/BoxStairs")]
private static void CreateBoxStairsGO ()
{
GameObject BoxStairs = new GameObject(DefaultName);
BoxStairs.AddComponent<BoxStairs>();
if (Selection.transforms.Length == 1)
{
BoxStairs.transform.SetParent(Selection.transforms[0]);
BoxStairs.transform.localPosition = new Vector3(0,0,0);
}
Selection.activeGameObject = BoxStairs;
Undo.RegisterCreatedObjectUndo(BoxStairs, "Create BoxStairs");
}
public override void OnInspectorGUI ()
{
if (GUILayout.Button("Finalize stairs"))
{
FinalizeStairs();
}
}
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
}
}
Analysis
I'm a programmer, so I just debug to find the problem (in my mind :D).
MissingReferenceException: The object of type 'BoxCollider' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEditor.Editor.IsEnabled () (at C:/buildslave/unity/build/Editor/Mono/Inspector/Editor.cs:590)
A MissingReferenceException occurs when the code trys to access a Unity3D.Object after it has been destroyed.
Let's look into the decompiled code of UnityEditor.Editor.IsEnabled().
internal virtual bool IsEnabled()
{
UnityEngine.Object[] targets = this.targets;
for (int i = 0; i < targets.Length; i++)
{
UnityEngine.Object #object = targets[i];
if ((#object.hideFlags & HideFlags.NotEditable) != HideFlags.None)
{
return false;
}
if (EditorUtility.IsPersistent(#object) && !AssetDatabase.IsOpenForEdit(#object))
{
return false;
}
}
return true;
}
We won't be able to know which line is the specific line 590. But, we can tell where can a MissingReferenceException happen:
// ↓↓↓↓↓↓
if ((#object.hideFlags & HideFlags.NotEditable) != HideFlags.None)
#object is assigned from Editor.targets which is an array of all the object being inspected. There should be only one target object in this array in your case - the BoxCollider component.
In conclusion, the inspector failed to access the target object (I mean targets[0]) after you calls Undo.DestroyObjectImmediate on the BoxCollider component.
If you dig into the decompiled code of the inspector(UnityEditor.InspectorWindow), you will see that the overridden OnInspectorGUI function is called per Editor in order in UnityEditor.InspectorWindow.DrawEditors, including the internal editor of BoxCollider and your custom editor BoxStairsEditor of BoxStairs .
Solutions
Do not destory a component that is being shown by the Inspector in OnInspectorGUI.
Maybe you can add a delegate instance to EditorApplication.update to do that instead. In this way, the deleting operation will not break the editor/inspector GUI of BoxCollider.
Move the created component BoxCollider upper than your BoxStairs component before you destroy it. This may work but I'm not sure if other internal editor will access the BoxCollider or not. This solution not works when using UnityEditorInternal.ComponentUtility.MoveComponentUp. But, if the user manually move up the BoxCollider component, it works without any code changes.
Code of solution
After using solution 1, the NRE is gone on Unity3D 5.4 on Win10.
using UnityEngine;
using UnityEditor;
namespace BoxStairsTool
{
[CustomEditor(typeof(BoxStairs))]
public sealed class BoxStairsEditor : Editor
{
private const string DefaultName = "BoxStairs";
[MenuItem("GameObject/3D Object/BoxStairs")]
private static void CreateBoxStairsGO ()
{
GameObject BoxStairs = new GameObject(DefaultName);
BoxStairs.AddComponent<BoxStairs>();
if (Selection.transforms.Length == 1)
{
BoxStairs.transform.SetParent(Selection.transforms[0]);
BoxStairs.transform.localPosition = new Vector3(0,0,0);
}
Selection.activeGameObject = BoxStairs;
Undo.RegisterCreatedObjectUndo(BoxStairs, "Create BoxStairs");
}
private void OnEnable ()
{
EditorApplication.update -= Update;
EditorApplication.update += Update;
}
public override void OnInspectorGUI ()
{
if (GUILayout.Button("Finalize stairs"))
{
needFinalize = true;
}
}
private void FinalizeStairs ()
{
Undo.SetCurrentGroupName("Finalize stairs");
BoxStairs script = (BoxStairs)target;
GameObject go = script.gameObject;
BoxCollider bc = go.GetComponent<BoxCollider>();
if (bc != null)
{
Undo.DestroyObjectImmediate(bc);
}
Undo.DestroyObjectImmediate(target);
}
bool needFinalize;
void Update()
{
if(needFinalize)
{
FinalizeStairs();
needFinalize = false;
EditorApplication.update -= Update;
}
}
}
}
Related
Im making a simple game in Unity 3d, basically you have a backdround object with more gameobjects on top, what you have to do is drag those game objects into some trash cans, as soon as you hit the correct trash can with the correct object said object gets deleted, the idea of the game is that when you put all the objects in the corect cans you win the game
I have the drag and acceptance all made but i cant figure out how to make the script so that when theres no more objects, you win the game.
I've been trying to use Hierarchy on an empty game object thats the parent for the objects that you have to destroy, but i dont know how to actually delete the objects so that the code can register it, any help?
The code for the drag and the code im using for the trash cans:
using UnityEngine;
using UnityEngine.EventSystems;
public class arrastrar : MonoBehaviour, IDragHandler
{
public float z = 0.0f;
public void OnDrag(PointerEventData eventData)
{
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = z;
transform.position = Camera.main.ScreenToWorldPoint(mousePosition);
}
}
And the code inside the trashcans
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class botarBasura: MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.name == "cubo")
Destroy(other.gameObject);
} else if (other.gameObject.name == "escombros")
{
other.gameObject.transform.position = new Vector3(-1, 2.5f, -2); //If its not the correct trash can it returns the object to the starting position
}
} else if ( other.gameObject.name == "botellas")
{
other.gameObject.transform.position = new Vector3(0, 2.5f, -2);
}
}
}
You can use a scriptable object to keep track of your object. Objects just need to add and remove themselves to a list in the scriptable object in their OnEnable() and OnDisable() callbacks. When an object removes itself you can have code check to see if there are no more objects left and trigger an event that something in your scene will react to.
(Dragable Object)
public class DragableObject : MonoBehaviour{
//reference to the scriptable object
public DragableObjectSet RuntimeSet;
void OnEnable(){
RuntimeSet.Add(this);
}
void OnDisable(){
RuntimeSet.Remove(this);
//you can put code checking if the runtime set is empty here
//or you can put it in the DragableObjectSet Remove method
}
}
Scriptable object example code
public DragableObjectSet : ScriptableObject
{
public List<DragableObject> RuntimeSet = new List<DragableObject>();
public void Add(DragableObject obj){
RuntimeSet.Add(obj);
}
public void Remove(DragableObject obj){
RuntimeSet.Remove(obj);
if(RuntimeSet.Count() == 0)
//some code here raising an event (if desired)
}
}
This type of object is called a runtime set and is detailed in this talk around the 40 minute mark.
https://www.youtube.com/watch?v=raQ3iHhE_Kk
A solution could possibly be using a singleton:
Create a new empty GameObject and add this code:
using UnityEngine;
public class TrashCounter : MonoBehaviour {
public static TrashCounter instance = null;
public int counter = 0;
void Awake(){ instance = this; }
}
Now, wherever you create an object (or in the Start() method of any object code), you just need to do TrashCounter.instance.counter++ and you will have your trash count.
After that, you just need to update this counter everytime you delete an object by doing TrashCounter.instance.counter-- and after you ask if(TrashCounter.instance.counter == 0) WinGame().
You can get an array of all active & not-yet-destroyed GameObjects with some enabled component with Object.FindObjectsOfType<ComponentType>();. Once you have that, you could then look at the length of that array.
So, you could do something like this in the code that destroys/deactivates the draggable gameobject:
GameObject otherObject = /* get draggable object to possibly destroy */;
if ( /* test to remove otherObject */ )
{
// deactivate the draggable object
otherObject.setActive = false;
// get all other draggable objects
DraggableObject[] remainingDraggableObjects = Object.FindObjectsOfType<DraggableObject>();
if (remainingDraggableObjects.Length == 0)
{
// there are no more active gameobjects with enabled DraggableObject component
// handle win condition here.
}
}
In your specific example, it could look like this:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.name == "cubo")
{
other.gameObject.setActive = false; // better than destroying in most cases
// get all other draggable objects
arrastrar[] remainingObjects = Object.FindObjectsOfType<arrastrar>();
if (remainingObjects.Length == 0)
{
// there are no more active gameobjects with enabled arrastrar component
// handle win condition here.
}
}
// ...
I have 2 buttons in my options menu, "return to main menu" and "mute music"
Each of these buttons has a script attached to it, and calls the OnPress() method of the script when it is pressed.
I also have a main Level object/script that handles all the scene loading and stuff.
So the script of the main menu button does FindObjectOfType() in its Start and then calls level.LoadStartScene() in its OnPress().
The script for the mute button does the same thing but calls level.ToggleMuteMusic().
So this worked perfectly before, but then I made the level a singleton with the following code:
public void Awake() {
InitializeSingleton();
}
private void InitializeSingleton() {
if (FindObjectsOfType(GetType()).Length > 1) {
Destroy(gameObject);
} else {
DontDestroyOnLoad(gameObject);
}
}
So now the main menu button works perfectly, but the mute button gives an error; I think this is because in Start() it finds the old level object, and then the one with DontDestroyOnLoad comes in and deletes the old one, but then why does the main menu button work???
Mute Button Code:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class MuteButton : MonoBehaviour {
[SerializeField] string mutedText = "Unmute Music";
[SerializeField] string unmutedText = "Mute Music";
private Level level;
private TextMeshProUGUI myText;
public void Start() {
level = FindObjectOfType<Level>();
myText = GetComponent<TextMeshProUGUI>();
}
public void OnPress() {
if (level == null) {
Debug.Log("log 1");
}
level.ToggleMuteMusic();
bool muteMusic = level.GetMuteMusic();
if (muteMusic == true) {
myText.SetText(mutedText);
} else if (muteMusic == false) {
myText.SetText(unmutedText);
}
}
}
Menu Button Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MenuButton : MonoBehaviour {
Level level;
public void Start() {
level = FindObjectOfType<Level>();
}
public void OnPress() {
level.LoadStartScene();
}
}
Full Error:
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.GameObject.GetComponent[T] () (at C:/buildslave/unity/build/Runtime/Export/Scripting/GameObject.bindings.cs:28)
Level.ToggleMuteMusic () (at Assets/Scripts/Level.cs:74)
MuteButton.OnPress () (at Assets/Scripts/MuteButton.cs:23)
Thanks for your time :)
It can be a bit difficult to determine the exact order that things are executing to cause this issue, but the fact that you're losing a reference in Start() that was handled in Awake() is odd. I've had issues using FindObjectOfType<> in Start() in the past, so maybe changing up how you're handling the singleton would be best.
What I'd recommend is making your Level static so you can reference it easier, since you're already implementing a form of Singleton.
Here's an example of how you could rewrite the top of your Level.cs file:
public static Level instance;
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(this.gameObject);
return;
}
else
{
instance = this;
}
DontDestroyOnLoad(this.gameObject);
}
This causes it to create the Level class once, then destroy all subsequent versions of it (like when you reload the scene it's placed in).
Now, to reference it in your other scripts, you never have to use FindObjectOfType<Level>() again! You can statically reference Level.instance, like so:
//New way to call ToggleMuteMusic()
Level.instance.ToggleMuteMusic();
Hopefully this helps!
I'm currently developing a game in Unity and I ran into a small problem. I'm working on a restart function that gets called automatically when the player dies and loads the first scene again. However for some reason when reloading the scene games objects are duplicated with the version of the gameobject that was active at the time of death being inactive and the version that gets loaded as should be loaded getting set to active and so on every time the player dies adding a new duplicate of the same gameobjects to the hierarchy. I tried to solve this problem in multiple ways. First by trying to check each the gameobjects that get duplicated already have an instance of themselves running by attaching a script that checks every time a change in scene occurs wether or not their already is an instance of the gameobjects present:
public static GameObject Instance;
void Awake()
{
if(Instance){
DestroyImmediate(gameObject);
}else
{
DontDestroyOnLoad(gameObject);
Instance = this;
}
}
This seemed to solve the problem at first but it became to teadious towards the end because the scripts made all of my other scene objects behave badly or not at all so I chose to look for another solution.
Secondly I tried to Destroy each individual gameobject before I start loading the first scene. This also seemed to work at first but now my object pooler just recreates new intances of the gameobjects that it adds too the hierarchy esentially displacing the same problem to other gameobjects.
Finally in order to solve this problem I tried to make my objectpooler run only once when the scene that requires it to be loaded gets called but this didn't seem to work either. Does anyone have any idea how I could solve this problem. This is part of the script responsible for loading the original scene upon player death:
void Restart()
{
GameObject[] allObjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
foreach (GameObject gos in allObjects)
{
if (gos.activeInHierarchy)
{
if (gos != GameObject.Find("GameManager") && gos != GameObject.Find("ScreenBound"))
{
gos.SetActive(false);
}
}
}
MySceneManager.LoadScene(0, this);
}
How could I change this in order to be able to reload the original scene without having any previously loaded GameObject get duplicated and behave according to how it should in the scene in which it got loaded originally?
The class responsible for loading and deloading scenes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class MySceneManager
{
private static int lastLoadedScene = 0;
public static void LoadScene(int index, MonoBehaviour caller)
{
ObjectPooler objP = new ObjectPooler();
objP.ReleaseAll();
caller.StartCoroutine(loadNextScene(index));
}
private static IEnumerator loadNextScene(int index)
{
var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
_async.allowSceneActivation = false;
while (_async.progress < 0.9f)
{
yield return null;
}
_async.allowSceneActivation = true;
while (!_async.isDone)
{
yield return null;
}
var newScene = SceneManager.GetSceneByBuildIndex(index);
if (!newScene.IsValid()) yield break;
SceneManager.SetActiveScene(newScene);
if (lastLoadedScene >= 0) SceneManager.UnloadSceneAsync(lastLoadedScene);
lastLoadedScene = index;
}
}
This is my ObjectPooler:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool
{
public string tag;
public GameObject prefab;
public int size;
}
#region Singleton
public static ObjectPooler Instance;
private void Awake()
{
if (Instance)
{
Destroy(this.gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
#endregion
public List<Pool> pools;
public Dictionary<string, Queue<GameObject>> poolDictionary;
private Dictionary<string, Pool> prefabPools;
void Start()
{
poolDictionary = new Dictionary<string, Queue<GameObject>>();
foreach (Pool pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
DontDestroyOnLoad(obj);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
poolDictionary.Add(pool.tag, objectPool);
}
}
private List<GameObject> currentlySpawnedObjects = new List<GameObject>();
public void Release(GameObject obj)
{
currentlySpawnedObjects.Remove(obj);
obj.SetActive(false);
obj.transform.SetParent(transform);
poolDictionary[obj.tag].Enqueue(obj);
DontDestroyOnLoad(obj);
}
public void ReleaseAll()
{
foreach (var child in currentlySpawnedObjects)
{
Release(child);
}
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if (!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn = poolDictionary[tag].Dequeue();
objectToSpawn.SetActive(true);
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if (pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
poolDictionary[tag].Enqueue(objectToSpawn);
return objectToSpawn;
currentlySpawnedObjects.Add(objectToSpawn);
return objectToSpawn;
}
}
Depends of your needs you can try next ways:
Use singleton pattern if you need save single instance of objects. This case relevant for saving managers (GameplayManager, SceneController, AssetBundleManager, etc.) in other cases will be better to use other ways. To read more about implementation you can see this article.
Destroy all old objects when loaded new scene. To do this you can use SceneManager.LoadScene method with LoadSceneMode.Single as parameter. It will keep DontDestoryOnLoad objects but will remove all others.
I'm not sure but the first possible issue to me already seems to be that it is running in Coroutine on an object in the scene you are going to unload.
It is cool that this is doable but have in mind that the Coroutine will stop working as soon as the caller object/component is destroyed or disabled.
To avoid that I would move your script to an object in the DontDestroyOnLoadScene using a Singleton pattern.
The next issue might be you going by SceneIndex ... both scenes, the one you want to unload and the one you want to load have index 0!
So maybe you get a conflict between the scene additively loading and the one you want to unload.
This also might happen again when you called
var newScene = SceneManager.GetSceneByIndex(lastLoadedScene);
To avoid this I would rather go by scene reference for the unloading
public class MySceneManager : MonoBehaviour
{
private static MySceneManager instance;
// Lazy initialization
// With this you wouldn't even need this object in the scene
public static MySceneManager Instance
{
if(instance) return instance;
instance = new GameObject ("MySceneManager").AddComponent<MySceneManager>();
DontDestroyOnLoad(instance);
}
// Usual instant initialization having this object in the scene
private void Awake ()
{
if(instance && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(this);
}
public void LoadScene(int index)
{
StartCoroutine(loadNextScene(index));
}
private IEnumerator loadNextScene(int index)
{
// I didn't completely go through your ObjectPooler but I guess you need to do this
ObjectPooler.Instance.ReleaseAll();
// Instead of the index get the actual current scene instance
var currentScene = SceneManager.GetActiveScene();
var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
_async.allowSceneActivation = false;
yield return new WaitWhile(() => _async.progress < 0.9f);
_async.allowSceneActivation = true;
yield return new WaitUntil(() => _async.isDone);
// You have to do this before otherwise you might again
// get by index the previous scene
var unloadAsync = SceneManager.UnloadSceneAsync(currentScene);
yield return new WaitUntil(()=>unloadAsync.isDone);
var newScene = SceneManager.GetSceneByBuildIndex(index);
SceneManager.SetActiveScene(newScene);
}
}
Alternatively since anyway you do nothing special while loading/unloading the scenes:
why using Additive scene loading at all if you could also simply call
ObjectPooler.Instance.ReleaseAll();
SceneManager.LoadSceneAsync(index);
without making it additive so the current scene is simply removed automatically as soon as the new scene is fully loaded.
Note: Types on Smartphone so no warranty but I hope the idea gets clear
i try to make a fire place were i need to collect wood and use it on fire place but nothing happen, i have used the debug.log to see if the variable are correct receive the changes and its work perfect but the if i have fireonplace variable = 1 then activate the fireplace object the wood its inserted on the variable but the trigger for activate the fire not happen.
This is my code
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class enablefire : MonoBehaviour {
public GameObject Enable_Disable;
public static int woodonfire = 0;
public void Enable ()
{
Enable_Disable.SetActive (true);
}
public void Disable()
{
Enable_Disable.SetActive (false);
}
// Use this for initialization
void Start ()
{
Enable ();
//Enable_Disable.SetActive (false);
}
void Update()
{
if (woodonfire >= 1)
{
Enable_Disable.SetActive (true);
}
if (woodonfire == 0)
{
Enable_Disable.SetActive (false);
//Enable_Disable.SetActive (false);
}
}
}
change the name of the game object, as a rule the name has to begin with lowercase, try names like enableDisable;
also you already created a method to enable/disable the GameObject why don't you just change this code...
void Update()
{
if (woodonfire >= 1)
{
Enable();
} else if (woodonfire == 0)
{
Disable();
}
}
You have to move the script onto another object.
Since an inactive gameobject has all its components inactive, the script on it will be inactive too and you won't be able to active the object again. It may be a better option to make the particle system in another object which contains the script. ( You will basicly activate and de-activate your child object (fire) ).
I'm pretty sure that
animation.Play("DoorOpen");
Would play the animation "DoorOpen", but when i'm trying to put it in my code, it just giving me an error message:
The Animation attached to this GameObject (null if there is none attached).
using UnityEngine;
using System.Collections;
public class DoorPhysics : MonoBehaviour {
int Open = 0;
// Update is called once per frame
void Update() {
if (Open == 0) {
if (Input.GetKeyDown("e")) {
animation.Play("DoorOpen");
}
}
}
}
You need to show location of gameobjects in unity, they do not know eachother, you have to always use :
GameObject.GetComponent<T>()
GetComponentInParent<T>()
GetComponentInChildren<T>()
best practice is to get object references at Start()
also you should attach IMPORTANT!!! Animation component to the object this script it attached to
public class DoorPhysics : MonoBehaviour {
public Animation animation;
int Open = 0;
void Start()
{
animation=GameObject.GetComponent<Animation>(); //if your have derived type change Animation to good class DoorAnimation for example
}
void Update()
{
if (Open == 0) {
if (Input.GetKeyDown("e")) {
this.animation.Play("DoorOpen");
}
}
}
}
if that code wont work, you will need show me your GameObject hierarchy
And if your just start your trip with it learn MonoBehaviour call order
and life cycles of events