Skip to content

Commit 2476854

Browse files
Merge pull request #1 from sator-imaging/active/v0.9
Active/v0.9
2 parents f7f0efa + ab5873c commit 2476854

10 files changed

+1586
-0
lines changed

LICENSE LICENSE.md

File renamed without changes.

LICENSE.md.meta

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LifecycleBehaviour.cs

+1,313
Large diffs are not rendered by default.

LifecycleBehaviour.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
This library helps managing MonoBehaviour lifetime by CancellationToken introduced in Unity 2022.
2+
Not only that but also has an ability to manage "Update" actions ordered and efficiently.
3+
4+
And also! library provides missing `destroyCancellationToken` feature for Unity 2021 LTS!!
5+
6+
**Feature Highlights**
7+
- [Class Instance Lifetime Management](#object-lifetime-management)
8+
- [Update Function Manager](#update-function-manager)
9+
- Designed to address consideration written in the following article
10+
- https://blog.unity.com/engine-platform/10000-update-calls
11+
- [API Reference](https://sator-imaging.github.io/Unity-LifecycleManager)
12+
13+
**Installation**
14+
- In Unity 2021 or later, Enter the following URL in Unity Package Manager (UPM)
15+
- https://github.com/sator-imaging/Unity-LifecycleManager.git
16+
17+
18+
Object Lifetime Management
19+
==========================
20+
Here is example to bind instance lifetime to cancellation token or MonoBehaviour.
21+
22+
```csharp
23+
using SatorImaging.LifecycleManager;
24+
25+
// works on Unity 2021 or later
26+
disposable.DestroyWith(monoBehaviourOrCancellationToken);
27+
gameObject.DestroyWith(cancellationToken);
28+
unityObj.DestroyUnityObjectWith(tokenOrBehaviour);
29+
30+
// bind to unity scene lifetime
31+
var sceneLifetime = SceneLifetime.Get(gameObject.scene);
32+
disposable.DestroyWith(sceneLifetime);
33+
sceneLifetime.Token.Register(() => DoSomethingOnSceneUnloading());
34+
35+
// lifecycle and its GameObject which will be destroyed on scene unloading
36+
var sceneLC = SceneLifecycle.Get();
37+
38+
// nesting lifecycles
39+
var root = LifecycleBehaviour.Create("Root Lifecycle");
40+
var child = LifecycleBehaviour.Create("Child Lifecycle");
41+
var grand = LifecycleBehaviour.Create("Grandchild Lifecycle");
42+
child.DestroyWith(root);
43+
grand.DestroyWith(child);
44+
// --> child and grand will be marked as DontDestroyOnLoad automatically
45+
46+
// action for debugging purpose which will be invoked before binding (when not null)
47+
LifetimeExtensions.DebuggerAction = (obj, token, ticket, ownerOrNull) =>
48+
{
49+
Debug.Log($"Target Object: {obj}");
50+
if (ownerOrNull != null)
51+
Debug.Log($"Lifetime Owner: {ownerOrNull}");
52+
Debug.Log($"CancellationToken: {token}");
53+
Debug.Log($"CancellationTokenRegistration: {ticket}");
54+
};
55+
```
56+
57+
58+
Technical Note
59+
--------------
60+
61+
### Unity Object/Component Binding Notice
62+
63+
You can bind `UnityEngine.Object` or component lifetime to cancellation token or MonoBehaviour by using
64+
`DestroyUnityObjectWith` extension method instead of `DestroyWith`.
65+
66+
Note that when binding unity object lifetime to other, need to consider both situation that component is
67+
destroyed by scene unloading OR by lifetime owner. As a result, it makes thing really complex and scene
68+
will be spaghetti-ed.
69+
70+
Strongly recommended that binding GameObject lifetime instead of component, or implement `IDisposable`
71+
on your unity engine object explicitly to preciously control behaviour.
72+
73+
74+
### Inter-Scene Binding Notice
75+
76+
Lifetime binding across scenes is restricted. Nevertheless you want to bind lifetime to another
77+
scene object, use `DestroyWith(CancellationToken)` method with `monoBehaviour.destroyCancellationToken`.
78+
79+
> [!WARNING]
80+
> When Unity object bound to another scene object, it will be destroyed by both when lifetime owner is
81+
> destroyed and scene which containing bound object is unloaded.
82+
83+
84+
### Quick Tests
85+
86+
Select menu commands in `Unity Editor > LifecycleManager > ...` to test the features.
87+
88+
89+
90+
Order of Destruction
91+
--------------------
92+
Destroy actions will happen in LIFO order, that is, last lifetime-bound object is destroyed first.
93+
(of course lifetime owner is destroyed before)
94+
95+
Note that C# class instances and GameObjects destruction order is stable whereas MonoBehaviours
96+
destruction order is NOT stable. For reference, MonoBehaviours (components) will be destroyed earlier
97+
when scene is unloaded otherwise destroyed based on binding order.
98+
99+
> [!NOTE]
100+
> To make destruction order stable, extension method automatically mark lifetime bound GameObjects as
101+
> `DontDestroyOnLoad`.
102+
103+
104+
105+
Update Function Manager
106+
=======================
107+
In this feature, each "update" function has 5 stages, Initialize, Early, Normal, Late and Finalize.
108+
Initialize and Finalize is designed for system usage, other 3 stages are for casual usage.
109+
110+
```csharp
111+
// create lifecycle behaviour
112+
var lifecycle = LifecycleBehaviour.Create("My Lifecycle!!");
113+
114+
// register action to lifecycle stages
115+
lifecycle.RegisterUpdateEarly(...);
116+
lifecycle.RegisterUpdate(...);
117+
lifecycle.RegisterUpdateLate(...);
118+
119+
lifecycle.RegisterLateUpdateEarly(...);
120+
lifecycle.RegisterLateUpdate(...);
121+
lifecycle.RegisterLateUpdateLate(...);
122+
123+
lifecycle.RegisterFixedUpdateEarly(...);
124+
lifecycle.RegisterFixedUpdate(...);
125+
lifecycle.RegisterFixedUpdateLate(...);
126+
127+
// to remove action manually, store and use instance which returned from register method
128+
var entry = lifecycle.RegisterUpdateLate(...);
129+
lifecycle.RemoveUpdateLate(entry);
130+
```
131+
132+
> [!NOTE]
133+
> For performance optimization, removing registered action will swap items in list instead of reordering
134+
> whole items in list.
135+
> ie. Order of update stages (early, late, etc) are promised but registered action order is NOT promised.
136+
> (like Unity)
137+
138+
139+
Automatic Unregistration
140+
------------------------
141+
If action is depending on instance that will be destroyed with cancellation token, You have to specify
142+
same token to unregister action together when token is canceled.
143+
144+
```csharp
145+
instance.DestroyWith(token);
146+
lifecycle.RegisterUpdate(() => instance.NoErrorUntilDisposed(), token); // <-- same token
147+
148+
// if don't, action will raise error after depending instance is destroyed
149+
lifecycle.RegisterUpdate(() => instance.NoErrorUntilDisposed()); // <-- error!!
150+
```
151+
152+
153+
Controlling Order of Multiple Update Managers
154+
---------------------------------------------
155+
To meet your app requirement, it allows to change lifecycle execution order while keeping
156+
registered actions order.
157+
158+
```csharp
159+
// simple but effective! don't try to dive into UnityEngine.LowLevel.PlayerLoop system!!
160+
class UpdateManagerOrganizer : MonoBehaviour
161+
{
162+
public LifecycleBehaviour lifecycle1;
163+
public LifecycleBehaviour lifecycle2;
164+
public LifecycleBehaviour lifecycle3;
165+
166+
void OnEnable()
167+
{
168+
lifecycle1.enabled = false;
169+
lifecycle2.enabled = false;
170+
lifecycle3.enabled = false;
171+
}
172+
void Update()
173+
{
174+
lifecycle3.Update();
175+
lifecycle1.Update();
176+
lifecycle2.Update();
177+
}
178+
void LateUpdate()
179+
{
180+
lifecycle1.LateUpdate();
181+
lifecycle3.LateUpdate();
182+
lifecycle2.LateUpdate();
183+
}
184+
void FixedUpdate()
185+
{
186+
lifecycle2.FixedUpdate();
187+
lifecycle3.FixedUpdate();
188+
lifecycle1.FixedUpdate();
189+
}
190+
}
191+
```

README.md.meta

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SatorImaging.LifecycleManager.asmdef

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "SatorImaging.LifecycleManager",
3+
"rootNamespace": "SatorImaging.LifecycleManager",
4+
"references": [],
5+
"includePlatforms": [],
6+
"excludePlatforms": [],
7+
"allowUnsafeCode": false,
8+
"overrideReferences": false,
9+
"precompiledReferences": [],
10+
"autoReferenced": true,
11+
"defineConstraints": [],
12+
"versionDefines": [],
13+
"noEngineReferences": false
14+
}

SatorImaging.LifecycleManager.asmdef.meta

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "com.sator-imaging.unity-lifecycle-manager",
3+
"displayName": "MonoBehaviour Lifecycle Manager",
4+
"version": "0.9.0",
5+
"unity": "2021.3",
6+
"description": "MonoBehaviour lifecycle and update function manager.",
7+
"author": {
8+
"name": "Sator Imaging",
9+
"url": "https://twitter.com/sator_imaging"
10+
},
11+
"category": "Unity",
12+
"keywords": [
13+
"unity",
14+
"monoBehaviour",
15+
"lifecycle",
16+
"updateManager",
17+
"cancellationToken"
18+
],
19+
"dependencies": {
20+
},
21+
"repository": {
22+
"type": "git",
23+
"url": "https://github.com/sator-imaging/Unity-LifecycleManager.git"
24+
},
25+
"url": "https://github.com/sator-imaging/Unity-LifecycleManager.git",
26+
"documentationUrl": "https://sator-imaging.github.io/Unity-LifecycleManager",
27+
"changelogUrl": "https://github.com/sator-imaging/Unity-LifecycleManager.git",
28+
"licensesUrl": "https://www.sator-imaging.com/"
29+
}

package.json.meta

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)