From 59fa0a60aa4d191f23b7bcf466b5f512a4004b85 Mon Sep 17 00:00:00 2001 From: Nils Brinkmann Date: Sun, 17 Sep 2017 20:42:00 +0200 Subject: [PATCH 1/5] Added: Package-management using NPM (see https://github.com/shadowmint/unity-package-template/blob/master/docs/npm.md) --- README.md | 22 ++++++++++++++++++++++ npm/postinstall.js | 23 +++++++++++++++++++++++ package.json | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 npm/postinstall.js create mode 100644 package.json diff --git a/README.md b/README.md index d722a68..3c00c95 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,28 @@ HomeBrew ```brew install mono``` cp build/SDD.EventManager.dll ~/your_unity_project/Assets/Lib/ +### Option 3: Installation via NPM + +To install via NPM the following is needed: +* Install the [Node.js package manager](https://nodejs.org/en/download/) +* In your Unity-project run `npm install --save unity3d.eventmanager` +* Extend your `.gitignore` (or whatever else you're using) + * Add `node_modules` + * Add `package-lock.json` + * Add `Assets/packages/` + * Add `Assets/packages.meta` + +You can also add a `package.json` to your Unity project that contains the following content: +``` +{ + "dependencies": { + "unity3d.eventmanager": "1.0.2" + } +} +``` +This way you can define what dependencies you need in a reproducible way. + + Testing ------- diff --git a/npm/postinstall.js b/npm/postinstall.js new file mode 100644 index 0000000..5dd3faf --- /dev/null +++ b/npm/postinstall.js @@ -0,0 +1,23 @@ +var mkdirp = require('mkdirp'); +var path = require('path'); +var ncp = require('ncp'); + +// Paths +var srcDir = path.join(__dirname, '..', 'Assets', 'EventManager', 'Source'); +var targetDir = path.join(__dirname, '..', '..', '..', 'Assets', 'packages', 'EventManager'); + +// Create folder if missing +mkdirp(targetDir, function (err) { + if (err) { + console.error(err) + process.exit(1); + } + + // Copy files + ncp(srcDir, targetDir, function (err) { + if (err) { + console.error(err); + process.exit(1); + } + }); +}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8caa741 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "unity3d.eventmanager", + "version": "1.0.2", + "description": "A type-safe event system for Unity3D based on the event listener pattern. More info http://saltydog.digital/usage-pattern-for-a-type-safe-unity-event-system/", + "main": "npm/index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/robertwahler/EventManager.git" + }, + "keywords": [ + "unity3d", + "eventsystem", + "events" + ], + "author": "Robert Wahler", + "license": "MIT", + "bugs": { + "url": "https://github.com/robertwahler/EventManager/issues" + }, + "homepage": "https://github.com/robertwahler/EventManager#readme", + + "files": [ + "npm/postinstall.js", + "Assets/EventManager/Source/Event.cs", + "Assets/EventManager/Source/EventManager.cs", + "Assets/EventManager/Source/IEventHandler.cs" + ], + "scripts": { + "postinstall": "node npm/postinstall.js" + }, + "dependencies": { + "ncp": "^2.0.0", + "mkdirp": "^0.5.1" + } +} From 78d16044832e0c9f7742078fc625386f15e07317 Mon Sep 17 00:00:00 2001 From: Nils Brinkmann Date: Fri, 16 Feb 2018 22:15:28 +0100 Subject: [PATCH 2/5] Added: First raw implementation of a Node-Graph showing which events got sent where --- Assets/EventManager/Source/Event.cs.meta | 5 +- Assets/EventManager/Source/EventManager.cs | 247 +++++++++++------- .../EventManager/Source/EventManager.cs.meta | 5 +- Assets/EventManager/Source/EventNodeview.cs | 86 ++++++ .../EventManager/Source/EventNodeview.cs.meta | 13 + .../EventManager/Source/IEventHandler.cs.meta | 5 +- 6 files changed, 261 insertions(+), 100 deletions(-) create mode 100644 Assets/EventManager/Source/EventNodeview.cs create mode 100644 Assets/EventManager/Source/EventNodeview.cs.meta diff --git a/Assets/EventManager/Source/Event.cs.meta b/Assets/EventManager/Source/Event.cs.meta index 5062693..fd592c8 100644 --- a/Assets/EventManager/Source/Event.cs.meta +++ b/Assets/EventManager/Source/Event.cs.meta @@ -1,8 +1,9 @@ fileFormatVersion: 2 -guid: 31025e63368c145d89224258a7e0f6d7 -timeCreated: 1455836011 +guid: 9990916267d7d03478fcc638ec84d163 +timeCreated: 1518809105 licenseType: Free MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Assets/EventManager/Source/EventManager.cs b/Assets/EventManager/Source/EventManager.cs index f748fdf..8ba5630 100644 --- a/Assets/EventManager/Source/EventManager.cs +++ b/Assets/EventManager/Source/EventManager.cs @@ -1,109 +1,168 @@ +using System; using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; -namespace SDD.Events { - - /// - /// Event Manager manages publishing raised events to subscribing/listening classes. - /// - /// @example subscribe - /// EventManager.Instance.AddListener(OnSomethingHappened); - /// - /// @example unsubscribe - /// EventManager.Instance.RemoveListener(OnSomethingHappened); - /// - /// @example publish an event - /// EventManager.Instance.Raise(new SomethingHappenedEvent()); - /// - /// This class is a minor variation on - /// - public class EventManager { - - public static EventManager Instance { - get { - if (instance == null) { - instance = new EventManager(); - } - - return instance; - } - } - private static EventManager instance = null; - - public delegate void EventDelegate(T e) where T : Event; - private delegate void EventDelegate(Event e); +namespace SDD.Events +{ /// - /// The actual delegate, there is one delegate per unique event. Each - /// delegate has multiple invocation list items. + /// Event Manager manages publishing raised events to subscribing/listening classes. + /// + /// @example subscribe + /// EventManager.Instance.AddListener(OnSomethingHappened); + /// + /// @example unsubscribe + /// EventManager.Instance.RemoveListener(OnSomethingHappened); + /// + /// @example publish an event + /// EventManager.Instance.Raise(new SomethingHappenedEvent()); + /// + /// This class is a minor variation on /// - private Dictionary delegates = new Dictionary(); + public class EventManager + { + + public static EventManager Instance + { + get + { + if (instance == null) + { + instance = new EventManager(); + } + + return instance; + } + } + private static EventManager instance = null; + + public delegate void EventDelegate(T e) where T : Event; + private delegate void EventDelegate(Event e); + + /// + /// The actual delegate, there is one delegate per unique event. Each + /// delegate has multiple invocation list items. + /// + private Dictionary delegates = new Dictionary(); + + /// + /// Lookups only, there is one delegate lookup per listener + /// + private Dictionary delegateLookup = new Dictionary(); + + /// + /// Add the delegate. + /// + public void AddListener(EventDelegate del) where T : Event + { + + if (delegateLookup.ContainsKey(del)) + { + return; + } + + LogListener(del); + + // Create a new non-generic delegate which calls our generic one. This + // is the delegate we actually invoke. + EventDelegate internalDelegate = (e) => del((T)e); + delegateLookup[del] = internalDelegate; + + EventDelegate tempDel; + if (delegates.TryGetValue(typeof(T), out tempDel)) + { + delegates[typeof(T)] = tempDel += internalDelegate; + } + else + { + delegates[typeof(T)] = internalDelegate; + } + } - /// - /// Lookups only, there is one delegate lookup per listener - /// - private Dictionary delegateLookup = new Dictionary(); + /// + /// Remove the delegate. Can be called multiple times on same delegate. + /// + public void RemoveListener(EventDelegate del) where T : Event + { + + EventDelegate internalDelegate; + if (delegateLookup.TryGetValue(del, out internalDelegate)) + { + EventDelegate tempDel; + if (delegates.TryGetValue(typeof(T), out tempDel)) + { + tempDel -= internalDelegate; + if (tempDel == null) + { + delegates.Remove(typeof(T)); + } + else + { + delegates[typeof(T)] = tempDel; + } + } + + delegateLookup.Remove(del); + } + } - /// - /// Add the delegate. - /// - public void AddListener(EventDelegate del) where T : Event { - - if (delegateLookup.ContainsKey(del)) { - return; - } - - // Create a new non-generic delegate which calls our generic one. This - // is the delegate we actually invoke. - EventDelegate internalDelegate = (e) => del((T)e); - delegateLookup[del] = internalDelegate; - - EventDelegate tempDel; - if (delegates.TryGetValue(typeof(T), out tempDel)) { - delegates[typeof(T)] = tempDel += internalDelegate; - } - else { - delegates[typeof(T)] = internalDelegate; - } - } + /// + /// The count of delegate lookups. The delegate lookups will increase by + /// one for each unique AddListener. Useful for debugging and not much else. + /// + public int DelegateLookupCount { get { return delegateLookup.Count; } } + + /// + /// Raise the event to all the listeners + /// + public void Raise(Event e) + { + LogRaise(e); + + EventDelegate del; + if (delegates.TryGetValue(e.GetType(), out del)) + { + del.Invoke(e); + } + } - /// - /// Remove the delegate. Can be called multiple times on same delegate. - /// - public void RemoveListener(EventDelegate del) where T : Event { - - EventDelegate internalDelegate; - if (delegateLookup.TryGetValue(del, out internalDelegate)) { - EventDelegate tempDel; - if (delegates.TryGetValue(typeof(T), out tempDel)) { - tempDel -= internalDelegate; - if (tempDel == null) { - delegates.Remove(typeof(T)); - } - else { - delegates[typeof(T)] = tempDel; - } + private string GetCaller() + { + var stacktrace = StackTraceUtility.ExtractStackTrace(); + if (stacktrace.Length != 0) + { + var stackEntries = stacktrace.Split('\n'); + foreach (var entry in stackEntries) + { + if(entry.StartsWith(this.GetType().ToString())) + { + continue; + } + + //First item that is not this class is our external caller + return Regex.Match(entry, "([^:]*):").Groups[1].Value; + } + } + + return ""; } - delegateLookup.Remove(del); - } - } + public Dictionary listeners = new Dictionary(); + public Dictionary senders = new Dictionary(); - /// - /// The count of delegate lookups. The delegate lookups will increase by - /// one for each unique AddListener. Useful for debugging and not much else. - /// - public int DelegateLookupCount { get { return delegateLookup.Count; }} + private void LogRaise(Event e) + { + senders[GetCaller()] = e.GetType().ToString(); + Debug.Log(GetCaller() + " raised " + e.GetType()); + } - /// - /// Raise the event to all the listeners - /// - public void Raise(Event e) { - EventDelegate del; - if (delegates.TryGetValue(e.GetType(), out del)) { - del.Invoke(e); - } - } + private void LogListener(EventDelegate del) where T : Event + { + listeners[GetCaller()] = typeof(T).ToString(); + Debug.Log(GetCaller() + " added Listener for " + typeof(T) + ": " + del.Method.Name); + } - } + } } diff --git a/Assets/EventManager/Source/EventManager.cs.meta b/Assets/EventManager/Source/EventManager.cs.meta index be45d09..d86e346 100644 --- a/Assets/EventManager/Source/EventManager.cs.meta +++ b/Assets/EventManager/Source/EventManager.cs.meta @@ -1,8 +1,9 @@ fileFormatVersion: 2 -guid: d462efaa2139342e88d7d1a538cb89bf -timeCreated: 1455836012 +guid: 9bdb458cfd353ee49a595870a85c4aa2 +timeCreated: 1518809105 licenseType: Free MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Assets/EventManager/Source/EventNodeview.cs b/Assets/EventManager/Source/EventNodeview.cs new file mode 100644 index 0000000..8e6ca1d --- /dev/null +++ b/Assets/EventManager/Source/EventNodeview.cs @@ -0,0 +1,86 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +public class EventNodeview : EditorWindow +{ + Dictionary rectangles = new Dictionary(); + + [MenuItem("Window/Event Nodeview")] + static void ShowEditor() + {} + + void OnGUI() + { + //Add all listeners and senders to our list of rectangles we wanna show + //First put the senders (left side) + int index = 0; + int posYIndex = 0; + foreach (var sender in SDD.Events.EventManager.Instance.senders.Keys) + { + if (!rectangles.ContainsKey(sender)) + { + index++; + posYIndex++; + var posY = (100 * posYIndex) + 10; + rectangles.Add(sender, new Rect(200, posY, 100, 100)); + } + } + //then put down the receivers (right side) + posYIndex = 0; + foreach (var listener in SDD.Events.EventManager.Instance.listeners.Keys) + { + if (!rectangles.ContainsKey(listener)) + { + index++; + posYIndex++; + var posY = (100 * posYIndex) + 10; + rectangles.Add(listener, new Rect(400, posY, 100, 100)); + } + } + + //Connect each Sender and Listener that have the same Event + foreach (var listener in SDD.Events.EventManager.Instance.listeners) + { + foreach (var sender in SDD.Events.EventManager.Instance.senders) + { + if(listener.Value.Equals(sender.Value)) + { + DrawArrowConnection(rectangles[sender.Key], rectangles[listener.Key]); + } + } + } + + //This updates the rectangles and their connections. This is needed to allow drag+drop + BeginWindows(); + index = 0; + List rectKeys = new List(rectangles.Keys); + foreach (var rectKey in rectKeys) + { + rectangles[rectKey] = GUI.Window(index++, rectangles[rectKey], DrawNodeWindow, rectKey); + } + EndWindows(); + } + + void DrawNodeWindow(int id) + { + GUI.DragWindow(); + } + + void DrawArrowConnection(Rect start, Rect end) + { + Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0); + Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0); + Vector3 startTan = startPos + Vector3.right * 50; + Vector3 endTan = endPos + Vector3.left * 50; + Color shadowCol = new Color(0, 0, 0, 0.06f); + for (int i = 0; i < 3; i++) // Draw a shadow + Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5); + Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 1); + + //add small arrow to the end + Handles.color = Color.black; + Handles.DrawLine(endPos, new Vector3(endPos.x - 10, endPos.y + 5)); + Handles.DrawLine(endPos, new Vector3(endPos.x - 10, endPos.y - 5)); + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/EventNodeview.cs.meta b/Assets/EventManager/Source/EventNodeview.cs.meta new file mode 100644 index 0000000..1203e41 --- /dev/null +++ b/Assets/EventManager/Source/EventNodeview.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: f3e58d6dee532b747acf68b349255532 +timeCreated: 1518812453 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/IEventHandler.cs.meta b/Assets/EventManager/Source/IEventHandler.cs.meta index 4ede3c7..a2c5428 100644 --- a/Assets/EventManager/Source/IEventHandler.cs.meta +++ b/Assets/EventManager/Source/IEventHandler.cs.meta @@ -1,8 +1,9 @@ fileFormatVersion: 2 -guid: b6ba3497f1dfd4d7bb4df5a099099e4d -timeCreated: 1455836012 +guid: a1cb9b0b0377133498a831e22f23928e +timeCreated: 1518809105 licenseType: Free MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 From 2972fa777a8fc531c3e4f729aacf8c17110f4915 Mon Sep 17 00:00:00 2001 From: Nils Brinkmann Date: Sat, 17 Feb 2018 21:33:33 +0100 Subject: [PATCH 3/5] Added: Visualization for events --- Assets/EventManager/Source/EventManager.cs | 61 ++++- Assets/EventManager/Source/EventNodeview.cs | 86 ------- .../Source/UnityEventVisualizer.meta | 10 + .../Source/UnityEventVisualizer/Editor.meta | 9 + .../UnityEventVisualizer/Editor/EdgeGUI.cs | 211 ++++++++++++++++++ .../Editor/EdgeGUI.cs.meta} | 5 +- .../Editor/EdgeTriggersTracker.cs | 57 +++++ .../Editor/EdgeTriggersTracker.cs.meta | 12 + .../Editor/EditorZoomArea.cs | 33 +++ .../Editor/EditorZoomArea.cs.meta | 12 + .../Editor/EventsGraph.cs | 101 +++++++++ .../Editor/EventsGraph.cs.meta | 12 + .../Editor/EventsGraphGUI.cs | 124 ++++++++++ .../Editor/EventsGraphGUI.cs.meta | 12 + .../Editor/EventsGraphWindow.cs | 83 +++++++ .../Editor/EventsGraphWindow.cs.meta | 12 + .../Editor/EventsVisualizer.cs | 13 ++ .../Editor/EventsVisualizer.cs.meta | 12 + .../Editor/FindInGraphButton.cs | 20 ++ .../Editor/FindInGraphButton.cs.meta | 12 + .../UnityEventVisualizer/Editor/NodeGUI.cs | 123 ++++++++++ .../Editor/NodeGUI.cs.meta | 12 + .../Editor/RectExtensions.cs | 47 ++++ .../Editor/RectExtensions.cs.meta | 12 + .../Source/UnityEventVisualizer/EventCall.cs | 29 +++ .../UnityEventVisualizer/EventCall.cs.meta | 12 + .../Source/UnityEventVisualizer/NodeData.cs | 74 ++++++ .../UnityEventVisualizer/NodeData.cs.meta | 12 + 28 files changed, 1118 insertions(+), 100 deletions(-) delete mode 100644 Assets/EventManager/Source/EventNodeview.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs rename Assets/EventManager/Source/{EventNodeview.cs.meta => UnityEventVisualizer/Editor/EdgeGUI.cs.meta} (70%) create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs.meta create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs create mode 100644 Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs.meta diff --git a/Assets/EventManager/Source/EventManager.cs b/Assets/EventManager/Source/EventManager.cs index 8ba5630..8c69300 100644 --- a/Assets/EventManager/Source/EventManager.cs +++ b/Assets/EventManager/Source/EventManager.cs @@ -38,6 +38,17 @@ public static EventManager Instance } private static EventManager instance = null; + public List Events { get; private set; } + public bool EnableDebugLog { get; set; } + public bool EnableEventVisualization { get; set; } + + private EventManager() + { + Events = new List(); + EnableEventVisualization = true; + EnableDebugLog = false; + } + public delegate void EventDelegate(T e) where T : Event; private delegate void EventDelegate(Event e); @@ -57,13 +68,12 @@ public static EventManager Instance /// public void AddListener(EventDelegate del) where T : Event { - if (delegateLookup.ContainsKey(del)) { return; } - LogListener(del); + if (EnableEventVisualization) LogAddListener(del); // Create a new non-generic delegate which calls our generic one. This // is the delegate we actually invoke. @@ -86,6 +96,7 @@ public void AddListener(EventDelegate del) where T : Event /// public void RemoveListener(EventDelegate del) where T : Event { + if (EnableEventVisualization) LogRemoveListener(del); EventDelegate internalDelegate; if (delegateLookup.TryGetValue(del, out internalDelegate)) @@ -119,7 +130,7 @@ public void RemoveListener(EventDelegate del) where T : Event /// public void Raise(Event e) { - LogRaise(e); + if (EnableEventVisualization) LogRaise(e); EventDelegate del; if (delegates.TryGetValue(e.GetType(), out del)) @@ -149,19 +160,47 @@ private string GetCaller() return ""; } - public Dictionary listeners = new Dictionary(); - public Dictionary senders = new Dictionary(); - private void LogRaise(Event e) { - senders[GetCaller()] = e.GetType().ToString(); - Debug.Log(GetCaller() + " raised " + e.GetType()); + string eventName = e.GetType().ToString(); + string sender = GetCaller(); + if(EnableDebugLog) Debug.Log(sender + " raised " + eventName); + + var newEvents = new List(); + foreach (var eventCall in Events) + { + if(eventCall.EventName == eventName) + { + newEvents.Add(new EventVisualizer.Base.EventCall(sender, eventCall.Receiver, eventCall.EventName, eventCall.Method)); + } + } + Events.AddRange(newEvents); + Events.RemoveAll(item => ((item.Sender == null) && + (item.EventName == eventName))); + } + + private void LogAddListener(EventDelegate del) where T : Event + { + string receiver = GetCaller(); + string eventName = typeof(T).ToString(); + string methodName = del.Method.Name; + if (EnableDebugLog) Debug.Log(receiver + " added Listener for " + eventName + ": " + methodName); + + //add this as an EventCall, even though there is no sender so far (this is needed to show empty slots) + EventVisualizer.Base.EventCall newCall = new EventVisualizer.Base.EventCall(null, receiver, eventName, methodName); + Events.Add(newCall); } - private void LogListener(EventDelegate del) where T : Event + private void LogRemoveListener(EventDelegate del) where T : Event { - listeners[GetCaller()] = typeof(T).ToString(); - Debug.Log(GetCaller() + " added Listener for " + typeof(T) + ": " + del.Method.Name); + string receiver = GetCaller(); + string eventName = typeof(T).ToString(); + string methodName = del.Method.Name; + if (EnableDebugLog) Debug.Log(receiver + " removed Listener for " + eventName + ": " + methodName); + + Events.RemoveAll(item => ( (item.EventName.Equals(eventName)) && + (item.Receiver.Equals(receiver)) && + (item.Method.Equals(methodName)))); } } diff --git a/Assets/EventManager/Source/EventNodeview.cs b/Assets/EventManager/Source/EventNodeview.cs deleted file mode 100644 index 8e6ca1d..0000000 --- a/Assets/EventManager/Source/EventNodeview.cs +++ /dev/null @@ -1,86 +0,0 @@ -using UnityEngine; -using UnityEditor; -using System.Collections.Generic; - -public class EventNodeview : EditorWindow -{ - Dictionary rectangles = new Dictionary(); - - [MenuItem("Window/Event Nodeview")] - static void ShowEditor() - {} - - void OnGUI() - { - //Add all listeners and senders to our list of rectangles we wanna show - //First put the senders (left side) - int index = 0; - int posYIndex = 0; - foreach (var sender in SDD.Events.EventManager.Instance.senders.Keys) - { - if (!rectangles.ContainsKey(sender)) - { - index++; - posYIndex++; - var posY = (100 * posYIndex) + 10; - rectangles.Add(sender, new Rect(200, posY, 100, 100)); - } - } - //then put down the receivers (right side) - posYIndex = 0; - foreach (var listener in SDD.Events.EventManager.Instance.listeners.Keys) - { - if (!rectangles.ContainsKey(listener)) - { - index++; - posYIndex++; - var posY = (100 * posYIndex) + 10; - rectangles.Add(listener, new Rect(400, posY, 100, 100)); - } - } - - //Connect each Sender and Listener that have the same Event - foreach (var listener in SDD.Events.EventManager.Instance.listeners) - { - foreach (var sender in SDD.Events.EventManager.Instance.senders) - { - if(listener.Value.Equals(sender.Value)) - { - DrawArrowConnection(rectangles[sender.Key], rectangles[listener.Key]); - } - } - } - - //This updates the rectangles and their connections. This is needed to allow drag+drop - BeginWindows(); - index = 0; - List rectKeys = new List(rectangles.Keys); - foreach (var rectKey in rectKeys) - { - rectangles[rectKey] = GUI.Window(index++, rectangles[rectKey], DrawNodeWindow, rectKey); - } - EndWindows(); - } - - void DrawNodeWindow(int id) - { - GUI.DragWindow(); - } - - void DrawArrowConnection(Rect start, Rect end) - { - Vector3 startPos = new Vector3(start.x + start.width, start.y + start.height / 2, 0); - Vector3 endPos = new Vector3(end.x, end.y + end.height / 2, 0); - Vector3 startTan = startPos + Vector3.right * 50; - Vector3 endTan = endPos + Vector3.left * 50; - Color shadowCol = new Color(0, 0, 0, 0.06f); - for (int i = 0; i < 3; i++) // Draw a shadow - Handles.DrawBezier(startPos, endPos, startTan, endTan, shadowCol, null, (i + 1) * 5); - Handles.DrawBezier(startPos, endPos, startTan, endTan, Color.black, null, 1); - - //add small arrow to the end - Handles.color = Color.black; - Handles.DrawLine(endPos, new Vector3(endPos.x - 10, endPos.y + 5)); - Handles.DrawLine(endPos, new Vector3(endPos.x - 10, endPos.y - 5)); - } -} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer.meta b/Assets/EventManager/Source/UnityEventVisualizer.meta new file mode 100644 index 0000000..330b3e6 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 37696b25b8158da4d8b73f14885324b7 +folderAsset: yes +timeCreated: 1518871822 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor.meta new file mode 100644 index 0000000..d233712 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 673c745b2caa2404db48f4bde78840d8 +folderAsset: yes +timeCreated: 1499875042 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs new file mode 100644 index 0000000..26787c6 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs @@ -0,0 +1,211 @@ +// +// Klak - Utilities for creative coding with Unity +// +// Copyright (C) 2016 Keijiro Takahashi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System; +using System.Reflection; +using Graphs = UnityEditor.Graphs; +using UnityEditor.Graphs; + +namespace EventVisualizer.Base +{ + // Specialized edge drawer class + public class EdgeGUI : Graphs.IEdgeGUI + { + #region Public members + + public Graphs.GraphGUI host { get; set; } + public List edgeSelection { get; set; } + + public EdgeGUI() + { + edgeSelection = new List(); + } + + #endregion + + #region IEdgeGUI implementation + + + public void DoEdges() + { + // Draw edges on repaint. + if (Event.current.type == EventType.Repaint) + { + foreach (var edge in host.graph.edges) + { + if (edge == null) continue; + + float hue = HueOutputSlot(edge.fromSlot); + DrawEdge(edge, Color.HSVToRGB(hue, 1f, 1f)); + } + } + } + + + private float HueOutputSlot(Slot slot) + { + int count = 0; + int index = 0; + foreach (var s in slot.node.outputSlots) + { + if (s == slot) + { + index = count; + } + count++; + } + + return Mathf.Repeat((index % 2 == 0 ? (float)index : index + (count + 1f) * 0.5f) / count, 1f); + + } + + public void DoDraggedEdge() + { + + } + + public void BeginSlotDragging(Graphs.Slot slot, bool allowStartDrag, bool allowEndDrag) + { + + } + + public void SlotDragging(Graphs.Slot slot, bool allowEndDrag, bool allowMultiple) + { + + } + + public void EndSlotDragging(Graphs.Slot slot, bool allowMultiple) + { + + } + + + public void EndDragging() + { + + } + + public Graphs.Edge FindClosestEdge() + { + return null; + } + + + #endregion + + #region Private members + + Graphs.Slot _dragSourceSlot; + Graphs.Slot _dropTarget; + + #endregion + + #region Edge drawer + + const float kEdgeWidth = 4; + const float kEdgeSlotYOffset = 9; + + static void DrawEdge(Graphs.Edge edge, Color color) + { + var p1 = GetPositionAsFromSlot(edge.fromSlot); + var p2 = GetPositionAsToSlot(edge.toSlot); + DrawEdge(p1, p2, color * edge.color, EdgeTriggersTracker.GetTimings(edge)); + } + + static void DrawEdge(Vector2 p1, Vector2 p2, Color color, List triggers) + { + Color prevColor = Handles.color; + Handles.color = color; + + var l = Mathf.Min(Mathf.Abs(p1.y - p2.y), 50); + Vector2 p3 = p1 + new Vector2(l, 0); + Vector2 p4 = p2 - new Vector2(l, 0); + var texture = (Texture2D)Graphs.Styles.connectionTexture.image; + Handles.DrawBezier(p1, p2, p3, p4, color, texture, kEdgeWidth); + + + foreach (var trigger in triggers) + { + Vector3 pos = CalculateBezierPoint(trigger, p1, p3, p4, p2); + Handles.DrawSolidArc(pos, Vector3.back, pos + Vector3.up, 360, kEdgeWidth *2); + + } + + Handles.color = prevColor; + } + + #endregion + + #region Utilities to access private members + + // Accessors for Slot.m_Position + static Rect GetSlotPosition(Graphs.Slot slot) + { + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + var func = typeof(Graphs.Slot).GetField("m_Position", flags); + return (Rect)func.GetValue(slot); + } + + static Vector2 GetPositionAsFromSlot(Graphs.Slot slot) + { + var rect = GetSlotPosition(slot); + return GUIClip(new Vector2(rect.xMax, rect.y + kEdgeSlotYOffset)); + } + + static Vector2 GetPositionAsToSlot(Graphs.Slot slot) + { + var rect = GetSlotPosition(slot); + return GUIClip(new Vector2(rect.x, rect.y + kEdgeSlotYOffset)); + } + + // Caller for GUIClip.Clip + static Vector2 GUIClip(Vector2 pos) + { + var type = Type.GetType("UnityEngine.GUIClip,UnityEngine"); + var method = type.GetMethod("Clip", new Type[] { typeof(Vector2) }); + return (Vector2)method.Invoke(null, new object[] { pos }); + } + + #endregion + + private static Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) + { + float u = 1.0f - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; + + Vector3 p = uuu * p0; + p += 3 * uu * t * p1; + p += 3 * u * tt * p2; + p += ttt * p3; + + return p; + } + } + +} diff --git a/Assets/EventManager/Source/EventNodeview.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs.meta similarity index 70% rename from Assets/EventManager/Source/EventNodeview.cs.meta rename to Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs.meta index 1203e41..6de8124 100644 --- a/Assets/EventManager/Source/EventNodeview.cs.meta +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeGUI.cs.meta @@ -1,9 +1,8 @@ fileFormatVersion: 2 -guid: f3e58d6dee532b747acf68b349255532 -timeCreated: 1518812453 +guid: 2985f4fac86ecd34eb637f29891d75d2 +timeCreated: 1500022382 licenseType: Free MonoImporter: - externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs new file mode 100644 index 0000000..e63f133 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs @@ -0,0 +1,57 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEditor.Graphs; +using UnityEngine; +using UnityEngine.Events; + +namespace EventVisualizer.Base +{ + public static class EdgeTriggersTracker + { + public class EdgeTrigger + { + public Edge edge; + public float triggeredTime; + } + + private readonly static float TimeToLive = 1f; + private static List triggers = new List(); + + public static void RegisterTrigger(Edge edge) + { + triggers.Add(new EdgeTrigger() { edge = edge, triggeredTime = Time.unscaledTime }); + } + + public static List GetTimings(Edge edge) + { + float now = Time.unscaledTime; + List acceptedTriggers = triggers.FindAll(t => t.edge == edge); + + List timings = new List();//TODO cache + foreach (EdgeTrigger t in acceptedTriggers) + { + float time = Mathf.Abs(t.triggeredTime - now) / TimeToLive; + if (time <= 1f) + { + timings.Add(time); + } + else + { + triggers.Remove(t); + } + } + return timings; + } + + public static void CleanObsolete() + { + float now = Time.unscaledTime; + triggers.RemoveAll(trigger => Mathf.Abs(now - trigger.triggeredTime) > TimeToLive); + } + + public static bool HasData() + { + return triggers.Count > 0; + } + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs.meta new file mode 100644 index 0000000..915d778 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EdgeTriggersTracker.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2f19a182d61c68542a747db2ee20d0d5 +timeCreated: 1512134666 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs new file mode 100644 index 0000000..284c079 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace EventVisualizer.Base +{ + public class EditorZoomArea + { + private const float kEditorWindowTabHeight = 21.0f; + private static Matrix4x4 _prevGuiMatrix; + + public static Rect Begin(float zoomScale, Rect screenCoordsArea) + { + GUI.EndGroup(); // End the group Unity begins automatically for an EditorWindow to clip out the window tab. This allows us to draw outside of the size of the EditorWindow. + + Rect clippedArea = screenCoordsArea.ScaleSizeBy(1.0f / zoomScale, screenCoordsArea.TopLeft()); + clippedArea.y += kEditorWindowTabHeight; + GUI.BeginGroup(clippedArea); + + _prevGuiMatrix = GUI.matrix; + Matrix4x4 translation = Matrix4x4.TRS(clippedArea.TopLeft(), Quaternion.identity, Vector3.one); + Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f)); + GUI.matrix = translation * scale * translation.inverse * GUI.matrix; + + return clippedArea; + } + + public static void End() + { + GUI.matrix = _prevGuiMatrix; + GUI.EndGroup(); + GUI.BeginGroup(new Rect(0.0f, kEditorWindowTabHeight, Screen.width, Screen.height)); + } + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs.meta new file mode 100644 index 0000000..d4d54b7 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EditorZoomArea.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: df84caadb31f9df48b22ab5d59ac9d5f +timeCreated: 1504788847 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs new file mode 100644 index 0000000..4cb147d --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs @@ -0,0 +1,101 @@ +using UnityEngine; +using EventVisualizer.Base; +using System.Collections.Generic; +using UnityEditor.Graphs; +using System.Linq; + +namespace EventVisualizer.Base +{ + [System.Serializable] + public class EventsGraph : Graph + { + static public EventsGraph Create() + { + var graph = CreateInstance(); + graph.hideFlags = HideFlags.HideAndDontSave; + return graph; + } + + public EventsGraphGUI GetEditor() + { + var gui = CreateInstance(); + gui.graph = this; + gui.hideFlags = HideFlags.HideAndDontSave; + return gui; + + } + + public void BuildGraph() + { + NodeData.Clear(); + Clear(true); + foreach (EventCall call in EventsFinder.FindAllEvents()) + { + NodeData.RegisterEvent(call); + } + foreach (NodeData data in NodeData.Nodes) + { + AddNode(NodeGUI.Create(data)); + } + + foreach (NodeGUI node in nodes) + { + node.PopulateEdges(); + } + + SortGraph(); + } + +#region sorting + + [SerializeField] + private HashSet positionedNodes = new HashSet(); + private const float VERTICAL_SPACING = 80f; + private const float HORIZONTAL_SPACING = 400f; + private void SortGraph() + { + positionedNodes.Clear(); + + List sortedNodes = new List(nodes); //cannot sort the original collection so a clone is needed + sortedNodes.Sort((x, y) => + { + int xScore = x.outputEdges.Count() - x.inputEdges.Count(); + int yScore = y.outputEdges.Count() - y.inputEdges.Count(); + return yScore.CompareTo(xScore); + }); + + Vector2 position = Vector2.zero; + foreach (Node node in sortedNodes) + { + if (!positionedNodes.Contains(node)) + { + positionedNodes.Add(node); + position.y += PositionNodeHierarchy(node, position); + } + } + } + + + private float PositionNodeHierarchy(Node currentNode, Vector2 masterPosition) + { + float height = VERTICAL_SPACING; + foreach (var outputEdge in currentNode.outputEdges) + { + Node node = outputEdge.toSlot.node; + if (!positionedNodes.Contains(node)) + { + positionedNodes.Add(node); + height += PositionNodeHierarchy(node, masterPosition + + Vector2.right * HORIZONTAL_SPACING + + Vector2.up * height); + } + } + currentNode.position = new Rect(masterPosition + Vector2.up * height * 0.5f, currentNode.position.size); + + return height; + } + +#endregion + + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs.meta new file mode 100644 index 0000000..3233162 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraph.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b8f9efaa5424bdf4ebdd9d4af48ca877 +timeCreated: 1499875263 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs new file mode 100644 index 0000000..e7f58a0 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs @@ -0,0 +1,124 @@ +using UnityEngine; +using UnityEditor; +using EventVisualizer.Base; +using System.Collections.Generic; +using UnityEditor.Graphs; + +namespace EventVisualizer.Base +{ + [System.Serializable] + public class EventsGraphGUI : GraphGUI + { + [SerializeField] + public int SelectionOverride; + + public override void OnGraphGUI() + { + // Show node subwindows. + m_Host.BeginWindows(); + + foreach (var node in graph.nodes) + { + // Recapture the variable for the delegate. + var node2 = node; + + // Subwindow style (active/nonactive) + var isActive = selection.Contains(node); + var style = Styles.GetNodeStyle(node.style, node.color, isActive); + + node.position = GUILayout.Window( + node.GetInstanceID(), + node.position, + delegate { NodeGUI(node2); }, + node.title, + style, + GUILayout.Width(150) + ); + } + + if (graph.nodes.Count == 0) + { + GUILayout.Window(0, new Rect(0, 0, 1, 1), delegate {}, "", "MiniLabel"); + } + + m_Host.EndWindows(); + + // Graph edges + edgeGUI.DoEdges(); + + // Mouse drag + DragSelection(); + + } + + public override IEdgeGUI edgeGUI + { + get + { + if (m_EdgeGUI == null) + m_EdgeGUI = new EdgeGUI { host = this }; + return m_EdgeGUI; + } + } + + + + + public override void NodeGUI(Node node) + { + SelectNode(node); + + foreach (var slot in node.inputSlots) + LayoutSlot(slot, slot.title, false, true, true, Styles.triggerPinIn); + + node.NodeUI(this); + + foreach (var slot in node.outputSlots) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + LayoutSlot(slot, slot.title, true, false, true, Styles.triggerPinOut); + EditorGUILayout.EndHorizontal(); + } + + DragNodes(); + + UpdateSelection(); + } + + private void UpdateSelection() + { + OverrideSelection(); + if (selection.Count > 0) + { + int[] selectedIds = new int[selection.Count]; + for (int i = 0; i < selection.Count; i++) + { + //TODO: .name contains the name of the Class (e.g. "Receiver2" as string, not parsable...) What ID did .name contain before? Added try/catch for now + selectedIds[i] = selection[i].GetInstanceID(); + } + Selection.instanceIDs = selectedIds; + } + } + + private void OverrideSelection() + { + if (SelectionOverride != 0) + { + Node selectedNode = graph[SelectionOverride.ToString()]; + if (selectedNode != null) + { + selection.Clear(); + selection.Add(selectedNode); + CenterGraph(selectedNode.position.position); + + } + SelectionOverride = 0; + } + } + + + + + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs.meta new file mode 100644 index 0000000..3431142 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 41d6ec2bce82c7c449da384062936274 +timeCreated: 1499875263 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs new file mode 100644 index 0000000..3a4b35e --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs @@ -0,0 +1,83 @@ +using UnityEditor; +using UnityEngine; + +namespace EventVisualizer.Base +{ + public class EventsGraphWindow : EditorWindow + { + [SerializeField] + private EventsGraph _graph; + [SerializeField] + private EventsGraphGUI _graphGUI; + + private const float kBarHeight = 17; + + [MenuItem("Window/Events Graph editor")] + static void ShowEditor() + { + EventsGraphWindow editor = EditorWindow.GetWindow(); + editor.hideFlags = HideFlags.HideAndDontSave; + editor.Initialize(); + } + + public void Initialize() + { + _graph = EventsGraph.Create(); + _graph.BuildGraph(); + + _graphGUI = _graph.GetEditor(); + _graphGUI.CenterGraph(); + + EditorUtility.SetDirty(_graphGUI); + EditorUtility.SetDirty(_graph); + } + + void OnGUI() + { + var width = position.width; + var height = position.height; + + if(_graphGUI != null) + { + // Main graph area + _graphGUI.BeginGraphGUI(this, new Rect(0, 0, width, height - kBarHeight)); + _graphGUI.OnGraphGUI(); + _graphGUI.EndGraphGUI(); + + // Clear selection on background click + var e = Event.current; + if (e.type == EventType.MouseDown && e.clickCount == 1) + _graphGUI.ClearSelection(); + } + + + // Status bar + GUILayout.BeginArea(new Rect(0, height - kBarHeight, width, kBarHeight)); + if (GUILayout.Button("Refresh")) + { + Refresh(); + } + GUILayout.EndArea(); + } + + private void Update() + { + if (EdgeTriggersTracker.HasData()) + { + Repaint(); + } + } + + public void OverrideSelection(int overrideIndex) + { + _graphGUI.SelectionOverride = overrideIndex; + } + + + void Refresh() + { + _graph.Clear(); + _graph.BuildGraph(); + } + } +} diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs.meta new file mode 100644 index 0000000..d80a409 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsGraphWindow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 190ddcd99b7cc1b48af8d612a8aea744 +timeCreated: 1499938262 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs new file mode 100644 index 0000000..431ebe6 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace EventVisualizer.Base +{ + + public static class EventsFinder + { + public static List FindAllEvents() + { + return SDD.Events.EventManager.Instance.Events; + } + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs.meta new file mode 100644 index 0000000..84bb3b7 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/EventsVisualizer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 48826a5b6d060a148b50a6670ba6d2a4 +timeCreated: 1499869669 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs new file mode 100644 index 0000000..cd7f8e0 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs @@ -0,0 +1,20 @@ +using UnityEditor; +using UnityEngine; + +namespace EventVisualizer.Base +{ + public static class FindInGraphButton + { + [MenuItem("GameObject/EventGraph/Find this", false, 0)] + static void FindEvents() + { + EventsGraphWindow window = EditorWindow.GetWindow(); + if(window != null) + { + window.OverrideSelection(Selection.activeInstanceID); + } + } + + } + +} diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs.meta new file mode 100644 index 0000000..7b63604 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/FindInGraphButton.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2dfbd442ff101084f9021be4d99f48b3 +timeCreated: 1500029743 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs new file mode 100644 index 0000000..6042a85 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs @@ -0,0 +1,123 @@ +using UnityEngine; +using UnityEngine.Events; +using UnityEditor; +using System; +using System.Reflection; +using Graphs = UnityEditor.Graphs; +using System.Linq; +using UnityEditor.Graphs; +using System.Collections.Generic; + +namespace EventVisualizer.Base +{ + public class NodeGUI : Graphs.Node + { + #region Public class methods + + // Factory method + static public NodeGUI Create(NodeData dataInstance) + { + var node = CreateInstance(); + node.Initialize(dataInstance); + node.name = dataInstance.Entity; + return node; + } + + #endregion + + #region Public member properties and methods + + // Runtime instance access + public NodeData runtimeInstance + { + get { return _runtimeInstance; } + } + + // Validity check + public bool isValid + { + get { return _runtimeInstance != null; } + } + + #endregion + + #region Overridden virtual methods + + // Node display title + public override string title + { + get { return isValid ? _runtimeInstance.Name : ""; } + } + + // Dirty callback + public override void Dirty() + { + base.Dirty(); + } + + #endregion + + #region Private members + + // Runtime instance of this node + NodeData _runtimeInstance; + + // Initializer (called from the Create method) + void Initialize(NodeData runtimeInstance) + { + hideFlags = HideFlags.DontSave; + + // Object references + _runtimeInstance = runtimeInstance; + position = new Rect(Vector2.one * UnityEngine.Random.Range(0, 500), Vector2.zero); + + PopulateSlots(); + } + + void PopulateSlots() + { + foreach (EventCall call in _runtimeInstance.Outputs) + { + string name = call.EventName; + string title = ObjectNames.NicifyVariableName(name); + if (!outputSlots.Any(s => s.title == title)) + { + var slot = AddOutputSlot(name); + slot.title = title; + } + } + + foreach (EventCall call in _runtimeInstance.Inputs) + { + string name = call.MethodFullPath; + string title = ObjectNames.NicifyVariableName(name); + if (!inputSlots.Any(s => s.title == title)) + { + var slot = AddInputSlot(name); + slot.title = title; + } + } + } + + public void PopulateEdges() + { + foreach (var outSlot in outputSlots) + { + List outCalls = _runtimeInstance.Outputs.FindAll(call => call.EventName == outSlot.name); + + foreach (EventCall call in outCalls) + { + var targetNode = graph[call.Receiver]; + var inSlot = targetNode[call.MethodFullPath]; + + if (!graph.Connected(outSlot, inSlot)) + { + graph.Connect(outSlot, inSlot); + } + } + } + } + + #endregion + } +} diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs.meta new file mode 100644 index 0000000..8793b27 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/NodeGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 24ae9b60aa42dfc4ba3dca7eb88a614d +timeCreated: 1499939700 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs b/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs new file mode 100644 index 0000000..82a3dd9 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +namespace EventVisualizer.Base +{ + + public static class RectExtensions + { + public static Vector2 TopLeft(this Rect rect) + { + return new Vector2(rect.xMin, rect.yMin); + } + public static Rect ScaleSizeBy(this Rect rect, float scale) + { + return rect.ScaleSizeBy(scale, rect.center); + } + public static Rect ScaleSizeBy(this Rect rect, float scale, Vector2 pivotPoint) + { + Rect result = rect; + result.x -= pivotPoint.x; + result.y -= pivotPoint.y; + result.xMin *= scale; + result.xMax *= scale; + result.yMin *= scale; + result.yMax *= scale; + result.x += pivotPoint.x; + result.y += pivotPoint.y; + return result; + } + public static Rect ScaleSizeBy(this Rect rect, Vector2 scale) + { + return rect.ScaleSizeBy(scale, rect.center); + } + public static Rect ScaleSizeBy(this Rect rect, Vector2 scale, Vector2 pivotPoint) + { + Rect result = rect; + result.x -= pivotPoint.x; + result.y -= pivotPoint.y; + result.xMin *= scale.x; + result.xMax *= scale.x; + result.yMin *= scale.y; + result.yMax *= scale.y; + result.x += pivotPoint.x; + result.y += pivotPoint.y; + return result; + } + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs.meta new file mode 100644 index 0000000..c59eca2 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/Editor/RectExtensions.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d731028bb06536145af5d62304d3edcf +timeCreated: 1504789257 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs b/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs new file mode 100644 index 0000000..dd83765 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs @@ -0,0 +1,29 @@ + +namespace EventVisualizer.Base +{ + [System.Serializable] + public class EventCall + { + public string Sender { get; set; } + public string Receiver { get; private set; } + public string EventName { get; private set; } + public string Method { get; private set; } + public string ReceiverComponentName { get; private set; } + + public string MethodFullPath + { + get + { + return ReceiverComponentName + Method; + } + } + + public EventCall(string sender, string receiver, string eventName, string methodName) + { + Sender = sender; + Receiver = receiver; + EventName = eventName; + Method = methodName; + } + } +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs.meta new file mode 100644 index 0000000..1daca82 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/EventCall.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 146bf08e47e9cb646bc3fdb8a2bc2c6c +timeCreated: 1499941586 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs b/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs new file mode 100644 index 0000000..2bd7ac0 --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace EventVisualizer.Base +{ + [System.Serializable] + public class NodeData + { + public string Entity { get; private set; } + + public string Name + { + get + { + return Entity != null ? Entity : ""; + } + } + + public List Outputs { get; private set; } + public List Inputs { get; private set; } + + [SerializeField] + private static Dictionary nodes = new Dictionary(); + + public static ICollection Nodes + { + get + { + return nodes != null ? nodes.Values : null; + } + } + + + + public static void Clear() + { + nodes.Clear(); + } + + public static void RegisterEvent(EventCall eventCall) + { + CreateNode(eventCall.Receiver); + nodes[eventCall.Receiver].Inputs.Add(eventCall); + + if (eventCall.Sender != null) + { + CreateNode(eventCall.Sender); + nodes[eventCall.Sender].Outputs.Add(eventCall); + } + } + + private static void CreateNode(string entity) + { + if(entity == null) + { + return; + } + + if(!nodes.ContainsKey(entity)) + { + nodes.Add(entity, new NodeData(entity)); + } + + } + + public NodeData(string entity) + { + Entity = entity; + Outputs = new List(); + Inputs = new List(); + } + } + +} \ No newline at end of file diff --git a/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs.meta b/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs.meta new file mode 100644 index 0000000..6d558bb --- /dev/null +++ b/Assets/EventManager/Source/UnityEventVisualizer/NodeData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 70667cf59022c0a42a20bae166613703 +timeCreated: 1499875742 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 452dda29ef23a6fca3c991d7ec49f99ecc3f8730 Mon Sep 17 00:00:00 2001 From: Nils Brinkmann Date: Sat, 17 Feb 2018 21:39:11 +0100 Subject: [PATCH 4/5] Added: Some documentation --- README.md | 5 ++++- package.json | 13 +++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3c00c95..57faae3 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ Unity's own EventSystem may be more appropriate for that use case. * Type-safe * Avoids breaking changes when new event parameters are added +EventManager includes a graphical EventVisualizer that shows how your classes are connected during runtime +![EventVisualizer](https://user-images.githubusercontent.com/4217216/36345399-bbc9cf42-1429-11e8-8c3c-2bdc6362ead8.png) + Installation ------------ @@ -81,7 +84,7 @@ You can also add a `package.json` to your Unity project that contains the follow ``` { "dependencies": { - "unity3d.eventmanager": "1.0.2" + "unity3d.eventmanager": "1.1.0" } } ``` diff --git a/package.json b/package.json index 8caa741..a22a5cf 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,30 @@ { "name": "unity3d.eventmanager", - "version": "1.0.2", + "version": "1.1.0", "description": "A type-safe event system for Unity3D based on the event listener pattern. More info http://saltydog.digital/usage-pattern-for-a-type-safe-unity-event-system/", "main": "npm/index.js", "repository": { "type": "git", - "url": "git+https://github.com/robertwahler/EventManager.git" + "url": "git+https://github.com/UbimaxFrontline/EventManager.git" }, "keywords": [ "unity3d", "eventsystem", "events" ], - "author": "Robert Wahler", + "author": "Robert Wahler, Nils Brinkmann", "license": "MIT", "bugs": { - "url": "https://github.com/robertwahler/EventManager/issues" + "url": "https://github.com/UbimaxFrontline/EventManager/issues" }, - "homepage": "https://github.com/robertwahler/EventManager#readme", + "homepage": "https://github.com/UbimaxFrontline/EventManager#readme", "files": [ "npm/postinstall.js", "Assets/EventManager/Source/Event.cs", "Assets/EventManager/Source/EventManager.cs", - "Assets/EventManager/Source/IEventHandler.cs" + "Assets/EventManager/Source/IEventHandler.cs", + "Assets/EventManager/Source/UnityEventVisualizer" ], "scripts": { "postinstall": "node npm/postinstall.js" From b48dd2d3109b73ff0ea2c8482934821224d36475 Mon Sep 17 00:00:00 2001 From: Nils Brinkmann Date: Mon, 26 Feb 2018 08:37:04 +0100 Subject: [PATCH 5/5] Added: Checking for UnityEditor before using Reflection in order to get the Method name (this functionality is not available on UWP solutions like HoloLens, so it leads to compile issues) --- Assets/EventManager/Source/EventManager.cs | 10 ++++++++-- package.json | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Assets/EventManager/Source/EventManager.cs b/Assets/EventManager/Source/EventManager.cs index 8c69300..531aa28 100644 --- a/Assets/EventManager/Source/EventManager.cs +++ b/Assets/EventManager/Source/EventManager.cs @@ -183,7 +183,10 @@ private void LogAddListener(EventDelegate del) where T : Event { string receiver = GetCaller(); string eventName = typeof(T).ToString(); - string methodName = del.Method.Name; + string methodName = ""; +#if UNITY_EDITOR + methodName = del.Method.Name; +#endif if (EnableDebugLog) Debug.Log(receiver + " added Listener for " + eventName + ": " + methodName); //add this as an EventCall, even though there is no sender so far (this is needed to show empty slots) @@ -195,7 +198,10 @@ private void LogRemoveListener(EventDelegate del) where T : Event { string receiver = GetCaller(); string eventName = typeof(T).ToString(); - string methodName = del.Method.Name; + string methodName = ""; +#if UNITY_EDITOR + methodName = del.Method.Name; +#endif if (EnableDebugLog) Debug.Log(receiver + " removed Listener for " + eventName + ": " + methodName); Events.RemoveAll(item => ( (item.EventName.Equals(eventName)) && diff --git a/package.json b/package.json index a22a5cf..8a960c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unity3d.eventmanager", - "version": "1.1.0", + "version": "1.1.1", "description": "A type-safe event system for Unity3D based on the event listener pattern. More info http://saltydog.digital/usage-pattern-for-a-type-safe-unity-event-system/", "main": "npm/index.js", "repository": {