-
Notifications
You must be signed in to change notification settings - Fork 1
/
GameStateBehaviour.cs
109 lines (97 loc) · 4.22 KB
/
GameStateBehaviour.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using UnityEngine;
using VContainer.Unity;
namespace Unity.BossRoom.Gameplay.GameState
{
public enum GameState
{
MainMenu,
CharSelect,
BossRoom,
PostGame
}
/// <summary>
/// A special component that represents a discrete game state and its dependencies. The special feature it offers is
/// that it provides some guarantees that only one such GameState will be running at a time.
/// </summary>
/// <remarks>
/// Q: what is the relationship between a GameState and a Scene?
/// A: There is a 1-to-many relationship between states and scenes. That is, every scene corresponds to exactly one state,
/// but a single state can exist in multiple scenes.
/// Q: How do state transitions happen?
/// A: They are driven implicitly by calling NetworkManager.SceneManager.LoadScene in server code. This is
/// important, because if state transitions were driven separately from scene transitions, then states that cared what
/// scene they ran in would need to carefully synchronize their logic to scene loads.
/// Q: How many GameStateBehaviours are there?
/// A: Exactly one on the server and one on the client (on the host a server and client GameStateBehaviour will run concurrently, as
/// with other networked prefabs).
/// Q: If these are MonoBehaviours, how do you have a single state that persists across multiple scenes?
/// A: Set your Persists property to true. If you transition to another scene that has the same gamestate, the
/// current GameState object will live on, and the version in the new scene will auto-destruct to make room for it.
///
/// Important Note: We assume that every Scene has a GameState object. If not, then it's possible that a Persisting game state
/// will outlast its lifetime (as there is no successor state to clean it up).
/// </remarks>
public abstract class GameStateBehaviour : LifetimeScope
{
/// <summary>
/// Does this GameState persist across multiple scenes?
/// </summary>
public virtual bool Persists
{
get { return false; }
}
/// <summary>
/// What GameState this represents. Server and client specializations of a state should always return the same enum.
/// </summary>
public abstract GameState ActiveState { get; }
/// <summary>
/// This is the single active GameState object. There can be only one.
/// </summary>
private static GameObject s_ActiveStateGO;
protected override void Awake()
{
base.Awake();
if (Parent != null)
{
Parent.Container.Inject(this);
}
}
// Start is called before the first frame update
protected virtual void Start()
{
if (s_ActiveStateGO != null)
{
if (s_ActiveStateGO == gameObject)
{
//nothing to do here, if we're already the active state object.
return;
}
//on the host, this might return either the client or server version, but it doesn't matter which;
//we are only curious about its type, and its persist state.
var previousState = s_ActiveStateGO.GetComponent<GameStateBehaviour>();
if (previousState.Persists && previousState.ActiveState == ActiveState)
{
//we need to make way for the DontDestroyOnLoad state that already exists.
Destroy(gameObject);
return;
}
//otherwise, the old state is going away. Either it wasn't a Persisting state, or it was,
//but we're a different kind of state. In either case, we're going to be replacing it.
Destroy(s_ActiveStateGO);
}
s_ActiveStateGO = gameObject;
if (Persists)
{
DontDestroyOnLoad(gameObject);
}
}
protected override void OnDestroy()
{
if (!Persists)
{
s_ActiveStateGO = null;
}
}
}
}