├── .gitignore ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── LICENSE.md ├── LICENSE.md.meta ├── Productivity.meta ├── Productivity ├── Editor.meta ├── Editor │ ├── CustomInspector.cs │ ├── CustomInspector.cs.meta │ ├── DecoratorDrawers.meta │ ├── DecoratorDrawers │ │ ├── HelpDecoratorDrawer.cs │ │ └── HelpDecoratorDrawer.cs.meta │ ├── Drawers.meta │ ├── Drawers │ │ ├── ButtonDrawer.cs │ │ ├── ButtonDrawer.cs.meta │ │ ├── ParameterDrawer.cs │ │ └── ParameterDrawer.cs.meta │ ├── HierarchyUtilities.cs │ ├── HierarchyUtilities.cs.meta │ ├── PropertyDrawers.meta │ ├── PropertyDrawers │ │ ├── MinMaxRangePropertyDrawer.cs │ │ ├── MinMaxRangePropertyDrawer.cs.meta │ │ ├── NavMeshAreaMaskPropertyDrawer.cs │ │ ├── NavMeshAreaMaskPropertyDrawer.cs.meta │ │ ├── ScenePropertyDrawer.cs │ │ ├── ScenePropertyDrawer.cs.meta │ │ ├── TagPropertyDrawer.cs │ │ └── TagPropertyDrawer.cs.meta │ ├── Utils.meta │ ├── Utils │ │ ├── EditorMessage.cs │ │ └── EditorMessage.cs.meta │ ├── io.tinu.unity.productivity.Editor.asmdef │ └── io.tinu.unity.productivity.Editor.asmdef.meta ├── Runtime.meta └── Runtime │ ├── Attributes.meta │ ├── Attributes │ ├── ButtonAttribute.cs │ ├── ButtonAttribute.cs.meta │ ├── HelpAttribute.cs │ ├── HelpAttribute.cs.meta │ ├── MinMaxRangeAttribute.cs │ ├── MinMaxRangeAttribute.cs.meta │ ├── NavMeshAreaMaskAttribute.cs │ ├── NavMeshAreaMaskAttribute.cs.meta │ ├── SceneAttribute.cs │ ├── SceneAttribute.cs.meta │ ├── TagAttribute.cs │ └── TagAttribute.cs.meta │ ├── TransformUtils.cs │ ├── TransformUtils.cs.meta │ ├── io.tinu.unity.productivity.asmdef │ └── io.tinu.unity.productivity.asmdef.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Unity 2 | # This .gitignore file should be placed at the root of your Unity project directory 3 | # 4 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 5 | # 6 | /[Ll]ibrary/ 7 | /[Tt]emp/ 8 | /[Oo]bj/ 9 | /[Bb]uild/ 10 | /[Bb]uilds/ 11 | /[Ll]ogs/ 12 | /[Mm]emoryCaptures/ 13 | 14 | # Asset meta data should only be ignored when the corresponding asset is also ignored 15 | !/[Aa]ssets/**/*.meta 16 | 17 | # Uncomment this line if you wish to ignore the asset store tools plugin 18 | # /[Aa]ssets/AssetStoreTools* 19 | 20 | # Autogenerated Jetbrains Rider plugin 21 | /[Aa]ssets/Plugins/Editor/JetBrains* 22 | 23 | # Visual Studio cache directory 24 | .vs/ 25 | 26 | # Gradle cache directory 27 | .gradle/ 28 | 29 | # Autogenerated VS/MD/Consulo solution and project files 30 | ExportedObj/ 31 | .consulo/ 32 | *.csproj 33 | *.unityproj 34 | *.sln 35 | *.suo 36 | *.tmp 37 | *.user 38 | *.userprefs 39 | *.pidb 40 | *.booproj 41 | *.svd 42 | *.pdb 43 | *.mdb 44 | *.opendb 45 | *.VC.db 46 | 47 | # Unity3D generated meta files 48 | *.pidb.meta 49 | *.pdb.meta 50 | *.mdb.meta 51 | 52 | # Unity3D generated file on crash reports 53 | sysinfo.txt 54 | 55 | # Builds 56 | *.apk 57 | *.unitypackage 58 | 59 | # Crashlytics generated file 60 | crashlytics-build.properties 61 | 62 | 63 | # ---> JetBrains 64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 65 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 66 | 67 | # User-specific stuff 68 | .idea/**/workspace.xml 69 | .idea/**/tasks.xml 70 | .idea/**/usage.statistics.xml 71 | .idea/**/dictionaries 72 | .idea/**/shelf 73 | 74 | # Generated files 75 | .idea/**/contentModel.xml 76 | 77 | # Sensitive or high-churn files 78 | .idea/**/dataSources/ 79 | .idea/**/dataSources.ids 80 | .idea/**/dataSources.local.xml 81 | .idea/**/sqlDataSources.xml 82 | .idea/**/dynamic.xml 83 | .idea/**/uiDesigner.xml 84 | .idea/**/dbnavigator.xml 85 | 86 | # Gradle 87 | .idea/**/gradle.xml 88 | .idea/**/libraries 89 | 90 | # Gradle and Maven with auto-import 91 | # When using Gradle or Maven with auto-import, you should exclude module files, 92 | # since they will be recreated, and may cause churn. Uncomment if using 93 | # auto-import. 94 | # .idea/artifacts 95 | # .idea/compiler.xml 96 | # .idea/jarRepositories.xml 97 | # .idea/modules.xml 98 | # .idea/*.iml 99 | # .idea/modules 100 | # *.iml 101 | # *.ipr 102 | 103 | # CMake 104 | cmake-build-*/ 105 | 106 | # Mongo Explorer plugin 107 | .idea/**/mongoSettings.xml 108 | 109 | # File-based project format 110 | *.iws 111 | 112 | # IntelliJ 113 | out/ 114 | 115 | # mpeltonen/sbt-idea plugin 116 | .idea_modules/ 117 | 118 | # JIRA plugin 119 | atlassian-ide-plugin.xml 120 | 121 | # Cursive Clojure plugin 122 | .idea/replstate.xml 123 | 124 | # Crashlytics plugin (for Android Studio and IntelliJ) 125 | com_crashlytics_export_strings.xml 126 | crashlytics.properties 127 | crashlytics-build.properties 128 | fabric.properties 129 | 130 | # Editor-based Rest Client 131 | .idea/httpRequests 132 | 133 | # Android studio 3.1+ serialized cache file 134 | .idea/caches/build_file_checksums.ser 135 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.0.0] - 2020-12-10 8 | ### Added 9 | - Tag Attribute PropertyDrawer for string properties 10 | - Button Attribute PropertyDrawer displaying inspector buttons to trigger methods of MonoBehaviours 11 | - MinMaxRange Attribute PropertyDrawer 12 | - NavMeshAreaMask Attribute PropertyDrawer 13 | - Scene Attribute to be able to Drag&Drop Scenes in the inspector 14 | - Help Attribute to display a HelpBox above the property 15 | - Transform Grouping in SceneView in the Menu Edit>Group or with the shortcut CTRL+G -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9a10eae878ad21a4d8bc2e60986e4109 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **MIT License** 2 | 3 | Copyright (c) 2020 Martin Hodler (tinu.io) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6314592dd404de740a4a08f0baddd0aa 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Productivity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: afbddce750e7e554385e4eee4ae08a70 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Productivity/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c69eac76e2f0b844497b9dcf17fb9729 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Productivity/Editor/CustomInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine.Productivity.Editor.Drawers; 3 | 4 | namespace UnityEngine.Productivity.Editor 5 | { 6 | [CanEditMultipleObjects] 7 | [CustomEditor(typeof(UnityEngine.Object), true)] 8 | public class CustomInspector : UnityEditor.Editor 9 | { 10 | ButtonDrawer _buttonDrawer = new ButtonDrawer(); 11 | 12 | public override void OnInspectorGUI() 13 | { 14 | _buttonDrawer.Draw(this); 15 | 16 | base.OnInspectorGUI(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Productivity/Editor/CustomInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f40b5dd79900476e8f4ea85f64daa080 3 | timeCreated: 1603729850 -------------------------------------------------------------------------------- /Productivity/Editor/DecoratorDrawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 072cb720553a4c9c9726c043400964a5 3 | timeCreated: 1607387908 -------------------------------------------------------------------------------- /Productivity/Editor/DecoratorDrawers/HelpDecoratorDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using UnityEditor; 3 | using UnityEngine.Productivity.Attributes; 4 | 5 | 6 | namespace UnityEngine.Productivity.Editor.PropertyDrawers 7 | { 8 | [CustomPropertyDrawer(typeof(HelpAttribute))] 9 | public class HelpDecoratorDrawer : DecoratorDrawer 10 | { 11 | private const float IconSpace = 32; 12 | private const float InnerPadding = 4; 13 | private const float MarginBottom = 2; 14 | private const float MarginTop = 2; 15 | 16 | public override void OnGUI(Rect position) 17 | { 18 | if (attribute is HelpAttribute helpAttributeAttribute) 19 | { 20 | position.y += MarginTop; 21 | position.height -= MarginBottom + MarginTop; 22 | EditorGUI.HelpBox(position, helpAttributeAttribute.Message, (MessageType) helpAttributeAttribute.Type); 23 | } 24 | } 25 | 26 | public override float GetHeight() 27 | { 28 | if (attribute is HelpAttribute helpAttributeAttribute) 29 | { 30 | float textWidth = (helpAttributeAttribute.Type != HelpAttribute.MessageType.None) 31 | ? EditorGUIUtility.currentViewWidth - IconSpace 32 | : EditorGUIUtility.currentViewWidth; 33 | 34 | 35 | GUIStyle style = EditorStyles.helpBox; 36 | return style.CalcHeight(new GUIContent(helpAttributeAttribute.Message), textWidth) + InnerPadding + MarginBottom + MarginTop; 37 | } 38 | 39 | return base.GetHeight(); 40 | } 41 | 42 | public override bool CanCacheInspectorGUI() 43 | { 44 | return true; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Productivity/Editor/DecoratorDrawers/HelpDecoratorDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df0f46ec89d147c3a104e84338dae0a8 3 | timeCreated: 1607385487 -------------------------------------------------------------------------------- /Productivity/Editor/Drawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c96774bb1b248c89ada0e90b2d609bf 3 | timeCreated: 1603730057 -------------------------------------------------------------------------------- /Productivity/Editor/Drawers/ButtonDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using UnityEditor; 5 | using UnityEngine.Productivity.Attributes; 6 | 7 | namespace UnityEngine.Productivity.Editor.Drawers 8 | { 9 | public class ButtonDrawer 10 | { 11 | private static Dictionary _buttonFoldouts = new Dictionary(); 12 | private static Dictionary> _parameters = new Dictionary>(); 13 | private UnityEditor.Editor _editor; 14 | private static ParameterDrawer _parameterDrawer = new ParameterDrawer(); 15 | 16 | public void Draw(UnityEditor.Editor editor) 17 | { 18 | _editor = editor; 19 | 20 | Type type = _editor.target.GetType(); 21 | bool buttonsCreated = false; 22 | 23 | MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 24 | foreach (var method in methods) 25 | { 26 | ButtonAttribute buttonAttributeAttribute = (ButtonAttribute)Attribute.GetCustomAttribute(method, typeof(ButtonAttribute)); 27 | 28 | if (buttonAttributeAttribute != null) 29 | { 30 | bool enabledState = GUI.enabled; 31 | GUI.enabled = buttonAttributeAttribute.Availability == ButtonAvailability.Always || 32 | (EditorApplication.isPlaying 33 | ? buttonAttributeAttribute.Availability == ButtonAvailability.Play 34 | : buttonAttributeAttribute.Availability == ButtonAvailability.Editor); 35 | 36 | string buttonName = String.IsNullOrEmpty(buttonAttributeAttribute.Name) 37 | ? ObjectNames.NicifyVariableName(method.Name) 38 | : buttonAttributeAttribute.Name; 39 | 40 | ParameterInfo[] parameterInfos = method.GetParameters(); 41 | 42 | if (parameterInfos.Length == 0) 43 | DrawButton(method, buttonName); 44 | else 45 | DrawButtonWithParameters(method, buttonName, parameterInfos); 46 | 47 | GUI.enabled = enabledState; 48 | buttonsCreated = true; 49 | } 50 | } 51 | 52 | if (buttonsCreated) 53 | EditorGUILayout.Space(20); 54 | } 55 | 56 | private void DrawButton(MethodInfo method, string buttonName) 57 | { 58 | bool activated = GUILayout.Button(buttonName); 59 | 60 | if (activated) 61 | { 62 | foreach (var target in _editor.targets) 63 | { 64 | method.Invoke(target, null); 65 | } 66 | } 67 | } 68 | 69 | private void DrawButtonWithParameters(MethodInfo method, string buttonName, ParameterInfo[] parameterInfos) 70 | { 71 | using (var buttonScope = new EditorGUILayout.VerticalScope("RL Background")) 72 | { 73 | GUI.Box(new Rect(buttonScope.rect.x, buttonScope.rect.y, buttonScope.rect.width, 20), "", "RL Header"); 74 | //GUILayout.Label(buttonName); 75 | if (GetFoldoutState(buttonName)) 76 | { 77 | GUILayout.Space(3); 78 | 79 | EditorGUI.indentLevel++; 80 | 81 | if (!_parameters.ContainsKey(_editor.target.GetHashCode())) 82 | _parameters.Add(_editor.target.GetHashCode(), new List()); 83 | 84 | List parameterValues = _parameters[_editor.target.GetHashCode()]; 85 | 86 | 87 | parameterValues = DrawParameters(parameterValues, parameterInfos); 88 | 89 | _parameters[_editor.target.GetHashCode()] = parameterValues; 90 | 91 | 92 | GUILayout.Space(3); 93 | bool activated = GUILayout.Button(buttonName); 94 | 95 | if (activated) 96 | { 97 | foreach (var target in _editor.targets) 98 | { 99 | method.Invoke(target, parameterValues.ToArray()); 100 | } 101 | } 102 | 103 | EditorGUI.indentLevel--; 104 | GUILayout.Space(2); 105 | } 106 | } 107 | } 108 | 109 | private List DrawParameters(List parameterValues, ParameterInfo[] parameterInfos) 110 | { 111 | int index = 0; 112 | List values = new List(); 113 | 114 | foreach (var parameter in parameterInfos) 115 | { 116 | GUIContent label = new GUIContent(ObjectNames.NicifyVariableName(parameter.Name)); 117 | object value = null; 118 | 119 | if (index < parameterValues.Count) 120 | value = parameterValues[index]; 121 | 122 | if (_parameterDrawer.CanEditType(parameter.ParameterType)) 123 | { 124 | value = _parameterDrawer.EditorField(parameter.ParameterType, label, value); 125 | } 126 | 127 | values.Add(value); 128 | ++index; 129 | } 130 | 131 | return values; 132 | } 133 | 134 | private static bool GetFoldoutState(string buttonName) 135 | { 136 | if (!_buttonFoldouts.ContainsKey(buttonName)) 137 | _buttonFoldouts.Add(buttonName, false); 138 | 139 | GUIStyle style = new GUIStyle("foldout"); 140 | style.padding.left = 15; 141 | style.margin.left = 8; 142 | 143 | _buttonFoldouts[buttonName] = EditorGUILayout.Foldout(_buttonFoldouts[buttonName], buttonName, style); 144 | return _buttonFoldouts[buttonName]; 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /Productivity/Editor/Drawers/ButtonDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33a8be782f54448b97e9837c92cb6d6c 3 | timeCreated: 1603730070 -------------------------------------------------------------------------------- /Productivity/Editor/Drawers/ParameterDrawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | 5 | namespace UnityEngine.Productivity.Editor.Drawers 6 | { 7 | public class ParameterDrawer 8 | { 9 | private Dictionary> _drawers = new Dictionary>(); 10 | private GUIContent _currentLabel; 11 | private object _value; 12 | private Type _currentType; 13 | 14 | public ParameterDrawer() 15 | { 16 | _drawers.Add(typeof(int), IntField); 17 | _drawers.Add(typeof(float), FloatField); 18 | _drawers.Add(typeof(string), StringField); 19 | _drawers.Add(typeof(Vector2), Vector2Field); 20 | _drawers.Add(typeof(Vector3), Vector3Field); 21 | _drawers.Add(typeof(UnityEngine.Object), ObjectField); 22 | } 23 | 24 | public object EditorField(Type type, GUIContent label, object value) 25 | { 26 | _currentType = type; 27 | _currentLabel = label; 28 | 29 | object tmpValue = null; 30 | if (type.IsValueType) 31 | tmpValue = Activator.CreateInstance(type); 32 | 33 | if (value != null && value.GetType() == type) 34 | tmpValue = value; 35 | 36 | if (_drawers.ContainsKey(_currentType)) 37 | { 38 | _drawers[_currentType].Invoke(tmpValue); 39 | return _value; 40 | } 41 | 42 | foreach (Type t in _drawers.Keys) 43 | { 44 | if (_currentType.IsSubclassOf(t)) 45 | { 46 | _drawers[t].Invoke(tmpValue); 47 | return _value; 48 | } 49 | } 50 | 51 | return tmpValue; 52 | } 53 | 54 | 55 | public T EditorField(GUIContent label, object value) 56 | { 57 | _currentType = typeof(T); 58 | _currentLabel = label; 59 | 60 | T tmpValue = default(T); 61 | if (value is T) 62 | tmpValue = (T)value; 63 | 64 | if (_drawers.ContainsKey(typeof(T))) 65 | { 66 | _drawers[typeof(T)].Invoke(tmpValue); 67 | return (T)_value; 68 | } 69 | 70 | return tmpValue; 71 | } 72 | 73 | public bool CanEditType(Type type) 74 | { 75 | if (_drawers.ContainsKey(type)) 76 | return true; 77 | else 78 | { 79 | foreach (Type t in _drawers.Keys) 80 | { 81 | if (type.IsSubclassOf(t)) 82 | { 83 | return true; 84 | } 85 | } 86 | } 87 | 88 | return false; 89 | } 90 | 91 | private void IntField(object value) 92 | { 93 | _value = EditorGUILayout.IntField(_currentLabel, (int) value); 94 | } 95 | 96 | private void FloatField(object value) 97 | { 98 | _value = EditorGUILayout.FloatField(_currentLabel, (float) value); 99 | } 100 | 101 | private void StringField(object value) 102 | { 103 | _value = EditorGUILayout.TextField(_currentLabel, (string) value); 104 | } 105 | 106 | private void Vector2Field(object value) 107 | { 108 | _value = EditorGUILayout.Vector2Field(_currentLabel, (Vector2) value); 109 | } 110 | 111 | private void Vector3Field(object value) 112 | { 113 | _value = EditorGUILayout.Vector3Field(_currentLabel, (Vector3) value); 114 | } 115 | 116 | private void ObjectField(object value) 117 | { 118 | _value = EditorGUILayout.ObjectField(_currentLabel, (UnityEngine.Object)value, _currentType, true); 119 | } 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /Productivity/Editor/Drawers/ParameterDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d22ace5cbc047258c0e9f16cc6d0753 3 | timeCreated: 1603735650 -------------------------------------------------------------------------------- /Productivity/Editor/HierarchyUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | using UnityEditor.ShortcutManagement; 4 | 5 | namespace UnityEngine.Productivity.Editor 6 | { 7 | public static class HierarchyUtilities 8 | { 9 | [MenuItem("Edit/Group %g", false, 125)] 10 | [Shortcut("Productivity/Edit/Group", KeyCode.G, ShortcutModifiers.Action)] 11 | private static void GroupMenuItem() 12 | { 13 | Group(); 14 | } 15 | 16 | private static void Group() 17 | { 18 | if (Selection.transforms.Length <= 1) 19 | return; 20 | 21 | Transform[] transforms = Selection.transforms; 22 | Transform parent = TransformUtils.GetClosestSharingParent(transforms); 23 | GameObject groupObject = new GameObject("Group"); 24 | 25 | Vector3[] positions = new Vector3[transforms.Length]; 26 | int lowestSiblingIndex = Int32.MaxValue; 27 | 28 | for (int i = 0; i < transforms.Length; ++i) 29 | { 30 | positions[i] = transforms[i].position; 31 | 32 | if (transforms[i].parent == parent) 33 | { 34 | int siblingIndex = transforms[i].GetSiblingIndex(); 35 | if (siblingIndex < lowestSiblingIndex) 36 | lowestSiblingIndex = siblingIndex; 37 | } 38 | } 39 | 40 | Bounds bounds = GeometryUtility.CalculateBounds(positions, groupObject.transform.localToWorldMatrix); 41 | groupObject.transform.position = bounds.center; 42 | groupObject.transform.SetParent(parent); 43 | groupObject.transform.SetSiblingIndex(lowestSiblingIndex); 44 | 45 | Undo.RegisterCreatedObjectUndo(groupObject, "group objects"); 46 | 47 | foreach (var transform in transforms) 48 | { 49 | Undo.SetTransformParent(transform, groupObject.transform, "group objects"); 50 | } 51 | } 52 | 53 | [MenuItem("Edit/Group %g", true, 125)] 54 | private static bool ValidateGroup() 55 | { 56 | return Selection.transforms.Length > 1; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Productivity/Editor/HierarchyUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b74e16659ddac940b194b4867e99131 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 191b601174219cd4abef5e85b2d16f17 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/MinMaxRangePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Globalization; 3 | using UnityEditor; 4 | using UnityEngine.Productivity.Attributes; 5 | 6 | namespace UnityEngine.Productivity.Editor.PropertyDrawers 7 | { 8 | [CustomPropertyDrawer(typeof(MinMaxRangeAttribute))] 9 | public class MinMaxRangePropertyDrawer : PropertyDrawer 10 | { 11 | private const float Space = 10f; 12 | private const float MinMaxLabelHeight = 12f; 13 | private const float DefaultPadding = 2f; 14 | 15 | private static readonly GUIStyle MinLabel = new GUIStyle() 16 | { 17 | alignment = TextAnchor.LowerLeft, 18 | normal = new GUIStyleState() 19 | { 20 | textColor = Color.gray 21 | } 22 | }; 23 | 24 | private static readonly GUIStyle MaxLabel = new GUIStyle() 25 | { 26 | alignment = TextAnchor.LowerRight, 27 | normal = new GUIStyleState() 28 | { 29 | textColor = Color.gray 30 | } 31 | }; 32 | 33 | 34 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 35 | { 36 | if (property.propertyType != SerializedPropertyType.Vector2) 37 | { 38 | //Debug.LogWarning($"Tag-Attribute was used on a non-Vector2 property: {property.serializedObject.targetObject.GetType().ToString()}.{property.name}"); 39 | EditorGUI.PropertyField(position, property, label); 40 | return; 41 | } 42 | 43 | MinMaxRangeAttribute minMaxRangeAttribute = attribute as MinMaxRangeAttribute; 44 | if (minMaxRangeAttribute == null) 45 | { 46 | Debug.LogWarning($"This error shouldn't happen. Unity called a CustomPropertyDrawer on a wrong type: {property.serializedObject.targetObject.GetType().ToString()}.{property.name}"); 47 | EditorGUI.PropertyField(position, property, label); 48 | return; 49 | } 50 | 51 | DrawMinMaxLabels(minMaxRangeAttribute); 52 | position.y += MinMaxLabelHeight; 53 | 54 | Vector2 value = property.vector2Value; 55 | EditorGUI.BeginProperty(position, label, property); 56 | 57 | Rect valueEditPosition = EditorGUI.PrefixLabel(position, new GUIContent(property.displayName)); 58 | EditorGUI.MinMaxSlider(valueEditPosition, ref value.x, ref value.y, minMaxRangeAttribute.Min, minMaxRangeAttribute.Max); 59 | 60 | EditorGUI.EndProperty(); 61 | 62 | DrawFloatFields(ref value); 63 | 64 | float stepSize = minMaxRangeAttribute.StepSize; 65 | if (stepSize == 0) 66 | stepSize = 1; 67 | 68 | value.x = Mathf.Floor(value.x / stepSize) * stepSize; 69 | value.y = Mathf.Floor(value.y / stepSize) * stepSize; 70 | 71 | property.vector2Value = value; 72 | } 73 | 74 | private static void DrawMinMaxLabels(MinMaxRangeAttribute minMaxRangeAttribute) 75 | { 76 | Rect minMaxLabelRect = EditorGUILayout.GetControlRect(true, MinMaxLabelHeight); 77 | minMaxLabelRect.y -= EditorGUIUtility.singleLineHeight - 2; 78 | minMaxLabelRect.x += EditorGUIUtility.labelWidth + DefaultPadding; 79 | minMaxLabelRect.width -= EditorGUIUtility.labelWidth + DefaultPadding; 80 | 81 | minMaxLabelRect.width *= 0.5f; 82 | minMaxLabelRect.width -= Space * 0.5f; 83 | 84 | 85 | EditorGUI.LabelField(minMaxLabelRect, minMaxRangeAttribute.Min.ToString(CultureInfo.InvariantCulture), MinLabel); 86 | minMaxLabelRect.x += minMaxLabelRect.width + Space; 87 | EditorGUI.LabelField(minMaxLabelRect, minMaxRangeAttribute.Max.ToString(CultureInfo.InvariantCulture), MaxLabel); 88 | } 89 | 90 | private void DrawFloatFields(ref Vector2 value) 91 | { 92 | Rect controlRect = EditorGUILayout.GetControlRect(true); 93 | controlRect.x += EditorGUIUtility.labelWidth + DefaultPadding; 94 | controlRect.width -= EditorGUIUtility.labelWidth + DefaultPadding; 95 | 96 | controlRect.width *= 0.5f; 97 | controlRect.width -= Space * 0.5f; 98 | 99 | value.x = EditorGUI.FloatField(controlRect, value.x); 100 | controlRect.x += controlRect.width + Space; 101 | value.y = EditorGUI.FloatField(controlRect, value.y); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/MinMaxRangePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fd164a05f9e84c369ef99cf51fe996f1 3 | timeCreated: 1606159254 -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/NavMeshAreaMaskPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using UnityEditor; 3 | using UnityEngine.Productivity.Attributes; 4 | 5 | namespace UnityEngine.Productivity.Editor.PropertyDrawers 6 | { 7 | [CustomPropertyDrawer(typeof(NavMeshAreaMaskAttribute))] 8 | public class NavMeshAreaMaskPropertyDrawer : PropertyDrawer 9 | { 10 | private const int MaxNavMeshAreas = 32; 11 | 12 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 13 | { 14 | if (property.propertyType != SerializedPropertyType.Integer) 15 | { 16 | EditorGUI.PropertyField(position, property, label); 17 | return; 18 | } 19 | 20 | var areaNames = GameObjectUtility.GetNavMeshAreaNames(); 21 | 22 | var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); 23 | EditorGUI.BeginProperty(rect, GUIContent.none, property); 24 | 25 | Rect valueEditPosition = EditorGUI.PrefixLabel(position, new GUIContent(property.displayName)); 26 | property.intValue = EditorGUI.MaskField(valueEditPosition, GUIContent.none, property.intValue, areaNames); 27 | 28 | EditorGUI.EndProperty(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/NavMeshAreaMaskPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d9b34cdd2c14c1b91faeab35b4f0245 3 | timeCreated: 1604151486 -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/ScenePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEditor; 4 | using UnityEditor.SceneManagement; 5 | using UnityEngine.Productivity.Editor.Utils; 6 | using UnityEngine.SceneManagement; 7 | 8 | namespace UnityEngine.Productivity.Editor.PropertyDrawers 9 | { 10 | [CustomPropertyDrawer(typeof(Attributes.SceneAttribute))] 11 | public class ScenePropertyDrawer : PropertyDrawer 12 | { 13 | private bool _sceneNotFound = false; 14 | 15 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 16 | { 17 | if (property.propertyType != SerializedPropertyType.String) 18 | { 19 | EditorGUI.PropertyField(position, property, label); 20 | return; 21 | } 22 | 23 | Attributes.SceneAttribute sceneAttributeAttribute = attribute as Attributes.SceneAttribute; 24 | if (sceneAttributeAttribute == null) 25 | { 26 | Debug.LogWarning($"This error shouldn't happen. Unity called a CustomPropertyDrawer on a wrong type: {property.serializedObject.targetObject.GetType().ToString()}.{property.name}"); 27 | EditorGUI.PropertyField(position, property, label); 28 | return; 29 | } 30 | 31 | SceneAsset scene = null; 32 | 33 | string sceneName = property.stringValue; 34 | scene = GetSceneAssetByName(sceneName); 35 | 36 | if (!string.IsNullOrEmpty(sceneName) && scene == null) 37 | { 38 | _sceneNotFound = true; 39 | EditorMessage.DisplayError($"The stored scene \"{sceneName}\" was not found.", "Dismiss", 40 | () => { _sceneNotFound = false; }); 41 | } 42 | 43 | if (scene != null && !IsSceneInBuildPath(scene) && sceneAttributeAttribute.InBuildCheck) 44 | { 45 | EditorMessage.DisplayWarning($"The scene \"{scene.name}\" was not added to list of \"Scenes in Build\".", "Add to Build", 46 | () => 47 | { 48 | AddSceneToBuild(scene); 49 | }); 50 | } 51 | 52 | EditorGUI.BeginProperty(position, label, property); 53 | 54 | Rect valueEditPosition = EditorGUI.PrefixLabel(position, new GUIContent(property.displayName)); 55 | 56 | scene = (SceneAsset)EditorGUI.ObjectField(valueEditPosition, scene, typeof(SceneAsset), false); 57 | 58 | 59 | if (scene != null) 60 | { 61 | property.stringValue = scene.name; 62 | } 63 | else 64 | { 65 | 66 | 67 | if (!_sceneNotFound || Event.current.keyCode == KeyCode.Delete || Event.current.keyCode == KeyCode.Backspace) 68 | property.stringValue = string.Empty; 69 | } 70 | 71 | EditorGUI.EndProperty(); 72 | } 73 | 74 | private static SceneAsset GetSceneAssetByName(string sceneName) 75 | { 76 | if (!string.IsNullOrWhiteSpace(sceneName)) 77 | { 78 | string[] assets = AssetDatabase.FindAssets("t:sceneAsset"); 79 | SceneAsset[] scenes = new SceneAsset[assets.Length]; 80 | 81 | for (int i = 0; i < assets.Length; ++i) 82 | { 83 | string path = AssetDatabase.GUIDToAssetPath(assets[i]); 84 | scenes[i] = AssetDatabase.LoadAssetAtPath(path); 85 | } 86 | 87 | return scenes.FirstOrDefault(sceneAsset => sceneAsset.name == sceneName); 88 | } 89 | 90 | return null; 91 | } 92 | 93 | private static SceneAsset GetSceneByBuildIndex(int buildIndex) 94 | { 95 | string path = SceneUtility.GetScenePathByBuildIndex(buildIndex); 96 | 97 | if (!string.IsNullOrEmpty(path)) 98 | return AssetDatabase.LoadAssetAtPath(path); 99 | 100 | return null; 101 | } 102 | 103 | private static bool IsSceneInBuildPath(SceneAsset sceneAsset) 104 | { 105 | string path = AssetDatabase.GetAssetPath(sceneAsset); 106 | return SceneUtility.GetBuildIndexByScenePath(path) != -1; 107 | } 108 | 109 | private static void AddSceneToBuild(SceneAsset sceneAsset) 110 | { 111 | string path = AssetDatabase.GetAssetPath(sceneAsset); 112 | 113 | List scenesInBuild = 114 | new List(EditorBuildSettings.scenes); 115 | 116 | 117 | scenesInBuild.Add(new EditorBuildSettingsScene(path, true)); 118 | EditorBuildSettings.scenes = scenesInBuild.ToArray(); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/ScenePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 621302cca2e34ff79848c6108178c610 3 | timeCreated: 1607375115 -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/TagPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using UnityEditor; 4 | using UnityEngine.Productivity.Attributes; 5 | 6 | namespace UnityEngine.Productivity.Editor.PropertyDrawers 7 | { 8 | [CustomPropertyDrawer(typeof(TagAttribute))] 9 | public class TagPropertyDrawer : PropertyDrawer 10 | { 11 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 12 | { 13 | if (property.propertyType != SerializedPropertyType.String) 14 | { 15 | //Debug.LogWarning($"Tag-Attribute was used on a non-string property: {property.serializedObject.targetObject.GetType().ToString()}.{property.name}"); 16 | EditorGUI.PropertyField(position, property, label); 17 | return; 18 | } 19 | 20 | string value = property.stringValue; 21 | int index = Array.IndexOf(UnityEditorInternal.InternalEditorUtility.tags, value); 22 | 23 | if (index < 0) 24 | index = 0; 25 | 26 | EditorGUI.BeginProperty(position, label, property); 27 | 28 | Rect valueEditPosition = EditorGUI.PrefixLabel(position, new GUIContent(property.displayName)); 29 | index = EditorGUI.Popup(valueEditPosition, index, UnityEditorInternal.InternalEditorUtility.tags); 30 | 31 | property.stringValue = UnityEditorInternal.InternalEditorUtility.tags[index]; 32 | 33 | EditorGUI.EndProperty(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Productivity/Editor/PropertyDrawers/TagPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6658098b3e174606abc4e9c7b42f1df9 3 | timeCreated: 1603726939 -------------------------------------------------------------------------------- /Productivity/Editor/Utils.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d3cdc6794dce4e518ee4f977d52f3043 3 | timeCreated: 1607376665 -------------------------------------------------------------------------------- /Productivity/Editor/Utils/EditorMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | 4 | namespace UnityEngine.Productivity.Editor.Utils 5 | { 6 | public static class EditorMessage 7 | { 8 | private const int HelpBoxIconSize = 24; 9 | private const int HelpBoxIconSpace = 32; 10 | private const int HelpBoxButtonMargin = 4; 11 | private const int InspectorDefaultPadding = 4; 12 | 13 | public static void Display(string message) 14 | { 15 | EditorGUILayout.HelpBox(message, MessageType.None); 16 | } 17 | 18 | public static void DisplayInfo(string infoMessage) 19 | { 20 | EditorGUILayout.HelpBox(infoMessage, MessageType.Error); 21 | } 22 | 23 | public static void DisplayError(string errorMessage) 24 | { 25 | EditorGUILayout.HelpBox(errorMessage, MessageType.Error); 26 | } 27 | 28 | public static void DisplayWarning(string warningMessage) 29 | { 30 | EditorGUILayout.HelpBox(warningMessage, MessageType.Warning); 31 | } 32 | 33 | public static void Display(string message, string actionName, Action actionCallback) 34 | { 35 | DisplayMessage(message, actionName, actionCallback, MessageType.None); 36 | } 37 | 38 | public static void DisplayInfo(string infoMessage, string actionName, Action actionCallback) 39 | { 40 | DisplayMessage(infoMessage, actionName, actionCallback, MessageType.Info); 41 | } 42 | 43 | public static void DisplayError(string errorMessage, string actionName, Action actionCallback) 44 | { 45 | DisplayMessage(errorMessage, actionName, actionCallback, MessageType.Error); 46 | } 47 | 48 | public static void DisplayWarning(string warningMessage, string actionName, Action actionCallback) 49 | { 50 | DisplayMessage(warningMessage, actionName, actionCallback, MessageType.Warning); 51 | } 52 | 53 | 54 | public static void DisplayMessage(string message, string actionName, Action actionCallback, MessageType messageType) 55 | { 56 | string iconStyle = GetIconStyleForMessageType(messageType); 57 | bool showIcon = !string.IsNullOrEmpty(iconStyle); 58 | 59 | float textWidth = (showIcon) ? EditorGUIUtility.currentViewWidth - HelpBoxIconSpace : EditorGUIUtility.currentViewWidth; 60 | 61 | using (var messageScope = new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) 62 | { 63 | GUIContent content = new GUIContent(message); 64 | GUIStyle style = EditorStyles.helpBox; 65 | 66 | float height = style.CalcHeight(content, textWidth); 67 | 68 | if (showIcon) 69 | { 70 | GUI.Box( 71 | new Rect(messageScope.rect.x + InspectorDefaultPadding, 72 | messageScope.rect.y + InspectorDefaultPadding, HelpBoxIconSize, HelpBoxIconSize), 73 | GUIContent.none, iconStyle); 74 | 75 | height = Mathf.Max(HelpBoxIconSpace + HelpBoxButtonMargin, height); 76 | } 77 | 78 | 79 | Rect contentRect = EditorGUILayout.GetControlRect(false, height); 80 | 81 | if (showIcon) 82 | { 83 | contentRect.x += HelpBoxIconSpace; 84 | contentRect.width -= HelpBoxIconSpace; 85 | } 86 | 87 | EditorGUI.LabelField(contentRect, string.Empty, message, EditorStyles.miniLabel); 88 | if (GUILayout.Button(actionName)) 89 | { 90 | actionCallback.Invoke(); 91 | } 92 | } 93 | } 94 | 95 | private static string GetIconStyleForMessageType(MessageType messageType) 96 | { 97 | switch (messageType) 98 | { 99 | case MessageType.Warning: 100 | return "CN EntryWarnIcon"; 101 | case MessageType.Error: 102 | return "CN EntryErrorIcon"; 103 | case MessageType.Info: 104 | return "CN EntryInfoIcon"; 105 | default: 106 | return null; 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Productivity/Editor/Utils/EditorMessage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a5c1702dcb24f85b84a51a1261b8112 3 | timeCreated: 1607376681 -------------------------------------------------------------------------------- /Productivity/Editor/io.tinu.unity.productivity.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "io.tinu.unity.productivity.Editor", 3 | "references": [ 4 | "io.tinu.unity.productivity" 5 | ], 6 | "includePlatforms": [ 7 | "Editor" 8 | ], 9 | "excludePlatforms": [] 10 | } -------------------------------------------------------------------------------- /Productivity/Editor/io.tinu.unity.productivity.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ca3d388457588a46ab120b447d45189 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Productivity/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba937cfc2859f7842bfd92b99ef02796 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fab18610eeea90b44b54de82b227b7f4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/ButtonAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | public enum ButtonAvailability 6 | { 7 | Always, 8 | Editor, 9 | Play 10 | } 11 | 12 | [AttributeUsage(AttributeTargets.Method)] 13 | public class ButtonAttribute : PropertyAttribute 14 | { 15 | public string Name { get; private set; } 16 | public ButtonAvailability Availability { get; private set; } 17 | 18 | public ButtonAttribute() {} 19 | 20 | public ButtonAttribute(string name) 21 | { 22 | Name = name; 23 | } 24 | 25 | public ButtonAttribute(ButtonAvailability availability) 26 | { 27 | Availability = availability; 28 | } 29 | 30 | /// 31 | /// Adds a button to the inspector to invoke the method with this attribute 32 | /// 33 | /// Name that shows up on the button 34 | /// The availability of the button (Design Time, Play or both). 35 | public ButtonAttribute(string name, ButtonAvailability availability) 36 | { 37 | Name = name; 38 | Availability = availability; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/ButtonAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 587fb8b7df0ba084b87d341f1430db44 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/HelpAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class HelpAttribute : PropertyAttribute 7 | { 8 | public enum MessageType 9 | { 10 | None, 11 | Info, 12 | Warning, 13 | Error 14 | } 15 | 16 | 17 | public string Message { get; } 18 | public MessageType Type { get; } 19 | 20 | public HelpAttribute(string message, MessageType type = MessageType.Info) 21 | { 22 | Message = message; 23 | Type = type; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/HelpAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fcb19b20b9194e2d85838be67b45a629 3 | timeCreated: 1607385186 -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/MinMaxRangeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class MinMaxRangeAttribute : PropertyAttribute 7 | { 8 | public float Min { get; private set; } 9 | public float Max { get; private set; } 10 | 11 | public float StepSize { get; private set; } 12 | 13 | public MinMaxRangeAttribute(float min, float max, float stepSize = 1f) 14 | { 15 | StepSize = stepSize; 16 | 17 | if (Min <= Max) 18 | { 19 | Min = min; 20 | Max = max; 21 | } 22 | else 23 | { 24 | Min = max; 25 | Max = min; 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/MinMaxRangeAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7dc599e7c3914d1996aa6a9dfc95377a 3 | timeCreated: 1606159159 -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/NavMeshAreaMaskAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class NavMeshAreaMaskAttribute : PropertyAttribute 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/NavMeshAreaMaskAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfa14831f5d743acac7e981a40d0babf 3 | timeCreated: 1604151416 -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/SceneAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class SceneAttribute : PropertyAttribute 7 | { 8 | public bool InBuildCheck { get; } 9 | 10 | /// 11 | /// Attribute to display the string as a SceneAsset in the inspector. 12 | /// 13 | /// If true the inspector shows a warning when the scene is not in the build. 14 | public SceneAttribute(bool inBuildCheck = true) 15 | { 16 | InBuildCheck = inBuildCheck; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/SceneAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44bec9db66ed48309f57b015705ecca1 3 | timeCreated: 1607375048 -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/TagAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityEngine.Productivity.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class TagAttribute : PropertyAttribute 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /Productivity/Runtime/Attributes/TagAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 88d636172379c0346b61e9ce0674b890 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Productivity/Runtime/TransformUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace UnityEngine.Productivity 5 | { 6 | public static class TransformUtils 7 | { 8 | /// 9 | /// Gets all parents of this Transform in order from closest to farthest. 10 | /// 11 | /// The transform to get the parents from 12 | /// All parents from closest to farthest 13 | public static Transform[] GetParents(this Transform transform) 14 | { 15 | List parents = new List(); 16 | Transform nextTransform = transform; 17 | 18 | while (nextTransform.parent != null) 19 | { 20 | nextTransform = nextTransform.parent; 21 | parents.Add(nextTransform); 22 | } 23 | 24 | return parents.ToArray(); 25 | } 26 | 27 | /// 28 | /// Gets the Transforms depth in the scene hierarchy. 29 | /// 30 | /// The Transform to get the hierarchy depth from 31 | /// A number representing the depth in the hierarchy (0 at the root) 32 | public static int GetHierarchyDepth(this Transform transform) 33 | { 34 | int depth = 0; 35 | while (transform != null) 36 | { 37 | transform = transform.parent; 38 | ++depth; 39 | } 40 | 41 | return depth; 42 | } 43 | 44 | /// 45 | /// Gets all parents of this Transform in order from closest to farthest. 46 | /// 47 | /// The transform to get the parents from 48 | /// All parents from closest to farthest 49 | public static Transform[] GetTransformParents(Transform transform) 50 | { 51 | return transform.GetParents(); 52 | } 53 | 54 | /// 55 | /// Gets the Transforms depth in the scene hierarchy. 56 | /// 57 | /// The Transform to get the hierarchy depth from 58 | /// A number representing the depth in the hierarchy (0 at the root) 59 | public static int GetTransformDepth(Transform transform) 60 | { 61 | return transform.GetHierarchyDepth(); 62 | } 63 | 64 | /// 65 | /// Gets the closest sharing parent Transform of all the passed Transforms. 66 | /// 67 | /// Transforms to lookup the closest sharing parent 68 | /// The closest sharing parent Transform 69 | public static Transform GetClosestSharingParent(Transform[] transforms) 70 | { 71 | Dictionary parentOccurrences = new Dictionary(); 72 | 73 | for (int i = 0; i < transforms.Length; ++i) 74 | { 75 | Transform[] parents = transforms[i].GetParents(); 76 | foreach (var parent in parents) 77 | { 78 | if (!parentOccurrences.ContainsKey(parent)) 79 | parentOccurrences.Add(parent, 0); 80 | 81 | ++parentOccurrences[parent]; 82 | } 83 | } 84 | 85 | Transform sharingParent = null; 86 | int highestDepth = 0; 87 | 88 | foreach (var entry in parentOccurrences) 89 | { 90 | if (entry.Value == transforms.Length) 91 | { 92 | int depth = entry.Key.GetHierarchyDepth(); 93 | if (depth > highestDepth) 94 | { 95 | sharingParent = entry.Key; 96 | highestDepth = depth; 97 | } 98 | } 99 | } 100 | 101 | return sharingParent; 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /Productivity/Runtime/TransformUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7b6d39061f3a44f1b44d1503c17f9b32 3 | timeCreated: 1603979179 -------------------------------------------------------------------------------- /Productivity/Runtime/io.tinu.unity.productivity.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "io.tinu.unity.productivity" 3 | } -------------------------------------------------------------------------------- /Productivity/Runtime/io.tinu.unity.productivity.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fb34033d482feef45bc491afc3fc5bf8 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Productivity Tools 2 | Extends the Unity Editor with simple productivity tools like an easy way to serialize SceneAssets in your component, grouping GameObjects in the scene and more. 3 | 4 | - [Scene View / Hierarchy](#scene-view--hierarchy) 5 | - [Grouping](#grouping) 6 | - [Attributes](#attributes) 7 | - [Button](#button) 8 | - [Help](#help) 9 | - [MinMaxRange](#minmaxrange) 10 | - [NavMeshAreaMask](#navmeshareamask) 11 | - [Scene](#scene) 12 | - [Tag](#tag) 13 | 14 | - [Install](#install) 15 | - [License](#license) 16 | 17 | 18 | ## Scene View / Hierarchy 19 | ### Grouping 20 | By selecting more than one GameObject in the Scene or in the Hierarchy you can group the selected objects by clicking on **Edit > Group** or by pressing Ctrl + G. 21 | 22 | 23 | ## Attributes 24 | Unity allows you to use Attributes on your properties and fields. UPT extends the set of Attributes with these custom attributes: 25 | 26 | ### Button 27 | `[Button (ButtonAvailability availability) ]` 28 | 29 | Displays a button at the top of the inspector which invokes the assigned Method.
Methods with params will be displayed as a foldout. 30 |
Limited to methods. 31 |
**Example:**
32 | ```c# 33 | [Button(ButtonAvailability.Play)] 34 | private void MyTestMethod(int testValue) 35 | { 36 | ... 37 | } 38 | ``` 39 | 40 | 41 | ### Help 42 | `[Help (string message, MessageType type = MessageType.Info) ]` 43 | 44 | Displays a HelpBox above any property. 45 |
**Example:**
46 | ```c# 47 | [Help("Little description on how you should set this variable")] 48 | public Vector3 spawnPosition; 49 | ```` 50 | 51 | ### MinMaxRange 52 | `[MinMaxRange (float min, float max, float stepSize = 1f) ]` 53 | 54 | Displays the property as a slider with a min and max value to choose from. 55 |
Limited to `float`. 56 |
**Example:**
57 | ```c# 58 | [MinMaxRange(0, 100, 1)] 59 | public Vector2 spawnDelayRange; 60 | ``` 61 | 62 | ### NavMeshAreaMask 63 | `[NavMeshAreaMask]` 64 | 65 | Displays the property as a Mask-Selection for NavMeshAreas similar to LayerMask for Layers. 66 |
Limited to `int`. 67 |
**Example:**
68 | ```c# 69 | [NavMeshAreaMask] 70 | public int walkableMask; 71 | ``` 72 | 73 | ### Scene 74 | `[Scene (inBuildCheck = true) ]` 75 | 76 | Displays the property as a SceneAsset which allows to drag & drop or select Scenes from the project folder. 77 |
Limited to `string`. 78 |
**Example:**
79 | ```c# 80 | [Scene] 81 | public string menuScene; 82 | ``` 83 | 84 | 85 | ### Tag 86 | `[Tag]` 87 | 88 | Displays the property as a dropdown with all the defined Tags. 89 |
Limited to `string`. 90 |
**Example:**
91 | ```c# 92 | [Tag] 93 | public string teamTag; 94 | ``` 95 | 96 | 97 | # Install 98 | * In Unity open the Package Manager (`Window > Package Manager`). 99 | * In the Package Manager click on the Plus-Icon in the top-left and select `Add package from git URL...` 100 | * Enter the URL of this Repository (`https://github.com/martinhodler/unity-productivity-tools.git`) and press Enter 101 | 102 | 103 | # License 104 | See [LICENSE.md](LICENSE.md) 105 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7725a6b1f2614f54293cf1f69a2c578e 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "io.tinu.unity-productivity-tools", 3 | "version": "1.0.0", 4 | "displayName": "Unity Productivity Tools", 5 | "description": "Expands the Unity Editor with simple productivity tools", 6 | "unity": "2019.1", 7 | "unityRelease": "0b5", 8 | "dependencies": { 9 | }, 10 | "keywords": [ 11 | "editor", 12 | "productivity", 13 | "tools" 14 | ], 15 | "author": { 16 | "name": "Martin Hodler (tinu.io)", 17 | "email": "contact@tinu.io", 18 | "url": "https://tinu.io" 19 | } 20 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4765eae0a7d0ca54caf38f67a1c22cd8 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------