Unity calling methods on new scene - unity3d

I have a game with a bunch of scenes, I load them and then do DoNotDestroy so I keep them (awake/start get called the first time).
Let's say we have following state changes - menu -> game -> game over -> menu -> game...
I want to call a method when the scene is shown each time to reset/set some values.
What's the cleanest way to do this? Currently I'm doing it with the update method, but it takes a bunch of lines and it's wasteful to use Update() in every script when you don't need to.

As per the Unity documentation on Multiple Scene Editing, it's recommended that you avoid DontDestroyOnLoad.
It is recommended to avoid using DontDestroyOnLoad to persist manager
GameObjects that you want to survive across scene loads. Instead,
create a manager scene that has all your managers and use
SceneManager.LoadScene(<path>, LoadSceneMode.Additive) and
SceneManager.UnloadScene to manage your game progress.
Therefore, in your case, you could handle resetting data in a number of ways. But one simple way would be to check the name of the scene that was just loaded, from a component in your "Manager" scene. E.g. MySceneManager in BaseScene. In the code following, we're making use of the sceneLoaded delegate of SceneManager.
using UnityEngine;
using UnityEngine.SceneManagement;
public class MySceneManager : MonoBehaviour
{
void OnEnable ( ) => SceneManager.sceneLoaded += OnSceneLoaded;
void OnSceneLoaded ( Scene scene, LoadSceneMode mode )
{
switch ( scene.name.ToLower ( ) )
{
case "menu":
// Do some "menu" initialisation here...
break;
case "game":
// Do some "game" initialisation here...
break;
case "gameover":
// Do some "gameover" initialisation here...
break;
}
}
void OnDisable ( ) => SceneManager.sceneLoaded -= OnSceneLoaded;
}
N.B. This method unfortunately won't have compile-time checking of your scene names, so you'll have to be careful to make sure your scene names correspond to the switch cases.
But, another thing the remember is that any component in your newly loaded scene will have Awake() called anyway, so you could just offload all of your scene initialisation to a component in each scene. This would decouple MySceneManager from the data in each scene.
So, you've got quite a few options, these being just two of them. The "best" option will be the one you're comfortable with, and that suits your needs.

Related

How to preserve object references when loading new scene

I am trying to implement a simple racing game in Unity 2021.2 and I have a GameManager Object that holds state across different scenes (i.e. races). To achieve this I use this code in my GameManager:
public static GameManager instance;
private void Awake()
{
if (instance == null)
{
DontDestroyOnLoad(gameObject);
instance = this;
} else if (instance != this)
{
Destroy(gameObject);
}
}
This works fine as long as the fields of the GameManager are of primitive types, such as int and string. However, my GameManager also has a field of type RacerData[], where RacerData is a class that I created in a separate script, which holds data associated with a racer, such as the name, score, etc.
Using this approach I ran into the following problem: When I transition from the first race to the second, i.e. when a new scene is loaded, the GameManager keeps the values of all the fields of primitive types, but loses the references to the RacerData objects.
My first attempt to solve this was that I turned my RacerData script into a MonoBehavior. Instead of just instantiating RacerData Objects in GameManager I created a prefab with the RacerData script attached and instantiated GameObjects from this prefab in my GameManager.
GameObject myRacerDataInstance = Instantiate(myRacerDataPrefab, …, …);
DontDestroyOnLoad(myRacerDataInstance);
instead of:
RacerData myRacerData = new RacerData();
This had the desired effect fo keeping the RacerData game objects when loading a new scene, however the references of the GameManager to those game objects is still lost when a new scene is loaded. I then proceeded to write a hacky workaround to reassign the objects in the next scene which, so far, I failed to make work. However, I'm less interested in making this workaround work rather than to understand why the references to the RacerData gameObjects are lost in the first place and what better way there is to keep data across scene loads.
This appears to be a very common use case to me and I can't quite believe that a developer is supposed to rewire the references manually, especially since those could form a much more complex object graph than mine.
So: Is there a way to preserve those references across scenes? Or is DontDestroyOnLoad() not the right mechanism in the first place and in that case: what better option is there to preserve data (including object references) accross scenes?
It turns out the problem wasn't quite what I thought it to be: The references weren't set on the GameManager object at the time I tried to access them, i.e. from the Start() method of another game object called RaceManager.
A quick fix to the problem was to simply wait for the references to be recreated like so (in RaceManager):
void Start()
{
StartCoroutine(WaitForRacers());
}
private IEnumerator WaitForRacers()
{
while (racers == null)
{
yield return new WaitForSeconds(1);
gameManager = FindObjectOfType<GameManager>();
racers = gameManager.GetRacers();
}
InitializeRace(); // initialization logic that relies on racers to be != null
}

Unity - Game modes as state machine?

I am making a quiz game in Unity and I've come across architectural problem.
I want the game to have few game modes, like standard, faster answer - more points, etc. Each of which will behave in its own specific way but some things will be very similar like answering questions, starting timer, etc.
Currently its structured based on this. There is a QuizSystem that holds reference to QuestionDatabase, UIReferences(buttons,score text, etc) and GameSettings (questions per game/per mode etc).
To start the game you need to call QuizSystem.Start() and it starts its current GameMode which derives from abstract StateMachine and is a monobehaviour (dont know if neccesary). I also have abstract State class from which different game states will derive from. It has a constructor with (GameMode owner) as paramenter and 3 functions: Start(), Tick(), End().
So, this way I can have Standard game mode which will instatiate lets say StandardPreparationState, which on end will call StandardAnswerState which will start the timer and wait for user input and again call StandardPreparationState. Cycle will repeat until questions per mode amount is reached and then delegate next action to QuizSystem.
The advantage of this approach is that every mode can behave in its own way like add additional steps in between but it kinda limits reusability. What I mean by that is if some OtherMode would have the same preparation functionality but different action afterwards, it wouldn't work beacause
StandardPreparationState would transition to StandardAnswerState.
I could add another parameter such as (GameMode owner, State transitionTo) to the State constructor but that somehow seems wrong I don't know why xD
What I want to know is how do you guys implement different game modes for your games? Do you make each mode as separate scene? Or maybe use States Machine pattern and have Manager class that takes care of starting/swaping modes?
I know that each game is different but are there maybe some common approaches for that?
Thanks in advance!
This question is quite open and opinion-based. However there are few "common" approaches, one of the most important is to make game "data-driven".
What? Why? How?
Imagine you are having space shooter, where you have your ship flying around and picking guns. Each time you add new gun, you will have to code its damage, kind of projectiles and how many of them you shoot, their color, in what pattern they spawn, speed, size, ...
Everytime you would want to add a new gun, you would need to enter the code and change it there, compile, ... Lot of work.
Instead people thought, "why don't we create simple class that holds all the parameters? We will make it editable from Unity, instatiate it in the project and we won't need to code that much."
This is when Unity brought Scriptable objects.
Scriptable objects
A ScriptableObject is a data container that you can use to save large amounts of data, independent of class instances. One of the main use cases for ScriptableObjects is to reduce your Project’s memory usage by avoiding copies of values.
The idea is to create scriptable object for your mode and set up multiple kinds of modifiers that will the mode use. Folder structure might look like:
> ScriptableObjects
| |--> Modes
| |-> NormalSO (instance)
| |-> HardWithLotOfExpSO (instance)
| |-> EasyWithLowerExpSO (instance)
> Script
|--> ScriptableObjects
|-> ModeSO
ScriptableObject is class that doesn't really have the logic inside, just creates "structure" for keeping data. Example of such class would be:
public class ModeSO : ScriptableObject
{
public string modeName;
public float scoreMultiplier;
public int numberOfEnemiesMaxAlive;
public int numberOfEnemiesTotal;
public Vector3[] spawnPoints;
}
In the Unity itself you would then create instance of such objects. And what about interaction with other classes? Well, they would just work as:
Game manager hold single instance of active mode
Class that would be handling score (e.g. player / scoreboard) or Enemy would ask GameManager what is current multiplier for score
WorldSpawner would ask GameManager how many enemies should he spawn, where, and when to spawn next ones
At the beginning of the game you would be able to select difficulty by its name
Example of one of the classes (Scoreboard):
public class ScoreBoard: MonoBehavior
{
GameManager manager;
private float totalScore;
OnEnemyDestroyed(float scoreForEnemy)
{
totalScore += scoreForEnemy * (manager?.activeMode?.modifier ?? 1);
}
}
And the best is, whenever you will change some data, you will just modify the existing instance in the Unity. No need to go into code. No need to recompile whole game.
I think having different scenes for each game mode, especially if the modes are very similar save for a few settings, is unnecessary. I'd have to see more of your design to know how your game logic is being handled (is it all managed in one GameManager script? Or are there multiple scripts in the scene that take values from this manager script to handle game mechanics?)
One way I've handled different game modes before is to use a public int value in the GameManager that represents the different modes (i.e 1 = easy, 2 = medium, 3 = hard). I would then use a switch statement in any scripts whose behavior/values depend on this mode and reference that public int to determine game settings.
Game Manager:
public class GameManager: MonoBehavior
{
public int gameMode = 0; //set to 1,2, or 3 by UI
[...] //rest of game manager code
}
Example Behavior Script:
public class EnemySpawn: MonoBehavior
{
public GameObject enemy;
GameManager gm;
private float spawnRate;
Start()
{
switch(gm.gameMode)
{
case 1:
spawnRate = 15f;
break;
case 2:
spawnRate = 10f;
break;
case 3:
spawnRate = 5f;
break;
default:
spawnRate = 10000f;
Debug.log("invalid mode");
break;
}
}
Awake()
{
InvokeRepeating("SpawnEnemy", spawnRate, 0f);
}
[...] //rest of EnemySpawn code, including a SpawnEnemy() function.
}
This example would reference the GameManager's gameMode int to determine what speed to spawn enemies at, assuming there is a SpawnEnemy() function somewhere down in the code. I can't verify if this example is syntactically correct, but it's just to show one way to handle game modes.

