This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2020-04-01
### Changed
- Package has been verified for 2020.2
## [0.1.0-preview.2] - 2020-03-17
### Changed
- Lowered data size for the EditorWaitForSeconds class by half.
## [0.1.0-preview.1] - 2020-01-08
### Added
- Added support for AsyncOperation subclasses.
### Changed
- Fixed unstable test.
## [0.0.2-preview.1] - 2019-01-25
### Changed
- Fixed a compilation issue caused by using the 'default' literal.
## [0.0.1-preview.5] - 2019-01-14
### Changed
- Updated Readme.md.
- Added unified yield statement processor.
- Added stack based processing of nested yield statements.
- Updated tests.
- Lowered memory footprint of editor coroutine instances.
### Removed
- Removed recursive handling of nested yield statements.
- Removed specialized yield statement processors.
## [0.0.1-preview.4] - 2018-12-7
### Added
- API documentation.
### Changed
- Fixed line endings for the EditorCourtineTests.cs source file.
## [0.0.1-preview.3] - 2018-10-11
### Changed
- Updated LICENSE.md.
- Updated manifest to reflect correct minimum supported version.
## [0.0.1-preview.2] - 2018-10-11
### Added
- Added stub documentation via com.unity.editorcoroutines.md.
## [0.0.1-preview.1] - 2018-10-10
### Added
- Added nesting support for editor coroutines.
- Added abitrary enumerator support for editor coroutines.
- Created specialized EditorWaitForSeconds class with access to it's wait time ( same behavior as WaitForSeconds).
### This is the first release of *Unity Package Editor Coroutines*.
Source code release of the Editor Coroutines package, with no added documentation or stripping of default Package Creation Kit files.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f4e822a0337b6cf4aade8abffaf819a8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
# Unity Editor Coroutines
* [Editor Coroutines overview](index)

View File

