Added Pacore
This commit is contained in:
8
Assets/Pacore/Editor/Caches.meta
Normal file
8
Assets/Pacore/Editor/Caches.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5feaa457922d235419f2e6d3f8f01c33
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
58
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs
Normal file
58
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using PashaBibko.Pacore.Attributes;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Caches
|
||||
{
|
||||
public static class InspectorCallableCache
|
||||
{
|
||||
public struct AttributeInfo
|
||||
{
|
||||
public InspectorCallableAttribute Attribute;
|
||||
public MethodInfo AttachedMethod;
|
||||
}
|
||||
|
||||
private const BindingFlags BINDING_FLAGS =
|
||||
BindingFlags.Public |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance;
|
||||
|
||||
private static Dictionary<Type, AttributeInfo[]> Cache { get; } = new();
|
||||
|
||||
public static AttributeInfo[] GetAllAttributesOfType(Type type)
|
||||
{
|
||||
/* Checks the cache for the type */
|
||||
if (Cache.TryGetValue(type, out AttributeInfo[] attributes))
|
||||
{
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/* Finds all the functions with the attribute */
|
||||
MethodInfo[] methods = type.GetMethods(BINDING_FLAGS);
|
||||
List<AttributeInfo> buttons = new();
|
||||
|
||||
foreach (MethodInfo method in methods)
|
||||
{
|
||||
InspectorCallableAttribute attribute
|
||||
= method.GetCustomAttribute<InspectorCallableAttribute>();
|
||||
|
||||
if (attribute != null)
|
||||
{
|
||||
AttributeInfo info = new()
|
||||
{
|
||||
Attribute = attribute,
|
||||
AttachedMethod = method,
|
||||
};
|
||||
|
||||
buttons.Add(info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adds it to the cache before returning */
|
||||
AttributeInfo[] array = buttons.ToArray();
|
||||
Cache.Add(type, array);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs.meta
Normal file
11
Assets/Pacore/Editor/Caches/InspectorCallableCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95392750cbc11ce4b9b9e6717c9e6588
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/Pacore/Editor/Caches/MonoScriptCache.cs
Normal file
34
Assets/Pacore/Editor/Caches/MonoScriptCache.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Caches
|
||||
{
|
||||
public static class MonoScriptCache
|
||||
{
|
||||
private static Dictionary<string, MonoScript> Cache { get; } = new();
|
||||
|
||||
static MonoScriptCache()
|
||||
{
|
||||
/* Finds all MonoScripts and adds them to the dictionary by name */
|
||||
MonoScript[] scripts = Resources.FindObjectsOfTypeAll<MonoScript>();
|
||||
foreach (MonoScript script in scripts)
|
||||
{
|
||||
Type scriptType = script.GetClass();
|
||||
if (scriptType is not null)
|
||||
{
|
||||
string name = scriptType.FullName;
|
||||
Cache.TryAdd(name, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MonoScript Get(string name)
|
||||
{
|
||||
/* Fetches the value (if there is one) without creating a default val */
|
||||
Cache.TryGetValue(name, out MonoScript script);
|
||||
return script;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/Caches/MonoScriptCache.cs.meta
Normal file
11
Assets/Pacore/Editor/Caches/MonoScriptCache.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 668c2ebd0824f1d4c9b324be7142823c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Pacore/Editor/Drawers.meta
Normal file
8
Assets/Pacore/Editor/Drawers.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47d063734da388944b54ed5a6ca386a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Pacore/Editor/Drawers/DetectInspectorChangesDrawer.cs
Normal file
46
Assets/Pacore/Editor/Drawers/DetectInspectorChangesDrawer.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using PashaBibko.Pacore.Attributes;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Drawers
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DetectInspectorChangesAttribute))]
|
||||
public sealed class DetectInspectorChangesDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
/* Draws the property and checks for changes */
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUI.PropertyField(position, property, label);
|
||||
if (EditorGUI.EndChangeCheck()) // Returns true if there were changes
|
||||
{
|
||||
property.serializedObject.ApplyModifiedProperties(); // Applies the changes
|
||||
if (attribute is DetectInspectorChangesAttribute inspectorChangesAttribute)
|
||||
{
|
||||
const BindingFlags BINDING_FLAGS =
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
string methodName = inspectorChangesAttribute.ActionName;
|
||||
|
||||
/* Fetches the method and the object to call it on */
|
||||
Object target = property.serializedObject.targetObject;
|
||||
MethodInfo method = target.GetType().GetMethod(methodName, BINDING_FLAGS);
|
||||
if (method == null)
|
||||
{
|
||||
Debug.LogError($"Method not found [{methodName}]");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
method.Invoke(target, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5fad12cc06f1a749bc75948a24e6306
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs
Normal file
23
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using PashaBibko.Pacore.Attributes;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Drawers
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(InspectorReadOnlyAttribute))]
|
||||
public sealed class InspectorReadOnlyDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (attribute is InspectorReadOnlyAttribute readOnlyAttribute)
|
||||
{
|
||||
GUI.enabled = false;
|
||||
|
||||
label.text = readOnlyAttribute.Name ?? label.text; // Uses custom name if it exists
|
||||
EditorGUI.PropertyField(position, property, label);
|
||||
|
||||
GUI.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/InspectorReadOnlyDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a90f608095c5b964fabd707859b141ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs
Normal file
42
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using PashaBibko.Pacore.Editor.Caches;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Drawers
|
||||
{
|
||||
[CustomEditor(typeof(MonoBehaviour), editorForChildClasses: true)]
|
||||
public class MonoBehaviourDrawer : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
DrawFunctionButtons(target);
|
||||
}
|
||||
|
||||
public static void DrawFunctionButtons(Object target)
|
||||
{
|
||||
Type type = target.GetType();
|
||||
InspectorCallableCache.AttributeInfo[] buttons
|
||||
= InspectorCallableCache.GetAllAttributesOfType(type);
|
||||
|
||||
if (buttons.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Functions", EditorStyles.boldLabel);
|
||||
|
||||
foreach (InspectorCallableCache.AttributeInfo button in buttons)
|
||||
{
|
||||
string name = button.Attribute.ButtonName;
|
||||
if (GUILayout.Button(name))
|
||||
{
|
||||
button.AttachedMethod.Invoke(target, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/MonoBehaviourDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0f42304568f6f54f9f7cf0c6e3e62ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs
Normal file
80
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using PashaBibko.Pacore.Editor.Caches;
|
||||
using PashaBibko.Pacore.Attributes;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace PashaBibko.Pacore.Editor.Drawers
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(MonoScriptAttribute))]
|
||||
public class MonoScriptDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
/* I'm not sure if this will ever happen */
|
||||
if (attribute is not MonoScriptAttribute attr)
|
||||
{
|
||||
return; // Stops NullReferenceExceptions
|
||||
}
|
||||
|
||||
/* Makes sure the type is a string */
|
||||
if (property.propertyType != SerializedPropertyType.String)
|
||||
{
|
||||
Debug.LogError($"Attribute [{nameof(MonoScriptAttribute)}] must be attached to a string");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Draws the label of the script variable */
|
||||
Rect rect = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
/* Fetches the script if there is a value assigned to the property */
|
||||
string propValue = property.stringValue;
|
||||
MonoScript script = null;
|
||||
if (!string.IsNullOrEmpty(propValue))
|
||||
{
|
||||
script = MonoScriptCache.Get(propValue);
|
||||
}
|
||||
|
||||
/* Draws the selected script and checks for changes */
|
||||
script = EditorGUI.ObjectField(rect, script, typeof(MonoScript), allowSceneObjects: false) as MonoScript;
|
||||
if (!GUI.changed)
|
||||
{
|
||||
return; // No changes to check
|
||||
}
|
||||
|
||||
/* Makes sure the script is valid */
|
||||
if (script is null)
|
||||
{
|
||||
property.stringValue = string.Empty; // No type means empty string
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fetches the type of the attached script, and checks if it is valid */
|
||||
Type type = script.GetClass();
|
||||
if (type is null || (attr.MonoType is not null && !attr.MonoType.IsAssignableFrom(type)))
|
||||
{
|
||||
Debug.LogError($"The script file [{script.name}] doesn't contain an assignable MonoBehaviour class");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If a forced inheritance has been set, checks its validity */
|
||||
if (attr.InheritedFrom is not null && !attr.InheritedFrom.IsAssignableFrom(type))
|
||||
{
|
||||
property.stringValue = type.FullName; // Still applies the changes to make it appear like it is functioning
|
||||
|
||||
string attachedObject = property.serializedObject.targetObject.name;
|
||||
string inherited = attr.InheritedFrom.FullName;
|
||||
|
||||
Debug.LogError
|
||||
(
|
||||
$"Field [{property.name}] as part of [{attachedObject}] is invalid.\n" +
|
||||
$"The type must inherit from [{inherited}]. Currently it is [{type.FullName}]"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assigns the name of the type to the property so it can be created */
|
||||
property.stringValue = type.FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta
Normal file
11
Assets/Pacore/Editor/Drawers/MonoScriptDrawer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21cdc55dcda1d9e40a1463929b020832
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Pacore/Editor/EditorWindows.meta
Normal file
8
Assets/Pacore/Editor/EditorWindows.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 534297012cecc0a4892f3262f829f411
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs
Normal file
97
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System.Collections.Generic;
|
||||
using PashaBibko.Pacore.DevTools;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pacore.Editor.EditorWindows
|
||||
{
|
||||
public class ProfilerWindow : EditorWindow
|
||||
{
|
||||
private double LastRepaint = double.MinValue;
|
||||
private Vector2 ScrollPosition;
|
||||
|
||||
[MenuItem("Pacore/Profiler")]
|
||||
public static void OpenWindow() => GetWindow<ProfilerWindow>();
|
||||
|
||||
/* Makes sure the window is repainted often to show latest info */
|
||||
private void OnDisable() => EditorApplication.update -= CheckForRepaint;
|
||||
private void OnEnable() => EditorApplication.update += CheckForRepaint;
|
||||
|
||||
private void CheckForRepaint()
|
||||
{
|
||||
/* Triggers a repaint when it has been over 1 second since last repaint */
|
||||
double now = EditorApplication.timeSinceStartup;
|
||||
if (now - LastRepaint > 1f)
|
||||
{
|
||||
LastRepaint = now;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawEmptyProfiler()
|
||||
{
|
||||
GUILayout.BeginVertical(style: "box");
|
||||
|
||||
GUILayout.Label(text: "No profiler snippets found", EditorStyles.boldLabel);
|
||||
GUILayout.Label(text: "Try running the game to collect profile samples");
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private static void DrawProfilerSnippet(string name, List<long> times)
|
||||
{
|
||||
GUILayout.BeginVertical(style: "box");
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(text: name, EditorStyles.boldLabel);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
float averageMs = times.Sum() / (float)times.Count;
|
||||
GUILayout.Label("Average time: " + averageMs);
|
||||
|
||||
IGrouping<long, long>[] ordered = times // List<long>
|
||||
.GroupBy(time => time) // IEnumerable<IGrouping<long, long>>
|
||||
.OrderByDescending(time => time.Key) // IOrdererEnumerable<IGrouping<long, long>>
|
||||
.ToArray();
|
||||
|
||||
foreach (IGrouping<long, long> group in ordered)
|
||||
{
|
||||
string text = group.Count() > 1
|
||||
? $"- {group.Key}ms {group.Count()}x"
|
||||
: $"- {group.Key}ms";
|
||||
|
||||
GUILayout.Label(text, EditorStyles.label);
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
/* Draws the different snippets to the screen */
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Space(5); // Stops the window rendering right at the top
|
||||
IReadOnlyDictionary<string, List<long>> snippets = CodeProfiler.GetProfilerSnippets();
|
||||
|
||||
/* If there are no snippets, draws an inspector with instructions */
|
||||
if (snippets.Count == 0)
|
||||
{
|
||||
DrawEmptyProfiler();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Draws a quick overview of all snippets found */
|
||||
GUILayout.BeginVertical(style: "box");
|
||||
GUILayout.Label(text: $"[{snippets.Count}] different profiler snippets found");
|
||||
GUILayout.EndVertical();
|
||||
|
||||
/* Draws each profiler snippet */
|
||||
ScrollPosition = EditorGUILayout.BeginScrollView(ScrollPosition);
|
||||
foreach (KeyValuePair<string, List<long>> snippet in snippets)
|
||||
{
|
||||
DrawProfilerSnippet(snippet.Key, snippet.Value);
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs.meta
Normal file
11
Assets/Pacore/Editor/EditorWindows/ProfilerWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4aab8fc202fced4c8c8b1b346526ccb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Assets/Pacore/Editor/Pacore.Editor.asmdef
Normal file
16
Assets/Pacore/Editor/Pacore.Editor.asmdef
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Pacore.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:4d6a1c1727d80284f86640644e0ea451"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
7
Assets/Pacore/Editor/Pacore.Editor.asmdef.meta
Normal file
7
Assets/Pacore/Editor/Pacore.Editor.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c0ae1ec093a9314e957ad337005e456
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user