├── .gitattributes
├── .gitignore
├── Archon.SwissArmyLib.Editor
├── Archon.SwissArmyLib.Editor.csproj
├── Properties
│ └── AssemblyInfo.cs
├── ResourceSystem
│ └── ResourcePoolEditor.cs
└── Utils
│ ├── ExecutionOrderManager.cs
│ └── ReadOnlyDrawer.cs
├── Archon.SwissArmyLib.sln
├── Archon.SwissArmyLib
├── Archon.SwissArmyLib.csproj
├── Archon.SwissArmyLib.csproj.DotSettings
├── Automata
│ ├── BaseState.cs
│ ├── FiniteStateMachine.cs
│ ├── IPdaState.cs
│ ├── IState.cs
│ ├── PdaState.cs
│ └── PushdownAutomaton.cs
├── Collections
│ ├── DelayedList.cs
│ ├── DictionaryWithDefault.cs
│ ├── Grid2D.cs
│ ├── Grid3D.cs
│ ├── PooledLinkedList.cs
│ └── PrioritizedList.cs
├── Coroutines
│ ├── BetterCoroutine.cs
│ ├── BetterCoroutines.cs
│ ├── BetterCoroutinesExtensions.cs
│ ├── IPoolableYieldInstruction.cs
│ ├── WaitForAsyncOperation.cs
│ ├── WaitForSecondsLite.cs
│ ├── WaitForSecondsRealtimeLite.cs
│ ├── WaitForWWW.cs
│ ├── WaitUntilLite.cs
│ └── WaitWhileLite.cs
├── Events
│ ├── BuiltinEventIds.cs
│ ├── Event.cs
│ ├── GlobalEvents.cs
│ ├── IEventListener.cs
│ ├── Loops
│ │ ├── CustomUpdateLoopBase.cs
│ │ ├── FrameIntervalUpdateLoop.cs
│ │ ├── ICustomUpdateLoop.cs
│ │ ├── ManagedUpdate.cs
│ │ ├── ManagedUpdateBehaviour.cs
│ │ └── TimeIntervalUpdateLoop.cs
│ └── TellMeWhen.cs
├── Gravity
│ ├── GravitationalEntity.cs
│ ├── GravitationalEntity2D.cs
│ ├── GravitationalSystem.cs
│ ├── IGravitationalAffecter.cs
│ └── SphericalGravitationalPoint.cs
├── Partitioning
│ ├── Bin2D.cs
│ ├── Bin3D.cs
│ ├── Octree.cs
│ └── Quadtree.cs
├── Pooling
│ ├── GameObjectPool.cs
│ ├── IPool.cs
│ ├── IPoolable.cs
│ ├── Pool.cs
│ ├── PoolHelper.cs
│ └── PoolableGroup.cs
├── Properties
│ └── AssemblyInfo.cs
├── ResourceSystem
│ ├── ResourceEvent.cs
│ ├── ResourcePool.cs
│ ├── ResourcePoolBase.cs
│ ├── ResourceRegen.cs
│ └── Shield.cs
└── Utils
│ ├── BetterTime.cs
│ ├── ColorUtils.cs
│ ├── Extensions
│ └── ListExtensions.cs
│ ├── Lazy.cs
│ ├── MainThreadDispatcher.cs
│ ├── ServiceLocator.cs
│ ├── Shake
│ ├── BaseShake.cs
│ ├── Shake.cs
│ └── Shake2D.cs
│ ├── UpdateLoop.cs
│ └── _Editor
│ ├── ExecutionOrderAttribute.cs
│ └── ReadOnlyAttribute.cs
├── Archon.SwissArmyLibTests
├── Archon.SwissArmyLibTests.csproj
├── Collections
│ ├── DelayedListTests.cs
│ ├── Grid2DTests.cs
│ ├── Grid3DTests.cs
│ └── PrioritizedListTests.cs
├── Events
│ └── EventTests.cs
├── Partitioning
│ ├── Bin2DTests.cs
│ ├── Bin3DTests.cs
│ └── QuadtreeTests.cs
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── Documentation
├── .gitignore
├── Content
│ └── Welcome.aml
├── ContentLayout.content
└── Documentation.shfbproj
├── LICENSE
├── README.rst
├── UnityLibraries
├── README.txt
├── UnityEditor.dll
├── UnityEditor.dll.mdb
├── UnityEditor.xml
├── UnityEngine.dll
├── UnityEngine.dll.mdb
└── UnityEngine.xml
├── appveyor.yml
└── logo.png
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.Editor/Archon.SwissArmyLib.Editor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {86576CEB-98C2-4234-9577-1335777CFB7D}
8 | Library
9 | Properties
10 | Archon.SwissArmyLib.Editor
11 | Archon.SwissArmyLib.Editor
12 | v3.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | ..\bin\Debug\Editor\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 | ..\bin\Debug\Editor\Archon.SwissArmyLib.Editor.xml
24 |
25 |
26 | pdbonly
27 | true
28 | ..\bin\Release\Editor\
29 | TRACE
30 | prompt
31 | 4
32 | ..\bin\Release\Editor\Archon.SwissArmyLib.Editor.xml
33 |
34 |
35 | ..\bin\Test\
36 | TRACE
37 | ..\bin\Test\Editor\Archon.SwissArmyLib.Editor.xml
38 | true
39 | pdbonly
40 | AnyCPU
41 | prompt
42 | MinimumRecommendedRules.ruleset
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | ..\UnityLibraries\UnityEditor.dll
53 | False
54 |
55 |
56 | ..\UnityLibraries\UnityEngine.dll
57 | False
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {4e6e4ed1-096a-4dd2-b88f-0704bc769f22}
69 | Archon.SwissArmyLib
70 | False
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.Editor/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Archon.SwissArmyLib.Editor")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Archon Interactive")]
12 | [assembly: AssemblyProduct("Archon.SwissArmyLib.Editor")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("86576ceb-98c2-4234-9577-1335777cfb7d")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.Editor/ResourceSystem/ResourcePoolEditor.cs:
--------------------------------------------------------------------------------
1 | using Archon.SwissArmyLib.ResourceSystem;
2 | using UnityEditor;
3 | using UnityEngine;
4 |
5 | namespace Archon.SwissArmyLib.Editor.ResourceSystem
6 | {
7 | ///
8 | /// Custom editor for components.
9 | ///
10 | /// Shows a health bar and debugging buttons.
11 | ///
12 | [CanEditMultipleObjects]
13 | [CustomEditor(typeof(ResourcePoolBase), true)]
14 | public class ResourcePoolEditor : UnityEditor.Editor
15 | {
16 | private float _addVal = 50;
17 | private float _removeVal = 50;
18 |
19 | ///
20 | public override void OnInspectorGUI()
21 | {
22 | if (targets.Length == 1)
23 | {
24 | var resourcePool = (ResourcePoolBase)target;
25 | EditorGUILayout.Separator();
26 | var containerRect = EditorGUILayout.BeginHorizontal();
27 | var barRect = GUILayoutUtility.GetRect(containerRect.width, 20);
28 | EditorGUI.ProgressBar(barRect, resourcePool.Percentage, string.Format("{0:F1} / {1:F1}", resourcePool.Current, resourcePool.Max));
29 | EditorGUILayout.EndHorizontal();
30 | EditorGUILayout.Separator();
31 | }
32 |
33 | DrawDefaultInspector();
34 |
35 | if (Application.isPlaying)
36 | {
37 | EditorGUILayout.Separator();
38 |
39 | EditorGUILayout.BeginHorizontal();
40 | _addVal = EditorGUILayout.FloatField(_addVal);
41 | if (GUILayout.Button("Add")) Add(_addVal);
42 | EditorGUILayout.EndHorizontal();
43 |
44 | EditorGUILayout.BeginHorizontal();
45 | _removeVal = EditorGUILayout.FloatField(_removeVal);
46 | if (GUILayout.Button("Remove")) Remove(_removeVal);
47 | EditorGUILayout.EndHorizontal();
48 |
49 | EditorGUILayout.BeginHorizontal();
50 | if (GUILayout.Button("Empty")) Empty();
51 | if (GUILayout.Button("Fill")) Fill();
52 | if (GUILayout.Button("Renew")) Renew();
53 | EditorGUILayout.EndHorizontal();
54 | }
55 | }
56 |
57 | ///
58 | /// Adds a resource amount to all targeted components.
59 | ///
60 | /// Amount to add
61 | protected void Add(float amount)
62 | {
63 | for (var i = 0; i < targets.Length; i++)
64 | ((ResourcePoolBase)targets[i]).Add(amount);
65 | }
66 |
67 | ///
68 | /// Removes a resource amount from all targeted components.
69 | ///
70 | /// Amount to remove
71 | protected void Remove(float amount)
72 | {
73 | for (var i = 0; i < targets.Length; i++)
74 | ((ResourcePoolBase)targets[i]).Remove(amount);
75 | }
76 |
77 | ///
78 | /// Empties all targeted components.
79 | ///
80 | protected void Empty()
81 | {
82 | for (var i = 0; i < targets.Length; i++)
83 | ((ResourcePoolBase)targets[i]).Empty();
84 | }
85 |
86 | ///
87 | /// Fills all targeted components.
88 | ///
89 | protected void Fill()
90 | {
91 | for (var i = 0; i < targets.Length; i++)
92 | ((ResourcePoolBase) targets[i]).Fill();
93 | }
94 |
95 | ///
96 | /// Renews all targeted components.
97 | ///
98 | protected void Renew()
99 | {
100 | for (var i = 0; i < targets.Length; i++)
101 | ((ResourcePoolBase)targets[i]).Renew();
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.Editor/Utils/ExecutionOrderManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Archon.SwissArmyLib.Utils.Editor;
3 | using UnityEditor;
4 |
5 | namespace Archon.SwissArmyLib.Editor.Utils
6 | {
7 | ///
8 | /// Looks for classes using the and sets their execution order.
9 | ///
10 | [InitializeOnLoad]
11 | public class ExecutionOrderManager : UnityEditor.Editor
12 | {
13 | static ExecutionOrderManager()
14 | {
15 | foreach (var script in MonoImporter.GetAllRuntimeMonoScripts())
16 | {
17 | if (script.GetClass() == null) continue;
18 |
19 | var attributes = Attribute.GetCustomAttributes(script.GetClass(), typeof(ExecutionOrderAttribute));
20 |
21 | for (var i = 0; i < attributes.Length; i++)
22 | {
23 | var attribute = (ExecutionOrderAttribute) attributes[i];
24 |
25 | var currentOrder = MonoImporter.GetExecutionOrder(script);
26 |
27 | if (currentOrder == attribute.Order)
28 | continue;
29 |
30 | if (currentOrder == 0 || attribute.Forced)
31 | MonoImporter.SetExecutionOrder(script, attribute.Order);
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.Editor/Utils/ReadOnlyDrawer.cs:
--------------------------------------------------------------------------------
1 | using Archon.SwissArmyLib.Utils.Editor;
2 | using UnityEditor;
3 | using UnityEngine;
4 |
5 | namespace Archon.SwissArmyLib.Editor.Utils
6 | {
7 | ///
8 | /// Makes fields marked with uninteractable via the inspector.
9 | ///
10 | [CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
11 | public class ReadOnlyDrawer : PropertyDrawer
12 | {
13 | ///
14 | public override void OnGUI(Rect position,
15 | SerializedProperty property,
16 | GUIContent label)
17 | {
18 | var readOnly = (ReadOnlyAttribute) attribute;
19 |
20 | if (readOnly.OnlyWhilePlaying && !Application.isPlaying)
21 | EditorGUI.PropertyField(position, property, label, true);
22 | else
23 | {
24 | GUI.enabled = false;
25 | EditorGUI.PropertyField(position, property, label, true);
26 | GUI.enabled = true;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.8
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Archon.SwissArmyLib", "Archon.SwissArmyLib\Archon.SwissArmyLib.csproj", "{4E6E4ED1-096A-4DD2-B88F-0704BC769F22}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Archon.SwissArmyLib.Editor", "Archon.SwissArmyLib.Editor\Archon.SwissArmyLib.Editor.csproj", "{86576CEB-98C2-4234-9577-1335777CFB7D}"
9 | EndProject
10 | Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Documentation", "Documentation\Documentation.shfbproj", "{0C59110B-107A-4B22-9783-8CC0737A033F}"
11 | ProjectSection(ProjectDependencies) = postProject
12 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22} = {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}
13 | {86576CEB-98C2-4234-9577-1335777CFB7D} = {86576CEB-98C2-4234-9577-1335777CFB7D}
14 | EndProjectSection
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Archon.SwissArmyLibTests", "Archon.SwissArmyLibTests\Archon.SwissArmyLibTests.csproj", "{38149324-335B-4316-8016-21AB9F9B3074}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | Test|Any CPU = Test|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Test|Any CPU.ActiveCfg = Test|Any CPU
30 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}.Test|Any CPU.Build.0 = Test|Any CPU
31 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Test|Any CPU.ActiveCfg = Test|Any CPU
36 | {86576CEB-98C2-4234-9577-1335777CFB7D}.Test|Any CPU.Build.0 = Test|Any CPU
37 | {0C59110B-107A-4B22-9783-8CC0737A033F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {0C59110B-107A-4B22-9783-8CC0737A033F}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {0C59110B-107A-4B22-9783-8CC0737A033F}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {0C59110B-107A-4B22-9783-8CC0737A033F}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {0C59110B-107A-4B22-9783-8CC0737A033F}.Test|Any CPU.ActiveCfg = Release|Any CPU
42 | {38149324-335B-4316-8016-21AB9F9B3074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {38149324-335B-4316-8016-21AB9F9B3074}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {38149324-335B-4316-8016-21AB9F9B3074}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {38149324-335B-4316-8016-21AB9F9B3074}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {38149324-335B-4316-8016-21AB9F9B3074}.Test|Any CPU.ActiveCfg = Test|Any CPU
47 | {38149324-335B-4316-8016-21AB9F9B3074}.Test|Any CPU.Build.0 = Test|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {9CE78E2F-6B87-474C-AC71-7FAE13DA0BB7}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Archon.SwissArmyLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {4E6E4ED1-096A-4DD2-B88F-0704BC769F22}
8 | Library
9 | Properties
10 | Archon.SwissArmyLib
11 | Archon.SwissArmyLib
12 | v3.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | ..\bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 | ..\bin\Debug\Archon.SwissArmyLib.xml
24 |
25 |
26 | pdbonly
27 | true
28 | ..\bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 | ..\bin\Release\Archon.SwissArmyLib.xml
33 |
34 |
35 | ..\bin\Test\
36 | TRACE;TEST
37 | ..\bin\Test\Archon.SwissArmyLib.xml
38 | true
39 | pdbonly
40 | AnyCPU
41 | prompt
42 | MinimumRecommendedRules.ruleset
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | ..\UnityLibraries\UnityEngine.dll
53 | False
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 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Archon.SwissArmyLib.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp40
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Automata/BaseState.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Automata
2 | {
3 | ///
4 | /// A simple abstract class that implements .
5 | ///
6 | /// You might be looking for or .
7 | ///
8 | /// The type of the machine.
9 | /// The type of the context.
10 | public abstract class BaseState : IState
11 | {
12 | ///
13 | public TMachine Machine { get; set; }
14 |
15 | ///
16 | public TContext Context { get; set; }
17 |
18 | ///
19 | /// Amount of (active) time spent in this state since it was entered.
20 | ///
21 | public float TimeInState { get; private set; }
22 |
23 | ///
24 | /// Called when the state is entered.
25 | ///
26 | public virtual void Begin()
27 | {
28 | TimeInState = 0;
29 | }
30 |
31 | ///
32 | /// Called every frame just before .
33 | /// Use this to check whether you should change state.
34 | ///
35 | public virtual void Reason() { }
36 |
37 | ///
38 | /// Called every frame after , if the state hasn't been changed.
39 | ///
40 | public virtual void Act(float deltaTime)
41 | {
42 | TimeInState += deltaTime;
43 | }
44 |
45 | ///
46 | /// Called when the state is exited.
47 | ///
48 | public virtual void End() { }
49 | }
50 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Automata/FiniteStateMachine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Archon.SwissArmyLib.Automata
5 | {
6 | ///
7 | /// Represents a state to be used in a .
8 | ///
9 | /// The type of the context.
10 | public interface IFsmState : IState, T> { }
11 |
12 | ///
13 | /// A simple abstract class that implements and can be used in a
14 | ///
15 | /// You're not required to use this, but it's easier.
16 | ///
17 | /// The type of the context.
18 | public abstract class FsmState : BaseState, T>, IFsmState { }
19 |
20 | ///
21 | /// A simple Finite State Machine with states as objects inspired by Prime31's excellent StateKit.
22 | ///
23 | /// If your state classes have an empty constructor, the state machine can create the states automatically when needed (using ).
24 | /// If not you should create the state instance yourself and register the state in the machine.
25 | ///
26 | /// Whether or not a null state is valid is up to your design.
27 | ///
28 | ///
29 | ///
30 | ///
31 | /// The type of the context.
32 | public class FiniteStateMachine
33 | {
34 | ///
35 | /// A shared context which all states have access to.
36 | ///
37 | public T Context { get; private set; }
38 |
39 | ///
40 | /// The active state.
41 | ///
42 | public IFsmState CurrentState { get; private set; }
43 |
44 | ///
45 | /// The previously active state.
46 | ///
47 | public IFsmState PreviousState { get; private set; }
48 |
49 | private readonly Dictionary> _states = new Dictionary>();
50 |
51 | ///
52 | /// Creates a new Finite State Machine.
53 | ///
54 | /// If you need control over how the states are created, you can register them manually using .
55 | /// If not, then you can freely use which will create the states using their default constructor.
56 | ///
57 | /// A shared context for the states.
58 | public FiniteStateMachine(T context)
59 | {
60 | Context = context;
61 | }
62 |
63 | ///
64 | /// Creates a new Finite State Machine and changes the state to .
65 | ///
66 | /// If you need control over how the states are created, you can register them manually using .
67 | /// If not, then you can freely use which will create the states using their default constructor.
68 | ///
69 | ///
70 | ///
71 | public FiniteStateMachine(T context, IFsmState startState) : this(context)
72 | {
73 | RegisterState(startState);
74 | ChangeState(startState);
75 | }
76 |
77 | ///
78 | /// Call this every time the machine should update. Eg. every frame.
79 | ///
80 | public void Update(float deltaTime)
81 | {
82 | var currentState = CurrentState;
83 |
84 | if (currentState != null)
85 | {
86 | currentState.Reason();
87 |
88 | // we only want to update the state if it's still the current one
89 | if (currentState == CurrentState)
90 | CurrentState.Act(deltaTime);
91 | }
92 | }
93 |
94 | ///
95 | /// Preemptively add a state instance.
96 | /// Useful if the state doesn't have an empty constructor and therefore cannot be used with ChangeStateAuto.
97 | ///
98 | /// The state to register.
99 | public void RegisterState(IFsmState state)
100 | {
101 | _states[state.GetType()] = state;
102 | }
103 |
104 | ///
105 | /// Checks whether a state type is registered.
106 | ///
107 | /// The state type to check.
108 | /// True if registered, false otherwise.
109 | public bool IsStateRegistered(Type stateType)
110 | {
111 | return _states.ContainsKey(stateType);
112 | ; }
113 |
114 | ///
115 | /// Generic version of .
116 | /// Checks whether a state type is registered.
117 | ///
118 | /// The state type to check.
119 | /// Tru if registered, false otherwise.
120 | public bool IsStateRegistered() where TState : IFsmState
121 | {
122 | return _states.ContainsKey(typeof(TState));
123 | }
124 |
125 | ///
126 | /// Changes the active state to the given state type.
127 | /// If a state of that type isn't already registered, it will automatically create a new instance using the empty constructor.
128 | ///
129 | ///
130 | ///
131 | public TState ChangeStateAuto() where TState : IFsmState, new()
132 | {
133 | var type = typeof(TState);
134 | IFsmState state;
135 |
136 | if (!_states.TryGetValue(type, out state))
137 | _states[type] = state = new TState();
138 |
139 | return ChangeState((TState) state);
140 | }
141 |
142 | ///
143 | /// Changes the active state to the given state type.
144 | /// An instance of that type should already had been registered to use this method.
145 | ///
146 | ///
147 | ///
148 | public TState ChangeState() where TState : IFsmState
149 | {
150 | var type = typeof(TState);
151 | IFsmState state;
152 |
153 | if (!_states.TryGetValue(type, out state))
154 | throw new InvalidOperationException(string.Format("A state of type '{0}' is not registered, did you mean to use ChangeStateAuto?", type));
155 |
156 | return ChangeState((TState)state);
157 | }
158 |
159 | ///
160 | /// Changes the active state to a specific state instance.
161 | /// This will (if not null) also register the state.
162 | ///
163 | ///
164 | ///
165 | ///
166 | public TState ChangeState(TState state) where TState : IFsmState
167 | {
168 | if (CurrentState != null)
169 | CurrentState.End();
170 |
171 | PreviousState = CurrentState;
172 | CurrentState = state;
173 |
174 | if (CurrentState != null)
175 | {
176 | RegisterState(state);
177 | CurrentState.Machine = this;
178 | CurrentState.Context = Context;
179 | CurrentState.Begin();
180 | }
181 |
182 | return state;
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Automata/IPdaState.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Automata
2 | {
3 | ///
4 | /// Represents a state to be used in a .
5 | ///
6 | /// The type of the context.
7 | public interface IPdaState : IState, T>
8 | {
9 | ///
10 | /// Called when a state is pushed ontop of this state.
11 | ///
12 | void Pause();
13 |
14 | ///
15 | /// Called when the state above us is popped.
16 | ///
17 | void Resume();
18 | }
19 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Automata/IState.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Automata
2 | {
3 | ///
4 | /// Represents a state to be used in a state machine.
5 | ///
6 | /// You might be looking for or .
7 | ///
8 | /// The type of the machine.
9 | /// The type of the context.
10 | public interface IState
11 | {
12 | ///
13 | /// The state machine this state belongs to.
14 | ///
15 | TMachine Machine { get; set; }
16 |
17 | ///
18 | /// The context for this state.
19 | ///
20 | TContext Context { get; set; }
21 |
22 | ///
23 | /// Called when the state is entered.
24 | ///
25 | void Begin();
26 |
27 | ///
28 | /// Called every frame just before .
29 | /// Use this to check whether you should change state.
30 | ///
31 | void Reason();
32 |
33 | ///
34 | /// Called every frame after , if the state hasn't been changed.
35 | ///
36 | void Act(float deltaTime);
37 |
38 | ///
39 | /// Called when the state is exited.
40 | ///
41 | void End();
42 | }
43 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Automata/PdaState.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Automata
2 | {
3 | ///
4 | /// A simple abstract class that implements and can be used in a
5 | ///
6 | /// You're not required to use this, but it's easier.
7 | ///
8 | /// The type of the context.
9 | public class PdaState : BaseState, T>, IPdaState
10 | {
11 | ///
12 | /// Called when a state is pushed ontop of this state.
13 | ///
14 | public virtual void Pause() { }
15 |
16 | ///
17 | /// Called when the state above us is popped.
18 | ///
19 | public virtual void Resume() { }
20 | }
21 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Collections/DictionaryWithDefault.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Archon.SwissArmyLib.Collections
4 | {
5 | ///
6 | /// A but with a default value for missing entries.
7 | ///
8 | ///
9 | ///
10 | public class DictionaryWithDefault : Dictionary
11 | {
12 | ///
13 | /// Default value for missing entries.
14 | ///
15 | public TValue DefaultValue { get; set; }
16 |
17 | ///
18 | /// Gets or sets the value associated with the given key.
19 | ///
20 | /// If the key isn't in the dictionary, will be returned.
21 | ///
22 | ///
23 | public new TValue this[TKey key]
24 | {
25 | get
26 | {
27 | TValue t;
28 | return TryGetValue(key, out t) ? t : DefaultValue;
29 | }
30 | set
31 | {
32 | base[key] = value;
33 | }
34 | }
35 |
36 | ///
37 | /// Creates a new Dictionary with DefaultValue set to TValue's default value.
38 | ///
39 | public DictionaryWithDefault()
40 | {
41 | }
42 |
43 | ///
44 | /// Creates a new Dictionary using the supplied value as the default for missing entries.
45 | ///
46 | ///
47 | public DictionaryWithDefault(TValue defaultValue)
48 | {
49 | DefaultValue = defaultValue;
50 | }
51 |
52 | ///
53 | /// Creates a new Dictionary using the supplied value as the default for missing entries and a specific comparer.
54 | ///
55 | ///
56 | ///
57 | public DictionaryWithDefault(TValue defaultValue, IEqualityComparer comparer) : base(comparer)
58 | {
59 | DefaultValue = defaultValue;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Collections/Grid2D.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Archon.SwissArmyLib.Collections
4 | {
5 | ///
6 | /// A generic two-dimensional grid.
7 | ///
8 | ///
9 | ///
10 | /// The type of content cells can contain.
11 | public class Grid2D
12 | {
13 | private T[][] _data;
14 |
15 | ///
16 | /// Gets the width (number of columns) of the grid.
17 | ///
18 | public int Width { get; private set; }
19 |
20 | ///
21 | /// Gets the height (number of rows) of the grid.
22 | ///
23 | public int Height { get; private set; }
24 |
25 | ///
26 | /// Gets or sets the default values used for clearing or initializing new cells.
27 | ///
28 | public T DefaultValue { get; set; }
29 |
30 | ///
31 | /// Gets or sets the value of the cell located at the specified coordinate.
32 | ///
33 | ///
34 | ///
35 | /// The contents of the cell.
36 | public T this[int x, int y]
37 | {
38 | get
39 | {
40 | if (x < 0 || y < 0 || x >= Width || y >= Height)
41 | throw new IndexOutOfRangeException();
42 |
43 | return _data[x][y];
44 | }
45 | set
46 | {
47 | if (x < 0 || y < 0 || x >= Width || y >= Height)
48 | throw new IndexOutOfRangeException();
49 |
50 | _data[x][y] = value;
51 | }
52 | }
53 |
54 | private int InternalWidth { get { return _data.Length; } }
55 | private int InternalHeight { get { return _data[0].Length; } }
56 |
57 | ///
58 | /// Creates a new 2D Grid with the specified width and height.
59 | /// Cells will be initialized with their type's default value.
60 | ///
61 | /// Number of columns
62 | /// Number of rows
63 | public Grid2D(int width, int height)
64 | {
65 | Width = width;
66 | Height = height;
67 |
68 | _data = CreateArrays(width, height);
69 | }
70 |
71 | ///
72 | /// Creates a new 2D Grid with the specified width and height.
73 | /// Cells will be initialized with the value of .
74 | ///
75 | /// Number of columns
76 | /// Number of rows
77 | /// The value used for initializing new cells.
78 | public Grid2D(int width, int height, T defaultValue) : this(width, height)
79 | {
80 | DefaultValue = defaultValue;
81 | Clear();
82 | }
83 |
84 | ///
85 | /// Gets the value of the cell located at the specified coordinate.
86 | ///
87 | ///
88 | ///
89 | ///
90 | ///
91 | /// The cell contents.
92 | public T Get(int x, int y)
93 | {
94 | return this[x, y];
95 | }
96 |
97 | ///
98 | /// Sets the value of the cell located at the specified coordinate.
99 | ///
100 | ///
101 | ///
102 | ///
103 | ///
104 | /// The value to set the cell to.
105 | public void Set(int x, int y, T value)
106 | {
107 | this[x, y] = value;
108 | }
109 |
110 | ///
111 | /// Clears the grid, setting every cell to .
112 | ///
113 | public void Clear()
114 | {
115 | Clear(DefaultValue);
116 | }
117 |
118 | ///
119 | /// Clears the grid, setting every cell to the given value.
120 | ///
121 | public void Clear(T clearValue)
122 | {
123 | Fill(clearValue, 0, 0, Width - 1, Height - 1);
124 | }
125 |
126 | ///
127 | /// Fills everything in the specified rectangle to the given value.
128 | ///
129 | /// The value to fill the cells with.
130 | /// Bottom left corner's x value.
131 | /// Bottom left corner's y value.
132 | /// Upper right corner's x value.
133 | /// Upper right corner's y value.
134 | public void Fill(T value, int minX, int minY, int maxX, int maxY)
135 | {
136 | for (var x = minX; x <= maxX; x++)
137 | {
138 | var heightArray = _data[x];
139 |
140 | for (var y = minY; y <= maxY; y++)
141 | heightArray[y] = value;
142 | }
143 | }
144 |
145 | ///
146 | /// Resizes the Grid to the given size, keeping data the same but any new cells will be set to .
147 | ///
148 | /// Growing the grid will allocate new arrays, shrinking will not.
149 | ///
150 | /// The new width.
151 | /// The new height.
152 | public void Resize(int width, int height)
153 | {
154 | var oldWidth = Width;
155 | var oldHeight = Height;
156 |
157 | if (width > InternalWidth || height > InternalHeight)
158 | {
159 | var oldData = _data;
160 | _data = CreateArrays(width, height);
161 | CopyArraysContents(oldData, _data);
162 | }
163 |
164 | Width = width;
165 | Height = height;
166 |
167 | if (width > oldWidth || height > oldHeight)
168 | {
169 | Fill(DefaultValue, 0, oldHeight, width - 1, height - 1);
170 | Fill(DefaultValue, oldWidth, 0, width - 1, oldHeight - 1);
171 | }
172 | }
173 |
174 | private static T[][] CreateArrays(int width, int height)
175 | {
176 | var arrays = new T[width][];
177 |
178 | for (var x = 0; x < width; x++)
179 | arrays[x] = new T[height];
180 |
181 | return arrays;
182 | }
183 |
184 | private static void CopyArraysContents(T[][] src, T[][] dst)
185 | {
186 | var srcWidth = src.Length;
187 | var srcHeight = src[0].Length;
188 |
189 | var dstWidth = dst.Length;
190 | var dstHeight = dst[0].Length;
191 |
192 | for (var x = 0; x < srcWidth && x < dstWidth; x++)
193 | {
194 | var srcHeightArray = src[x];
195 | var dstHeightArray = dst[x];
196 |
197 | for (var y = 0; y < srcHeight && y < dstHeight; y++)
198 | dstHeightArray[y] = srcHeightArray[y];
199 | }
200 | }
201 | }
202 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/BetterCoroutine.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using Archon.SwissArmyLib.Events.Loops;
3 | using Archon.SwissArmyLib.Pooling;
4 | using UnityEngine;
5 |
6 | namespace Archon.SwissArmyLib.Coroutines
7 | {
8 | internal sealed class BetterCoroutine : IPoolable
9 | {
10 | internal int Id;
11 | internal bool IsDone;
12 | internal bool IsPaused;
13 | internal bool IsParentPaused;
14 | internal int UpdateLoopId;
15 | internal IEnumerator Enumerator;
16 | internal BetterCoroutine Parent;
17 | internal BetterCoroutine Child;
18 |
19 | internal float WaitTillTime = float.MinValue;
20 | internal bool WaitTimeIsUnscaled;
21 | internal bool WaitingForEndOfFrame;
22 |
23 | internal bool IsLinkedToObject;
24 | internal GameObject LinkedObject;
25 | internal bool IsLinkedToComponent;
26 | internal MonoBehaviour LinkedComponent;
27 |
28 | void IPoolable.OnSpawned()
29 | {
30 |
31 | }
32 |
33 | void IPoolable.OnDespawned()
34 | {
35 | var poolableYieldInstruction = Enumerator as IPoolableYieldInstruction;
36 | if (poolableYieldInstruction != null)
37 | poolableYieldInstruction.Despawn();
38 |
39 | Id = -1;
40 | IsDone = false;
41 | IsPaused = false;
42 | IsParentPaused = false;
43 | UpdateLoopId = ManagedUpdate.EventIds.Update;
44 | Enumerator = null;
45 | Parent = null;
46 | Child = null;
47 |
48 | WaitTillTime = float.MinValue;
49 | WaitTimeIsUnscaled = false;
50 | WaitingForEndOfFrame = false;
51 |
52 | LinkedObject = null;
53 | LinkedComponent = null;
54 | IsLinkedToObject = false;
55 | IsLinkedToComponent = false;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/BetterCoroutinesExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using Archon.SwissArmyLib.Events.Loops;
3 | using Archon.SwissArmyLib.Utils;
4 | using UnityEngine;
5 |
6 | namespace Archon.SwissArmyLib.Coroutines
7 | {
8 | ///
9 | /// A bunch of helpful extensions for starting and stopping coroutines.
10 | ///
11 | public static class BetterCoroutinesExtensions
12 | {
13 | ///
14 | /// Starts a new coroutine.
15 | ///
16 | ///
17 | ///
18 | /// Which update loop should the coroutine be part of?
19 | /// The id of the coroutine.
20 | public static int StartBetterCoroutine(this Object unityObject, IEnumerator enumerator,
21 | UpdateLoop updateLoop = UpdateLoop.Update)
22 | {
23 | return BetterCoroutines.Start(enumerator, updateLoop);
24 | }
25 |
26 | ///
27 | /// Starts a new coroutine.
28 | ///
29 | ///
30 | ///
31 | /// Which update loop should the coroutine be part of?
32 | /// The id of the coroutine.
33 | public static int StartBetterCoroutine(this Object unityObject, IEnumerator enumerator,
34 | int updateLoopId)
35 | {
36 | return BetterCoroutines.Start(enumerator, updateLoopId);
37 | }
38 |
39 | ///
40 | /// Starts a new coroutine with its lifetime linked to this component.
41 | /// The coroutine will be stopped when the linked component is disabled or destroyed.
42 | ///
43 | /// The component to link the coroutine to.
44 | ///
45 | /// Which update loop should the coroutine be part of?
46 | /// The id of the coroutine.
47 | public static int StartBetterCoroutineLinked(this MonoBehaviour monoBehaviour, IEnumerator enumerator,
48 | UpdateLoop updateLoop = UpdateLoop.Update)
49 | {
50 | return BetterCoroutines.Start(enumerator, monoBehaviour, updateLoop);
51 | }
52 |
53 | ///
54 | /// Starts a new coroutine with its lifetime linked to this component.
55 | /// The coroutine will be stopped when the linked component is disabled or destroyed.
56 | ///
57 | /// The component to link the coroutine to.
58 | ///
59 | /// Which update loop should the coroutine be part of?
60 | /// The id of the coroutine.
61 | public static int StartBetterCoroutineLinked(this MonoBehaviour monoBehaviour, IEnumerator enumerator,
62 | int updateLoopId)
63 | {
64 | return BetterCoroutines.Start(enumerator, monoBehaviour, updateLoopId);
65 | }
66 |
67 | ///
68 | /// Starts a new coroutine with its lifetime linked to this gameobject.
69 | /// The coroutine will be stopped when the linked gameobject is disabled or destroyed.
70 | ///
71 | /// The gameobject to link the coroutine to.
72 | ///
73 | /// Which update loop should the coroutine be part of?
74 | /// The id of the coroutine.
75 | public static int StartBetterCoroutineLinked(this GameObject gameObject, IEnumerator enumerator,
76 | UpdateLoop updateLoop = UpdateLoop.Update)
77 | {
78 | return BetterCoroutines.Start(enumerator, gameObject, updateLoop);
79 | }
80 |
81 | ///
82 | /// Starts a new coroutine with its lifetime linked to this gameobject.
83 | /// The coroutine will be stopped when the linked gameobject is disabled or destroyed.
84 | ///
85 | /// The gameobject to link the coroutine to.
86 | ///
87 | /// Which update loop should the coroutine be part of?
88 | /// The id of the coroutine.
89 | public static int StartBetterCoroutineLinked(this GameObject gameObject, IEnumerator enumerator,
90 | int updateLoopId)
91 | {
92 | return BetterCoroutines.Start(enumerator, gameObject, updateLoopId);
93 | }
94 |
95 | ///
96 | /// Stops a running coroutine prematurely.
97 | ///
98 | /// This will stop any child coroutines as well.
99 | ///
100 | ///
101 | /// The id of the coroutine to stop.
102 | /// True if the coroutine was found and stopped, otherwise false.
103 | public static bool StopBetterCoroutine(this Object unityObject, int coroutineId)
104 | {
105 | return BetterCoroutines.Stop(coroutineId);
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/IPoolableYieldInstruction.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Coroutines
2 | {
3 | ///
4 | /// Represents a yield instruction that can be freed when the coroutine they're running in is despawned.
5 | ///
6 | public interface IPoolableYieldInstruction
7 | {
8 | ///
9 | /// Frees the yield instruction placing them back in its pool.
10 | ///
11 | void Despawn();
12 | }
13 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitForAsyncOperation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using Archon.SwissArmyLib.Pooling;
3 | using UnityEngine;
4 |
5 | namespace Archon.SwissArmyLib.Coroutines
6 | {
7 | internal sealed class WaitForAsyncOperation : CustomYieldInstruction, IPoolableYieldInstruction
8 | {
9 | private static readonly Pool Pool = new Pool(() => new WaitForAsyncOperation());
10 |
11 | public static IEnumerator Create(AsyncOperation operation)
12 | {
13 | var waiter = Pool.Spawn();
14 | waiter._operation = operation;
15 | return waiter;
16 | }
17 |
18 | private AsyncOperation _operation;
19 |
20 | private WaitForAsyncOperation()
21 | {
22 |
23 | }
24 |
25 | ///
26 | public override bool keepWaiting
27 | {
28 | get { return !_operation.isDone; }
29 | }
30 |
31 | public void Despawn()
32 | {
33 | _operation = null;
34 | Pool.Despawn(this);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitForSecondsLite.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Coroutines
2 | {
3 | internal sealed class WaitForSecondsLite
4 | {
5 | internal static readonly WaitForSecondsLite Instance = new WaitForSecondsLite();
6 |
7 | public float Duration;
8 | public bool Unscaled;
9 |
10 | private WaitForSecondsLite() { }
11 | }
12 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitForSecondsRealtimeLite.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using Archon.SwissArmyLib.Pooling;
3 | using UnityEngine;
4 |
5 | namespace Archon.SwissArmyLib.Coroutines
6 | {
7 | internal sealed class WaitForSecondsRealtimeLite : CustomYieldInstruction, IPoolableYieldInstruction
8 | {
9 | private static readonly Pool Pool = new Pool(() => new WaitForSecondsRealtimeLite());
10 |
11 | public static IEnumerator Create(float seconds)
12 | {
13 | var waiter = Pool.Spawn();
14 | waiter._expirationTime = Time.realtimeSinceStartup + seconds;
15 | return waiter;
16 | }
17 |
18 | private float _expirationTime;
19 |
20 | private WaitForSecondsRealtimeLite()
21 | {
22 | }
23 |
24 | ///
25 | public override bool keepWaiting
26 | {
27 | get { return Time.realtimeSinceStartup < _expirationTime; }
28 | }
29 |
30 | public void Despawn()
31 | {
32 | _expirationTime = 0;
33 | Pool.Despawn(this);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitForWWW.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using Archon.SwissArmyLib.Pooling;
3 | using UnityEngine;
4 |
5 | namespace Archon.SwissArmyLib.Coroutines
6 | {
7 | internal sealed class WaitForWWW : CustomYieldInstruction, IPoolableYieldInstruction
8 | {
9 | private static readonly Pool Pool = new Pool(() => new WaitForWWW());
10 |
11 | public static IEnumerator Create(WWW wwwObject)
12 | {
13 | var waiter = Pool.Spawn();
14 | waiter._wwwObject = wwwObject;
15 | return waiter;
16 | }
17 |
18 | private WWW _wwwObject;
19 |
20 | private WaitForWWW()
21 | {
22 |
23 | }
24 |
25 | ///
26 | public override bool keepWaiting
27 | {
28 | get { return !_wwwObject.isDone; }
29 | }
30 |
31 | public void Despawn()
32 | {
33 | _wwwObject = null;
34 | Pool.Despawn(this);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitUntilLite.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using Archon.SwissArmyLib.Pooling;
4 | using UnityEngine;
5 |
6 | namespace Archon.SwissArmyLib.Coroutines
7 | {
8 | internal sealed class WaitUntilLite : CustomYieldInstruction, IPoolableYieldInstruction
9 | {
10 | private static readonly Pool Pool = new Pool(() => new WaitUntilLite());
11 |
12 | public static IEnumerator Create(Func predicate)
13 | {
14 | var waiter = Pool.Spawn();
15 | waiter._predicate = predicate;
16 | return waiter;
17 | }
18 |
19 | private Func _predicate;
20 |
21 | private WaitUntilLite()
22 | {
23 |
24 | }
25 |
26 | ///
27 | public override bool keepWaiting
28 | {
29 | get { return !_predicate(); }
30 | }
31 |
32 | public void Despawn()
33 | {
34 | _predicate = null;
35 | Pool.Despawn(this);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Coroutines/WaitWhileLite.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using Archon.SwissArmyLib.Pooling;
4 | using UnityEngine;
5 |
6 | namespace Archon.SwissArmyLib.Coroutines
7 | {
8 | internal sealed class WaitWhileLite : CustomYieldInstruction, IPoolableYieldInstruction
9 | {
10 | private static readonly Pool Pool = new Pool(() => new WaitWhileLite());
11 |
12 | public static IEnumerator Create(Func predicate)
13 | {
14 | var waiter = Pool.Spawn();
15 | waiter._predicate = predicate;
16 | return waiter;
17 | }
18 |
19 | private Func _predicate;
20 |
21 | private WaitWhileLite()
22 | {
23 |
24 | }
25 |
26 | ///
27 | public override bool keepWaiting
28 | {
29 | get { return _predicate(); }
30 | }
31 |
32 | public void Despawn()
33 | {
34 | _predicate = null;
35 | Pool.Despawn(this);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/BuiltinEventIds.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Events
2 | {
3 | ///
4 | /// Contains the event ids used by SwissArmyLib.
5 | ///
6 | public static class BuiltinEventIds
7 | {
8 | ///
9 | /// ManagedUpdate
10 | ///
11 | public const int
12 | Update = -1000,
13 | LateUpdate = -1001,
14 | FixedUpdate = -1002;
15 |
16 | ///
17 | /// ResourcePool
18 | ///
19 | public const int
20 | PreChange = -8000,
21 | Change = -8001,
22 | Empty = -8002,
23 | Full = -8003,
24 | Renew = -8004;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/IEventListener.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Events
2 | {
3 | ///
4 | /// Defines a method to be used for event callbacks.
5 | ///
6 | public interface IEventListener
7 | {
8 | ///
9 | /// Called when an event is invoked.
10 | ///
11 | /// The id of the event.
12 | void OnEvent(int eventId);
13 | }
14 |
15 | ///
16 | /// Defines a method to be used for event callbacks with a parameter of type .
17 | ///
18 | ///
19 | public interface IEventListener
20 | {
21 | ///
22 | /// Called when an event is invoked.
23 | ///
24 | /// The id of the event.
25 | /// The args for the event.
26 | void OnEvent(int eventId, TArgs args);
27 | }
28 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/Loops/CustomUpdateLoopBase.cs:
--------------------------------------------------------------------------------
1 | using Archon.SwissArmyLib.Utils;
2 |
3 | namespace Archon.SwissArmyLib.Events.Loops
4 | {
5 | ///
6 | /// An abstract class for custom update loops that implement basic
7 | /// functionality to track invokation times and deltatimes.
8 | ///
9 | public abstract class CustomUpdateLoopBase : ICustomUpdateLoop
10 | {
11 | ///
12 | /// Gets or sets in scaled time when this update loop last ran.
13 | ///
14 | protected float PreviousRunTimeScaled;
15 |
16 | ///
17 | /// Gets or sets in unscaled time when this update loop last ran.
18 | ///
19 | protected float PreviousRunTimeUnscaled;
20 |
21 | ///
22 | public Event Event { get; protected set; }
23 |
24 | ///
25 | public abstract bool IsTimeToRun { get; }
26 |
27 | ///
28 | public float DeltaTime
29 | {
30 | get { return BetterTime.Time - PreviousRunTimeScaled; }
31 | }
32 |
33 | ///
34 | public float UnscaledDeltaTime
35 | {
36 | get { return BetterTime.UnscaledTime - PreviousRunTimeUnscaled; }
37 | }
38 |
39 | ///
40 | public virtual void Invoke()
41 | {
42 | Event.Invoke();
43 |
44 | PreviousRunTimeScaled = BetterTime.Time;
45 | PreviousRunTimeUnscaled = BetterTime.UnscaledTime;
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/Loops/FrameIntervalUpdateLoop.cs:
--------------------------------------------------------------------------------
1 | using Archon.SwissArmyLib.Utils;
2 |
3 | namespace Archon.SwissArmyLib.Events.Loops
4 | {
5 | ///
6 | /// A basic custom update loop that runs every nth frame.
7 | ///
8 | ///
9 | ///
10 | public class FrameIntervalUpdateLoop : CustomUpdateLoopBase
11 | {
12 | private int _previousUpdateFrame;
13 | private int _nextUpdateFrame;
14 | private int _interval;
15 |
16 | ///
17 | public override bool IsTimeToRun
18 | {
19 | get
20 | {
21 | return BetterTime.FrameCount >= _nextUpdateFrame;
22 | }
23 | }
24 |
25 | ///
26 | /// Gets or sets the frame interval that this update loop should run.
27 | ///
28 | public int Interval
29 | {
30 | get { return _interval; }
31 | set
32 | {
33 | _interval = value;
34 | _nextUpdateFrame = _previousUpdateFrame + value;
35 | }
36 | }
37 |
38 | ///
39 | /// Creates a new FrameIntervalUpdateLoop.
40 | ///
41 | /// The event id that this update loop should use.
42 | /// The amount of frames between each call of this update loop.
43 | public FrameIntervalUpdateLoop(int eventId, int interval)
44 | {
45 | Event = new Event(eventId);
46 | Interval = interval;
47 | }
48 |
49 | ///
50 | public override void Invoke()
51 | {
52 | base.Invoke();
53 |
54 | _previousUpdateFrame = BetterTime.FrameCount;
55 | _nextUpdateFrame = _previousUpdateFrame + Interval;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/Loops/ICustomUpdateLoop.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Events.Loops
2 | {
3 | ///
4 | /// Represents an implementation of a custom update loop.
5 | ///
6 | /// You will probably be better of subclassing for
7 | /// a simpler start, but it's here if you need it.
8 | ///
9 | public interface ICustomUpdateLoop
10 | {
11 | ///
12 | /// Gets the event associated with this update loop.
13 | ///
14 | Event Event { get; }
15 |
16 | ///
17 | /// Gets whether it's time for this update loop to run again.
18 | ///
19 | bool IsTimeToRun { get; }
20 |
21 | ///
22 | /// Gets the scaled time since this update loop last ran.
23 | ///
24 | float DeltaTime { get; }
25 |
26 | ///
27 | /// Gets the unscaled time since this update loop last ran.
28 | ///
29 | float UnscaledDeltaTime { get; }
30 |
31 | ///
32 | /// Runs this update loop's event.
33 | ///
34 | void Invoke();
35 | }
36 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/Loops/ManagedUpdateBehaviour.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Archon.SwissArmyLib.Events.Loops
4 | {
5 | ///
6 | /// Makes a subclass get notified on an update.
7 | ///
8 | public interface IUpdateable
9 | {
10 | ///
11 | /// Called every frame.
12 | ///
13 | void OnUpdate();
14 | }
15 |
16 | ///
17 | /// Makes a subclass get notified on a late update.
18 | ///
19 | public interface ILateUpdateable
20 | {
21 | ///
22 | /// Called every frame, after the regular update loop.
23 | ///
24 | void OnLateUpdate();
25 | }
26 |
27 | ///
28 | /// Makes a subclass get notified on a fixed update.
29 | ///
30 | public interface IFixedUpdateable
31 | {
32 | ///
33 | /// Called every fixed update.
34 | ///
35 | void OnFixedUpdate();
36 | }
37 |
38 | ///
39 | /// Makes a subclass get notified when a custom update loop ticks.
40 | ///
41 | public interface ICustomUpdateable
42 | {
43 | ///
44 | /// Gets the event ids for the custom update loops that should be listened to.
45 | ///
46 | /// The event ids to listen for.
47 | int[] GetCustomUpdateIds();
48 |
49 | ///
50 | /// Called whenever one of the custom update loops tick.
51 | ///
52 | ///
53 | void OnCustomUpdate(int eventId);
54 | }
55 |
56 | ///
57 | /// A subclass of MonoBehaviour that uses for update events.
58 | ///
59 | /// To receive updates implement one or more of the appropriate interfaces:
60 | /// , , and .
61 | ///
62 | public abstract class ManagedUpdateBehaviour : MonoBehaviour, IEventListener
63 | {
64 | ///
65 | /// Affects whether this components' events will be called before or after others'.
66 | ///
67 | /// Basically a reimplementation of Unity's ScriptExecutionOrder.
68 | ///
69 | protected virtual int ExecutionOrder { get { return 0; } }
70 |
71 | private bool _startWasCalled;
72 | private bool _isListening;
73 | private IUpdateable _updateable;
74 | private ILateUpdateable _lateUpdateable;
75 | private IFixedUpdateable _fixedUpdateable;
76 | private ICustomUpdateable _customUpdateable;
77 |
78 | private int[] _customUpdateIds;
79 |
80 | ///
81 | /// Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.
82 | ///
83 | protected virtual void Start()
84 | {
85 | // ReSharper disable SuspiciousTypeConversion.Global
86 | _updateable = this as IUpdateable;
87 | _lateUpdateable = this as ILateUpdateable;
88 | _fixedUpdateable = this as IFixedUpdateable;
89 | _customUpdateable = this as ICustomUpdateable;
90 | // ReSharper restore SuspiciousTypeConversion.Global
91 |
92 | if (_updateable == null
93 | && _lateUpdateable == null
94 | && _fixedUpdateable == null
95 | && _customUpdateable == null)
96 | {
97 | Debug.LogWarning("This component doesn't implement any update interfaces.", this);
98 | }
99 |
100 | _startWasCalled = true;
101 | StartListening();
102 | }
103 |
104 | ///
105 | /// Called when the component is enabled.
106 | ///
107 | protected virtual void OnEnable()
108 | {
109 | // interface references aren't serialized when unity hot-reloads
110 | if (Application.isEditor)
111 | {
112 | // ReSharper disable SuspiciousTypeConversion.Global
113 | if (_updateable == null) _updateable = this as IUpdateable;
114 | if (_lateUpdateable == null) _lateUpdateable = this as ILateUpdateable;
115 | if (_fixedUpdateable == null) _fixedUpdateable = this as IFixedUpdateable;
116 | if (_customUpdateable == null) _customUpdateable = this as ICustomUpdateable;
117 | // ReSharper restore SuspiciousTypeConversion.Global
118 | }
119 |
120 | // We don't want Update calls before Start has been called.
121 | if (_startWasCalled)
122 | StartListening();
123 | }
124 |
125 | ///
126 | /// Called when the component is disabled.
127 | ///
128 | protected virtual void OnDisable()
129 | {
130 | if (_startWasCalled)
131 | StopListening();
132 | }
133 |
134 | private void StartListening()
135 | {
136 | if (_isListening)
137 | {
138 | Debug.LogError("Attempt at starting to listen for updates, while already listening. Did you forget to call base.OnDisable()?");
139 | return;
140 | }
141 |
142 | var executionOrder = ExecutionOrder;
143 |
144 | if (_updateable != null)
145 | ManagedUpdate.OnUpdate.AddListener(this, executionOrder);
146 | if (_lateUpdateable != null)
147 | ManagedUpdate.OnLateUpdate.AddListener(this, executionOrder);
148 | if (_fixedUpdateable != null)
149 | ManagedUpdate.OnFixedUpdate.AddListener(this, executionOrder);
150 |
151 | if (_customUpdateable != null)
152 | {
153 | if (_customUpdateIds == null)
154 | _customUpdateIds = _customUpdateable.GetCustomUpdateIds() ?? new int[0];
155 |
156 | for (var i = 0; i < _customUpdateIds.Length; i++)
157 | ManagedUpdate.AddListener(_customUpdateIds[i], this, executionOrder);
158 | }
159 |
160 | _isListening = true;
161 | }
162 |
163 | private void StopListening()
164 | {
165 | if (!_isListening)
166 | {
167 | Debug.LogError("Attempted to stop listening for updates while not listening. Did you forget to call base.Start() or base.OnEnable()?");
168 | return;
169 | }
170 |
171 | if (_updateable != null)
172 | ManagedUpdate.OnUpdate.RemoveListener(this);
173 | if (_lateUpdateable != null)
174 | ManagedUpdate.OnLateUpdate.RemoveListener(this);
175 | if (_fixedUpdateable != null)
176 | ManagedUpdate.OnFixedUpdate.RemoveListener(this);
177 |
178 | if (_customUpdateable != null && _customUpdateIds != null)
179 | {
180 | for (var i = 0; i < _customUpdateIds.Length; i++)
181 | ManagedUpdate.RemoveListener(_customUpdateIds[i], this);
182 | }
183 |
184 | _isListening = false;
185 | }
186 |
187 | ///
188 | public virtual void OnEvent(int eventId)
189 | {
190 | switch (eventId)
191 | {
192 | case ManagedUpdate.EventIds.Update:
193 | _updateable.OnUpdate();
194 | return;
195 | case ManagedUpdate.EventIds.LateUpdate:
196 | _lateUpdateable.OnLateUpdate();
197 | return;
198 | case ManagedUpdate.EventIds.FixedUpdate:
199 | _fixedUpdateable.OnFixedUpdate();
200 | return;
201 | }
202 |
203 | if (_customUpdateIds != null)
204 | {
205 | for (var i = 0; i < _customUpdateIds.Length; i++)
206 | {
207 | if (_customUpdateIds[i] != eventId) continue;
208 |
209 | _customUpdateable.OnCustomUpdate(eventId);
210 | return;
211 | }
212 | }
213 | }
214 | }
215 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Events/Loops/TimeIntervalUpdateLoop.cs:
--------------------------------------------------------------------------------
1 | using Archon.SwissArmyLib.Utils;
2 |
3 | namespace Archon.SwissArmyLib.Events.Loops
4 | {
5 | ///
6 | /// A basic custom update loop that runs every nth second either in scaled or unscaled time.
7 | ///
8 | ///
9 | ///
10 | public class TimeIntervalUpdateLoop : CustomUpdateLoopBase
11 | {
12 | private float _nextUpdateTime;
13 | private float _interval;
14 |
15 | ///
16 | public override bool IsTimeToRun
17 | {
18 | get
19 | {
20 | var time = UsingScaledTime ? BetterTime.Time : BetterTime.UnscaledTime;
21 | return time >= _nextUpdateTime;
22 | }
23 | }
24 |
25 | ///
26 | /// Gets or sets the amount of seconds between each time this update loop runs.
27 | ///
28 | public float Interval
29 | {
30 | get { return _interval; }
31 | set
32 | {
33 | _interval = value;
34 | var previousTime = UsingScaledTime ? PreviousRunTimeScaled : PreviousRunTimeUnscaled;
35 | _nextUpdateTime = previousTime + value;
36 | }
37 | }
38 |
39 | ///
40 | /// Gets whether this interval update loop uses scaled or unscaled time for its interval.
41 | ///
42 | public bool UsingScaledTime { get; private set; }
43 |
44 | ///
45 | /// Creates a new TimeIntervalUpdateLoop.
46 | ///
47 | /// The event id that the update loop should use.
48 | /// The amount of seconds between each time this update loop should run.
49 | /// Whether the interval should use scaled or unscaled time.
50 | public TimeIntervalUpdateLoop(int eventId, float interval, bool usingScaledTime = true)
51 | {
52 | Event = new Event(eventId);
53 | Interval = interval;
54 | UsingScaledTime = usingScaledTime;
55 | }
56 |
57 | ///
58 | public override void Invoke()
59 | {
60 | base.Invoke();
61 |
62 | var time = UsingScaledTime ? BetterTime.Time : BetterTime.UnscaledTime;
63 | _nextUpdateTime = time + Interval;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Gravity/GravitationalEntity.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using UnityEngine;
3 |
4 | namespace Archon.SwissArmyLib.Gravity
5 | {
6 | ///
7 | /// Makes this 's part of the gravitational system.
8 | ///
9 | /// For 2D physics see .
10 | ///
11 | [AddComponentMenu("Archon/Gravity/GravitationalEntity")]
12 | [RequireComponent(typeof(Rigidbody))]
13 | public class GravitationalEntity : MonoBehaviour
14 | {
15 | private Rigidbody _rigidbody;
16 |
17 | [UsedImplicitly]
18 | private void Awake()
19 | {
20 | _rigidbody = GetComponent();
21 | }
22 |
23 | [UsedImplicitly]
24 | private void OnEnable()
25 | {
26 | GravitationalSystem.Register(_rigidbody);
27 | }
28 |
29 | [UsedImplicitly]
30 | private void OnDisable()
31 | {
32 | GravitationalSystem.Unregister(_rigidbody);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Gravity/GravitationalEntity2D.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using UnityEngine;
3 |
4 | namespace Archon.SwissArmyLib.Gravity
5 | {
6 | ///
7 | /// Makes this 's part of the gravitational system.
8 | ///
9 | /// For 3D physics see .
10 | ///
11 | [AddComponentMenu("Archon/Gravity/GravitationalEntity2D")]
12 | [RequireComponent(typeof(Rigidbody2D))]
13 | public class GravitationalEntity2D : MonoBehaviour
14 | {
15 | private Rigidbody2D _rigidbody;
16 |
17 | [UsedImplicitly]
18 | private void Awake()
19 | {
20 | _rigidbody = GetComponent();
21 | }
22 |
23 | [UsedImplicitly]
24 | private void OnEnable()
25 | {
26 | GravitationalSystem.Register(_rigidbody);
27 | }
28 |
29 | [UsedImplicitly]
30 | private void OnDisable()
31 | {
32 | GravitationalSystem.Unregister(_rigidbody);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Gravity/GravitationalSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Archon.SwissArmyLib.Events;
4 | using Archon.SwissArmyLib.Events.Loops;
5 | using Archon.SwissArmyLib.Utils;
6 | using UnityEngine;
7 |
8 | namespace Archon.SwissArmyLib.Gravity
9 | {
10 | ///
11 | /// A gravitational system to allow for a more flexible gravity instead of just a constant directional gravity.
12 | ///
13 | /// Useful for planets, black holes, magnets etc.
14 | ///
15 | /// Rigidbodies that should be affected should have the component (or if using 2d physics).
16 | ///
17 | /// Add gravitational forces by implementing the interface and registering it in the system.
18 | /// See for a simple example implementation.
19 | ///
20 | /// You might want to set Unity's gravity to (0,0,0).
21 | ///
22 | public class GravitationalSystem : IEventListener {
23 |
24 | private static readonly List Affecters = new List();
25 | private static readonly List Rigidbodies = new List();
26 | private static readonly List Rigidbodies2D = new List();
27 |
28 | static GravitationalSystem()
29 | {
30 | var instance = new GravitationalSystem();
31 | ServiceLocator.RegisterSingleton(instance);
32 | ServiceLocator.GlobalReset += () => ServiceLocator.RegisterSingleton(instance);
33 | }
34 |
35 | private GravitationalSystem()
36 | {
37 | ManagedUpdate.OnFixedUpdate.AddListener(this);
38 | }
39 |
40 | ///
41 | /// Destructor
42 | ///
43 | ~GravitationalSystem()
44 | {
45 | ManagedUpdate.OnFixedUpdate.RemoveListener(this);
46 | }
47 |
48 | ///
49 | /// Registers a gravitational affecter to be part of the system.
50 | ///
51 | /// The affecter to register.
52 | public static void Register(IGravitationalAffecter affecter)
53 | {
54 | if (ReferenceEquals(affecter, null))
55 | throw new ArgumentNullException("affecter");
56 |
57 | Affecters.Add(affecter);
58 | }
59 |
60 | ///
61 | /// Registers a that should be affected by gravitational forces in this system.
62 | ///
63 | /// The rigidbody to register.
64 | public static void Register(Rigidbody rigidbody)
65 | {
66 | if (ReferenceEquals(rigidbody, null))
67 | throw new ArgumentNullException("rigidbody");
68 |
69 | Rigidbodies.Add(rigidbody);
70 | }
71 |
72 | ///
73 | /// Registers a that should be affected by gravitational forces in this system.
74 | ///
75 | /// The rigidbody to register.
76 | public static void Register(Rigidbody2D rigidbody)
77 | {
78 | if (ReferenceEquals(rigidbody, null))
79 | throw new ArgumentNullException("rigidbody");
80 |
81 | Rigidbodies2D.Add(rigidbody);
82 | }
83 |
84 | ///
85 | /// Unregisters a gravitational affecter from the system, so it no longer affects entities.
86 | ///
87 | /// The affecter to unregister.
88 | public static void Unregister(IGravitationalAffecter affecter)
89 | {
90 | if (ReferenceEquals(affecter, null))
91 | throw new ArgumentNullException("affecter");
92 |
93 | Affecters.Remove(affecter);
94 | }
95 |
96 | ///
97 | /// Unregisters a from the system, so it no longer is affected by gravitational forces in this system.
98 | ///
99 | /// The rigidbody to unregister.
100 | public static void Unregister(Rigidbody rigidbody)
101 | {
102 | if (ReferenceEquals(rigidbody, null))
103 | throw new ArgumentNullException("rigidbody");
104 |
105 | Rigidbodies.Remove(rigidbody);
106 | }
107 |
108 | ///
109 | /// Unregisters a from the system, so it no longer is affected by gravitational forces in this system.
110 | ///
111 | /// The rigidbody to unregister.
112 | public static void Unregister(Rigidbody2D rigidbody)
113 | {
114 | if (ReferenceEquals(rigidbody, null))
115 | throw new ArgumentNullException("rigidbody");
116 |
117 | Rigidbodies2D.Remove(rigidbody);
118 | }
119 |
120 | ///
121 | /// Gets the sum of all gravitational forces at a specific location.
122 | ///
123 | /// The location to test.
124 | /// A vector representing the sum of gravitational force at .
125 | public static Vector3 GetGravityAtPoint(Vector3 location)
126 | {
127 | var gravity = new Vector3();
128 |
129 | var affecterCount = Affecters.Count;
130 | for (var i = 0; i < affecterCount; i++)
131 | {
132 | var force = Affecters[i].GetForceAt(location);
133 | gravity.x += force.x;
134 | gravity.y += force.y;
135 | gravity.z += force.z;
136 | }
137 |
138 | return gravity;
139 | }
140 |
141 | void IEventListener.OnEvent(int eventId)
142 | {
143 | if (eventId != ManagedUpdate.EventIds.FixedUpdate)
144 | return;
145 |
146 | var rigidbodyCount = Rigidbodies.Count;
147 | for (var i = 0; i < rigidbodyCount; i++)
148 | {
149 | var body = Rigidbodies[i];
150 |
151 | if (body.useGravity && !body.IsSleeping())
152 | {
153 | var gravity = GetGravityAtPoint(body.position);
154 | if (gravity.sqrMagnitude > 0.0001f)
155 | body.AddForce(gravity);
156 | }
157 | }
158 |
159 | var rigidbody2DCount = Rigidbodies2D.Count;
160 | for (var i = 0; i < rigidbody2DCount; i++)
161 | {
162 | var body = Rigidbodies2D[i];
163 | var gravityScale = body.gravityScale;
164 |
165 | if (body.simulated && gravityScale > 0 && body.IsAwake())
166 | {
167 | Vector2 gravity = GetGravityAtPoint(body.position);
168 | if (gravity.sqrMagnitude < 0.0001f)
169 | continue;
170 |
171 | gravity.x *= gravityScale;
172 | gravity.y *= gravityScale;
173 | body.AddForce(gravity);
174 | }
175 | }
176 | }
177 | }
178 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Gravity/IGravitationalAffecter.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Archon.SwissArmyLib.Gravity
4 | {
5 | ///
6 | /// Represents a gravitational pull on entities.
7 | ///
8 | public interface IGravitationalAffecter
9 | {
10 | ///
11 | /// Calculates how much gravitational pull is at a specific location caused by this affecter.
12 | ///
13 | /// The location to test.
14 | /// A vector representing the force at .
15 | Vector3 GetForceAt(Vector3 location);
16 | }
17 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Gravity/SphericalGravitationalPoint.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using UnityEngine;
3 |
4 | namespace Archon.SwissArmyLib.Gravity
5 | {
6 | ///
7 | /// A sphere-shaped gravitational point.
8 | ///
9 | /// The force is currently constant and not dependent on how close the entities are.
10 | ///
11 | [AddComponentMenu("Archon/Gravity/SphericalGravitationalPoint")]
12 | public class SphericalGravitationalPoint : MonoBehaviour, IGravitationalAffecter
13 | {
14 | [SerializeField] private float _strength = 9.82f;
15 | [SerializeField] private float _radius = 1;
16 | [SerializeField] private AnimationCurve _dropoffCurve = AnimationCurve.Linear(0, 1, 1, 0);
17 | [SerializeField] private bool _isGlobal;
18 |
19 | private float _radiusSqr;
20 | private Transform _transform;
21 |
22 | ///
23 | /// The gravitational pull of this point.
24 | ///
25 | public float Strength
26 | {
27 | get { return _strength; }
28 | set { _strength = value; }
29 | }
30 |
31 | ///
32 | /// Gets or sets the radius of this gravitational point.
33 | ///
34 | /// If is true, then this property is ignored.
35 | ///
36 | public float Radius
37 | {
38 | get { return _radius; }
39 | set
40 | {
41 | _radius = value;
42 | _radiusSqr = value * value;
43 | }
44 | }
45 |
46 | ///
47 | /// Gets or sets the dropoff curve of the gravitational force.
48 | ///
49 | public AnimationCurve DropoffCurve
50 | {
51 | get { return _dropoffCurve; }
52 | set { _dropoffCurve = value; }
53 | }
54 |
55 | ///
56 | /// Gets or sets whether this point should affect all entities regardless of whether they're in range.
57 | ///
58 | public bool IsGlobal
59 | {
60 | get { return _isGlobal; }
61 | set { _isGlobal = value; }
62 | }
63 |
64 | [UsedImplicitly]
65 | private void Awake()
66 | {
67 | _transform = transform;
68 | _radiusSqr = _radius * _radius;
69 | }
70 |
71 | [UsedImplicitly]
72 | private void OnEnable()
73 | {
74 | GravitationalSystem.Register(this);
75 | }
76 |
77 | [UsedImplicitly]
78 | private void OnDisable()
79 | {
80 | GravitationalSystem.Unregister(this);
81 | }
82 |
83 | [UsedImplicitly]
84 | private void OnDrawGizmos()
85 | {
86 | Gizmos.color = Color.magenta;
87 | Gizmos.DrawWireSphere(_transform.position, _radius);
88 | }
89 |
90 | ///
91 | public Vector3 GetForceAt(Vector3 location)
92 | {
93 | var deltaPos = _transform.position - location;
94 |
95 | var sqrDist = deltaPos.sqrMagnitude;
96 |
97 | if (_isGlobal || sqrDist < _radiusSqr)
98 | {
99 | var strength = _dropoffCurve.Evaluate(sqrDist / _radiusSqr) * _strength;
100 | var force = deltaPos.normalized;
101 | force.x *= strength;
102 | force.y *= strength;
103 | force.z *= strength;
104 | return force;
105 | }
106 |
107 | return new Vector3();
108 | }
109 | }
110 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Pooling/GameObjectPool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using UnityEngine.SceneManagement;
4 | using Object = UnityEngine.Object;
5 |
6 | namespace Archon.SwissArmyLib.Pooling
7 | {
8 | ///
9 | /// An object pool that can recycle prefab instances.
10 | ///
11 | /// The type of the component on the prefab.
12 | public class GameObjectPool : Pool, IDisposable where T : Object
13 | {
14 | ///
15 | /// Gets the prefab used to instantiate GameObjects.
16 | ///
17 | public T Prefab { get; private set; }
18 |
19 | private readonly Transform _root;
20 | private readonly bool _multiScene;
21 |
22 | ///
23 | /// Creates a new GameObject pool for the specified prefab.
24 | ///
25 | /// The prefab used for instantiating instances.
26 | /// Should the pool and its contents survive a scene change?
27 | public GameObjectPool(T prefab, bool multiScene) : this(prefab.name, () => Object.Instantiate(prefab), multiScene)
28 | {
29 | if (ReferenceEquals(prefab, null))
30 | throw new ArgumentNullException("prefab");
31 |
32 | Prefab = prefab;
33 | }
34 |
35 | ///
36 | /// Creates a new GameObject pool with a custom name and a factory method used for instantiating instances.
37 | ///
38 | /// The name of the pool.
39 | /// The factory method used to instantiating instances.
40 | /// Should the pool and its contents survive a scene change?
41 | public GameObjectPool(string name, Func create, bool multiScene) : base(create)
42 | {
43 | var rootGO = new GameObject(string.Format("'{0}' Pool", name));
44 | _root = rootGO.transform;
45 |
46 | _multiScene = multiScene;
47 | if (multiScene)
48 | Object.DontDestroyOnLoad(rootGO);
49 |
50 | SceneManager.sceneUnloaded += OnSceneUnloaded;
51 | }
52 |
53 | ///
54 | /// Destructor.
55 | ///
56 | ~GameObjectPool()
57 | {
58 | if (_root)
59 | Object.Destroy(_root.gameObject);
60 | }
61 |
62 | ///
63 | /// Destroys the pool and any despawned objects in it.
64 | ///
65 | public void Dispose()
66 | {
67 | SceneManager.sceneUnloaded -= OnSceneUnloaded;
68 | if (_root)
69 | Object.Destroy(_root.gameObject);
70 | Free.Clear();
71 | }
72 |
73 | private void OnSceneUnloaded(Scene unloadedScene)
74 | {
75 | // clean up any instances that might've been destroyed
76 | for (var i = Free.Count - 1; i >= 0; i--)
77 | {
78 | if (!Free[i])
79 | Free.RemoveAt(i);
80 | }
81 | }
82 |
83 | ///
84 | protected override T SpawnInternal()
85 | {
86 | var obj = base.SpawnInternal();
87 |
88 | var gameObject = GetGameObject(obj);
89 | gameObject.transform.SetParent(null, false);
90 | if (_multiScene)
91 | SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene());
92 |
93 | return obj;
94 | }
95 |
96 | ///
97 | /// Spawns a recycled object if there's one available, otherwise creates a new instance.
98 | ///
99 | /// The spawned object.
100 | public T Spawn(Transform parent)
101 | {
102 | var obj = SpawnInternal();
103 |
104 | var gameObject = GetGameObject(obj);
105 |
106 | var transform = gameObject.transform;
107 | transform.SetParent(parent, false);
108 |
109 | OnSpawned(obj);
110 |
111 | return obj;
112 | }
113 |
114 | ///
115 | /// Spawns a recycled object if there's one available, otherwise creates a new instance.
116 | ///
117 | /// The spawned object.
118 | public T Spawn(Vector3 position)
119 | {
120 | var obj = SpawnInternal();
121 |
122 | var gameObject = GetGameObject(obj);
123 |
124 | var transform = gameObject.transform;
125 | transform.SetPositionAndRotation(position, Quaternion.identity);
126 |
127 | OnSpawned(obj);
128 |
129 | return obj;
130 | }
131 |
132 | ///
133 | /// Spawns a recycled object if there's one available, otherwise creates a new instance.
134 | ///
135 | /// The spawned object.
136 | public T Spawn(Vector3 position, Quaternion rotation)
137 | {
138 | var obj = SpawnInternal();
139 |
140 | var gameObject = GetGameObject(obj);
141 |
142 | var transform = gameObject.transform;
143 | transform.SetPositionAndRotation(position, rotation);
144 |
145 | OnSpawned(obj);
146 |
147 | return obj;
148 | }
149 |
150 | ///
151 | /// Spawns a recycled object if there's one available, otherwise creates a new instance.
152 | ///
153 | /// The spawned object.
154 | public T Spawn(Vector3 position, Quaternion rotation, Transform parent)
155 | {
156 | var obj = SpawnInternal();
157 |
158 | var gameObject = GetGameObject(obj);
159 |
160 | var transform = gameObject.transform;
161 | transform.SetParent(parent, false);
162 | transform.SetPositionAndRotation(position, rotation);
163 |
164 | OnSpawned(obj);
165 |
166 | return obj;
167 | }
168 |
169 | ///
170 | public override void Despawn(T target)
171 | {
172 | if (ReferenceEquals(target, null))
173 | throw new ArgumentNullException("target");
174 |
175 | try
176 | {
177 | base.Despawn(target);
178 | }
179 | catch (ArgumentException)
180 | {
181 | throw new ArgumentException(string.Format("Target '{0}' is already despawned!", target.name), "target");
182 | }
183 |
184 | var gameObject = GetGameObject(target);
185 | gameObject.SetActive(false);
186 |
187 | var transform = gameObject.transform;
188 | transform.SetParent(_root, false);
189 | }
190 |
191 | ///
192 | protected override void OnSpawned(T target)
193 | {
194 | var gameObject = GetGameObject(target);
195 | gameObject.SetActive(true);
196 |
197 | base.OnSpawned(target);
198 | }
199 |
200 | private static GameObject GetGameObject(T obj)
201 | {
202 | if (ReferenceEquals(obj, null))
203 | throw new ArgumentNullException("obj");
204 |
205 | var component = obj as Component;
206 | if (component != null)
207 | return component.gameObject;
208 |
209 | return obj as GameObject;
210 | }
211 | }
212 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Pooling/IPool.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Pooling
2 | {
3 | ///
4 | /// Represents an object pool that has methods for spawning and despawning objects.
5 | ///
6 | /// The type of objects that this pool can be used for.
7 | public interface IPool
8 | {
9 | ///
10 | /// Spawns a recycled or new instance of the type .
11 | ///
12 | /// The spawned instance.
13 | T Spawn();
14 |
15 | ///
16 | /// Despawns an instance of the type and marks it for reuse.
17 | ///
18 | /// The instance to despawn.
19 | void Despawn(T target);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Pooling/IPoolable.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.Pooling
2 | {
3 | ///
4 | /// Represents an object that can be recycled in an and should be notified when it's spawned and despawned.
5 | ///
6 | public interface IPoolable
7 | {
8 | ///
9 | /// Called when the object is spawned (either fresh or recycled).
10 | ///
11 | void OnSpawned();
12 |
13 | ///
14 | /// Called when the object is despawned and marked for recycling.
15 | ///
16 | void OnDespawned();
17 | }
18 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Pooling/Pool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Archon.SwissArmyLib.Events;
4 | using UnityEngine;
5 |
6 | namespace Archon.SwissArmyLib.Pooling
7 | {
8 | ///
9 | /// An object pool that can recycle objects of the type .
10 | ///
11 | /// If the type implements they will be notified when they're spawned and despawned.
12 | ///
13 | /// The type of objects this object pool should contain.
14 | public class Pool : IPool, TellMeWhen.ITimerCallback where T : class
15 | {
16 | ///
17 | /// Gets the current amount of free instances in the pool.
18 | ///
19 | public int FreeCount { get { return Free.Count; } }
20 |
21 | ///
22 | /// Contains the items ready to be reused.
23 | ///
24 | protected readonly List Free = new List();
25 |
26 | private readonly Func _factory;
27 |
28 | private int _nextTimerId;
29 | private readonly Dictionary _instanceToTimerId = new Dictionary();
30 |
31 | ///
32 | /// Creates a new object pool that uses the specified factory method to create object instances.
33 | ///
34 | /// Factory method to use for creating new instances.
35 | public Pool(Func create)
36 | {
37 | if (ReferenceEquals(create, null))
38 | throw new ArgumentNullException("create");
39 |
40 | _factory = create;
41 | }
42 |
43 | ///
44 | /// Fills the pool with objects so that it contains the specified amount of objects.
45 | ///
46 | /// If it already contains the specified amount or more, nothing will be done.
47 | ///
48 | ///
49 | public void Prewarm(int targetCount)
50 | {
51 | if (Free.Capacity < targetCount)
52 | Free.Capacity = targetCount;
53 |
54 | for (var i = 0; i < targetCount && Free.Count < targetCount; i++)
55 | Despawn(_factory());
56 | }
57 |
58 | ///
59 | /// Spawns a recycled object if there's one available, otherwise creates a new instance.
60 | ///
61 | /// The spawned object.
62 | public T Spawn()
63 | {
64 | var obj = SpawnInternal();
65 | OnSpawned(obj);
66 |
67 | return obj;
68 | }
69 |
70 | ///
71 | /// Recycles or creates a object if there's one available without calling .
72 | ///
73 | /// The spawned object.
74 | protected virtual T SpawnInternal()
75 | {
76 | T obj;
77 |
78 | if (Free.Count > 0)
79 | {
80 | obj = Free[Free.Count - 1];
81 | Free.RemoveAt(Free.Count - 1);
82 | }
83 | else
84 | obj = _factory();
85 |
86 | return obj;
87 | }
88 |
89 | ///
90 | /// Despawns an object, adding it back to the pool.
91 | ///
92 | /// The object to despawn.
93 | public virtual void Despawn(T target)
94 | {
95 | if (ReferenceEquals(target, null))
96 | throw new ArgumentNullException("target");
97 |
98 | #if !TEST
99 | // costly check, so we only run it in the editor
100 | if (Application.isEditor && Free.Contains(target))
101 | throw new ArgumentException("Target is already despawned!", "target");
102 | #endif
103 |
104 | _instanceToTimerId.Remove(target);
105 | OnDespawned(target);
106 | Free.Add(target);
107 | }
108 |
109 | ///
110 | /// Called when an object has been spawned and removed from the pool.
111 | ///
112 | /// The spawned object.
113 | protected virtual void OnSpawned(T target)
114 | {
115 | var poolable = target as IPoolable;
116 |
117 | if (poolable != null)
118 | poolable.OnSpawned();
119 | }
120 |
121 | ///
122 | /// Called when an object has been despawned and placed back in the pool.
123 | ///
124 | /// The despawned object.
125 | protected virtual void OnDespawned(T target)
126 | {
127 | var poolable = target as IPoolable;
128 |
129 | if (poolable != null)
130 | poolable.OnDespawned();
131 | }
132 |
133 | ///
134 | /// Despawns an object after a delay.
135 | ///
136 | /// The target to despawn.
137 | /// Time in seconds to wait before despawning the target.
138 | /// Should the delay be according to or ?
139 | public void Despawn(T target, float delay, bool unscaledTime = false)
140 | {
141 | if (ReferenceEquals(target, null))
142 | throw new ArgumentNullException("target");
143 |
144 | var id = _nextTimerId++;
145 | _instanceToTimerId[target] = id;
146 |
147 | if (unscaledTime)
148 | TellMeWhen.SecondsUnscaled(delay, this, id, target);
149 | else
150 | TellMeWhen.Seconds(delay, this, id, target);
151 | }
152 |
153 | ///
154 | /// Cancels a pending timed despawn.
155 | ///
156 | /// The target that shouldn't despawn after all.
157 | public void CancelDespawn(T target)
158 | {
159 | if (ReferenceEquals(target, null))
160 | throw new ArgumentNullException("target");
161 |
162 | _instanceToTimerId.Remove(target);
163 | }
164 |
165 | void TellMeWhen.ITimerCallback.OnTimesUp(int id, object args)
166 | {
167 | var target = args as T;
168 |
169 | int mappedId;
170 |
171 | if (target != null && _instanceToTimerId.TryGetValue(target, out mappedId) && mappedId == id)
172 | Despawn(target);
173 | }
174 | }
175 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Pooling/PoolableGroup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Archon.SwissArmyLib.Utils.Editor;
5 | using JetBrains.Annotations;
6 | using UnityEngine;
7 |
8 | namespace Archon.SwissArmyLib.Pooling
9 | {
10 | ///
11 | /// Manages a list of IPoolable components found in the hierarchy of this GameObject and notifies them when it is spawned and despawned.
12 | ///
13 | [AddComponentMenu("Archon/Poolable Group")]
14 | public sealed class PoolableGroup : MonoBehaviour, IPoolable, ISerializationCallbackReceiver
15 | {
16 | [SerializeField, ReadOnly, UsedImplicitly]
17 | private List _poolableComponents = new List();
18 |
19 | void IPoolable.OnSpawned()
20 | {
21 | for (var i = 0; i < _poolableComponents.Count; i++)
22 | ((IPoolable)_poolableComponents[i]).OnSpawned();
23 | }
24 |
25 | void IPoolable.OnDespawned()
26 | {
27 | for (var i = 0; i < _poolableComponents.Count; i++)
28 | ((IPoolable) _poolableComponents[i]).OnDespawned();
29 | }
30 |
31 | ///
32 | /// Manually add a poolable object to be notified when this component is spawned or despawned.
33 | ///
34 | /// Useful if you dynamically add IPoolable components at runtime.
35 | ///
36 | /// The poolable object that should be notified.
37 | public void AddManually(T poolable) where T : MonoBehaviour, IPoolable
38 | {
39 | if (ReferenceEquals(poolable, null))
40 | throw new ArgumentNullException("poolable");
41 |
42 | _poolableComponents.Add(poolable);
43 | }
44 |
45 | ///
46 | /// Manually removes a poolable object so that it no longer is notified when this component is spawned or despawned.
47 | ///
48 | /// The poolable object that should no longer be notified.
49 | public void RemoveManually(T poolable) where T : MonoBehaviour, IPoolable
50 | {
51 | if (ReferenceEquals(poolable, null))
52 | throw new ArgumentNullException("poolable");
53 |
54 | _poolableComponents.Remove(poolable);
55 | }
56 |
57 | void ISerializationCallbackReceiver.OnBeforeSerialize()
58 | {
59 | if (!Application.isPlaying)
60 | {
61 | _poolableComponents.Clear();
62 | var children = GetComponentsInChildren(true).Cast();
63 | _poolableComponents.AddRange(children);
64 | _poolableComponents.Remove(this);
65 | }
66 | }
67 |
68 | void ISerializationCallbackReceiver.OnAfterDeserialize()
69 | {
70 |
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Archon.SwissArmyLib")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Archon Interactive")]
11 | [assembly: AssemblyProduct("Archon.SwissArmyLib")]
12 | [assembly: AssemblyCopyright("Copyright © 2017")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("4e6e4ed1-096a-4dd2-b88f-0704bc769f22")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/ResourceSystem/ResourceEvent.cs:
--------------------------------------------------------------------------------
1 | namespace Archon.SwissArmyLib.ResourceSystem
2 | {
3 | internal class ResourceEvent : IResourceChangeEvent, IResourcePreChangeEvent
4 | {
5 | ///
6 | public float OriginalDelta { get; set; }
7 |
8 | ///
9 | public float ModifiedDelta { get; set; }
10 |
11 | ///
12 | public float AppliedDelta { get; set; }
13 |
14 | ///
15 | public TSource Source { get; set; }
16 |
17 | ///
18 | public TArgs Args { get; set; }
19 | }
20 |
21 | ///
22 | /// Defines an event for after a resource pool has been changed.
23 | ///
24 | public interface IResourceChangeEvent : IResourceEvent
25 | {
26 | ///
27 | /// Gets the originally requested resource change.
28 | ///
29 | float OriginalDelta { get; }
30 |
31 | ///
32 | /// Gets the modified delta after listeners of had their chance to affect it.
33 | ///
34 | float ModifiedDelta { get; }
35 |
36 | ///
37 | /// Gets the actual applied (and clamped) delta.
38 | /// Basically just the difference in resource amount before and after the change.
39 | ///
40 | float AppliedDelta { get; }
41 | }
42 |
43 | ///
44 | /// Defines a change event that has not yet happened, and can be altered.
45 | ///
46 | public interface IResourcePreChangeEvent : IResourceEvent
47 | {
48 | ///
49 | /// Gets the originally requested resource change.
50 | ///
51 | float OriginalDelta { get; }
52 |
53 | ///
54 | /// Gets or sets the modified delta that will be applied after this event.
55 | ///
56 | float ModifiedDelta { get; set; }
57 | }
58 |
59 | ///
60 | /// Defines a barebones resource event.
61 | ///
62 | public interface IResourceEvent
63 | {
64 | ///
65 | /// Gets or sets the source of the resource change.
66 | ///
67 | TSource Source { get; set; }
68 |
69 | ///
70 | /// Gets or sets the args that the sender sent with the change.
71 | ///
72 | TArgs Args { get; set; }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/ResourceSystem/ResourcePoolBase.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Archon.SwissArmyLib.ResourceSystem
4 | {
5 | ///
6 | /// Non-generic base class for to allow its editor to work for subclasses.
7 | ///
8 | public abstract class ResourcePoolBase : MonoBehaviour
9 | {
10 | ///
11 | /// Gets the current amount of resource in this pool.
12 | ///
13 | public abstract float Current { get; protected set; }
14 |
15 | ///
16 | /// Gets or sets the maximum amount of source that can be in this pool.
17 | ///
18 | public abstract float Max { get; set; }
19 |
20 | ///
21 | /// Gets or sets whether adding resource should be disabled after the pool is completely empty, until it is renewed again.
22 | ///
23 | public abstract bool EmptyTillRenewed { get; set; }
24 |
25 | ///
26 | /// Gets a how full the resource is percentage-wise (0 to 1)
27 | ///
28 | public abstract float Percentage { get; }
29 |
30 | ///
31 | /// Gets whether the pool is completely empty.
32 | ///
33 | public abstract bool IsEmpty { get; }
34 |
35 | ///
36 | /// Gets whether the pool is completely empty.
37 | ///
38 | public abstract bool IsFull { get; }
39 |
40 | ///
41 | /// Get the (scaled) time since this pool was last empty.
42 | ///
43 | public abstract float TimeSinceEmpty { get; }
44 |
45 | ///
46 | /// Adds the specified amount of resource to the pool.
47 | ///
48 | /// The amount to add.
49 | /// Controls whether to force the change, despite modifications by listeners.
50 | /// The resulting change in the pool.
51 | public abstract float Add(float amount, bool forced = false);
52 |
53 | ///
54 | /// Removes the specified amount of resource to the pool.
55 | ///
56 | /// The amount to remove.
57 | /// Controls whether to force the change, despite modifications by listeners.
58 | /// The resulting change in the pool.
59 | public abstract float Remove(float amount, bool forced = false);
60 |
61 | ///
62 | /// Completely empties the pool.
63 | ///
64 | /// Controls whether to force the change, despite modifications by listeners.
65 | /// The resulting change in the pool.
66 | public abstract float Empty(bool forced = false);
67 |
68 | ///
69 | /// Fully fills the pool.
70 | ///
71 | /// Controls whether to force the change, despite modifications by listeners.
72 | /// The resulting change in the pool.
73 | public abstract float Fill(bool forced = false);
74 |
75 | ///
76 | /// Fills the pool to the specified amount.
77 | ///
78 | /// The amount of resource to restore to.
79 | /// Controls whether to force the change, despite modifications by listeners.
80 | /// The resulting change in the pool.
81 | public abstract float Fill(float toValue, bool forced = false);
82 |
83 | ///
84 | /// Fully restores the pool, regardless of .
85 | ///
86 | /// Controls whether to force the change, despite modifications by listeners.
87 | /// The resulting change in the pool.
88 | public abstract float Renew(bool forced = false);
89 |
90 | ///
91 | /// Restores the pool to the specified amount, regardless of .
92 | ///
93 | /// The amount of resource to restore to.
94 | /// Controls whether to force the change, despite modifications by listeners.
95 | /// The resulting change in the pool.
96 | public abstract float Renew(float toValue, bool forced = false);
97 | }
98 | }
--------------------------------------------------------------------------------
/Archon.SwissArmyLib/ResourceSystem/ResourceRegen.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Archon.SwissArmyLib.Events;
3 | using Archon.SwissArmyLib.Utils;
4 | using Archon.SwissArmyLib.Utils.Editor;
5 | using JetBrains.Annotations;
6 | using UnityEngine;
7 |
8 | namespace Archon.SwissArmyLib.ResourceSystem
9 | {
10 | ///
11 | /// Adds resource to a at a constant rate or in intervals.
12 | ///
13 | /// If the is not set, it will try to find a on the same GameObject.
14 | ///
15 | /// If you need type-safety consider subclassing the generic version: .
16 | ///
17 | ///
18 | /// This non-generic version only works for the non-generic .
19 | ///
20 | ///
21 | public class ResourceRegen : ResourceRegen