@@ -0,0 +1,49 @@
# About Editor Coroutines
The Editor Coroutines package allows the user to start the execution of [iterator methods](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) within the Editor similar to how we handle [Coroutines](https://docs.unity3d.com/Manual/Coroutines.html) inside [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html) scripts during runtime.
# Installing Editor Coroutines
To install this package, follow the instructions in the [Package Manager documentation](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@latest/index.html).
> **Note**: While this package is in preview, the Package Manager needs to be configured to show __Preview Packages__. (Under the __Advanced__ drop-down menu, enable __Show preview packages__.) Then search for the Editor Coroutines package.
<a name="UsingPackageName"></a>
# Using Editor Coroutines
To learn how to use the Editor Coroutines package in your project, please refer to the Scripting API section of the documentation.
# Technical details
## Requirements
This version of Editor Coroutines is compatible with the following versions of the Unity Editor:
* 2018.1 and later (recommended)
> **Note**: If you install the Memory Profiler package it will automatically install the Editor Coroutines package as a dependency.
## Known limitations
Editor Coroutines version 0.0.1-preview.2 includes the following known limitation(s):
The iterator functions passed to Editor Coroutines do not support yielding any of the instruction classes present inside the Unity Scripting API (e.g., [WaitForSeconds](https://docs.unity3d.com/ScriptReference/WaitForSeconds.html), [WaitForEndOfFrame](https://docs.unity3d.com/ScriptReference/WaitForEndOfFrame.html)), except for the [CustomYieldInstruction](https://docs.unity3d.com/ScriptReference/CustomYieldInstruction.html) derived classes with the `MoveNext` method implemented.
> **Tip**: `yield return null` is a way to skip a frame within the Editor.
## Package contents
The following table indicates the root folders in the package where you can find useful resources:
| Location | Description |
| ---------------- | ------------------------------------------- |
| `Documentation~` | Contains the documentation for the package. |
| `Tests` | Contains the unit tests for the package. |
## Document revision history
|Date|Reason|
|---|---|
|June 20, 2019|Removed deprecated manual link.|
|Dec 7, 2018|Api documentation added. Matches package version 0.0.1-preview.4.|
|Oct 11, 2018|Document created. Matches package version 0.0.1-preview.2.|

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4baf9d3a89175c143b9d1bb3e62526e7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,161 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.EditorCoroutines.Editor
{
/// <summary>
/// A handle to an EditorCoroutine, can be passed to <see cref="EditorCoroutineUtility">EditorCoroutineUtility</see> methods to control lifetime.
/// </summary>
public class EditorCoroutine
{
private struct YieldProcessor
{
enum DataType : byte
{
None = 0,
WaitForSeconds = 1,
EditorCoroutine = 2,
AsyncOP = 3,
}
struct ProcessorData
{
public DataType type;
public double targetTime;
public object current;
}
ProcessorData data;
public void Set(object yield)
{
if (yield == data.current)
return;
var type = yield.GetType();
var dataType = DataType.None;
double targetTime = -1;
if(type == typeof(EditorWaitForSeconds))
{
targetTime = EditorApplication.timeSinceStartup + (yield as EditorWaitForSeconds).WaitTime;
dataType = DataType.WaitForSeconds;
}
else if(type == typeof(EditorCoroutine))
{
dataType = DataType.EditorCoroutine;
}
else if(type == typeof(AsyncOperation) || type.IsSubclassOf(typeof(AsyncOperation)))
{
dataType = DataType.AsyncOP;
}
data = new ProcessorData { current = yield, targetTime = targetTime, type = dataType };
}
public bool MoveNext(IEnumerator enumerator)
{
bool advance = false;
switch (data.type)
{
case DataType.WaitForSeconds:
advance = data.targetTime <= EditorApplication.timeSinceStartup;
break;
case DataType.EditorCoroutine:
advance = (data.current as EditorCoroutine).m_IsDone;
break;
case DataType.AsyncOP:
advance = (data.current as AsyncOperation).isDone;
break;
default:
advance = data.current == enumerator.Current; //a IEnumerator or a plain object was passed to the implementation
break;
}
if(advance)
{
data = default(ProcessorData);
return enumerator.MoveNext();
}
return true;
}
}
internal WeakReference m_Owner;
IEnumerator m_Routine;
YieldProcessor m_Processor;
bool m_IsDone;
internal EditorCoroutine(IEnumerator routine)
{
m_Owner = null;
m_Routine = routine;
EditorApplication.update += MoveNext;
}
internal EditorCoroutine(IEnumerator routine, object owner)
{
m_Processor = new YieldProcessor();
m_Owner = new WeakReference(owner);
m_Routine = routine;
EditorApplication.update += MoveNext;
}
internal void MoveNext()
{
if (m_Owner != null && !m_Owner.IsAlive)
{
EditorApplication.update -= MoveNext;
return;
}
bool done = ProcessIEnumeratorRecursive(m_Routine);
m_IsDone = !done;
if (m_IsDone)
EditorApplication.update -= MoveNext;
}
static Stack<IEnumerator> kIEnumeratorProcessingStack = new Stack<IEnumerator>(32);
private bool ProcessIEnumeratorRecursive(IEnumerator enumerator)
{
var root = enumerator;
while(enumerator.Current as IEnumerator != null)
{
kIEnumeratorProcessingStack.Push(enumerator);
enumerator = enumerator.Current as IEnumerator;
}
//process leaf
m_Processor.Set(enumerator.Current);
var result = m_Processor.MoveNext(enumerator);
while (kIEnumeratorProcessingStack.Count > 1)
{
if (!result)
{
result = kIEnumeratorProcessingStack.Pop().MoveNext();
}
else
kIEnumeratorProcessingStack.Clear();
}
if (kIEnumeratorProcessingStack.Count > 0 && !result && root == kIEnumeratorProcessingStack.Pop())
{
result = root.MoveNext();
}
return result;
}
internal void Stop()
{
m_Owner = null;
m_Routine = null;
EditorApplication.update -= MoveNext;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f93931a6fe807b45809dbc0ddf91260
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,122 @@
using System.Collections;
using UnityEngine;
namespace Unity.EditorCoroutines.Editor
{
public static class EditorCoroutineUtility
{
/// <summary>
/// Starts an <see cref ="EditorCoroutine">EditorCoroutine</see> with the specified owner object.
/// If the garbage collector collects the owner object, while the resulting coroutine is still executing, the coroutine will stop running.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class ExampleWindow : EditorWindow
/// {
/// int m_Updates = 0;
/// void OnEnable()
/// {
/// EditorCoroutineUtility.StartCoroutine(CountEditorUpdates(), this);
/// }
///
/// IEnumerator CountEditorUpdates()
/// {
/// while (true)
/// {
/// ++m_Updates;
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"> IEnumerator to iterate over. </param>
/// <param name="owner">Object owning the coroutine. </param>
/// <remarks>
/// Only types that don't inherit from <see cref="UnityEngine.Object">UnityEngine.Object</see> will get collected the next time the GC runs instead of getting null-ed immediately.
/// </remarks>
/// <returns>A handle to an <see cref="EditorCoroutine">EditorCoroutine</see>.</returns>
public static EditorCoroutine StartCoroutine(IEnumerator routine, object owner)
{
return new EditorCoroutine(routine, owner);
}
/// <summary>
/// This method starts an <see cref="EditorCoroutine">EditorCoroutine</see> without an owning object. The <see cref="EditorCoroutine">EditorCoroutine</see> runs until it completes or is canceled using <see cref="StopCoroutine(EditorCoroutine)">StopCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// void OnEnable()
/// {
/// EditorCoroutineUtility.StartCoroutineOwnerless(LogTimeSinceStartup());
/// }
///
/// IEnumerator LogTimeSinceStartup()
/// {
/// while (true)
/// {
/// Debug.LogFormat("Time since startup: {0} s", Time.realtimeSinceStartup);
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"> Generator function to execute. </param>
/// <returns>A handle to an <see cref="EditorCoroutine">EditorCoroutine.</see></returns>
public static EditorCoroutine StartCoroutineOwnerless(IEnumerator routine)
{
return new EditorCoroutine(routine);
}
/// <summary>
/// Immediately stop an <see cref="EditorCoroutine">EditorCoroutine</see>. This method is safe to call on an already completed <see cref="EditorCoroutine">EditorCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// EditorCoroutine m_LoggerCoroutine;
/// void OnEnable()
/// {
/// m_LoggerCoroutine = EditorCoroutineUtility.StartCoroutineOwnerless(LogRunning());
/// }
///
/// void OnDisable()
/// {
/// EditorCoroutineUtility.StopCoroutine(m_LoggerCoroutine);
/// }
///
/// IEnumerator LogRunning()
/// {
/// while (true)
/// {
/// Debug.Log("Running");
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="coroutine">A handle to an <see cref="EditorCoroutine">EditorCoroutine.</see></param>
public static void StopCoroutine(EditorCoroutine coroutine)
{
if (coroutine == null)
{
Debug.LogAssertion("EditorCoroutine handle is null.");
return;
}
coroutine.Stop();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a672678b43d60c542ad9d861eba67c99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
namespace Unity.EditorCoroutines.Editor
{
/// <summary>
/// Suspends the <see cref="EditorCoroutine">EditorCoroutine</see> execution for the given amount of seconds, using unscaled time.
/// The coroutine execution continues after the specified time has elapsed.
/// <code>
/// using System.Collections;
/// using UnityEngine;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class MyEditorWindow : EditorWindow
/// {
/// IEnumerator PrintEachSecond()
/// {
/// var waitForOneSecond = new EditorWaitForSeconds(1.0f);
///
/// while (true)
/// {
/// yield return waitForOneSecond;
/// Debug.Log("Printing each second");
/// }
/// }
/// }
/// </code>
/// </summary>
public class EditorWaitForSeconds
{
/// <summary>
/// The time to wait in seconds.
/// </summary>
public float WaitTime { get; }
/// <summary>
/// Creates a instruction object for yielding inside a generator function.
/// </summary>
/// <param name="time">The amount of time to wait in seconds.</param>
public EditorWaitForSeconds(float time)
{
WaitTime = time;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 087be8c1503662144b530229dddcfce4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,103 @@
using System.Collections;
using UnityEditor;
using UnityEngine;
namespace Unity.EditorCoroutines.Editor
{
public static class EditorWindowCoroutineExtension
{
/// <summary>
/// Start an <see cref="EditorCoroutine">EditorCoroutine</see>, owned by the calling <see cref="EditorWindow">EditorWindow</see> instance.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
///
/// public class ExampleWindow : EditorWindow
/// {
/// void OnEnable()
/// {
/// this.StartCoroutine(CloseWindowDelayed());
/// }
///
/// IEnumerator CloseWindowDelayed() //close the window after 1000 frames have elapsed
/// {
/// int count = 1000;
/// while (count > 0)
/// {
/// yield return null;
/// }
/// Close();
/// }
/// }
/// </code>
/// </summary>
/// <param name="routine"></param>
/// <returns></returns>
public static EditorCoroutine StartCoroutine(this EditorWindow window, IEnumerator routine)
{
return new EditorCoroutine(routine, window);
}
/// <summary>
/// Immediately stop an <see cref="EditorCoroutine">EditorCoroutine</see> that was started by the calling <see cref="EditorWindow"/> instance. This method is safe to call on an already completed <see cref="EditorCoroutine">EditorCoroutine</see>.
/// <code>
/// using System.Collections;
/// using Unity.EditorCoroutines.Editor;
/// using UnityEditor;
/// using UnityEngine;
///
/// public class ExampleWindow : EditorWindow
/// {
/// EditorCoroutine coroutine;
/// void OnEnable()
/// {
/// coroutine = this.StartCoroutine(CloseWindowDelayed());
/// }
///
/// private void OnDisable()
/// {
/// this.StopCoroutine(coroutine);
/// }
///
/// IEnumerator CloseWindowDelayed()
/// {
/// while (true)
/// {
/// Debug.Log("Running");
/// yield return null;
/// }
/// }
/// }
/// </code>
/// </summary>
/// <param name="coroutine"></param>
public static void StopCoroutine(this EditorWindow window, EditorCoroutine coroutine)
{
if(coroutine == null)
{
Debug.LogAssertion("Provided EditorCoroutine handle is null.");
return;
}
if(coroutine.m_Owner == null)
{
Debug.LogError("The EditorCoroutine is ownerless. Please use EditorCoroutineEditor.StopCoroutine to terminate such coroutines.");
return;
}
if (!coroutine.m_Owner.IsAlive)
return; //The EditorCoroutine's owner was already terminated execution will cease next time it is processed
var owner = coroutine.m_Owner.Target as EditorWindow;
if (owner == null || owner != null && owner != window)
{
Debug.LogErrorFormat("The EditorCoroutine is owned by another object: {0}.", coroutine.m_Owner.Target);
return;
}
EditorCoroutineUtility.StopCoroutine(coroutine);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3e62174891db9d43b9adee028159625
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
{
"name": "Unity.EditorCoroutines.Editor",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 478a2357cc57436488a56e564b08d223
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
com.unity.core.editorcoroutines copyright © 2018 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7fd8423f910d2394b8580e2705aa247a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1 @@
Editor Coroutines

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ee9ad019452796e46945e4870e3717e4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
{
"name": "com.unity.editorcoroutines",
"displayName": "Editor Coroutines",
"version": "1.0.0",
"unity": "2018.1",
"description": "The editor coroutines package allows developers to start constructs similar to Unity's monobehaviour based coroutines within the editor using abitrary objects. ",
"keywords": [
"coroutine",
"coroutines",
"editor"
],
"dependencies": {},
"relatedPackages": {
"com.unity.editorcoroutines.tests": "1.0.0"
},
"repository": {
"footprint": "91e3fb8c8cff373235a5ba68d1a9cabd02080f3e",
"type": "git",
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.editorcoroutines.git",
"revision": "f67fc9992bbc7a553b17375de53a8b2db136528e"
},
"_fingerprint": "7d48783e7b8cfcee5f8ef9ba787ed0d9dad4ebca"
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6428ca6f30e71ee4dbea7e1d0787cb07
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: