├── .gitignore ├── Editor.meta ├── Editor ├── DevSlem.MoveTool.Editor.asmdef ├── DevSlem.MoveTool.Editor.asmdef.meta ├── MoveToolDrawer.cs ├── MoveToolDrawer.cs.meta ├── MoveToolEditor.cs └── MoveToolEditor.cs.meta ├── Images.meta ├── Images ├── move-tool-collection.webp ├── move-tool-collection.webp.meta ├── move-tool-custom-type-collection.webp ├── move-tool-custom-type-collection.webp.meta ├── move-tool-float.webp ├── move-tool-float.webp.meta ├── move-tool-vector2.webp ├── move-tool-vector2.webp.meta ├── move-tool-vector3.webp └── move-tool-vector3.webp.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── DevSlem.MoveTool.asmdef ├── DevSlem.MoveTool.asmdef.meta ├── MoveToolAttributes.cs ├── MoveToolAttributes.cs.meta ├── StringExtension.cs └── StringExtension.cs.meta ├── Samples~ ├── SamplesMoveTool.meta └── SamplesMoveTool │ ├── MoveTool Sample.prefab │ ├── MoveTool Sample.prefab.meta │ ├── MoveToolSample.cs │ ├── MoveToolSample.cs.meta │ ├── MoveToolSampleScene.unity │ └── MoveToolSampleScene.unity.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Uu]ser[Ss]ettings/ 12 | 13 | # MemoryCaptures can get excessive in size. 14 | # They also could contain extremely sensitive data 15 | /[Mm]emoryCaptures/ 16 | 17 | # Recordings can get excessive in size 18 | /[Rr]ecordings/ 19 | 20 | # Uncomment this line if you wish to ignore the asset store tools plugin 21 | # /[Aa]ssets/AssetStoreTools* 22 | 23 | # Autogenerated Jetbrains Rider plugin 24 | /[Aa]ssets/Plugins/Editor/JetBrains* 25 | 26 | # Visual Studio cache directory 27 | .vs/ 28 | 29 | # Gradle cache directory 30 | .gradle/ 31 | 32 | # Autogenerated VS/MD/Consulo solution and project files 33 | ExportedObj/ 34 | .consulo/ 35 | *.csproj 36 | *.unityproj 37 | *.sln 38 | *.suo 39 | *.tmp 40 | *.user 41 | *.userprefs 42 | *.pidb 43 | *.booproj 44 | *.svd 45 | *.pdb 46 | *.mdb 47 | *.opendb 48 | *.VC.db 49 | 50 | # Unity3D generated meta files 51 | *.pidb.meta 52 | *.pdb.meta 53 | *.mdb.meta 54 | 55 | # Unity3D generated file on crash reports 56 | sysinfo.txt 57 | 58 | # Builds 59 | *.apk 60 | *.aab 61 | *.unitypackage 62 | *.app 63 | 64 | # Crashlytics generated file 65 | crashlytics-build.properties 66 | 67 | # Packed Addressables 68 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 69 | 70 | # Temporary auto-generated Android Assets 71 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 72 | /[Aa]ssets/[Ss]treamingAssets/aa/* -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30ce4f09ef0b16d4493dc4c5350555db 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/DevSlem.MoveTool.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DevSlem.MoveTool.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:a31c1babfbfb4654fb3f28ab7a1ed780" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /Editor/DevSlem.MoveTool.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c240c42246d5ecb42a73b23d9c9fdab2 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/MoveToolDrawer.cs: -------------------------------------------------------------------------------- 1 | //#define MOVETOOLDRAWER_DEPRECATED 2 | using System.Collections; 3 | using System.Linq; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using System.Collections.Generic; 7 | using DevSlem.Extensions; 8 | 9 | namespace DevSlem.UnityEditor 10 | { 11 | #if UNITY_EDITOR 12 | /// 13 | /// Display the position mode of MoveToolAttribute on editor inspector. 14 | /// 15 | [CustomPropertyDrawer(typeof(MoveToolAttribute), true)] 16 | public sealed class MoveToolDrawer : PropertyDrawer 17 | { 18 | // For setting MoveToolEditors 19 | private static readonly MoveToolEditorManager manager = new MoveToolEditorManager(); 20 | private bool isArray; 21 | 22 | #if MOVETOOLDRAWER_DEPRECATED 23 | private static GameObject targetGameObject; 24 | private static List moveToolEditors = new List(); 25 | private static bool isAdded; 26 | 27 | public MoveToolDrawer() 28 | { 29 | if (!isAdded) 30 | { 31 | SceneView.duringSceneGui += SetMoveTool; 32 | isAdded = true; 33 | } 34 | } 35 | #endif 36 | 37 | private class MoveToolEditorManager 38 | { 39 | private Dictionary container; 40 | private GameObject targetGameObject; 41 | 42 | public MoveToolEditorManager() 43 | { 44 | container = new Dictionary(); 45 | SceneView.duringSceneGui += SetMoveTool; 46 | } 47 | 48 | private void SetMoveTool(SceneView obj) 49 | { 50 | // If there's no target game-object, destory every old editors and terminate. 51 | if (Selection.activeGameObject == null) 52 | { 53 | foreach (var moveTool in container.Values) 54 | Object.DestroyImmediate(moveTool.editor); 55 | 56 | container.Clear(); 57 | targetGameObject = null; 58 | return; 59 | } 60 | 61 | // for prefab game-object 62 | if (Selection.activeGameObject.scene.name == null) 63 | { 64 | foreach (var moveTool in container.Values) 65 | Object.DestroyImmediate(moveTool.editor); 66 | 67 | container.Clear(); 68 | return; 69 | } 70 | 71 | var monoBehaviours = Selection.activeGameObject.GetComponents(); 72 | // If active target game-object is changed. 73 | if (Selection.activeGameObject != targetGameObject) 74 | { 75 | // Destory every old editors. 76 | foreach (var moveTool in container.Values) 77 | Object.DestroyImmediate(moveTool.editor); 78 | container.Clear(); 79 | 80 | // Create editors of new object. 81 | targetGameObject = Selection.activeGameObject; 82 | for (int i = 0; i < monoBehaviours.Length; i++) 83 | container[monoBehaviours[i]] = (Editor.CreateEditor(monoBehaviours[i], typeof(MoveToolEditor)) as MoveToolEditor, true); 84 | } 85 | 86 | // If a new component is added to the target game-object. 87 | if (monoBehaviours.Length > container.Count) 88 | { 89 | var targets = container.Values.Select(m => m.editor.target); 90 | for (int i = 0; i < monoBehaviours.Length; i++) 91 | { 92 | if (!targets.Contains(monoBehaviours[i])) 93 | { 94 | container[monoBehaviours[i]] = (Editor.CreateEditor(monoBehaviours[i], typeof(MoveToolEditor)) as MoveToolEditor, true); 95 | } 96 | } 97 | } 98 | // If a component is removed or switched from the target game-object. 99 | else 100 | { 101 | var keys = container.Keys.ToArray(); 102 | foreach (var key in keys) 103 | { 104 | var editor = container[key].editor; 105 | if (editor.target == null) 106 | { 107 | Object.DestroyImmediate(editor); 108 | container.Remove(key); 109 | } 110 | } 111 | } 112 | 113 | // Set move-tool. 114 | try 115 | { 116 | foreach (var pair in container) 117 | { 118 | if (pair.Value.isNeeded && !pair.Value.editor.SetMoveTool()) 119 | { 120 | container[pair.Key] = (pair.Value.editor, false); 121 | Object.DestroyImmediate(pair.Value.editor); 122 | } 123 | } 124 | } 125 | catch (System.Exception e) 126 | { 127 | Debug.LogException(e); 128 | } 129 | } 130 | } 131 | 132 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 133 | { 134 | // Check to change move-tool editor 135 | //CheckToChangeMoveToolEditor(property); 136 | 137 | var attr = attribute as MoveToolAttribute; 138 | 139 | // if LabelMode doesn't contain inspector-view, just display default property field in the inpsector. 140 | if (!attr.LabelMode.HasFlag(MoveToolLabel.InspectorView)) 141 | { 142 | EditorGUI.PropertyField(position, property, label, true); 143 | return; 144 | } 145 | 146 | 147 | string[] splitPath = property.propertyPath.Split('.'); 148 | 149 | // If property is just single field. 150 | if (splitPath.LastOrDefault(p => p == "Array") == null) 151 | { 152 | label.text = string.IsNullOrEmpty(attr.Label) ? property.name.InspectorLabel() : attr.Label; 153 | EditorGUI.PropertyField(position, property, label, true); // Default form 154 | 155 | var modePos = position; 156 | modePos.y += EditorGUI.GetPropertyHeight(property, true); 157 | modePos.height = EditorGUIUtility.singleLineHeight; 158 | DisplayModeInfo(modePos); 159 | } 160 | // If property is the element of a collection. 161 | else 162 | { 163 | this.isArray = true; 164 | ICollection field = fieldInfo.GetValue(property.serializedObject.targetObject) as ICollection; 165 | int idx = GetCollectionPropertyIndex(splitPath[splitPath.Length - 1]); 166 | bool isLast = idx + 1 == (field?.Count ?? -1); 167 | 168 | label.text = (string.IsNullOrEmpty(attr.Label) ? fieldInfo.Name.InspectorLabel() : attr.Label) + $" [{idx}]"; 169 | 170 | EditorGUI.PropertyField(position, property, label, true); // Default form 171 | 172 | // If this property is the last of the collection 173 | if (isLast) 174 | { 175 | var modePos = position; 176 | modePos.height = EditorGUIUtility.singleLineHeight; 177 | modePos.y += modePos.height + EditorGUI.GetPropertyHeight(property) - 2f * EditorGUIUtility.standardVerticalSpacing; 178 | DisplayModeInfo(modePos); 179 | } 180 | } 181 | } 182 | 183 | private int GetCollectionPropertyIndex(string str) 184 | { 185 | if (str != null) 186 | { 187 | (int start, int end) = (str.LastIndexOf('['), str.LastIndexOf(']')); 188 | if (start >= 0 && end > start + 1 && int.TryParse(str.Substring(start + 1, end - start - 1), out int result)) 189 | return result; 190 | } 191 | return -1; 192 | } 193 | 194 | public override float GetPropertyHeight(SerializedProperty property, GUIContent label) 195 | { 196 | return EditorGUI.GetPropertyHeight(property, true) + (this.isArray ? 0f : EditorGUIUtility.singleLineHeight); 197 | } 198 | 199 | // Display current position mode. 200 | private void DisplayModeInfo(Rect position) 201 | { 202 | var indent = EditorGUI.indentLevel; 203 | EditorGUI.indentLevel = 1; 204 | 205 | string modeLabel = (attribute as MoveToolAttribute).PositionMode.ToString(); 206 | EditorGUI.Foldout(position, false, $"Position Mode - {modeLabel}"); // Display information. 207 | 208 | // Set indent back to what it was 209 | EditorGUI.indentLevel = indent; 210 | 211 | //Debug.Log(EditorGUIUtility.singleLineHeight); 212 | //EditorGUI.Space(this.modeInfoHeight); 213 | } 214 | 215 | #if MOVETOOLDRAWER_DEPRECATED 216 | [System.Obsolete] 217 | private void CheckToChangeMoveToolEditor(SerializedProperty property) 218 | { 219 | if (moveToolEditors.Count > 0) 220 | return; 221 | 222 | var target = property.serializedObject.targetObject; 223 | var targetGameObject = fieldInfo.ReflectedType.GetProperty("gameObject").GetValue(target) as GameObject; 224 | var monoBehaviours = targetGameObject.GetComponents(); 225 | for (int i = 0; i < monoBehaviours.Length; i++) 226 | { 227 | moveToolEditors.Add(Editor.CreateEditor(monoBehaviours[i], typeof(MoveToolEditor)) as MoveToolEditor); 228 | MoveToolDrawer.targetGameObject = targetGameObject; 229 | } 230 | } 231 | 232 | [System.Obsolete] 233 | private static void SetMoveTool(SceneView obj) 234 | { 235 | // If there's no target game-object, destory every old editors and terminate. 236 | if (Selection.activeGameObject == null) 237 | { 238 | for (int i = 0; i < moveToolEditors.Count; i++) 239 | Object.DestroyImmediate(moveToolEditors[i]); 240 | 241 | moveToolEditors.Clear(); 242 | targetGameObject = null; 243 | return; 244 | } 245 | 246 | var monoBehaviours = Selection.activeGameObject.GetComponents(); 247 | // If active target game-object is changed. 248 | if (Selection.activeGameObject != targetGameObject) 249 | { 250 | // Destory every old editors. 251 | for (int i = 0; i < moveToolEditors.Count; i++) 252 | Object.DestroyImmediate(moveToolEditors[i]); 253 | moveToolEditors.Clear(); 254 | 255 | // Create editors of new object. 256 | targetGameObject = targetGameObject = Selection.activeGameObject; 257 | for (int i = 0; i < monoBehaviours.Length; i++) 258 | { 259 | //Debug.Log($"{monoBehaviours[i].GetType().Name} : {monoBehaviours[i].GetInstanceID()}"); 260 | moveToolEditors.Add(Editor.CreateEditor(monoBehaviours[i], typeof(MoveToolEditor)) as MoveToolEditor); 261 | 262 | } 263 | } 264 | 265 | // If a new component is added to the target game-object. 266 | if (monoBehaviours.Length > moveToolEditors.Count) 267 | { 268 | var targets = moveToolEditors.Select(e => e.target); 269 | for (int i = 0; i < monoBehaviours.Length; i++) 270 | { 271 | if (!targets.Contains(monoBehaviours[i])) 272 | { 273 | moveToolEditors.Add(Editor.CreateEditor(monoBehaviours[i], typeof(MoveToolEditor)) as MoveToolEditor); 274 | //Debug.Log($"{monoBehaviours[i].GetType().Name} : {monoBehaviours[i].GetInstanceID()}"); 275 | } 276 | } 277 | } 278 | // If a component is removed from the target game-object. 279 | else 280 | { 281 | for (int i = 0; i < moveToolEditors.Count; i++) 282 | { 283 | if (moveToolEditors[i].target == null) 284 | { 285 | Object.DestroyImmediate(moveToolEditors[i]); 286 | moveToolEditors.RemoveAt(i--); 287 | } 288 | } 289 | } 290 | 291 | // Set move-tool. 292 | try 293 | { 294 | for (int i = 0; i < moveToolEditors.Count; i++) 295 | moveToolEditors[i].SetMoveTool(); 296 | } 297 | catch (System.Exception e) 298 | { 299 | Debug.LogException(e); 300 | } 301 | 302 | } 303 | #endif 304 | 305 | } 306 | #endif 307 | } 308 | -------------------------------------------------------------------------------- /Editor/MoveToolDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: add52d0d500eb5b4f953b2706f7a2baa 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/MoveToolEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using DevSlem.Extensions; 8 | 9 | namespace DevSlem.UnityEditor 10 | { 11 | #if UNITY_EDITOR 12 | /// 13 | /// It sets position handles for the fields that define MoveToolAttribute. Note that it's an editor for MonoBehaviour. 14 | /// 15 | [CustomEditor(typeof(MonoBehaviour), false)] 16 | public class MoveToolEditor : Editor 17 | { 18 | private static readonly GUIStyle style = new GUIStyle(); 19 | 20 | private readonly static Vector3[] axisVector = new Vector3[3] 21 | { 22 | Vector3.right, 23 | Vector3.up, 24 | Vector3.forward 25 | }; 26 | 27 | public void OnEnable() 28 | { 29 | style.fontStyle = FontStyle.Bold; 30 | style.normal.textColor = Color.white; 31 | } 32 | 33 | public void OnSceneGUI() 34 | { 35 | SetOnlyHasMoveToolAttribute(); 36 | } 37 | 38 | /// 39 | /// Run MoveToolEditor. You can use move-tools of fields which define MoveToolAttribute in your monobehavior class. 40 | /// 41 | public bool SetMoveTool() 42 | { 43 | style.fontStyle = FontStyle.Bold; 44 | style.normal.textColor = Color.white; 45 | 46 | return SetOnlyHasMoveToolAttribute(); 47 | } 48 | 49 | private bool SetOnlyHasMoveToolAttribute() 50 | { 51 | var targetType = target.GetType(); 52 | var fields = GetSerializedFields(targetType); 53 | bool isExisting = false; 54 | foreach (var field in fields) 55 | { 56 | // Check if MoveToolAttribute is defined. 57 | var attr = field.GetCustomAttribute(false); 58 | if (attr == null) 59 | continue; 60 | isExisting = true; 61 | SetMoveToolAvailableField((field, -1), (this.target, field, -1), attr); 62 | } 63 | 64 | return isExisting; 65 | } 66 | 67 | /// 68 | /// Set Position Handles in the unity editor scene view. 69 | /// 70 | /// top level field declared in the MonoBehaviour component 71 | /// current field checked now, current.obj is the instance where current.field is declared 72 | /// defined for the top level field 73 | /// Don't set any value. It's the count of recursive calls. 74 | private void SetMoveToolAvailableField((FieldInfo field, int index) top, (object obj, FieldInfo field, int index) current, MoveToolAttribute attr, int n = 0) 75 | { 76 | // If it's vector, call immediately SetPositionHandle() method and then terminate. 77 | object fieldValue = current.field.GetValue(current.obj); 78 | 79 | if (IsVector(fieldValue)) 80 | { 81 | string label = string.Empty; 82 | if (attr.LabelMode.HasFlag(MoveToolLabel.SceneView)) 83 | { 84 | label = string.IsNullOrEmpty(attr.Label) ? AddIndexLabel(top.field.Name.InspectorLabel(), top.index) : AddIndexLabel(attr.Label, top.index); 85 | if (top.field != current.field) 86 | label += $" - {(n > 1 ? AddIndexLabel(current.field.Name.InspectorLabel(), current.index, true) : current.field.Name.InspectorLabel())}"; 87 | } 88 | 89 | SetVectorField(current.obj, current.field, label, attr.PositionMode); 90 | return; 91 | } 92 | 93 | var fieldType = current.field.FieldType; //current field type 94 | 95 | // Array field, List field, Custom type field which inherit from IList 96 | if (fieldType.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)) is var listType && listType != null) 97 | { 98 | var elementType = listType.GetGenericArguments()[0]; 99 | if (!HasAvailableAttribute(elementType)) 100 | return; 101 | 102 | var serializedFields = GetSerializedFields(elementType); 103 | 104 | var collectionType = listType.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); 105 | var accessor = listType.GetProperty("Item"); 106 | int count = (int)collectionType.GetProperty("Count").GetValue(fieldValue, null); 107 | object[] index = { 0 }; 108 | 109 | for (int i = 0; i < count; i++) 110 | { 111 | if (top.field == current.field) 112 | top.index = i; 113 | 114 | index[0] = i; 115 | object element = accessor.GetValue(fieldValue, index); 116 | 117 | // Recursive call for each field declared in the current field type 118 | foreach (var nextField in serializedFields) 119 | SetMoveToolAvailableField(top, (element, nextField, i), attr, n + 1); 120 | 121 | // If the current element is a value type, you must paste boxed element to this element. 122 | if (elementType.IsValueType) 123 | accessor.SetValue(fieldValue, element, index); 124 | } 125 | } 126 | // Single custom type field which isn't collection 127 | else 128 | { 129 | if (!HasAvailableAttribute(fieldType)) 130 | return; 131 | 132 | var serializedFields = GetSerializedFields(fieldType); 133 | // Recursive call for each field declared in the current field type 134 | object obj = current.field.GetValue(current.obj); 135 | foreach (var nextField in serializedFields) 136 | SetMoveToolAvailableField(top, (obj, nextField, -1), attr, n + 1); 137 | 138 | // If the current field is a value type, you must paste boxed obj to this field. It's because obj isn't the field instance itself, but new boxed instance. 139 | if (fieldType.IsValueType) 140 | current.field.SetValue(current.obj, obj); 141 | } 142 | 143 | #region === Deprecated === 144 | //// Array 145 | //if (fieldType.IsArray) 146 | //{ 147 | // fieldType = fieldType.GetElementType(); 148 | // if (!HasAvailableAttribute(fieldType)) 149 | // return; 150 | 151 | // var serializedFields = GetSerializedFields(fieldType); 152 | // var array = current.field.GetValue(current.obj) as Array; 153 | // for (int i = 0; i < array.Length; i++) 154 | // { 155 | // if (top.field == current.field) 156 | // top.index = i; 157 | 158 | // // Recursive call for each field declared in the element type of current array 159 | // object obj = array.GetValue(i); 160 | // foreach (var nextField in serializedFields) 161 | // SetMoveToolAvailableField(top, (obj, nextField, i), attr, n + 1); 162 | // if (fieldType.IsValueType) 163 | // array.SetValue(obj, i); 164 | // } 165 | //} 166 | //// List 167 | //else if (fieldType.IsGenericType && typeof(IList).IsAssignableFrom(fieldType)) 168 | //{ 169 | // fieldType = fieldType.GetGenericArguments()[0]; 170 | // if (!HasAvailableAttribute(fieldType)) 171 | // return; 172 | 173 | // var serializedFields = GetSerializedFields(fieldType); 174 | // var list = current.field.GetValue(current.obj) as IList; 175 | // for (int i = 0; i < list.Count; i++) 176 | // { 177 | // if (top.field == current.field) 178 | // top.index = i; 179 | 180 | // // Recursive call for each field declared in the element type of current list 181 | // object obj = list[i]; 182 | // foreach (var nextField in serializedFields) 183 | // SetMoveToolAvailableField(top, (obj, nextField, i), attr, n + 1); 184 | // if (fieldType.IsValueType) 185 | // list[i] = obj; 186 | // } 187 | //} 188 | //// Just single field 189 | //else 190 | //{ 191 | // if (!HasAvailableAttribute(fieldType)) 192 | // return; 193 | 194 | // var serializedFields = GetSerializedFields(fieldType); 195 | 196 | // // Recursive call for each field declared in the current field type 197 | // object obj = current.field.GetValue(current.obj); 198 | // foreach (var nextField in serializedFields) 199 | // SetMoveToolAvailableField(top, (obj, nextField, -1), attr, n + 1); 200 | 201 | // // If current field is a value type, you must copy boxed obj to this field. It's because obj isn't the field instance itself, but new boxed instance. 202 | // if (fieldType.IsValueType) 203 | // current.field.SetValue(current.obj, obj); 204 | //} 205 | #endregion 206 | } 207 | 208 | /// 209 | /// Classify wheter the field is a vector field or a vector collection field and then place position handles of it in unity-editor scene view. 210 | /// 211 | private void SetVectorField(object obj, FieldInfo field, string label, MoveToolPosition mode) 212 | { 213 | // the position origin. 214 | Vector3 origin = Vector3.zero; 215 | 216 | switch (mode) 217 | { 218 | case MoveToolPosition.Local: 219 | // If it's local mode, the origin point is set to target(MonoBehaviour) position. 220 | origin = (this.target as MonoBehaviour).transform.position; 221 | break; 222 | } 223 | 224 | //var fieldType = field.FieldType; 225 | bool shiftClicked = Event.current.shift; // check if you've clicked a shift button. 226 | object fieldValue = field.GetValue(obj); 227 | 228 | // Pattern matching to test the fieldValue to see if it matches a vector type. 229 | switch (fieldValue) 230 | { 231 | case float oldFloat: 232 | field.SetValue(obj, SetPositionHandle(label, origin.x, oldFloat, obj, field, target)); 233 | break; 234 | case Vector3 oldVector3: 235 | field.SetValue(obj, SetPositionHandle(label, origin, oldVector3, obj, field, target)); 236 | break; 237 | case Vector2 oldVector2: 238 | field.SetValue(obj, SetPositionHandle(label, (Vector2)origin, oldVector2, obj, field, target)); 239 | break; 240 | case IList floatList: 241 | for (int i = 0; i < floatList.Count; i++) 242 | { 243 | string temp = label; 244 | if (!string.IsNullOrEmpty(label)) 245 | temp += $" [{i}]"; 246 | 247 | float oldValue = floatList[i]; 248 | float newValue = SetPositionHandle(temp, origin.x, oldValue, obj, field, target); 249 | //SetHandleVector3(temp, origin, oldValue, obj, field, v => list[i] = v); 250 | if (shiftClicked && newValue != oldValue) 251 | { 252 | float delta = newValue - oldValue; 253 | for (int j = 0; j < floatList.Count; j++) 254 | floatList[j] += delta; 255 | break; 256 | } 257 | floatList[i] = newValue; 258 | } 259 | break; 260 | case IList vector3List: 261 | for (int i = 0; i < vector3List.Count; i++) 262 | { 263 | string temp = label; 264 | if (!string.IsNullOrEmpty(label)) 265 | temp += $" [{i}]"; 266 | 267 | Vector3 oldValue = vector3List[i]; 268 | Vector3 newValue = SetPositionHandle(temp, origin, oldValue, obj, field, target); 269 | //SetHandleVector3(temp, origin, oldValue, obj, field, v => list[i] = v); 270 | if (shiftClicked && newValue != oldValue) 271 | { 272 | Vector3 delta = newValue - oldValue; 273 | for (int j = 0; j < vector3List.Count; j++) 274 | vector3List[j] += delta; 275 | break; 276 | } 277 | vector3List[i] = newValue; 278 | } 279 | break; 280 | case IList vector2List: 281 | for (int i = 0; i < vector2List.Count; i++) 282 | { 283 | string temp = label; 284 | if (!string.IsNullOrEmpty(label)) 285 | temp += $" [{i}]"; 286 | 287 | Vector2 oldValue = vector2List[i]; 288 | Vector2 newValue = SetPositionHandle(temp, (Vector2)origin, oldValue, obj, field, target); 289 | //SetHandleVector2(temp, origin, oldValue, obj, field, v => list[i] = v); 290 | if (shiftClicked && newValue != oldValue) 291 | { 292 | Vector2 delta = newValue - oldValue; 293 | for (int j = 0; j < vector2List.Count; j++) 294 | vector2List[j] += delta; 295 | break; 296 | } 297 | vector2List[i] = newValue; 298 | } 299 | break; 300 | // If you want to use position handles of other serializable collection, then add here. 301 | default: 302 | break; 303 | } 304 | 305 | #region === Deprecated === 306 | //if (fieldValue is Vector3 oldVector3) 307 | //{ 308 | // //Vector3 oldValue = (Vector3)field.GetValue(obj); 309 | // field.SetValue(obj, SetPositionHandle(label, origin, oldVector3, obj, field)); 310 | // //SetHandleVector3(label, origin, oldValue, obj, field, v => field.SetValue(obj, v)); 311 | //} 312 | //else if (fieldValue is Vector2 oldVector2) 313 | //{ 314 | // //Vector2 oldValue = (Vector2)field.GetValue(obj); 315 | // field.SetValue(obj, SetPositionHandle(label, (Vector2)origin, oldVector2, obj, field)); 316 | // //SetHandleVector2(label, origin, oldValue, obj, field, v => field.SetValue(obj, v)); 317 | //} 318 | //// Collection which interits from IList interface. Both Array and List belong to it. 319 | //else if (fieldValue is IList vector3List) 320 | //{ 321 | // for (int i = 0; i < vector3List.Count; i++) 322 | // { 323 | // string temp = label; 324 | // if (!string.IsNullOrEmpty(label)) 325 | // temp += $" [{i}]"; 326 | 327 | // Vector3 oldValue = vector3List[i]; 328 | // Vector3 newValue = SetPositionHandle(temp, origin, oldValue, obj, field); 329 | // //SetHandleVector3(temp, origin, oldValue, obj, field, v => list[i] = v); 330 | // if (shiftClicked && newValue != oldValue) 331 | // { 332 | // Vector3 delta = newValue - oldValue; 333 | // for (int j = 0; j < vector3List.Count; j++) 334 | // vector3List[j] += delta; 335 | // break; 336 | // } 337 | // vector3List[i] = newValue; 338 | // } 339 | //} 340 | //else if (fieldValue is IList vector2List) 341 | //{ 342 | // for (int i = 0; i < vector2List.Count; i++) 343 | // { 344 | // string temp = label; 345 | // if (!string.IsNullOrEmpty(label)) 346 | // temp += $" [{i}]"; 347 | 348 | // Vector2 oldValue = vector2List[i]; 349 | // Vector2 newValue = SetPositionHandle(temp, (Vector2)origin, oldValue, obj, field); 350 | // //SetHandleVector2(temp, origin, oldValue, obj, field, v => list[i] = v); 351 | // if (shiftClicked && newValue != oldValue) 352 | // { 353 | // Vector2 delta = newValue - oldValue; 354 | // for (int j = 0; j < vector2List.Count; j++) 355 | // vector2List[j] += delta; 356 | // break; 357 | // } 358 | // vector2List[i] = newValue; 359 | // } 360 | //} 361 | //// Array 362 | //else if (fieldType.GetElementType() == typeof(Vector3)) 363 | //{ 364 | // var array = field.GetValue(obj) as Array; 365 | // for (int i = 0; i < array.Length; i++) 366 | // { 367 | // string temp = label; 368 | // if (!string.IsNullOrEmpty(label)) 369 | // temp += $" [{i}]"; 370 | 371 | // Vector3 oldValue = (Vector3)array.GetValue(i); 372 | // Vector3 newValue = SetPositionHandle(temp, origin, oldValue, obj, field); 373 | // //SetHandleVector3(temp, origin, oldValue, obj, field, v => newValue = v); 374 | // if (shiftClicked && newValue != oldValue) 375 | // { 376 | // Vector3 delta = newValue - oldValue; 377 | // for (int j = 0; j < array.Length; j++) 378 | // array.SetValue((Vector3)array.GetValue(j) + delta, j); 379 | // break; 380 | // } 381 | // array.SetValue(newValue, i); 382 | // } 383 | //} 384 | //else if (fieldType.GetElementType() == typeof(Vector2)) 385 | //{ 386 | // var array = field.GetValue(obj) as Array; 387 | // for (int i = 0; i < array.Length; i++) 388 | // { 389 | // string temp = label; 390 | // if (!string.IsNullOrEmpty(label)) 391 | // temp += $" [{i}]"; 392 | 393 | // Vector2 oldValue = (Vector2)array.GetValue(i); 394 | // Vector2 newValue = SetPositionHandle(temp, (Vector2)origin, oldValue, obj, field); 395 | // //SetHandleVector2(temp, origin, oldValue, obj, field, v => array.SetValue(v, i)); 396 | // if (shiftClicked && newValue != oldValue) 397 | // { 398 | // Vector2 delta = newValue - oldValue; 399 | // for (int j = 0; j < array.Length; j++) 400 | // array.SetValue((Vector2)array.GetValue(j) + delta, j); 401 | // break; 402 | // } 403 | // array.SetValue(newValue, i); 404 | // } 405 | //} 406 | // List 407 | //else if (fieldType == typeof(List)) 408 | //{ 409 | // var list = field.GetValue(obj) as List; 410 | // for (int i = 0; i < list.Count; i++) 411 | // { 412 | // string temp = label; 413 | // if (!string.IsNullOrEmpty(label)) 414 | // temp += $" [{i}]"; 415 | 416 | // Vector3 oldValue = list[i]; 417 | // Vector3 newValue = SetPositionHandle(temp, origin, oldValue, obj, field); 418 | // //SetHandleVector3(temp, origin, oldValue, obj, field, v => list[i] = v); 419 | // if (shiftClicked && newValue != oldValue) 420 | // { 421 | // Vector3 delta = newValue - oldValue; 422 | // for (int j = 0; j < list.Count; j++) 423 | // list[j] += delta; 424 | // break; 425 | // } 426 | // list[i] = newValue; 427 | // } 428 | //} 429 | //else if (fieldType == typeof(List)) 430 | //{ 431 | // var list = field.GetValue(obj) as List; 432 | // for (int i = 0; i < list.Count; i++) 433 | // { 434 | // string temp = label; 435 | // if (!string.IsNullOrEmpty(label)) 436 | // temp += $" [{i}]"; 437 | 438 | // Vector2 oldValue = list[i]; 439 | // Vector2 newValue = SetPositionHandle(temp, (Vector2)origin, oldValue, obj, field); 440 | // //SetHandleVector2(temp, origin, oldValue, obj, field, v => list[i] = v); 441 | // if (shiftClicked && newValue != oldValue) 442 | // { 443 | // Vector2 delta = newValue - oldValue; 444 | // for (int j = 0; j < list.Count; j++) 445 | // list[j] += delta; 446 | // break; 447 | // } 448 | // list[i] = newValue; 449 | // } 450 | //} 451 | #endregion 452 | } 453 | 454 | 455 | /// 456 | /// Create a position handle for the vector3 oldValue. If it's changed, record the taget. 457 | /// 458 | /// changed vector3 459 | public static Vector3 SetPositionHandle(string label, Vector3 origin, Vector3 oldValue, object obj, FieldInfo field, UnityEngine.Object target = null) 460 | { 461 | Handles.Label(origin + oldValue, label, style); 462 | EditorGUI.BeginChangeCheck(); 463 | Vector3 newValue = Handles.PositionHandle(origin + oldValue, Quaternion.identity) - origin; 464 | if (EditorGUI.EndChangeCheck() && target != null) 465 | { 466 | // enable ctrl + z & set dirty 467 | Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 468 | 469 | // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 470 | // But, even if i don't call this method, it works well. I don't know the reason. 471 | PrefabUtility.RecordPrefabInstancePropertyModifications(target); 472 | } 473 | return newValue; 474 | } 475 | 476 | /// 477 | /// Create Position Handle for Vector2. If it's changed, record the target. 478 | /// 479 | /// changed vector2 480 | public static Vector2 SetPositionHandle(string label, Vector2 origin, Vector2 oldValue, object obj, FieldInfo field, UnityEngine.Object target = null) 481 | { 482 | //Handles.Label(origin + oldValue, label, style); 483 | //EditorGUI.BeginChangeCheck(); 484 | //Vector2 newValue = (Vector2)Handles.PositionHandle(origin + oldValue, Quaternion.identity) - origin; 485 | //if (EditorGUI.EndChangeCheck()) 486 | //{ 487 | // // enable ctrl + z & set dirty 488 | // Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 489 | 490 | // // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 491 | // // But, even if i don't call this method, it works well. I don't know the reason. 492 | // PrefabUtility.RecordPrefabInstancePropertyModifications(target); 493 | //} 494 | //return newValue; 495 | 496 | Color[] axisColor = new Color[] 497 | { 498 | Handles.xAxisColor, 499 | Handles.yAxisColor, 500 | Handles.zAxisColor 501 | }; 502 | 503 | Vector3 position = origin + oldValue; 504 | Handles.Label(position, label, style); 505 | EditorGUI.BeginChangeCheck(); 506 | 507 | Handles.color = Handles.zAxisColor; 508 | var size = HandleUtility.GetHandleSize(position) * 0.125f; 509 | var temp = Handles.Slider2D(position + new Vector3(1f, 1f) * size, Vector3.forward, axisVector[0], axisVector[1], size, Handles.RectangleHandleCap, new Vector2(EditorSnapSettings.move[0], EditorSnapSettings.move[1])) 510 | - new Vector3(1f, 1f) * size; 511 | position = (position - temp).sqrMagnitude > 0.001f ? temp : position; 512 | 513 | for (int axis = 0; axis < 2; axis++) 514 | { 515 | Handles.color = axisColor[axis]; 516 | Vector3 dir = axisVector[axis]; 517 | position = Handles.Slider(position, dir, HandleUtility.GetHandleSize(position), Handles.ArrowHandleCap, EditorSnapSettings.move[axis]) 518 | - (Vector3)origin; 519 | } 520 | 521 | if (EditorGUI.EndChangeCheck() && target != null) 522 | { 523 | // enable ctrl + z & set dirty 524 | Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 525 | 526 | // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 527 | // But, even if i don't call this method, it works well. I don't know the reason. 528 | PrefabUtility.RecordPrefabInstancePropertyModifications(target); 529 | } 530 | return position; 531 | } 532 | 533 | public static float SetPositionHandle(string label, float origin, float oldValue, object obj, FieldInfo field, UnityEngine.Object target = null) 534 | { 535 | var position = new Vector3(origin + oldValue, 0f, 0f); 536 | Handles.Label(position, label, style); 537 | EditorGUI.BeginChangeCheck(); 538 | Handles.color = Handles.xAxisColor; 539 | Vector3 newValue = Handles.Slider(position, Vector3.right, HandleUtility.GetHandleSize(position), Handles.ArrowHandleCap, EditorSnapSettings.move.x) 540 | - new Vector3(origin, 0f, 0f); 541 | if (EditorGUI.EndChangeCheck() && target != null) 542 | { 543 | // enable ctrl + z & set dirty 544 | Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 545 | 546 | // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 547 | // But, even if i don't call this method, it works well. I don't know the reason. 548 | PrefabUtility.RecordPrefabInstancePropertyModifications(target); 549 | } 550 | return newValue.x; 551 | } 552 | 553 | /// 554 | /// Check the type for which both MoveToolAvailableAttribute and SerializableAttribute are defined. 555 | /// 556 | private bool HasAvailableAttribute(Type type) 557 | { 558 | var available = type.GetCustomAttribute(false); 559 | var seralizable = type.GetCustomAttribute(false); 560 | return available != null && seralizable != null; 561 | } 562 | 563 | /// 564 | /// Return the serialized fields for the type. 565 | /// 566 | private IEnumerable GetSerializedFields(Type type) 567 | { 568 | var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 569 | 570 | // If it's the public field and doesn't have NonSerializedAttribute, then add it. 571 | // If it's the non-public field and has UnityEngine.SerializeField, then add it. 572 | var serializedFields = 573 | from f in fields 574 | where (f.IsPublic && f.GetCustomAttribute(false) == null) || (!f.IsPublic && f.GetCustomAttribute(false) != null) 575 | select f; 576 | 577 | return serializedFields; 578 | } 579 | 580 | /// 581 | /// Check wheter obj is vector type instance or vector type collection. 582 | /// 583 | private bool IsVector(object obj) => obj is Vector3 || obj is Vector2 || obj is float || obj is IList || obj is IList || obj is IList; 584 | 585 | 586 | /// 587 | /// Add index label to this label parameter.
588 | /// e.g. Label [index] 589 | ///
590 | private string AddIndexLabel(string label, int index, bool isFront = false) 591 | { 592 | if (index >= 0) 593 | { 594 | if (isFront) 595 | { 596 | label = $"[{index}] {label}"; 597 | } 598 | else 599 | { 600 | label += $" [{index}]"; 601 | } 602 | } 603 | 604 | return label; 605 | } 606 | 607 | #region === Deprecated === 608 | // Create Position Handle for Vector3. If it's changed, set and record new value. 609 | // You need to implement a mechanism to set the new Vector3 value in setValue delegate. 610 | [Obsolete] 611 | private void SetHandleVector3(string label, Vector3 origin, Vector3 oldValue, object obj, FieldInfo field, Action setValue) 612 | { 613 | Handles.Label(origin + oldValue, label, style); 614 | EditorGUI.BeginChangeCheck(); 615 | Vector3 newValue = Handles.PositionHandle(origin + oldValue, Quaternion.identity) - origin; 616 | if (EditorGUI.EndChangeCheck()) 617 | { 618 | // enable ctrl + z & set dirty 619 | Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 620 | 621 | setValue(newValue); 622 | 623 | // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 624 | // But, even if i don't call this method, it works well. I don't know the reason. 625 | PrefabUtility.RecordPrefabInstancePropertyModifications(target); 626 | } 627 | } 628 | 629 | // Create Position Handle for Vector2. If it's changed, set and record new value. 630 | // You need to implement a mechanism to set the new Vector2 value in setValue delegate. 631 | [Obsolete] 632 | private void SetHandleVector2(string label, Vector2 origin, Vector2 oldValue, object obj, FieldInfo field, Action setValue) 633 | { 634 | Handles.Label(origin + oldValue, label, style); 635 | EditorGUI.BeginChangeCheck(); 636 | Vector2 newValue = (Vector2)Handles.PositionHandle(origin + oldValue, Quaternion.identity) - origin; 637 | if (EditorGUI.EndChangeCheck()) 638 | { 639 | // enable ctrl + z & set dirty 640 | Undo.RecordObject(target, $"{target.name}_{target.GetInstanceID()}_{obj.GetHashCode()}_{field.Name}"); 641 | 642 | setValue(newValue); 643 | 644 | // In the unity document, if the object may be part of a Prefab instance, we have to call this method. 645 | // But, even if i don't call this method, it works well. I don't know the reason. 646 | PrefabUtility.RecordPrefabInstancePropertyModifications(target); 647 | } 648 | } 649 | 650 | // Check if it's vector type or vector collection type. 651 | [Obsolete] 652 | private bool IsVector(Type type) => type == typeof(Vector2) || type == typeof(Vector3) || 653 | typeof(IEnumerable).IsAssignableFrom(type) || typeof(IEnumerable).IsAssignableFrom(type); 654 | #endregion 655 | } 656 | #endif 657 | } 658 | -------------------------------------------------------------------------------- /Editor/MoveToolEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 131bfc6da0e3e5e4786d05a7be3cc201 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Images.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63dd707dbde2cce46bc7dd735350a757 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Images/move-tool-collection.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevSlem/unity-move-tool/5b12d13ef8c5d00e53d8b60c40ec7376a624ddf5/Images/move-tool-collection.webp -------------------------------------------------------------------------------- /Images/move-tool-collection.webp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e2c8050ad13b61a42aab53da21bfcfd9 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Images/move-tool-custom-type-collection.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevSlem/unity-move-tool/5b12d13ef8c5d00e53d8b60c40ec7376a624ddf5/Images/move-tool-custom-type-collection.webp -------------------------------------------------------------------------------- /Images/move-tool-custom-type-collection.webp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35050653890877b40b9db98b93b9c583 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Images/move-tool-float.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevSlem/unity-move-tool/5b12d13ef8c5d00e53d8b60c40ec7376a624ddf5/Images/move-tool-float.webp -------------------------------------------------------------------------------- /Images/move-tool-float.webp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c29e1960ed0264f4bbc32b6cbe39f955 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Images/move-tool-vector2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevSlem/unity-move-tool/5b12d13ef8c5d00e53d8b60c40ec7376a624ddf5/Images/move-tool-vector2.webp -------------------------------------------------------------------------------- /Images/move-tool-vector2.webp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d16543998c0b75c42a9ad877a88789db 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Images/move-tool-vector3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevSlem/unity-move-tool/5b12d13ef8c5d00e53d8b60c40ec7376a624ddf5/Images/move-tool-vector3.webp -------------------------------------------------------------------------------- /Images/move-tool-vector3.webp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce45d4fe38420e94fbfa751a487888a5 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 kgmslem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 426441d2742ce7c4680cddded53f7ada 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Move-Tool for Unity 2 | 3 | **Move-Tool** is a tool that makes you use position handles for vector in unity editor scene view. 4 | You can use move-tool so easily by just defining ***some attributes***. 5 | 6 | > Note that it require **C# 7** or higher. 7 | > If you want to use it in a lower version, you need to modify **pattern matching**(only can use in C# 7 or higher) part in `MoveToolEditor` class to `if` statement and simple type checking, but it'll make you tired. 8 | 9 | * [Basic usage](#basic-usage) 10 | * [MoveToolAttribute Properties](#movetoolattribute-properties) 11 | * [Move-Tool available custom type](#move-tool-available-custom-type) 12 | * [Editor](#editor) 13 | 14 | ## Releases 15 | 16 | It is recommended to use a latest, stable version. 17 | [main](https://github.com/DevSlem/unity-move-tool/tree/main) version is unstable because it's under active development. 18 | 19 | | Version | Release Date | Source | C# | .Net Compatibility | 20 | | :------------------------------------------------------------------------------------: | :----------: | :----------------------------------------------------------------------------: | :-----------: | :-------------------------: | 21 | | main(unstable) | -- | [main](https://github.com/DevSlem/unity-move-tool/tree/main) | 7.0 or higher | .Net Standard 2.0 or higher | 22 | | [Release 3.1.0](https://github.com/DevSlem/unity-move-tool/releases/tag/release-3.1.0) | 2022-09-16 | [release-3.1.0](https://github.com/DevSlem/unity-move-tool/tree/release-3.1.0) | 7.0 or higher | .Net standard 2.0 or higher | 23 | 24 | ## Latest Update 25 | 26 | * You can use **Move-Tool** for the `float` field. 27 | * Arrow of z direction of **Move-Tool** for the `Vector2` field has been removed. To be more intuitive! 28 | 29 | ## Installation 30 | 31 | You can select 2 installation methods. 32 | 33 | * Clone the repository and add `package.json` file to your project through Unity Package Manager. 34 | * You can add directly the package from git URL to your project through Unity Package Manager. See the [Installing from a Git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html). 35 | 36 | ## Basic usage 37 | 38 | You just define `MoveTool` attribute for a field for which you want to use position handle. 39 | The field is okay whether it's vector or vector collection. 40 | It works only if the type of the field is one of the `Vector3`, `Vector2`, `float` type. 41 | 42 | If you want to use ***attributes*** about move-tool, you must declare the following `using` directive. 43 | 44 | ```c# 45 | using DevSlem; 46 | ``` 47 | 48 | > Note that the any type that you want to use Move-Tool, it must be serializable. 49 | 50 | ### Vector3 51 | 52 | ```c# 53 | public class MoveToolSample : MonoBehaviour 54 | { 55 | [MoveTool] public Vector3 vector; 56 | } 57 | ``` 58 | 59 | ![](/Images/move-tool-vector3.webp) 60 | 61 | ### Vector2 62 | 63 | ```c# 64 | [MoveTool] public Vector2 vector2; 65 | ``` 66 | 67 | > Note that `Vector2` type field only moves along the x and y axes. 68 | 69 | ![](/Images/move-tool-vector2.webp) 70 | 71 | ### Float 72 | 73 | ```c# 74 | [MoveTool] public float floatField; 75 | ``` 76 | 77 | > Note that `float` type field only moves along the x axis. 78 | 79 | ![](/Images/move-tool-float.webp) 80 | 81 | ### Collection 82 | 83 | You can use move-tool to `Array` or `List` collection where each element is vector value. 84 | While you click the **shift** key, you can control all elements of the list at once. 85 | 86 | ```c# 87 | [MoveTool] public List vectorCollection = new List(); // Vector3[] array is also okay. 88 | ``` 89 | 90 | ![](/Images/move-tool-collection.webp) 91 | 92 | ### Non-Public field 93 | 94 | You can only use move-tool for a serializable vector. 95 | So, if you want to use move-tool for a ***non-public*** field like `private` or `protected`, you have to define `UnityEngine.SerializeField` attribute for the field. 96 | See the following code. 97 | 98 | ```c# 99 | [SerializeField, MoveTool] private Vector3 privateVector; 100 | [SerializeField, MoveTool] private List privateCollection = new List(); 101 | ``` 102 | 103 | ## MoveToolAttribute Properties 104 | 105 | `MoveToolAttribute` has the following properties. 106 | 107 | * `PositionMode` sets the coordinate space of the vector. If you set it to `MoveToolPosition.Local` enum value, the vector works in local coordinate. Default is world coordinate. 108 | * `LabelMode` is a enum flag property. You can display the move-tool label on unity editor through it. By default, the label is displayed on both scene and inspector view. 109 | * `Label` is a custom label that you want to display your own label instead of the default label. Default label is the field name for display. 110 | 111 | ### Sample Code 112 | 113 | ```c# 114 | [MoveTool(PositionMode = MoveToolPosition.Local, LabelMode = MoveToolLabel.SceneView, Label = "My Custom Label")] 115 | public Vector3 customPropertyVector; 116 | ``` 117 | 118 | ## Move-Tool available custom type 119 | 120 | If you want to use move-tool for a custom type field which declare vector fields, you must define `System.Serializable` and `MoveToolAvailable` attributes for the type. 121 | The custom type is okay whether class or struct. 122 | 123 | ```c# 124 | public class MoveToolSample : MonoBehaviour 125 | { 126 | [MoveTool] public List customClasses = new List(); 127 | } 128 | 129 | [Serializable, MoveToolAvailable] 130 | public class CustomClass 131 | { 132 | public List vectors = new List(); 133 | } 134 | ``` 135 | 136 | ![](/Images/move-tool-custom-type-collection.webp) 137 | 138 | ### Serialize 139 | 140 | A custom type for which you define `MoveToolAvailable` attribute must be serializable. So, if you don't want to use move-tools for the fields which is declared in the custom type, you should set the fields to be non-serializable. 141 | 142 | > Note that you can only use move-tool for a serialized field. 143 | 144 | See the following example. 145 | 146 | ```c# 147 | [Serializable, MoveToolAvailable] 148 | public class CustomClass 149 | { 150 | public Vector3 publicVector; // Can use move-tool. 151 | [SerializeField] private Vector3 serializedPrivateVector; // Can use move-tool. 152 | [NonSerialized] public Vector3 nonSerializedPublicVector; // Can't use move-tool. 153 | private Vector3 privateVector; // Can't use move-tool. 154 | } 155 | ``` 156 | 157 | Public vector is always serialized. If you don't want to use move-tool for a public field, you need to define `System.NonSerialized` attribute for it. 158 | Non-public vector isn't always serialized. If you want to use move-tool for a non-public field, you need to define `UnityEngine.SerializeField` attribute for it. 159 | 160 | ## Editor 161 | 162 | Now, you don't have to worry about editor conflict problem. It is solved. 163 | You don't need to create `MoveToolEditor` instance for using concurrently `AnotherEditor` with it. 164 | You just use normally `MoveTool` attribute. 165 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 86be942ab938a6740a4a4fb361dd8910 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bdb7a32dbe2d2694c9544a078d41456d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/DevSlem.MoveTool.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DevSlem.MoveTool", 3 | "rootNamespace": "", 4 | "references": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [], 13 | "noEngineReferences": false 14 | } -------------------------------------------------------------------------------- /Runtime/DevSlem.MoveTool.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a31c1babfbfb4654fb3f28ab7a1ed780 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/MoveToolAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace DevSlem 5 | { 6 | /// 7 | /// You can use move-tool for the vector in unity editor scene view. 8 | /// 9 | [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)] 10 | public class MoveToolAttribute : PropertyAttribute 11 | { 12 | /// 13 | /// You can control the position mode of move-tool. Default is world position mode. 14 | /// 15 | public MoveToolPosition PositionMode { get; set; } = MoveToolPosition.World; 16 | 17 | /// 18 | /// You can display the move-tool label on unity editor through this enum flags. 19 | /// 20 | public MoveToolLabel LabelMode { get; set; } = MoveToolLabel.SceneView | MoveToolLabel.InspectorView; 21 | 22 | /// 23 | /// Custom Label. Default is field label for display. 24 | /// 25 | public string Label { get; set; } = string.Empty; 26 | 27 | } 28 | 29 | /// 30 | /// If it's defined for a custom type, you can use MoveToolAttribute to the type field. 31 | /// 32 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] 33 | public class MoveToolAvailableAttribute : PropertyAttribute { } 34 | 35 | public enum MoveToolPosition 36 | { 37 | World, 38 | Local 39 | } 40 | 41 | [Flags] 42 | public enum MoveToolLabel 43 | { 44 | None = 0, 45 | SceneView = 1, 46 | InspectorView = 2 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Runtime/MoveToolAttributes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f1146a6e9251184458089671fb4e5704 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/StringExtension.cs: -------------------------------------------------------------------------------- 1 | namespace DevSlem.Extensions 2 | { 3 | public static class StringExtension 4 | { 5 | /// 6 | /// Change the variable name to the display name for inspector. 7 | /// 8 | /// display name for inspector 9 | public static string InspectorLabel(this string variableName) 10 | { 11 | if (variableName == null) 12 | throw new System.NullReferenceException(); 13 | 14 | if (variableName.Length == 0) 15 | return string.Empty; 16 | 17 | string temp = variableName.Trim(); 18 | temp = char.ToUpper(temp[0]) + temp.Substring(1); // capitalize the first letter 19 | 20 | // If it changes small letter to capital letter, insert a space. 21 | for (int i = 0; i < temp.Length - 1; i++) 22 | { 23 | if (!char.IsLetterOrDigit(temp[i]) || !char.IsLetterOrDigit(temp[i + 1])) 24 | continue; 25 | 26 | if ((char.IsLower(temp[i]) && !char.IsLower(temp[i + 1])) || (!char.IsUpper(temp[i]) && char.IsUpper(temp[i + 1]))) 27 | { 28 | temp = temp.Insert(i + 1, " "); 29 | } 30 | } 31 | 32 | return temp; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Runtime/StringExtension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5e4aea9a5c041d24b84408155b886a75 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3bcdc680c1f8c064faa7c36432a9e773 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveTool Sample.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &8872950196097312925 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 8872950196097312926} 12 | - component: {fileID: 8872950196097312927} 13 | m_Layer: 0 14 | m_Name: MoveTool Sample 15 | m_TagString: Untagged 16 | m_Icon: {fileID: 0} 17 | m_NavMeshLayer: 0 18 | m_StaticEditorFlags: 0 19 | m_IsActive: 1 20 | --- !u!4 &8872950196097312926 21 | Transform: 22 | m_ObjectHideFlags: 0 23 | m_CorrespondingSourceObject: {fileID: 0} 24 | m_PrefabInstance: {fileID: 0} 25 | m_PrefabAsset: {fileID: 0} 26 | m_GameObject: {fileID: 8872950196097312925} 27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 28 | m_LocalPosition: {x: 0, y: 0, z: 0} 29 | m_LocalScale: {x: 1, y: 1, z: 1} 30 | m_ConstrainProportionsScale: 0 31 | m_Children: [] 32 | m_Father: {fileID: 0} 33 | m_RootOrder: 0 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!114 &8872950196097312927 36 | MonoBehaviour: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 8872950196097312925} 42 | m_Enabled: 1 43 | m_EditorHideFlags: 0 44 | m_Script: {fileID: 11500000, guid: b71346e81de833240992d50ebdf6e848, type: 3} 45 | m_Name: 46 | m_EditorClassIdentifier: 47 | vector: {x: 3, y: 0, z: 0} 48 | vector2: {x: 6, y: 0} 49 | floatField: 9 50 | vectorCollection: 51 | - {x: 12, y: 0, z: 0} 52 | - {x: 15, y: 0, z: 0} 53 | privateVector: {x: 18, y: 0, z: 0} 54 | privateCollection: 55 | - {x: 21, y: 0, z: 0} 56 | - {x: 24, y: 0, z: 0} 57 | customPropertyVector: {x: 27, y: 0, z: 0} 58 | customClasses: 59 | - publicVector: {x: 30, y: 0, z: 0} 60 | serializedPrivateVector: {x: 33, y: 0, z: 0} 61 | - publicVector: {x: 36, y: 0, z: 0} 62 | serializedPrivateVector: {x: 39, y: 0, z: 0} 63 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveTool Sample.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15615e270044b4b4bae1016dc8ac08f0 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveToolSample.cs: -------------------------------------------------------------------------------- 1 | using DevSlem; 2 | using System; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | public class MoveToolSample : MonoBehaviour 7 | { 8 | [MoveTool] public Vector3 vector; 9 | [MoveTool] public Vector2 vector2; 10 | [MoveTool] public float floatField; 11 | [MoveTool] public List vectorCollection = new List(); // Vector3[] array is also okay. 12 | [SerializeField, MoveTool] private Vector3 privateVector; 13 | [SerializeField, MoveTool] private List privateCollection = new List(); 14 | [MoveTool(PositionMode = MoveToolPosition.Local, LabelMode = MoveToolLabel.SceneView, Label = "My Custom Label")] 15 | public Vector3 customPropertyVector; 16 | [MoveTool] public List customClasses = new List(); 17 | } 18 | 19 | [Serializable, MoveToolAvailable] 20 | public class CustomClass 21 | { 22 | public Vector3 publicVector; // Can use move-tool. 23 | [SerializeField] private Vector3 serializedPrivateVector; // Can use move-tool. 24 | [NonSerialized] public Vector3 nonSerializedPublicVector; // Can't use move-tool. 25 | private Vector3 privateVector; // Can't use move-tool. 26 | } -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveToolSample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b71346e81de833240992d50ebdf6e848 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveToolSampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 705507994} 41 | m_IndirectSpecularColor: {r: 0.18028378, g: 0.22571412, b: 0.30692285, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 12 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 2 59 | m_BakeResolution: 40 60 | m_AtlasSize: 1024 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 500 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 500 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 2 83 | m_PVRDenoiserTypeDirect: 0 84 | m_PVRDenoiserTypeIndirect: 0 85 | m_PVRDenoiserTypeAO: 0 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 0 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ExportTrainingData: 0 98 | m_TrainingDataDestination: TrainingData 99 | m_LightProbeSampleCountMultiplier: 4 100 | m_LightingDataAsset: {fileID: 0} 101 | m_LightingSettings: {fileID: 0} 102 | --- !u!196 &4 103 | NavMeshSettings: 104 | serializedVersion: 2 105 | m_ObjectHideFlags: 0 106 | m_BuildSettings: 107 | serializedVersion: 2 108 | agentTypeID: 0 109 | agentRadius: 0.5 110 | agentHeight: 2 111 | agentSlope: 45 112 | agentClimb: 0.4 113 | ledgeDropHeight: 0 114 | maxJumpAcrossDistance: 0 115 | minRegionArea: 2 116 | manualCellSize: 0 117 | cellSize: 0.16666667 118 | manualTileSize: 0 119 | tileSize: 256 120 | accuratePlacement: 0 121 | maxJobWorkers: 0 122 | preserveTilesOutsideBounds: 0 123 | debug: 124 | m_Flags: 0 125 | m_NavMeshData: {fileID: 0} 126 | --- !u!1 &705507993 127 | GameObject: 128 | m_ObjectHideFlags: 0 129 | m_CorrespondingSourceObject: {fileID: 0} 130 | m_PrefabInstance: {fileID: 0} 131 | m_PrefabAsset: {fileID: 0} 132 | serializedVersion: 6 133 | m_Component: 134 | - component: {fileID: 705507995} 135 | - component: {fileID: 705507994} 136 | - component: {fileID: 705507996} 137 | m_Layer: 0 138 | m_Name: Directional Light 139 | m_TagString: Untagged 140 | m_Icon: {fileID: 0} 141 | m_NavMeshLayer: 0 142 | m_StaticEditorFlags: 0 143 | m_IsActive: 1 144 | --- !u!108 &705507994 145 | Light: 146 | m_ObjectHideFlags: 0 147 | m_CorrespondingSourceObject: {fileID: 0} 148 | m_PrefabInstance: {fileID: 0} 149 | m_PrefabAsset: {fileID: 0} 150 | m_GameObject: {fileID: 705507993} 151 | m_Enabled: 1 152 | serializedVersion: 10 153 | m_Type: 1 154 | m_Shape: 0 155 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 156 | m_Intensity: 1 157 | m_Range: 10 158 | m_SpotAngle: 30 159 | m_InnerSpotAngle: 21.80208 160 | m_CookieSize: 10 161 | m_Shadows: 162 | m_Type: 2 163 | m_Resolution: -1 164 | m_CustomResolution: -1 165 | m_Strength: 1 166 | m_Bias: 0.05 167 | m_NormalBias: 0.4 168 | m_NearPlane: 0.2 169 | m_CullingMatrixOverride: 170 | e00: 1 171 | e01: 0 172 | e02: 0 173 | e03: 0 174 | e10: 0 175 | e11: 1 176 | e12: 0 177 | e13: 0 178 | e20: 0 179 | e21: 0 180 | e22: 1 181 | e23: 0 182 | e30: 0 183 | e31: 0 184 | e32: 0 185 | e33: 1 186 | m_UseCullingMatrixOverride: 0 187 | m_Cookie: {fileID: 0} 188 | m_DrawHalo: 0 189 | m_Flare: {fileID: 0} 190 | m_RenderMode: 0 191 | m_CullingMask: 192 | serializedVersion: 2 193 | m_Bits: 4294967295 194 | m_RenderingLayerMask: 1 195 | m_Lightmapping: 1 196 | m_LightShadowCasterMode: 0 197 | m_AreaSize: {x: 1, y: 1} 198 | m_BounceIntensity: 1 199 | m_ColorTemperature: 6570 200 | m_UseColorTemperature: 0 201 | m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} 202 | m_UseBoundingSphereOverride: 0 203 | m_UseViewFrustumForShadowCasterCull: 1 204 | m_ShadowRadius: 0 205 | m_ShadowAngle: 0 206 | --- !u!4 &705507995 207 | Transform: 208 | m_ObjectHideFlags: 0 209 | m_CorrespondingSourceObject: {fileID: 0} 210 | m_PrefabInstance: {fileID: 0} 211 | m_PrefabAsset: {fileID: 0} 212 | m_GameObject: {fileID: 705507993} 213 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 214 | m_LocalPosition: {x: 0, y: 3, z: 0} 215 | m_LocalScale: {x: 1, y: 1, z: 1} 216 | m_ConstrainProportionsScale: 0 217 | m_Children: [] 218 | m_Father: {fileID: 0} 219 | m_RootOrder: 1 220 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 221 | --- !u!114 &705507996 222 | MonoBehaviour: 223 | m_ObjectHideFlags: 0 224 | m_CorrespondingSourceObject: {fileID: 0} 225 | m_PrefabInstance: {fileID: 0} 226 | m_PrefabAsset: {fileID: 0} 227 | m_GameObject: {fileID: 705507993} 228 | m_Enabled: 1 229 | m_EditorHideFlags: 0 230 | m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} 231 | m_Name: 232 | m_EditorClassIdentifier: 233 | m_Version: 1 234 | m_UsePipelineSettings: 1 235 | m_AdditionalLightsShadowResolutionTier: 2 236 | m_LightLayerMask: 1 237 | m_CustomShadowLayers: 0 238 | m_ShadowLayerMask: 1 239 | m_LightCookieSize: {x: 1, y: 1} 240 | m_LightCookieOffset: {x: 0, y: 0} 241 | --- !u!1 &963194225 242 | GameObject: 243 | m_ObjectHideFlags: 0 244 | m_CorrespondingSourceObject: {fileID: 0} 245 | m_PrefabInstance: {fileID: 0} 246 | m_PrefabAsset: {fileID: 0} 247 | serializedVersion: 6 248 | m_Component: 249 | - component: {fileID: 963194228} 250 | - component: {fileID: 963194227} 251 | - component: {fileID: 963194226} 252 | m_Layer: 0 253 | m_Name: Main Camera 254 | m_TagString: MainCamera 255 | m_Icon: {fileID: 0} 256 | m_NavMeshLayer: 0 257 | m_StaticEditorFlags: 0 258 | m_IsActive: 1 259 | --- !u!81 &963194226 260 | AudioListener: 261 | m_ObjectHideFlags: 0 262 | m_CorrespondingSourceObject: {fileID: 0} 263 | m_PrefabInstance: {fileID: 0} 264 | m_PrefabAsset: {fileID: 0} 265 | m_GameObject: {fileID: 963194225} 266 | m_Enabled: 1 267 | --- !u!20 &963194227 268 | Camera: 269 | m_ObjectHideFlags: 0 270 | m_CorrespondingSourceObject: {fileID: 0} 271 | m_PrefabInstance: {fileID: 0} 272 | m_PrefabAsset: {fileID: 0} 273 | m_GameObject: {fileID: 963194225} 274 | m_Enabled: 1 275 | serializedVersion: 2 276 | m_ClearFlags: 1 277 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 278 | m_projectionMatrixMode: 1 279 | m_GateFitMode: 2 280 | m_FOVAxisMode: 0 281 | m_SensorSize: {x: 36, y: 24} 282 | m_LensShift: {x: 0, y: 0} 283 | m_FocalLength: 50 284 | m_NormalizedViewPortRect: 285 | serializedVersion: 2 286 | x: 0 287 | y: 0 288 | width: 1 289 | height: 1 290 | near clip plane: 0.3 291 | far clip plane: 1000 292 | field of view: 60 293 | orthographic: 0 294 | orthographic size: 5 295 | m_Depth: -1 296 | m_CullingMask: 297 | serializedVersion: 2 298 | m_Bits: 4294967295 299 | m_RenderingPath: -1 300 | m_TargetTexture: {fileID: 0} 301 | m_TargetDisplay: 0 302 | m_TargetEye: 3 303 | m_HDR: 1 304 | m_AllowMSAA: 1 305 | m_AllowDynamicResolution: 0 306 | m_ForceIntoRT: 0 307 | m_OcclusionCulling: 1 308 | m_StereoConvergence: 10 309 | m_StereoSeparation: 0.022 310 | --- !u!4 &963194228 311 | Transform: 312 | m_ObjectHideFlags: 0 313 | m_CorrespondingSourceObject: {fileID: 0} 314 | m_PrefabInstance: {fileID: 0} 315 | m_PrefabAsset: {fileID: 0} 316 | m_GameObject: {fileID: 963194225} 317 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 318 | m_LocalPosition: {x: 0, y: 1, z: -10} 319 | m_LocalScale: {x: 1, y: 1, z: 1} 320 | m_ConstrainProportionsScale: 0 321 | m_Children: [] 322 | m_Father: {fileID: 0} 323 | m_RootOrder: 0 324 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 325 | --- !u!1001 &8872950196796448384 326 | PrefabInstance: 327 | m_ObjectHideFlags: 0 328 | serializedVersion: 2 329 | m_Modification: 330 | m_TransformParent: {fileID: 0} 331 | m_Modifications: 332 | - target: {fileID: 8872950196097312925, guid: 15615e270044b4b4bae1016dc8ac08f0, 333 | type: 3} 334 | propertyPath: m_Name 335 | value: MoveTool Sample 336 | objectReference: {fileID: 0} 337 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 338 | type: 3} 339 | propertyPath: m_RootOrder 340 | value: 2 341 | objectReference: {fileID: 0} 342 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 343 | type: 3} 344 | propertyPath: m_LocalPosition.x 345 | value: 0 346 | objectReference: {fileID: 0} 347 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 348 | type: 3} 349 | propertyPath: m_LocalPosition.y 350 | value: 0 351 | objectReference: {fileID: 0} 352 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 353 | type: 3} 354 | propertyPath: m_LocalPosition.z 355 | value: 0 356 | objectReference: {fileID: 0} 357 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 358 | type: 3} 359 | propertyPath: m_LocalRotation.w 360 | value: 1 361 | objectReference: {fileID: 0} 362 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 363 | type: 3} 364 | propertyPath: m_LocalRotation.x 365 | value: 0 366 | objectReference: {fileID: 0} 367 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 368 | type: 3} 369 | propertyPath: m_LocalRotation.y 370 | value: 0 371 | objectReference: {fileID: 0} 372 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 373 | type: 3} 374 | propertyPath: m_LocalRotation.z 375 | value: 0 376 | objectReference: {fileID: 0} 377 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 378 | type: 3} 379 | propertyPath: m_LocalEulerAnglesHint.x 380 | value: 0 381 | objectReference: {fileID: 0} 382 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 383 | type: 3} 384 | propertyPath: m_LocalEulerAnglesHint.y 385 | value: 0 386 | objectReference: {fileID: 0} 387 | - target: {fileID: 8872950196097312926, guid: 15615e270044b4b4bae1016dc8ac08f0, 388 | type: 3} 389 | propertyPath: m_LocalEulerAnglesHint.z 390 | value: 0 391 | objectReference: {fileID: 0} 392 | m_RemovedComponents: [] 393 | m_SourcePrefab: {fileID: 100100000, guid: 15615e270044b4b4bae1016dc8ac08f0, type: 3} 394 | -------------------------------------------------------------------------------- /Samples~/SamplesMoveTool/MoveToolSampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9fc0d4010bbf28b4594072e72b8655ab 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.devslem.movetool", 3 | "version": "3.1.0", 4 | "displayName": "Move Tool", 5 | "description": "You can use position handles of fields for which you define MoveToolAttribute in the unity editor scene view.", 6 | "unity": "2020.3", 7 | "documentationUrl": "https://github.com/DevSlem/unity-move-tool/blob/release-3.0.0/README.md", 8 | "changelogUrl": "https://github.com/DevSlem/unity-move-tool/releases", 9 | "licensesUrl": "https://github.com/DevSlem/unity-move-tool/blob/main/LICENSE", 10 | "keywords": [ 11 | "Move Tool", 12 | "Position Handle", 13 | "Unity Editor" 14 | ], 15 | "author": { 16 | "name": "DevSlem", 17 | "email": "devslem12@gmail.com", 18 | "url": "https://github.com/DevSlem" 19 | }, 20 | "hideInEditor": false, 21 | "samples": [ 22 | { 23 | "displayName": "MoveTool Sample", 24 | "description": "You can see a MoveTool sample code and usages.", 25 | "path": "Samples~/SamplesMoveTool" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b13454772efb5049a020f4faa760ede 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------