diff --git a/X10D.Tests/X10D.Tests.csproj b/X10D.Tests/X10D.Tests.csproj
index 684b34a..bdba516 100644
--- a/X10D.Tests/X10D.Tests.csproj
+++ b/X10D.Tests/X10D.Tests.csproj
@@ -24,9 +24,11 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/X10D.Tests/src/Unity/Vector3Tests.cs b/X10D.Tests/src/Unity/Vector3Tests.cs
new file mode 100644
index 0000000..2e02cf0
--- /dev/null
+++ b/X10D.Tests/src/Unity/Vector3Tests.cs
@@ -0,0 +1,93 @@
+namespace X10D.Tests.Unity
+{
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+ using UnityEngine;
+ using X10D.Unity;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class Vector3Tests
+ {
+ ///
+ /// Tests for by rounding to the nearest 0.5.
+ ///
+ [TestMethod]
+ public void TestRoundHalf()
+ {
+ var vector = new Vector3(1.8f, 2.1f, 3.37f);
+ Assert.AreEqual(new Vector3(2, 2, 3.5f), vector.Round(0.5f));
+ }
+
+ ///
+ /// Tests for by rounding to the nearest integer.
+ ///
+ [TestMethod]
+ public void TestRoundInteger()
+ {
+ var vector = new Vector3(1.8f, 2.1f, 3.37f);
+ Assert.AreEqual(new Vector3(2, 2, 3), vector.Round());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithX()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(4, 2, 3), vector.WithX(4));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithXY()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(8, 10, 3), vector.WithXY(8, 10));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithXZ()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(8, 2, 10), vector.WithXZ(8, 10));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithY()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(1, 4, 3), vector.WithY(4));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithYZ()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(1, 8, 10), vector.WithYZ(8, 10));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void TestWithZ()
+ {
+ var vector = new Vector3(1, 2, 3);
+ Assert.AreEqual(new Vector3(1, 2, 4), vector.WithZ(4));
+ }
+ }
+}
diff --git a/X10D.Unity/X10D.Unity.csproj b/X10D.Unity/X10D.Unity.csproj
new file mode 100644
index 0000000..41ea494
--- /dev/null
+++ b/X10D.Unity/X10D.Unity.csproj
@@ -0,0 +1,72 @@
+
+
+
+ netstandard2.0
+ 8.0
+ Oliver Booth
+ en
+ true
+ https://github.com/oliverbooth/X10D
+ git
+ Extension methods on crack.
+ LICENSE.md
+ icon.png
+
+ dotnet extension-methods unity
+ 2.2.0
+ ..\X10D.ruleset
+ true
+ 2.4.0
+ 2.4.0
+ 2.4.0
+
+
+
+
+ True
+
+
+
+ True
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resource.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resource.Designer.cs
+
+
+
+
\ No newline at end of file
diff --git a/X10D.Unity/src/BetterBehavior.cs b/X10D.Unity/src/BetterBehavior.cs
new file mode 100644
index 0000000..6069676
--- /dev/null
+++ b/X10D.Unity/src/BetterBehavior.cs
@@ -0,0 +1,107 @@
+namespace X10D.Unity
+{
+ using System.Diagnostics.CodeAnalysis;
+ using System.Threading.Tasks;
+ using UnityEngine;
+
+ ///
+ /// Represents a class which inherits to offer wider functionality.
+ ///
+ [SuppressMessage("ReSharper", "MemberCanBePrivate.Global", Justification = "Unity property")]
+ [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Unity property")]
+ [SuppressMessage("ReSharper", "SA1300", Justification = "Unity API-compliant property")]
+ [SuppressMessage("ReSharper", "CA2007", Justification = "Unnecessary")]
+ [SuppressMessage("ReSharper", "RCS1090", Justification = "Unnecessary")]
+ [SuppressMessage("ReSharper", "RCS1213", Justification = "Unity method")]
+ [SuppressMessage("ReSharper", "UnusedParameter.Global", Justification = "Override method")]
+ public abstract class BetterBehavior : MonoBehaviour
+ {
+ ///
+ /// Gets the component attached to this object.
+ ///
+ public new Transform transform { get; private set; }
+
+ ///
+ /// Awake is called when the script instance is being loaded.
+ ///
+ protected virtual void Awake()
+ {
+ this.transform = this.GetComponent();
+ }
+
+ ///
+ /// Frame-rate independent messaging for physics calculations.
+ ///
+ /// A snapshot of timing values.
+ protected virtual void OnFixedUpdate(in GameTime gameTime)
+ {
+ }
+
+ ///
+ /// Frame-rate independent messaging for physics calculations.
+ ///
+ /// A snapshot of timing values.
+ /// Returns a task representing the result of the operation.
+ protected virtual Task OnFixedUpdateAsync(GameTime gameTime)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Called once per frame, after all Update calls.
+ ///
+ /// A snapshot of timing values.
+ protected virtual void OnLateUpdate(in GameTime gameTime)
+ {
+ }
+
+ ///
+ /// Called once per frame, after all Update calls.
+ ///
+ /// A snapshot of timing values.
+ /// Returns a task representing the result of the operation.
+ protected virtual Task OnLateUpdateAsync(GameTime gameTime)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Called once per frame.
+ ///
+ /// A snapshot of timing values.
+ protected virtual void OnUpdate(in GameTime gameTime)
+ {
+ }
+
+ ///
+ /// Called once per frame.
+ ///
+ /// A snapshot of timing values.
+ /// Returns a task representing the result of the operation.
+ protected virtual Task OnUpdateAsync(GameTime gameTime)
+ {
+ return Task.CompletedTask;
+ }
+
+ private async void FixedUpdate()
+ {
+ var time = GameTime.CreateFromCurrentTimes();
+ this.OnFixedUpdate(time);
+ await this.OnFixedUpdateAsync(time);
+ }
+
+ private async void LateUpdate()
+ {
+ var time = GameTime.CreateFromCurrentTimes();
+ this.OnLateUpdate(time);
+ await this.OnLateUpdateAsync(time);
+ }
+
+ private async void Update()
+ {
+ var time = GameTime.CreateFromCurrentTimes();
+ this.OnUpdate(time);
+ await this.OnUpdateAsync(time);
+ }
+ }
+}
diff --git a/X10D.Unity/src/GameObjectExtensions.cs b/X10D.Unity/src/GameObjectExtensions.cs
new file mode 100644
index 0000000..3341e87
--- /dev/null
+++ b/X10D.Unity/src/GameObjectExtensions.cs
@@ -0,0 +1,64 @@
+namespace X10D.Unity
+{
+ using System;
+ using JetBrains.Annotations;
+ using UnityEngine;
+
+ ///
+ /// Extension methods for .
+ ///
+ public static class GameObjectExtensions
+ {
+ ///
+ /// Rotates the component on the current such that is is facing another
+ /// .
+ ///
+ /// The current game object.
+ /// The target.
+ ///
+ /// is null
+ /// - or -
+ /// is null.
+ ///
+ public static void LookAt([NotNull] this GameObject gameObject, [NotNull] GameObject other)
+ {
+ if (gameObject is null)
+ {
+ throw new ArgumentNullException(nameof(gameObject));
+ }
+
+ if (other is null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ gameObject.LookAt(other.transform);
+ }
+
+ ///
+ /// Rotates the component on the current such that is is facing another
+ /// .
+ ///
+ /// The current game object.
+ /// The target.
+ ///
+ /// is null
+ /// - or -
+ /// is null.
+ ///
+ public static void LookAt([NotNull] this GameObject gameObject, [NotNull] Transform other)
+ {
+ if (gameObject is null)
+ {
+ throw new ArgumentNullException(nameof(gameObject));
+ }
+
+ if (other is null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ gameObject.transform.LookAt(other);
+ }
+ }
+}
diff --git a/X10D.Unity/src/GameTime.cs b/X10D.Unity/src/GameTime.cs
new file mode 100644
index 0000000..f59a6df
--- /dev/null
+++ b/X10D.Unity/src/GameTime.cs
@@ -0,0 +1,54 @@
+namespace X10D.Unity
+{
+ using System;
+ using UnityEngine;
+
+ ///
+ /// Represents a struct which contains game timing information.
+ ///
+ public readonly struct GameTime
+ {
+ private GameTime(float totalTime, float deltaTime, float fixedDeltaTime, int frameCount, float timeScale)
+ {
+ this.TotalTime = TimeSpan.FromSeconds(totalTime);
+ this.DeltaTime = TimeSpan.FromSeconds(deltaTime);
+ this.FixedDeltaTime = TimeSpan.FromSeconds(fixedDeltaTime);
+ this.FrameCount = frameCount;
+ this.TimeScale = timeScale;
+ }
+
+ ///
+ /// Gets the time since the last frame was rendered.
+ ///
+ public TimeSpan DeltaTime { get; }
+
+ ///
+ /// Gets the time since the last physics time step.
+ ///
+ public TimeSpan FixedDeltaTime { get; }
+
+ ///
+ /// Gets the total number of frames which have been rendered.
+ ///
+ public int FrameCount { get; }
+
+ ///
+ /// Gets the total time for which the game has been running.
+ ///
+ public TimeSpan TotalTime { get; }
+
+ ///
+ /// Gets the time scale.
+ ///
+ public float TimeScale { get; }
+
+ ///
+ /// Creates a new instance of the struct by creating a snapshot of values offered by .
+ ///
+ /// An instance of .
+ public static GameTime CreateFromCurrentTimes()
+ {
+ return new GameTime(Time.time, Time.deltaTime, Time.fixedDeltaTime, Time.frameCount, Time.timeScale);
+ }
+ }
+}
diff --git a/X10D.Unity/src/TransformExtensions.cs b/X10D.Unity/src/TransformExtensions.cs
new file mode 100644
index 0000000..6cdb7e3
--- /dev/null
+++ b/X10D.Unity/src/TransformExtensions.cs
@@ -0,0 +1,37 @@
+namespace X10D.Unity
+{
+ using System;
+ using JetBrains.Annotations;
+ using UnityEngine;
+
+ ///
+ /// Extension methods for .
+ ///
+ public static class TransformExtensions
+ {
+ ///
+ /// Rotates the current such that is is facing another .
+ ///
+ /// The current transform.
+ /// The target.
+ ///
+ /// is null
+ /// - or -
+ /// is null.
+ ///
+ public static void LookAt([NotNull] this Transform transform, [NotNull] GameObject other)
+ {
+ if (transform is null)
+ {
+ throw new ArgumentNullException(nameof(transform));
+ }
+
+ if (other is null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ transform.LookAt(other.transform);
+ }
+ }
+}
diff --git a/X10D.Unity/src/Vector3Extensions.cs b/X10D.Unity/src/Vector3Extensions.cs
new file mode 100644
index 0000000..3f9b1eb
--- /dev/null
+++ b/X10D.Unity/src/Vector3Extensions.cs
@@ -0,0 +1,111 @@
+namespace X10D.Unity
+{
+ using UnityEngine;
+
+ ///
+ /// Extension methods for .
+ ///
+ public static class Vector3Extensions
+ {
+ ///
+ /// Rounds a by calling on each of the components.
+ ///
+ /// The vector to round.
+ /// The nearest value.
+ /// rounded to the nearest .
+ public static Vector3 Round(this Vector3 vector, float nearest = 1)
+ {
+ return new Vector3(vector.x.Round(nearest), vector.y.Round(nearest), vector.z.Round(nearest));
+ }
+
+ ///
+ /// Returns a vector whose Y and Z components match that of a provided vector, and sets the X component to a provided value.
+ ///
+ /// The input vector.
+ /// The new X value.
+ ///
+ /// Returns a whose Y and Z components match that of ,
+ /// but with the component set to .
+ ///
+ public static Vector3 WithX(this Vector3 vector, float x)
+ {
+ return new Vector3(x, vector.y, vector.z);
+ }
+
+ ///
+ /// Returns a vector whose Z component matches that of a provided vector, and sets the X and Y components to provided values.
+ ///
+ /// The input vector.
+ /// The new X value.
+ /// The new Y value.
+ ///
+ /// Returns a whose Z component matches that of ,
+ /// but with the and components set to and
+ /// respectively.
+ ///
+ public static Vector3 WithXY(this Vector3 vector, float x, float y)
+ {
+ return new Vector3(x, y, vector.z);
+ }
+
+ ///
+ /// Returns a vector whose Y component matches that of a provided vector, and sets the X and Z components to provided values.
+ ///
+ /// The input vector.
+ /// The new X value.
+ /// The new Z value.
+ ///
+ /// Returns a whose Y component matches that of ,
+ /// but with the and components set to and
+ /// respectively.
+ ///
+ public static Vector3 WithXZ(this Vector3 vector, float x, float z)
+ {
+ return new Vector3(x, vector.y, z);
+ }
+
+ ///
+ /// Returns a vector whose X and Z components match that of a provided vector, and sets the Y component to a provided value.
+ ///
+ /// The input vector.
+ /// The new Y value.
+ ///
+ /// Returns a whose X and Z components match that of ,
+ /// but with the component set to .
+ ///
+ public static Vector3 WithY(this Vector3 vector, float y)
+ {
+ return new Vector3(vector.x, y, vector.z);
+ }
+
+ ///
+ /// Returns a vector whose X component matches that of a provided vector, and sets the Y and Z components to provided values.
+ ///
+ /// The input vector.
+ /// The new Y value.
+ /// The new Z value.
+ ///
+ /// Returns a whose X component matches that of ,
+ /// but with the and components set to and
+ /// respectively.
+ ///
+ public static Vector3 WithYZ(this Vector3 vector, float y, float z)
+ {
+ return new Vector3(vector.x, y, z);
+ }
+
+ ///
+ /// Returns a vector whose X and Y components match that of a provided vector, and sets the Z component to a provided value.
+ ///
+ /// The input vector.
+ /// The new Z value.
+ ///
+ /// Returns a whose X and Y components match that of ,
+ /// but with the component set to .
+ ///
+ public static Vector3 WithZ(this Vector3 vector, float z)
+ {
+ return new Vector3(vector.x, vector.y, z);
+ }
+ }
+}
diff --git a/X10D.sln b/X10D.sln
index 82dcd54..d20ed46 100644
--- a/X10D.sln
+++ b/X10D.sln
@@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X10D.Unity", "X10D.Unity\X10D.Unity.csproj", "{C21ABC58-68D6-4CA0-9CE6-A2E96C5E89AE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -26,6 +28,10 @@ Global
{DF228EA2-D8EC-4A40-8917-E1E62E3B7D8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF228EA2-D8EC-4A40-8917-E1E62E3B7D8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF228EA2-D8EC-4A40-8917-E1E62E3B7D8E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C21ABC58-68D6-4CA0-9CE6-A2E96C5E89AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C21ABC58-68D6-4CA0-9CE6-A2E96C5E89AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C21ABC58-68D6-4CA0-9CE6-A2E96C5E89AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C21ABC58-68D6-4CA0-9CE6-A2E96C5E89AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE