diff --git a/UOP1_Project/Assets/Prefabs/Characters/PigChef.prefab b/UOP1_Project/Assets/Prefabs/Characters/PigChef.prefab index 4dddf91ae..736e33b03 100644 --- a/UOP1_Project/Assets/Prefabs/Characters/PigChef.prefab +++ b/UOP1_Project/Assets/Prefabs/Characters/PigChef.prefab @@ -361,6 +361,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _inputReader: {fileID: 11400000, guid: 945ec0365077176418488737deed54be, type: 2} + _eventAggregator: {fileID: 11400000, guid: c92bfb91ff27b83459c86d7413b0081a, type: 2} gameplayCameraTransform: {fileID: 11400000, guid: bc205269957643647a8b5f98f028f9fb, type: 2} _openInventoryChannel: {fileID: 11400000, guid: 30f6db2122a30480b996908173e1c7d7, diff --git a/UOP1_Project/Assets/Prefabs/Gameplay/CameraSystem.prefab b/UOP1_Project/Assets/Prefabs/Gameplay/CameraSystem.prefab index 075b44269..4d5c12057 100644 --- a/UOP1_Project/Assets/Prefabs/Gameplay/CameraSystem.prefab +++ b/UOP1_Project/Assets/Prefabs/Gameplay/CameraSystem.prefab @@ -779,7 +779,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 085156cba0e34b540aeddafe12d1e2f1, type: 3} m_Name: m_EditorClassIdentifier: - inputReader: {fileID: 11400000, guid: 945ec0365077176418488737deed54be, type: 2} + eventBus: {fileID: 11400000, guid: b4774447b4f72b8408dbd98248f139ca, type: 2} mainCamera: {fileID: 8745341642014614481} freeLookVCam: {fileID: 8745341641394998849} _speedMultiplier: 0.5 diff --git a/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset b/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset new file mode 100644 index 000000000..ad6fa4c75 --- /dev/null +++ b/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c3683d4b74c443d4a48ca7b36086f246, type: 3} + m_Name: EventAggregatorSO + m_EditorClassIdentifier: diff --git a/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset.meta b/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset.meta new file mode 100644 index 000000000..62a0398ef --- /dev/null +++ b/UOP1_Project/Assets/ScriptableObjects/Events/EventAggregatorSO.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c92bfb91ff27b83459c86d7413b0081a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset b/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset new file mode 100644 index 000000000..ac6d4a328 --- /dev/null +++ b/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c44145970255443c86929d77c7ac2d2a, type: 3} + m_Name: UnityEventBusSO + m_EditorClassIdentifier: diff --git a/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset.meta b/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset.meta new file mode 100644 index 000000000..444a74652 --- /dev/null +++ b/UOP1_Project/Assets/ScriptableObjects/Events/UnityEventBusSO.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4774447b4f72b8408dbd98248f139ca +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UOP1_Project/Assets/Scripts/Camera/CameraManager.cs b/UOP1_Project/Assets/Scripts/Camera/CameraManager.cs index 11fcb332a..7bee2cdd5 100644 --- a/UOP1_Project/Assets/Scripts/Camera/CameraManager.cs +++ b/UOP1_Project/Assets/Scripts/Camera/CameraManager.cs @@ -5,13 +5,14 @@ public class CameraManager : MonoBehaviour { - public InputReader inputReader; + [SerializeField] private UnityEventBusSO eventBus; + public Camera mainCamera; public CinemachineFreeLook freeLookVCam; private bool _isRMBPressed; [SerializeField, Range(.5f, 3f)] - private float _speedMultiplier = 1f; //TODO: make this modifiable in the game settings + private float _speedMultiplier = 1f; //TODO: make this modifiable in the game settings [SerializeField] private TransformAnchor _cameraTransformAnchor = default; [Header("Listening on channels")] @@ -30,9 +31,9 @@ public void SetupProtagonistVirtualCamera(Transform target) private void OnEnable() { - inputReader.cameraMoveEvent += OnCameraMove; - inputReader.enableMouseControlCameraEvent += OnEnableMouseControlCamera; - inputReader.disableMouseControlCameraEvent += OnDisableMouseControlCamera; + eventBus.Subscribe(OnCameraMove); + eventBus.Subscribe(OnEnableMouseControlCamera); + eventBus.Subscribe(OnDisableMouseControlCamera); if (_frameObjectChannel != null) _frameObjectChannel.OnEventRaised += OnFrameObjectEvent; @@ -42,15 +43,15 @@ private void OnEnable() private void OnDisable() { - inputReader.cameraMoveEvent -= OnCameraMove; - inputReader.enableMouseControlCameraEvent -= OnEnableMouseControlCamera; - inputReader.disableMouseControlCameraEvent -= OnDisableMouseControlCamera; + eventBus.Unsubscribe(OnCameraMove); + eventBus.Unsubscribe(OnEnableMouseControlCamera); + eventBus.Unsubscribe(OnDisableMouseControlCamera); if (_frameObjectChannel != null) _frameObjectChannel.OnEventRaised -= OnFrameObjectEvent; } - private void OnEnableMouseControlCamera() + private void OnEnableMouseControlCamera(EnableMouseControlEvent evt) { _isRMBPressed = true; @@ -67,7 +68,7 @@ IEnumerator DisableMouseControlForFrame() _cameraMovementLock = false; } - private void OnDisableMouseControlCamera() + private void OnDisableMouseControlCamera(DisableMouseControlEvent evt) { _isRMBPressed = false; @@ -80,16 +81,16 @@ private void OnDisableMouseControlCamera() freeLookVCam.m_YAxis.m_InputAxisValue = 0; } - private void OnCameraMove(Vector2 cameraMovement, bool isDeviceMouse) + private void OnCameraMove(CameraMoveEvent moveEvent) { if (_cameraMovementLock) return; - if (isDeviceMouse && !_isRMBPressed) + if (moveEvent.IsDeviceMouse && !_isRMBPressed) return; - freeLookVCam.m_XAxis.m_InputAxisValue = cameraMovement.x * Time.deltaTime * _speedMultiplier; - freeLookVCam.m_YAxis.m_InputAxisValue = cameraMovement.y * Time.deltaTime * _speedMultiplier; + freeLookVCam.m_XAxis.m_InputAxisValue = moveEvent.Movement.x * Time.deltaTime * _speedMultiplier; + freeLookVCam.m_YAxis.m_InputAxisValue = moveEvent.Movement.y * Time.deltaTime * _speedMultiplier; } private void OnFrameObjectEvent(Transform value) diff --git a/UOP1_Project/Assets/Scripts/Characters/Protagonist.cs b/UOP1_Project/Assets/Scripts/Characters/Protagonist.cs index 9bb715f5e..6cbf54b8e 100644 --- a/UOP1_Project/Assets/Scripts/Characters/Protagonist.cs +++ b/UOP1_Project/Assets/Scripts/Characters/Protagonist.cs @@ -1,12 +1,21 @@ using System; using UnityEngine; +using static EventAggregator; /// /// This component consumes input on the InputReader and stores its values. The input is then read, and manipulated, by the StateMachines's Actions. /// -public class Protagonist : MonoBehaviour +public class Protagonist : MonoBehaviour, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle { - [SerializeField] private InputReader _inputReader = default; + [SerializeField] private EventAggregatorSO _eventAggregator; + public TransformAnchor gameplayCameraTransform; [SerializeField] private VoidEventChannelSO _openInventoryChannel = default; @@ -38,27 +47,13 @@ private void OnControllerColliderHit(ControllerColliderHit hit) //Adds listeners for events being triggered in the InputReader script private void OnEnable() { - _inputReader.jumpEvent += OnJumpInitiated; - _inputReader.jumpCanceledEvent += OnJumpCanceled; - _inputReader.moveEvent += OnMove; - _inputReader.openInventoryEvent += OnOpenInventory; - _inputReader.startedRunning += OnStartedRunning; - _inputReader.stoppedRunning += OnStoppedRunning; - _inputReader.attackEvent += OnStartedAttack; - //... + _eventAggregator.Subscribe(this); } //Removes all listeners to the events coming from the InputReader script private void OnDisable() { - _inputReader.jumpEvent -= OnJumpInitiated; - _inputReader.jumpCanceledEvent -= OnJumpCanceled; - _inputReader.moveEvent -= OnMove; - _inputReader.openInventoryEvent -= OnOpenInventory; - _inputReader.startedRunning -= OnStartedRunning; - _inputReader.stoppedRunning -= OnStoppedRunning; - _inputReader.attackEvent -= OnStartedAttack; - //... + _eventAggregator.Unsubscribe(this); } private void Update() @@ -107,37 +102,42 @@ private void RecalculateMovement() _previousSpeed = targetSpeed; } - //---- EVENT LISTENERS ---- + // Triggered from Animation Event + public void ConsumeAttackInput() => attackInput = false; - private void OnMove(Vector2 movement) + public void Handle(AttackEvent msg) { - _inputVector = movement; + attackInput = true; } - private void OnJumpInitiated() + public void Handle(JumpEvent msg) { jumpInput = true; } - private void OnJumpCanceled() + public void Handle(JumpCancelledEvent msg) { jumpInput = false; } - private void OnStoppedRunning() => isRunning = false; - - private void OnStartedRunning() => isRunning = true; - - private void OnOpenInventory() + public void Handle(MoveEvent msg) { - _openInventoryChannel.RaiseEvent(); - - + _inputVector = msg.Movement; } + public void Handle(StartedRunningEvent msg) + { + isRunning = true; + } - private void OnStartedAttack() => attackInput = true; + public void Handle(StoppedRunningEvent msg) + { + isRunning = false; + } - // Triggered from Animation Event - public void ConsumeAttackInput() => attackInput = false; + public void Handle(OpenInventoryEvent msg) + { // Clearly this event should be handled elsewhere and not on the + // protagonist but for now limited changes. + _openInventoryChannel.RaiseEvent(); + } } diff --git a/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs b/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs new file mode 100644 index 000000000..7d905289d --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + + +/// +/// Enables loose coupling of events from publishers to subscribers +/// +/// inspired from https://github.com/Caliburn-Micro/Caliburn.Micro/blob/master/src/Caliburn.Micro.Core/IEventAggregator.cs +/// and also https://github.com/google/guava/blob/master/guava/src/com/google/common/eventbus/EventBus.java +/// +/// Runs within single threaded unity environment so no need to lock any structures. +/// There is no current requirement to dispatch or consume events asynchronously and sync with unity main thread. +/// +public interface IEventAggregator +{ + void Subscribe(object observer); + + void Unsubscribe(object observer); + + void Publish(object msg); +} + +public class EventAggregator : IEventAggregator +{ + private readonly Dictionary> _handlers = new Dictionary>(); + private readonly Dictionary _subByObserver = new Dictionary(); + private readonly object[] _paramHolder = {null}; + + public void Subscribe(object observer) + { + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + if (_subByObserver.ContainsKey(observer)) + { + return; + } + + var subs = new Subscriptions(); + + var interfaces = observer.GetType().GetTypeInfo().ImplementedInterfaces + .Where(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IHandle<>)); + + foreach (var @interface in interfaces) + { + var type = @interface.GetTypeInfo().GenericTypeArguments[0]; + var method = @interface.GetRuntimeMethod("Handle", new[] {type}); + + if (method != null) + { + var handler = new Handler(method, observer); + + if (!_handlers.TryGetValue(type, out var handlers)) + { + handlers = new HashSet(); + _handlers[type] = handlers; + } + + handlers.Add(handler); + subs.Interested[type] = handler; + } + } + + if (subs.Interested.Count > 0) + { + _subByObserver[observer] = subs; + } + } + + public void Unsubscribe(object observer) + { + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + + if (_subByObserver.TryGetValue(observer, out var subscriptions)) + { + _subByObserver.Remove(observer); + + foreach (var entry in subscriptions.Interested) + { + if (_handlers.TryGetValue(entry.Key, out var handlers)) + { + handlers.Remove(entry.Value); + } + } + } + } + + public void Publish(object msg) + { + if (msg == null) + { + throw new ArgumentNullException(nameof(msg)); + } + + if (_handlers.TryGetValue(msg.GetType(), out var handlers)) + { + _paramHolder[0] = msg; + + foreach (var handler in handlers) + { + handler.Notify(_paramHolder); + } + } + } + + private class Subscriptions + { + private readonly Dictionary _interested = new Dictionary(); + + public Dictionary Interested => _interested; + } + + private class Handler + { + private readonly MethodInfo _method; + private readonly object _observer; + + public Handler(MethodInfo method, object observer) + { + _method = method; + _observer = observer; + } + + public void Notify(object[] param) + { + _method.Invoke(_observer, param); + } + } + + public interface IHandle + { + void Handle(T msg); + } +} diff --git a/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs.meta b/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs.meta new file mode 100644 index 000000000..6bd88b182 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/EventAggregator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa8b4461b92ba7c4a86bd2a8f0f89d5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UOP1_Project/Assets/Scripts/Events/Input.meta b/UOP1_Project/Assets/Scripts/Events/Input.meta new file mode 100644 index 000000000..0bc3f020b --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc1d749a583a49548d472953a83cdefc +timeCreated: 1620200166 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs new file mode 100644 index 000000000..c60cc4870 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs @@ -0,0 +1,4 @@ +public class AttackCancelledEvent +{ + public static readonly AttackCancelledEvent Event = new AttackCancelledEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs.meta new file mode 100644 index 000000000..67ea8ae03 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/AttackCancelledEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f40b4b3b23e4ea287f06b483995f27a +timeCreated: 1620200355 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs new file mode 100644 index 000000000..cc41e482f --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs @@ -0,0 +1,4 @@ +public class AttackEvent +{ + public static readonly AttackEvent Event = new AttackEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs.meta new file mode 100644 index 000000000..199dcac38 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/AttackEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9c6d9f27a8cb4aefad8e71c45ac9f57f +timeCreated: 1620200178 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs new file mode 100644 index 000000000..f0c3e8bf8 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs @@ -0,0 +1,7 @@ +using UnityEngine; + +public class CameraMoveEvent +{ + public Vector2 Movement { get; set; } + public bool IsDeviceMouse { get; set; } +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs.meta new file mode 100644 index 000000000..0d9aa7aa8 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/CameraMoveEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 09da76db91954356a35ef2e359f00813 +timeCreated: 1620210604 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs new file mode 100644 index 000000000..059f07fe2 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs @@ -0,0 +1,4 @@ +public class DisableMouseControlEvent +{ + public static readonly DisableMouseControlEvent Event = new DisableMouseControlEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs.meta new file mode 100644 index 000000000..3b970b52b --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/DisableMouseControlEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a0ce839adb8f4d57a335a10105cef5d3 +timeCreated: 1620211722 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs new file mode 100644 index 000000000..6ff3f7ea2 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs @@ -0,0 +1,4 @@ +public class EnableMouseControlEvent +{ + public static readonly EnableMouseControlEvent Event = new EnableMouseControlEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs.meta new file mode 100644 index 000000000..5c3e44454 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/EnableMouseControlEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0559f4ea2ee34ba997d7bc29f8e55f06 +timeCreated: 1620211696 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs new file mode 100644 index 000000000..7041346a3 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs @@ -0,0 +1,4 @@ +public class JumpCancelledEvent +{ + public static readonly JumpCancelledEvent Event = new JumpCancelledEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs.meta new file mode 100644 index 000000000..a3952a118 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/JumpCancelledEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f07eb2adec604dd98b11900b0907f9e9 +timeCreated: 1620202960 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs new file mode 100644 index 000000000..e52eaafa3 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs @@ -0,0 +1,4 @@ +public class JumpEvent +{ + public static readonly JumpEvent Event = new JumpEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs.meta new file mode 100644 index 000000000..c316c674c --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/JumpEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 84d9f4250722426fa7bc2c12149efd65 +timeCreated: 1620202922 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs new file mode 100644 index 000000000..d6843c36a --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs @@ -0,0 +1,6 @@ +using UnityEngine; + +public class MoveEvent +{ + public Vector2 Movement { get; set; } +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs.meta new file mode 100644 index 000000000..04bfa8b68 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/MoveEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 47eee1f57b5045e1b4274885fd027656 +timeCreated: 1620203505 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs new file mode 100644 index 000000000..dacacc741 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs @@ -0,0 +1,4 @@ +public class OpenInventoryEvent +{ + public static readonly OpenInventoryEvent Event = new OpenInventoryEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs.meta new file mode 100644 index 000000000..ba58c62fd --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/OpenInventoryEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d62d887de6d44e0f8b08d6b81c007969 +timeCreated: 1620204438 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs new file mode 100644 index 000000000..c5526081c --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs @@ -0,0 +1,4 @@ +public class StartedRunningEvent +{ + public static readonly StartedRunningEvent Event = new StartedRunningEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs.meta new file mode 100644 index 000000000..e9cc393f5 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/StartedRunningEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6f3c3f18caf845d2b79a6169d2119ba9 +timeCreated: 1620203920 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs b/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs new file mode 100644 index 000000000..16cf8a6ab --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs @@ -0,0 +1,4 @@ +public class StoppedRunningEvent +{ + public static readonly StoppedRunningEvent Event = new StoppedRunningEvent(); +} diff --git a/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs.meta b/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs.meta new file mode 100644 index 000000000..bbe7302b6 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/Input/StoppedRunningEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 676f9a7bbbb247079ddf7fe7ae2e493a +timeCreated: 1620203958 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs new file mode 100644 index 000000000..a288d0b2d --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs @@ -0,0 +1,22 @@ +using UnityEngine; + +[CreateAssetMenu(fileName = "EventAggregatorSO", menuName = "Events/Event Aggregator")] +public class EventAggregatorSO : ScriptableObject, IEventAggregator +{ + private readonly EventAggregator _eventAggregator = new EventAggregator(); + + public void Subscribe(object observer) + { + _eventAggregator.Subscribe(observer); + } + + public void Unsubscribe(object observer) + { + _eventAggregator.Unsubscribe(observer); + } + + public void Publish(object msg) + { + _eventAggregator.Publish(msg); + } +} diff --git a/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs.meta b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs.meta new file mode 100644 index 000000000..809dcf997 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/EventAggregatorSO.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3683d4b74c443d4a48ca7b36086f246 +timeCreated: 1620199406 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs new file mode 100644 index 000000000..672ae223e --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs @@ -0,0 +1,23 @@ +using UnityEngine; +using UnityEngine.Events; + +[CreateAssetMenu(fileName = "UnityEventBusSO", menuName = "Events/Unity Event Bus")] +public class UnityEventBusSO : ScriptableObject, IUnityEventBus +{ + private readonly UnityEventBus _unityEventBus = new UnityEventBus(); + + public void Subscribe(UnityAction action) + { + _unityEventBus.Subscribe(action); + } + + public void Unsubscribe(UnityAction action) + { + _unityEventBus.Unsubscribe(action); + } + + public void Publish(T msg) + { + _unityEventBus.Publish(msg); + } +} diff --git a/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs.meta b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs.meta new file mode 100644 index 000000000..d46db36a2 --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/ScriptableObjects/UnityEventBusSO.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c44145970255443c86929d77c7ac2d2a +timeCreated: 1620210159 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs b/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs new file mode 100644 index 000000000..04452357c --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.Events; + +/// +/// Enables loose coupling of events from publishers to subscribers +/// +/// A UnityAction based subscription where the T is event type used to find +/// a suitable UnityAction. +/// +/// Note the code is typesafe although somewhat ugly with HashSet holder. +/// +/// +public interface IUnityEventBus +{ + public void Subscribe(UnityAction action); + + public void Unsubscribe(UnityAction action); + + public void Publish(T msg); +} + +public class UnityEventBus : IUnityEventBus +{ + private readonly Dictionary> _handlers = new Dictionary>(); + + public void Subscribe(UnityAction action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var type = typeof(T); + + if (!_handlers.TryGetValue(type, out var handlers)) + { + handlers = new HashSet(); + _handlers[type] = handlers; + } + + handlers.Add(action); + } + + public void Unsubscribe(UnityAction action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + var type = typeof(T); + + if (_handlers.TryGetValue(type, out var handlers)) + { + handlers.Remove(action); + } + } + + public void Publish(T msg) + { + if (msg == null) + { + throw new ArgumentNullException(nameof(msg)); + } + + if (_handlers.TryGetValue(msg.GetType(), out var handlers)) + { + foreach (var handler in handlers.Cast>()) + { + handler.Invoke(msg); + } + } + } +} diff --git a/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs.meta b/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs.meta new file mode 100644 index 000000000..0116944aa --- /dev/null +++ b/UOP1_Project/Assets/Scripts/Events/UnityEventBus.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8373a96aa4c4bb1825d986bd9f75aa2 +timeCreated: 1620205812 \ No newline at end of file diff --git a/UOP1_Project/Assets/Scripts/Input/InputReader.cs b/UOP1_Project/Assets/Scripts/Input/InputReader.cs index 4244c4c26..bc440f1f9 100644 --- a/UOP1_Project/Assets/Scripts/Input/InputReader.cs +++ b/UOP1_Project/Assets/Scripts/Input/InputReader.cs @@ -1,29 +1,26 @@ -using UnityEngine; +using System; +using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.Events; [CreateAssetMenu(fileName = "InputReader", menuName = "Game/Input Reader")] public class InputReader : ScriptableObject, GameInput.IGameplayActions, GameInput.IDialoguesActions, GameInput.IMenusActions { + [SerializeField] private EventAggregatorSO eventAggregator; + + [SerializeField] private UnityEventBusSO eventBus; + + private readonly MoveEvent _moveEvent = new MoveEvent(); + private readonly CameraMoveEvent _cameraMoveEvent = new CameraMoveEvent(); + // Assign delegate{} to events to initialise them with an empty delegate // so we can skip the null check when we use them // Gameplay - public event UnityAction jumpEvent = delegate { }; - public event UnityAction jumpCanceledEvent = delegate { }; - public event UnityAction attackEvent = delegate { }; - public event UnityAction attackCanceledEvent = delegate { }; public event UnityAction interactEvent = delegate { }; // Used to talk, pickup objects, interact with tools like the cooking cauldron - public event UnityAction openInventoryEvent = delegate { }; // Used to bring up the inventory public event UnityAction closeInventoryEvent = delegate { };// Used to bring up the inventory public event UnityAction inventoryActionButtonEvent = delegate { }; public event UnityAction pauseEvent = delegate { }; - public event UnityAction moveEvent = delegate { }; - public event UnityAction cameraMoveEvent = delegate { }; - public event UnityAction enableMouseControlCameraEvent = delegate { }; - public event UnityAction disableMouseControlCameraEvent = delegate { }; - public event UnityAction startedRunning = delegate { }; - public event UnityAction stoppedRunning = delegate { }; // Shared between menus and dialogues public event UnityAction moveSelectionEvent = delegate { }; @@ -65,10 +62,10 @@ public void OnAttack(InputAction.CallbackContext context) switch (context.phase) { case InputActionPhase.Performed: - attackEvent.Invoke(); + eventAggregator.Publish(AttackEvent.Event); break; case InputActionPhase.Canceled: - attackCanceledEvent.Invoke(); + eventAggregator.Publish(AttackCancelledEvent.Event); break; } } @@ -76,7 +73,7 @@ public void OnAttack(InputAction.CallbackContext context) public void OnOpenInventory(InputAction.CallbackContext context) { if (context.phase == InputActionPhase.Performed) - openInventoryEvent.Invoke(); + eventAggregator.Publish(OpenInventoryEvent.Event); } public void OnCancel(InputAction.CallbackContext context) { @@ -100,16 +97,21 @@ public void OnInteract(InputAction.CallbackContext context) public void OnJump(InputAction.CallbackContext context) { - if (context.phase == InputActionPhase.Performed) - jumpEvent.Invoke(); - - if (context.phase == InputActionPhase.Canceled) - jumpCanceledEvent.Invoke(); + switch (context.phase) + { + case InputActionPhase.Performed: + eventAggregator.Publish(JumpEvent.Event); + break; + case InputActionPhase.Canceled: + eventAggregator.Publish(JumpCancelledEvent.Event); + break; + } } public void OnMove(InputAction.CallbackContext context) { - moveEvent.Invoke(context.ReadValue()); + _moveEvent.Movement = context.ReadValue(); + eventAggregator.Publish(_moveEvent); } public void OnRun(InputAction.CallbackContext context) @@ -117,10 +119,10 @@ public void OnRun(InputAction.CallbackContext context) switch (context.phase) { case InputActionPhase.Performed: - startedRunning.Invoke(); + eventAggregator.Publish(StartedRunningEvent.Event); break; case InputActionPhase.Canceled: - stoppedRunning.Invoke(); + eventAggregator.Publish(StoppedRunningEvent.Event); break; } } @@ -133,16 +135,22 @@ public void OnPause(InputAction.CallbackContext context) public void OnRotateCamera(InputAction.CallbackContext context) { - cameraMoveEvent.Invoke(context.ReadValue(), IsDeviceMouse(context)); + _cameraMoveEvent.Movement = context.ReadValue(); + _cameraMoveEvent.IsDeviceMouse = IsDeviceMouse(context); + eventBus.Publish(_cameraMoveEvent); } public void OnMouseControlCamera(InputAction.CallbackContext context) { - if (context.phase == InputActionPhase.Performed) - enableMouseControlCameraEvent.Invoke(); - - if (context.phase == InputActionPhase.Canceled) - disableMouseControlCameraEvent.Invoke(); + switch (context.phase) + { + case InputActionPhase.Performed: + eventBus.Publish(EnableMouseControlEvent.Event); + break; + case InputActionPhase.Canceled: + eventBus.Publish(DisableMouseControlEvent.Event); + break; + } } private bool IsDeviceMouse(InputAction.CallbackContext context) => context.control.device.name == "Mouse"; @@ -216,7 +224,7 @@ public void DisableAllInput() public void OnChangeTab(InputAction.CallbackContext context) { if (context.phase == InputActionPhase.Performed) - menuSwitchTab.Invoke(context.ReadValue()); + menuSwitchTab.Invoke(context.ReadValue()); }