Lashonda Lagant
Thursday, June 9, 2022
Tuesday, March 22, 2022
Get All The Game Objects In A Scene Unity
I've already shown several examples of using the scriptable objects to preserve data between scenes. A ScriptableObject is essentially a data container that can store data independently from the class instances. The most typical use case for it is to set up an object under your project's 'Assets' and access the data that it holds at runtime. However, since the scriptable objects use the editor's namespace you cannot use it to permanently save data in deployed builds.
You can only use the data you set up during the development for a single game session. We can avoid using Unity's Find functions by being smart with how we structure our code. Two easy methods to avoid finding game objects is to have a script have a direct reference to that game object.
Useful if the game object already exists in the scene. Alternatively, we can have a game object manages a group of game objects, then have another script reference and request access to those game objects through that script. The second and slightly less obvious problem is that shape instances that were inactive before a recompilation never get reused. That's because we've lost the lists that kept track of them.
First, retrieve an array containing all root game objects of the pool scene, via the Scene.GetRootGameObjects method. We can use the static function GameObject.FindGameObjectsWithTag to look for all game objects that use a particular tag. This is useful when we want iterate through a group of particular game objects. This can also be useful if we want to find a single game object, but may have multiple game objects using the same tag.
I hope that with this understanding of find you can appreciate why these functions are slow even when used in Start or Awake. When a scene is loading, any game objects in that scene will call Start and Awake on any scripts attached. Using a function like Find when initialising game objects as a scene is loading can slow down the time it takes for our scene to load.
Unity has a lot of built-in functions that allow us to find game objects in any loaded scene. These find functions can look for game objects using tags, or based on an object's type. Although you might find them used in many online tutorials, they are not a good option for finding game objects.. The biggest problem being that they don't perform well in larger games.
This one should be pretty obvious, but I noticed that some developers are not aware of it. It is entirely possible to write a public static class and access all it's primitive components and member functions from any script in your scenes. This can be perceived as a very crude way of storing and accessing the data across your scenes.
However, for a small project it can be quite a handy solution that doesn't need too much elaboration. While you work on your project in Unity, sooner or later you will come across a problem of data persistence. It may occur at the stage in your development when you have to maintain information between loading different scenes or even different game sessions. That goes without saying that even the smallest game may require persistent data, which you can always access from various scripts inside your system's architecture. Luckily, both C# and Unity provide few solutions to this problem.
All of them have pros and cons and it is entirely up to developer to decide which one is the best for the project. In this tutorial I'll describe the most important ones. We can use the static function GameObject.FindGameObjectWithTag to look for individual game objects. It is important to note that, in this way, game objects are not queried in any particular order. If you search for a tag that is used on multiple game objects in the scene, this function will not be able to guarantee which game object is returned. As such, it is more appropriate when we know that only one game object uses such tag, or when we are not worried about the exact instance of GameObject that is returned.
Environmental lighting too dark.Besides being a collection of game objects, scenes also have their own lighting settings. The environmental lighting changed because the main scene no longer has a light in it, and as a result its environmental lighting has gone dark. We get this result because the lighting settings of the active scene is used. As I mentioned above, we can create references to game objects and components by searching and finding them using built-in methods in Unity. This is useful especially when you want to stay the variable private or when you want to access an object that is created during runtime.
Unity has been able to handle multiple scenes at once for a long time. This is great for dynamic loading and unloading of scene parts e.g. chunks in a voxel like environment, single map-parts etc. but also for your object management. To keep your objects persistent, put them in a scene which you won't unload at runtime and is always loaded. This persistent scene is the only scene which is already loaded at game start and decides which additional scenes should be loaded and unloaded.
This is possible due to the SceneManager's additive loading mode. In order to tackle the problem, let's first define it. All scripts and components that are attached to GameObject inside a Unity project are destroyed each time you load a scene.
One may think that this can be easily solved by marking any of these objects as static. However, that is not the case and you will still be faced with losing the data. The way Unity deals with any kind of Object, especially Component or GameObject type instances, is that it destroys them every time you transit from one scene to another.
This is an expensive way to find root game objects, yet this is what you must do for older versions of Unity to identify root nodes in the scene. To reference these variables, we have a couple of options. We can search the game object that we want to assign using built-in methods that are included in Unity.
We will see this option in later sections of this article. Another option is to assign relevant game objects directly in the Unity editor. But to do this we have to declare the objects either public or private with attribute. Most of the time, we want to access other game objects and their components.
In these cases, we need an address(or let's say a phone number) of the game object and thus, we can dial whenever we want. Let's assume we have some kind of LevelManager script that keeps track and maintains the state of our game. Presumably we want to have only one instance of this object that we can access from any script. To that end, I'm going to create an Empty GameObject at the root of project Hierarchy and attach the LevelManager script component to it. Next, I'll instantiate it using the Singleton pattern and make sure that it doesn't get destroyed when loading a new scene with DontDestroyOnLoad. I'll encapsulate the entire logic inside the Awake() method.
Contrary to what you might intuitively think, public static classes do not actually persist game-wide. Since any class is attached to a game object, it will get destroyed when you load a new scene. Even if another scene has a new public static class in it, the data inside will be reset – that is, the static class will be initialized anew at scene load. That code transforms a collection of game objects to a collection of root game objects. Note that we are overloading BreathFirstTraversal to take a single game object as its parameter.
The overloaded function simply repackages the single object in an array and passes it into the original version of the function. All this code is doing is accumulating the time that passes, and after 2 seconds instantiating a new enemy and adding it to our list. If we ever need to search for an enemy, then all we need to do now is access them from this list. No need to call Find and search through the entire scene which includes searching through game objects that aren't enemies.
First, before looking at any alternatives, let's identify why these Find functions are useful. They let us find a single game object by name, tags, or object type. We can also find a group of game objects using a tag or by type of object. Finally, looking at Find that takes a string as a parameter, the name of a game object, is even slower than other find functions. With this approach, we start loading the level while we still need to read and create all shapes. As the level loading is asynchronous, the current level scene is still the loaded scene while we are reading and creating the shapes.
Only later will the correct level become loaded and the active scene. This isn't a problem, because we're putting all shapes in a separate factory scene and they don't depend on anything from the levels. This might change in the future, making this process more complex.
Level count set to 2.Now we have to check whether the player pressed one of the number keys to load a level. We can do this by looping through all valid build indices. The corresponding key code is KeyCode.Alpha0 plus the index. If the key is pressed, start loading that level and skip the rest of the Update method. This doesn't work, again because the scene hasn't been marked as loaded yet.
Trying it in Awake is too early, but if we delay a bit and use a Start method instead, it works. This is true for all game objects that are part of a scene. Awake gets invoked immediately when the scene is loaded, but doesn't count as loaded yet. Start and later Update invocation happens afterwards, when the scene is officially loaded.
Although Level 1 now correctly becomes the active scene, we still do not get correct environmental lighting. Builds are fine, because all lighting gets properly included. But in the editor automatically generated lighting data doesn't work correctly when loading scenes in play mode.
Scenes aren't only useful for grouping objects in play mode. Often, projects are partitioned into multiple scenes. The most obvious configuration is one scene per game level. But a game usually has objects that do not belong to a single level, but to the entire game. Instead of putting a copy of those objects in every scene, they can be put in their own scene too.
This allows you to break up your project into multiple scenes, but requires multiple scenes to be open at the same time while editing. If there are multiple objects that have the same tag and we want to get them all, we need to use the method GameObject.FindGameObjectsWithTag. Likewise, this method also takes a string parameter and returns an array of all game objects. Therefore, we have to declare an array that will store the game objects. I've written a bit more about optimization here.
This function can return any type of Unity object that is loaded, including game objects, prefabs, materials, ... Unity is a game engine with its own philosophy. Even though there are quite a few alternatives, it's quite unique in the way it handles the building blocks of any game – game objects, scenes, code, scene graph. And by unique, I mean how easy it is to understand it. Don't let spawned objects clutter your hierarchy when the game runs.Set their parents to a scene object to make it easier to find stuff when the game is running. You could use an empty game object, or even a singleton with no behaviour to make it easier to access from code.
To perform a recursive traversal of the scene we must first identify the root nodes of the scene. That is those game objects that have no parent because they are at the top of the hierarchy. VictorI prefer to do it with the "base" scene that contains all the common elements throughout the game (settings, loading screen, etc.). All other scenes are loading asynchronously in additive mode, and the base scene is responsible for loading screen. As well as this, if you create game objects whilst the game is running, when these game objects are being initialised, they call Find from Start or Awake. This means that the game can still incur a performance penalty when calling find in these functions.
On the frame in which Find is called, the frame rate could drop. It might not be noticeable over a single frame but if you create many game objects that all make use of Find then you could soon see consist drops in your frame rate. However, it's not all bad news, as you will soon discover there are many alternatives to getting a hold of game objects which aren't as taxing on performance. I'll start my discussion by first giving you a good reason for using these functions, prototyping. When wanting to make a proof of concept or create something for a game jam, they work really well.
They avoid you having to think about the structure of your code and give you access to any game object in any script. In bigger games though this power comes at a big cost. While it is possible to have both level scenes open at the same time, it mostly makes sense to work with only a single level. Maybe it's convenient to have multiple levels open to copy or move stuff around, but that should be temporary. When entering play mode, we expect to either have no or exactly one level open, besides the main scene.
This works fine when level 1 is open, but if level 2 is open then level 1 gets loaded too when we enter play mode. Now our game doesn't freeze when loading a level. This means that it's possible that our game's Update method gets invoked an arbitrary amount of times before the level is loaded and has become the active scene. This is a problem, because it makes it possible for the player to issue commands before the level has been loaded. To prevent this, the Game component must disable itself before beginning the loading process, and enable itself again after loading has finished.
-
I've already shown several examples of using the scriptable objects to preserve data between scenes. A ScriptableObject is essentially a...




























