diff --git a/.example/Assets/csc.rsp b/.example/Assets/csc.rsp
new file mode 100644
index 0000000..df842cd
--- /dev/null
+++ b/.example/Assets/csc.rsp
@@ -0,0 +1,2 @@
+-nowarn:649
+-warnaserror+
diff --git a/.example/Assets/csc.rsp.meta b/.example/Assets/csc.rsp.meta
new file mode 100644
index 0000000..e515ac4
--- /dev/null
+++ b/.example/Assets/csc.rsp.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e9f4eafbf5d7b43b681fb51b68743d8a
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/src/ComponentExtensions.cs b/src/ComponentExtensions.cs
index b924f58..99fe446 100644
--- a/src/ComponentExtensions.cs
+++ b/src/ComponentExtensions.cs
@@ -38,10 +38,14 @@ public static class ComponentExtensions
///
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func taskCreator)
+ public static Task StartTask(
+ this Component component,
+ Func taskCreator,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -52,7 +56,7 @@ public static Task StartTask(this Component component, Func taskCreator)
UnityHelper.ThrowForNonUnityThreadOrEditMode();
// Start the task on a runner for the component.
- return GetTaskRunnerUnchecked(component).StartTask(taskCreator);
+ return GetTaskRunnerUnchecked(component, options).StartTask(taskCreator);
}
///
@@ -81,10 +85,14 @@ public static Task StartTask(this Component component, Func taskCreator)
///
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func taskCreator)
+ public static Task StartTask(
+ this Component component,
+ Func taskCreator,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -95,7 +103,7 @@ public static Task StartTask(this Component component, Func
@@ -122,10 +130,15 @@ public static Task StartTask(this Component component, Func
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func taskCreator, TIn data)
+ public static Task StartTask(
+ this Component component,
+ Func taskCreator,
+ TIn data,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -136,7 +149,7 @@ public static Task StartTask(this Component component, Func task
UnityHelper.ThrowForNonUnityThreadOrEditMode();
// Start the task on a runner for the component.
- return GetTaskRunnerUnchecked(component).StartTask(taskCreator, data);
+ return GetTaskRunnerUnchecked(component, options).StartTask(taskCreator, data);
}
///
@@ -165,10 +178,15 @@ public static Task StartTask(this Component component, Func task
///
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func taskCreator, TIn data)
+ public static Task StartTask(
+ this Component component,
+ Func taskCreator,
+ TIn data,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -179,7 +197,7 @@ public static Task StartTask(this Component component, Func
@@ -206,10 +224,14 @@ public static Task StartTask(this Component component, Func
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func> taskCreator)
+ public static Task StartTask(
+ this Component component,
+ Func> taskCreator,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -220,7 +242,7 @@ public static Task StartTask(this Component component, Func
@@ -249,10 +271,14 @@ public static Task StartTask(this Component component, Func
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func> taskCreator)
+ public static Task StartTask(
+ this Component component,
+ Func> taskCreator,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -263,7 +289,7 @@ public static Task StartTask(this Component component, Func
@@ -290,10 +316,15 @@ public static Task StartTask(this Component component, Func
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func> taskCreator, TIn data)
+ public static Task StartTask(
+ this Component component,
+ Func> taskCreator,
+ TIn data,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -304,7 +335,7 @@ public static Task StartTask(this Component component, Func
@@ -333,10 +364,15 @@ public static Task StartTask(this Component component, Func
/// Component to run the task 'on'.
/// Function for creating the task.
+ /// Options for configuring how the task is run.
///
/// Task that completes when the original task completes or when the component gets destroyed.
///
- public static Task StartTask(this Component component, Func> taskCreator, TIn data)
+ public static Task StartTask(
+ this Component component,
+ Func> taskCreator,
+ TIn data,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -347,7 +383,7 @@ public static Task StartTask(this Component component, Func
@@ -366,8 +402,11 @@ public static Task StartTask(this Component component, Func
/// Component to get the runner for.
+ /// Options for configuring how tasks are run on this runner.
/// scoped to the given component.
- public static ITaskRunner GetTaskRunner(this Component component)
+ public static ITaskRunner GetTaskRunner(
+ this Component component,
+ TaskRunOptions options = TaskRunOptions.Default)
{
// Validate params.
UnityHelper.ThrowForInvalidObjectParam(component, nameof(component));
@@ -376,10 +415,10 @@ public static ITaskRunner GetTaskRunner(this Component component)
UnityHelper.ThrowForNonUnityThreadOrEditMode();
// Start the task on a runner for the component.
- return GetTaskRunnerUnchecked(component);
+ return GetTaskRunnerUnchecked(component, options);
}
- internal static ITaskRunner GetTaskRunnerUnchecked(this Component component)
+ internal static ITaskRunner GetTaskRunnerUnchecked(Component component, TaskRunOptions options)
{
var owner = component.gameObject;
@@ -388,12 +427,13 @@ internal static ITaskRunner GetTaskRunnerUnchecked(this Component component)
foreach (var runner in monoBehaviourRunners)
{
// If there is a runner for that component then return that.
- if (runner.ComponentToFollow == component)
+ if (!runner.IsFinished && runner.RunOptions == options && runner.ComponentToFollow == component)
return runner;
}
// Otherwise create a new runner for this component.
var newRunner = owner.AddComponent();
+ newRunner.RunOptions = options;
newRunner.ComponentToFollow = component;
newRunner.hideFlags = HideFlags.HideInInspector;
return newRunner;
diff --git a/src/ComponentTask/Internal/MonoBehaviourTaskRunner.cs b/src/ComponentTask/Internal/MonoBehaviourTaskRunner.cs
index aafe3ac..c393336 100644
--- a/src/ComponentTask/Internal/MonoBehaviourTaskRunner.cs
+++ b/src/ComponentTask/Internal/MonoBehaviourTaskRunner.cs
@@ -1,10 +1,11 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using UnityEngine;
namespace ComponentTask.Internal
{
- internal sealed class MonoBehaviourTaskRunner : UnityEngine.MonoBehaviour, ITaskRunner, IExceptionHandler
+ internal sealed class MonoBehaviourTaskRunner : MonoBehaviour, ITaskRunner, IExceptionHandler
{
private readonly LocalTaskRunner taskRunner;
@@ -13,7 +14,9 @@ public MonoBehaviourTaskRunner()
this.taskRunner = new LocalTaskRunner(exceptionHandler: this);
}
- public UnityEngine.Component ComponentToFollow { get; set; }
+ public TaskRunOptions RunOptions { get; set; }
+
+ public Component ComponentToFollow { get; set; }
public bool IsFinished { get; private set; }
@@ -86,7 +89,9 @@ private void LateUpdate()
// If the component is a 'Behaviour' then we update when its enabled.
if (ComponentToFollow is UnityEngine.Behaviour behaviour)
{
- if (behaviour.isActiveAndEnabled)
+ var updateWhileDisabled =
+ (this.RunOptions & TaskRunOptions.UpdateWhileComponentDisabled) == TaskRunOptions.UpdateWhileComponentDisabled;
+ if (updateWhileDisabled || behaviour.isActiveAndEnabled)
this.Execute();
}
else
diff --git a/src/GameObjectExtensions.cs b/src/GameObjectExtensions.cs
index c287faf..e9d86e6 100644
--- a/src/GameObjectExtensions.cs
+++ b/src/GameObjectExtensions.cs
@@ -35,6 +35,7 @@ public static ITaskRunner CreateTaskRunner(this GameObject gameObject)
// Create runner.
var runner = gameObject.AddComponent();
+ runner.RunOptions = TaskRunOptions.Default;
runner.hideFlags = HideFlags.HideInInspector;
return runner;
diff --git a/src/TaskRunOptions.cs b/src/TaskRunOptions.cs
new file mode 100644
index 0000000..e7a89f5
--- /dev/null
+++ b/src/TaskRunOptions.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace UnityEngine
+{
+ ///
+ /// Flags for configuring how tasks are being run.
+ ///
+ [Flags]
+ public enum TaskRunOptions : int
+ {
+ ///
+ /// Default run options.
+ ///
+ ///
+ /// Tasks are updated in 'LateUpdate' when the component is enabled.
+ ///
+ Default = 0,
+
+ ///
+ /// Tasks are updated even if the component is disabled.
+ ///
+ UpdateWhileComponentDisabled = 1 << 0,
+ }
+}
diff --git a/src/TaskRunOptions.cs.meta b/src/TaskRunOptions.cs.meta
new file mode 100644
index 0000000..cd73320
--- /dev/null
+++ b/src/TaskRunOptions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2fcdce5ac99a486ead26ebddc5b3d95
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/tests/playmode/ComponentExtensionsTests.cs b/tests/playmode/ComponentExtensionsTests.cs
index 400e6c8..1e7d106 100644
--- a/tests/playmode/ComponentExtensionsTests.cs
+++ b/tests/playmode/ComponentExtensionsTests.cs
@@ -209,7 +209,42 @@ async Task IncrementCountAsync()
}
[UnityTest]
- public IEnumerator SameRunnerIsReusedForTheSameComponent()
+ public IEnumerator TaskRunsWhileComponentIsDisabledWhenConfigured()
+ {
+ var count = 0;
+ var go = new GameObject("TestGameObject");
+ var comp = go.AddComponent();
+ comp.StartTask(IncrementCountAsync, TaskRunOptions.UpdateWhileComponentDisabled);
+
+ // Assert task is running.
+ yield return null;
+ Assert.AreEqual(1, count);
+
+ // Disable component.
+ comp.enabled = false;
+
+ // Assert task is still running.
+ yield return null;
+ Assert.AreEqual(2, count);
+ yield return null;
+ Assert.AreEqual(3, count);
+
+ // Cleanup.
+ Object.Destroy(go);
+
+ async Task IncrementCountAsync()
+ {
+ await Task.Yield();
+ count++;
+ await Task.Yield();
+ count++;
+ await Task.Yield();
+ count++;
+ }
+ }
+
+ [UnityTest]
+ public IEnumerator SameRunnerIsReusedForTheSameComponentAndSameOptions()
{
var go = new GameObject("TestGameObject");
var comp = go.AddComponent();
@@ -222,6 +257,20 @@ public IEnumerator SameRunnerIsReusedForTheSameComponent()
Object.Destroy(go);
}
+ [UnityTest]
+ public IEnumerator DifferentRunnerIsUsedForSameComponentAndDifferentOptions()
+ {
+ var go = new GameObject("TestGameObject");
+ var comp = go.AddComponent();
+
+ var runner1 = comp.GetTaskRunner(TaskRunOptions.Default);
+ var runner2 = comp.GetTaskRunner(TaskRunOptions.UpdateWhileComponentDisabled);
+ Assert.False(object.ReferenceEquals(runner1, runner2));
+
+ yield return null;
+ Object.Destroy(go);
+ }
+
[UnityTest]
public IEnumerator RunnerDestroysItselfWhenComponentIsDestroyed()
{