Singletons in Unity

I want to keep my player when switching scenes but without getting lots of copies of it , so I found out about singletons.You do something like this:
public class Player : Monobehaviour
{
private static Player instance = null;
void Start()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameobject);
}
else
Destroy(gameobject);
}
}
And it's pretty hard for me to understand.I get how static variables work , they belong to the class not to the instance.So , first my objects is in scene A.I switch to scene B,a new gameobject will be created with the script Player on it.Now , since instance holds the reference to the gameobject in scene A , why won't the else statement get called and a new gameobject will actually be created?Also , if you could explain a little bit more detailed than unitydoc how DontDestroyOnLoad() works , I'd be thankful.
This code runs after the object is created. In fact it runs on a script on the GameObject. The else statement will run, and it will destroy the GameObject this script is attached to, which is what you want because you already had another copy in the world from previously (the one that is referenced by the "instance" variable).
JSchiff is correct that this code will run after an instance of this object is created, storing the current instance in your static variable for unconditional access to "the current instance" or destroying new instances if there is one already. The else branch in your code will run.
To answer your extended question about DontDestroyOnLoad and it's interaction with this static variable: all the objects in the Engine are held in memory by the Engine. A Scene can be thought of as a "collection of objects in memory" by the engine. When a scene is unloaded, all objects in that collection are destroyed, unless the engine has been told DontDestroyOnLoad (as in un-load, although documentation refers to it as a "swap". You can load scenes without unloading scenes, this might have been terminology which just grew outdated but that's conjecture on my part).
These ideas are combined to make your Singleton, a highly accessible (in this case static instance variable), persistent (through DontDestroyOnLoad), one of a kind (through destroying any instance after the first) object. This isn't the only way to apply the Singleton pattern, but it is a common one in Unity.

how to clone several game objects in a way that clone properties of one can be adjusted to match all others in scene view

I asked How can I adjust shape/dimensions of one clone to affect all other clones in the scene view and the accepted answer was spot on. It could only clone one game object. I tried making some adjustments but the only solution I came up with was adding duplicate methods for additional objects. This doesn't work well when dealing with several game objects to be cloned.
How can I clone several unique game objects so that adjusting the components/properties of one clone would affect all other clones of that object in the scene view?
Please note that I don't want to achieve this at runtime and I don’t want to use prefabs. I am using this to help with creation of complex levels so the live update of clones being adjusted is very important.
Additionally, I also need a way to turn off the this repeated property/component replication on each clone, preferably with a button.
I don’t want to use prefabs
The new prefab system in Unity is exactly what you need. It fits all of your requirements:
Clone several unique game objects
The prefab system is made for cloning unique gameobjects. It even supports prefab nesting.
I don't want to achieve this at runtime
Great, prefabs only update globally when you click the override button in the editor.
I need a way to turn off the this repeated property/component replication on each clone
That's equivalent to unpacking the object (breaking the connection).
If you have a good reason to avoid using prefabs, you can always write a custom script that tracks changes in the properties you want to share, and updates all other objects immediately. You can make that script run in edit mode by adding the [ExecuteInEditMode] attribute to the class it's in, just don't forget to disable it when running the project. Again, I highly recommend using prefabs instead.
You should use a ScriptableObject as data container and attach that to the gameobject, all clones will use the same synchronized ScriptableObject.
You should use events. Unity3d tutorials has a good, simple explanation: https://unity3d.com/learn/tutorials/topics/scripting/events
Is this only for editing the objects in the editor? If so, then it sounds like prefabs are the way to go; you can directly edit the prefab and all of its 'clones' in the scene will have all changes, including all monobehaviours, transforms, and whatnot replicated to that of the prefab.
If you need this to work at runtime, then you will likely need some code to do this for you. You haven't quite provided enough clarification as to what exactly you want to do, so for the below example I'll assume that you have a gameobject with a mesh or sprite component, and want its size/scale modified alongside all of its "clones";
using UnityEngine;
using System.Collections.Generic;
public class ShapeClone : MonoBehaviour
{
//This will hold references to the other "clone" gameobjects.
public List<GameObject> otherClones = new List<GameObject>();
//All the "clones" in the list otherClones will have their scale matched to this gameobject's scale
public bool leader;
private void Update()
{
if (leader) //Only change other clones' scales if marked as leader, to avoid every single clone
//overriding each other's scale every single frame, which could be rather chaotic
{
for (int i = 0; i < otherClones.Count; i++)
{
//setting each of the other clones' scale to that of this object.
otherClones[i].transform.localScale = this.transform.localScale;
}
}
}
}
The above is a brief example to give you an idea and is by no means extensive, but you should be able to apply it to what you're trying to do; for example, if you wanted to replicate the colour of sprites across gameobjects instead, you can modify otherClones to be a list of Sprite references instead, and instead of setting the scale in update, you can set the colour of each of the Sprite components to that of this object.
If you're only needing this functionality in the editor and not during runtime, though - I highly recommend going with the first option using prefabs, as it will give you far more functionality at a fraction of the cost, performance wise.
It sounds like you have an object that has several clones. You want changing the shape or dimensions of any of those objects to affect the other ones?
For this to happen, each object needs to know about the other ones. You can do this decentralized (each object contains a reference to each other) or centralized (one object governs the rest).
The centralized approach is more simple so I'll give a simple example.
public class Shape
{
public int length;
}
public class ShapeCentral
{
public List<Shape> shapes = new List<Shape>();
public void CloneShape()
{
//instantiate new shape
shapes.Add(new Shape());
}
public void SetCloneLength(int l)
{
shapes.ForEach(x => x.length = l);
}
}
As you can see, one object can control all the clones at once. The trick is to not create clones using other methods or you will run into trouble.
If you want to tighten up your variable access (which I recommend, its a good exercise) you could use a publisher/subscriber pattern. In this, when a new clone is instantiated, it subscribes to the SetCloneLength method. When you want to change the length, the central class publishes that message and it is sent to all the subscribers.
The difference here is that in my example, the central class needs to keep track of all the clones, in publisher/subscriber, you don't.
Create script CopycatManager that will hold a leader and then use dedicated setters for copying the other object properties that have the same type. If a property is a default one may need to set up either a proxy of such property within' the script or play with triggers. I would recommend proxy. Like this:
class CopycatManager {
public GameObject leader;
SomeAttributeType attributeToCopyFromLeader {get; private set}
void Start () {
// The first CopycatManager to start is the leader
List<CopycatManager> allCMs = parent.GetComponentsInChildren();
CopycatManager foundLeader = allCMs.Find(o => o.leader == o);
if (foundLeader == null) {
// There's no leader yet, set yourself a leader
leader = this;
} else {
// Found a leader, accept
leader = foundLeader;
}
}
public void SetAttribute (SomeAttributeType newVal) {
// If we're setting the attribute of the leader - we should set this attribute for all children
if (leader == gameObject) {
// Find all copycat manager scripts attached to children of current parent
// Meaning siblings
// WARNING: It will include children of siblings and the leader itself
// WARNING: It will not include parents of the Copycat Manager type, add if required
List<CopycatManager> allCMs = parent.GetComponentsInChildren();
foreach (CopycatManager manager in allCMs) {
SetAttributeFromLeader (newVal);
}
} else {
// Non-leader is attempting to change attribute - call leader
leader.SetAttribute(newVal);
}
}
// Called by leader to each child
public void SetAttributeFromLeader (SomeAttributeType newVal) {
attributeToCopyFromLeader = newVal;
}
}
Make sure to assign a new leader if the old one destroyed. Only destroy objects with CopycatManager through dedicated function.
make all items that need scaling children of an empty called WorldObjects then scale the world object, it will scale all its children accordingly. you can then either manually or through script remove the parent to make objects independent. best way without prefabs...
use a singleton class. add that script to all the objects, then you can make a call to one and it will adjust all of them.
you can also do this with a static class, but the singleton approach is cleaner, and gives you more options.
public class MySingleton
{
private static MySingleton fetch; // keep the static reference private
public bool myBool = false;
// and expose static members through properties
// this way, you have a lot more control over what is actually being sent out.
public static bool MyBool { get { return fetch ? fetch.myBool : false; } }
void Awake()
{
fetch = this;
}
}
read here for some great information on both options!

Use the collider of multiple objects as one collider

In my fps level (Unity), targets spawn at a random position. I want to make sure targets can't spawn behind objects or inside objects.
To make sure they don't spawn behind an object, I've made a raycast going from the player to the target. If it's obstructed I recalculate the spawn point. This works fine, but, since the targets are spheres the raycast won't be obstructed when a target is 50% inside an object, for example the floor. I don't want that, obviously.
To determine whether or not the target is in the bounds of another object, I tried using OnCollisionEnter and OnCollisionExit. While this works when simply moving a target inside another object, it seems to be unreliable when one script's Update cycle is recalculating the spawn position while the target's Update cycle is keeping track of the Collision.
So I looked for a different approach. Here's what I came up with (from the Unity docs):
m_Collider2 = spawnpoints[i].GetComponent<Collider>();
m_Collider = world.GetComponentInChildren<Collider>();
if (m_Collider.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
}
The Game Object world is the parent in which I put all the objects of my gaming world.
The problem is that he only takes into account the collider of the first object. I basically want to use one big collider, which is composed by all the level objects.
Is this possible? Or does anyone know a different approach on how I can achieve this?
You should use the GetComponentsInChildren method instead of GetComponentInChildren, so that you can get from it an array of colliders on which you can execute a foreach to check if the bounds are intersecting.
I.E.:
m_Collider2 = spawnpoints [i].GetComponent<Collider>();
m_Collider = world.GetComponentsInChildren<Collider>();
foreach(Collider objCollider in m_Collider) {
if (objCollider.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
break;
}
}
But, this way of doing things is very heavy for the CPU, since GetComponent methods are really slow, so their use should be limited inside Awake and Start methods if possible.
Another approach to the problem would be to create a List<Collider> at the start, and add to it the starting children of your World game object. If another one is instantiated, just Add it to your list, if it's destroyed, just Remove it.
Then, just before instantiation, you can check the bounds by looping inside the List with a foreach, the check will be a lot more faster.
==================================
EDIT:
Ok, here's the deal. First of all, add these lines to your World game object script (I guess you called the class World):
using UnityEngine;
using System.Collections.Generic; //Namespace needed to use the List type
public class World : MonoBehaviour {
//The list which will hold references to the children game objects colliders
public List<Collider> childrenColliders;
private void Start() {
//Code used to populate the list at start
childrenColliders = new List<Collider>(GetComponentsInChildren<Collider>());
}
Now, since in the script which spawns a new object has already a world variable which holds a reference to the World class:
foreach(Collider coll in world.childrenColliders) {
if (coll.bounds.Intersects(m_Collider2.bounds))
{
Debug.Log("Bounds intersecting");
break;
}
}
And, of course, as I said before remember to add a newly spawned game object's collider to the list with:
void AddNewGameObject() {
// spawnPoint is the transform.position Vector3 you'll use for the new game object
var newGameObject = Instantiate(yourObjectPrefab, spawnPoint, Quaternion.identity, world.transform);
world.childrenColliders.Add(newGameObject.GetComponent<Collider>());
}
That's pretty much it. ;)