diff --git a/Runtime/Cast/Event.meta b/Runtime/Cast/Event.meta new file mode 100644 index 00000000..ff7f0028 --- /dev/null +++ b/Runtime/Cast/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d682a641a14eb0498704d848f9f27dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Cast/Event/Proxy.meta b/Runtime/Cast/Event/Proxy.meta new file mode 100644 index 00000000..4c3d9f87 --- /dev/null +++ b/Runtime/Cast/Event/Proxy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 09f268040a7b0cf46827a884dd279303 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs b/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs new file mode 100644 index 00000000..9ab022f2 --- /dev/null +++ b/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs @@ -0,0 +1,73 @@ +namespace Zinnia.Cast.Event.Proxy +{ + using System; + using UnityEngine; + using UnityEngine.Events; + using Zinnia.Event.Proxy; + using Zinnia.Extension; + + /// + /// Emits a with a payload whenever is called. + /// + public class PointsCastEventProxyEmitter : RestrictableSingleEventProxyEmitter + { + /// + /// The types of that can be used for the rule source. + /// + public enum RuleSourceType + { + /// + /// Use the actual hit as the source for the rule. + /// + Collider, + /// + /// Use the parent hit as the target for the rule. + /// + Rigidbody + } + + [Tooltip("The source GameObject to apply to the RestrictableSingleEventProxyEmitter.ReceiveValidity.")] + [SerializeField] + private RuleSourceType ruleSource; + /// + /// The source to apply to the . + /// + public RuleSourceType RuleSource + { + get + { + return ruleSource; + } + set + { + ruleSource = value; + } + } + + /// + /// Defines the event with the specified state. + /// + [Serializable] + public class UnityEvent : UnityEvent { } + + /// + protected override object GetTargetToCheck() + { + if (Payload == null || Payload.HitData == null) + { + return null; + } + + RaycastHit hitData = (RaycastHit)Payload.HitData; + switch (RuleSource) + { + case RuleSourceType.Collider: + return hitData.collider.gameObject; + case RuleSourceType.Rigidbody: + return hitData.collider.GetContainingTransform().gameObject; + } + + return null; + } + } +} \ No newline at end of file diff --git a/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs.meta b/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs.meta new file mode 100644 index 00000000..557d20e4 --- /dev/null +++ b/Runtime/Cast/Event/Proxy/PointsCastEventProxyEmitter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb9ca9e2bfd53be4c8822920af53c734 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Cast/Event.meta b/Tests/Editor/Cast/Event.meta new file mode 100644 index 00000000..d428f585 --- /dev/null +++ b/Tests/Editor/Cast/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cdd13003b42bd2f4e8519d6a1c552eda +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Cast/Event/Proxy.meta b/Tests/Editor/Cast/Event/Proxy.meta new file mode 100644 index 00000000..bcbe737f --- /dev/null +++ b/Tests/Editor/Cast/Event/Proxy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a72c05e97cc1f33418c3272fa4d6b350 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs b/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs new file mode 100644 index 00000000..25874a49 --- /dev/null +++ b/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs @@ -0,0 +1,181 @@ +using Zinnia.Cast; +using Zinnia.Cast.Event.Proxy; +using Zinnia.Data.Collection.List; +using Zinnia.Rule; + +namespace Test.Zinnia.Cast.Event.Proxy +{ + using NUnit.Framework; + using Test.Zinnia.Utility.Helper; + using Test.Zinnia.Utility.Mock; + using UnityEngine; + + public class PointsCastEventProxyEmitterTest + { + private GameObject containingObject; + private PointsCastEventProxyEmitter subject; + + [SetUp] + public void SetUp() + { + containingObject = new GameObject("PointsCastEventProxyEmitterTest"); + subject = containingObject.AddComponent(); + } + + [TearDown] + public void TearDown() + { + Object.DestroyImmediate(containingObject); + } + + [Test] + public void Receive() + { + UnityEventListenerMock emittedMock = new UnityEventListenerMock(); + subject.Emitted.AddListener(emittedMock.Listen); + + PointsCast.EventData data = new PointsCast.EventData(); + data.HitData = RaycastHitHelper.GetRaycastHit(); + data.IsValid = true; + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Receive(data); + + Assert.AreEqual(data, subject.Payload); + Assert.IsTrue(emittedMock.Received); + } + + [Test] + public void ReceiveWithRuleRestrictions() + { + UnityEventListenerMock emittedMock = new UnityEventListenerMock(); + subject.Emitted.AddListener(emittedMock.Listen); + + GameObject validBlockerParent = new GameObject("PointsCastEventProxyEmitterTest_validParent"); + validBlockerParent.AddComponent().isKinematic = true; + GameObject validBlockerChild = GameObject.CreatePrimitive(PrimitiveType.Cube); + validBlockerChild.name = "PointsCastEventProxyEmitterTest_validChild"; + validBlockerChild.transform.SetParent(validBlockerParent.transform); + validBlockerParent.transform.position = Vector3.left + Vector3.forward; + + GameObject invalidBlockerParent = new GameObject("PointsCastEventProxyEmitterTest_invalidParent"); + invalidBlockerParent.AddComponent().isKinematic = true; + GameObject invalidBlockerChild = GameObject.CreatePrimitive(PrimitiveType.Cube); + invalidBlockerChild.name = "PointsCastEventProxyEmitterTest_invalidChild"; + invalidBlockerChild.transform.SetParent(invalidBlockerParent.transform); + invalidBlockerParent.transform.position = Vector3.right + Vector3.forward; + + ListContainsRule rule = subject.gameObject.AddComponent(); + UnityObjectObservableList objects = containingObject.AddComponent(); + rule.Objects = objects; + + objects.Add(validBlockerChild); + subject.ReceiveValidity = new RuleContainer + { + Interface = rule + }; + + subject.RuleSource = PointsCastEventProxyEmitter.RuleSourceType.Collider; + + PointsCast.EventData data = new PointsCast.EventData(); + data.HitData = RaycastHitHelper.GetRaycastHit(validBlockerParent, false, Vector3.left, Vector3.forward); + data.IsValid = true; + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Receive(data); + + Assert.AreEqual(data, subject.Payload); + Assert.IsTrue(emittedMock.Received); + + subject.Payload = null; + emittedMock.Reset(); + + subject.RuleSource = PointsCastEventProxyEmitter.RuleSourceType.Rigidbody; + + subject.Receive(data); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Payload = null; + emittedMock.Reset(); + + objects.Add(validBlockerParent); + + subject.Receive(data); + + Assert.AreEqual(data, subject.Payload); + Assert.IsTrue(emittedMock.Received); + + subject.Payload = null; + emittedMock.Reset(); + + subject.RuleSource = PointsCastEventProxyEmitter.RuleSourceType.Collider; + + data.HitData = RaycastHitHelper.GetRaycastHit(invalidBlockerParent, false, Vector3.right, Vector3.forward); + data.IsValid = true; + + subject.Receive(data); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Payload = null; + emittedMock.Reset(); + + subject.RuleSource = PointsCastEventProxyEmitter.RuleSourceType.Rigidbody; + + subject.Receive(data); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + Object.DestroyImmediate(validBlockerParent); + Object.DestroyImmediate(invalidBlockerParent); + } + + [Test] + public void ReceiveInactiveGameObject() + { + UnityEventListenerMock emittedMock = new UnityEventListenerMock(); + subject.Emitted.AddListener(emittedMock.Listen); + PointsCast.EventData data = new PointsCast.EventData(); + data.HitData = RaycastHitHelper.GetRaycastHit(); + data.IsValid = true; + + subject.gameObject.SetActive(false); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Receive(data); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + } + + [Test] + public void ReceiveInactiveComponent() + { + UnityEventListenerMock emittedMock = new UnityEventListenerMock(); + subject.Emitted.AddListener(emittedMock.Listen); + PointsCast.EventData data = new PointsCast.EventData(); + data.HitData = RaycastHitHelper.GetRaycastHit(); + data.IsValid = true; + + subject.enabled = false; + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + + subject.Receive(data); + + Assert.IsNull(subject.Payload); + Assert.IsFalse(emittedMock.Received); + } + } +} \ No newline at end of file diff --git a/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs.meta b/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs.meta new file mode 100644 index 00000000..6a5dcba8 --- /dev/null +++ b/Tests/Editor/Cast/Event/Proxy/PointsCastEventProxyEmitterTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64c8628533928b14bb31164a1f676771 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: