├── .gitattributes ├── ScriptableObjects ├── Readme ├── EnemyBase.cs ├── Color SOs │ ├── ColorData.cs │ ├── PanelBackgroundColor.cs │ └── ColorReference.cs ├── EnemyData.cs ├── EnemyManager.cs └── EnemyControl.cs ├── Inspector Groups ├── Readme ├── Columns.cs ├── Tabs.cs └── Grouping.cs ├── Custom Validator Scripts ├── Plant.cs ├── Plant.cs.meta ├── TestClass.cs.meta ├── PlantValidator.cs.meta ├── EmptyStringValidator.cs.meta ├── SpriteSizeAttribute.cs.meta ├── NeedsComponentAttribute.cs.meta ├── TestClass.cs ├── EmptyStringValidator.cs ├── NeedsComponentAttribute.cs ├── SpriteSizeAttribute.cs └── PlantValidator.cs ├── First Custom Inspector ├── Readme.txt └── GameManager.cs ├── AttributeDrawers ├── MyColorAttribute.cs ├── MyClass.cs ├── Vector2SliderAttribute.cs └── Editor │ ├── Vector2SliderAttributeDrawer.cs │ └── MyColorAttributeDrawer.cs ├── Composite Attributes ├── PreFabAssetList.cs ├── PropertyOnly.cs ├── PropertyKeyWordExample.cs ├── TextureView.cs ├── StatsObject.cs └── Stats.cs ├── Shorts ├── TabGroup.meta ├── Button Attribute.meta ├── Color Palette.meta ├── TabGroup │ ├── CharacterTabExample.cs.meta │ └── CharacterTabExample.cs ├── Color Palette │ ├── ColorPaletteExamples.cs.meta │ └── ColorPaletteExamples.cs └── Button Attribute │ ├── ButtonShortExamples.cs.meta │ └── ButtonShortExamples.cs ├── README.md ├── Action Resolver ├── ActionButtonAttribute.cs ├── ActionButtonExample.cs └── ActionButtonAttributeDrawer.cs ├── Struct Value Drawers ├── MyClass.cs └── Editor │ └── DateTimeDrawer.cs ├── 2D Character Creator ├── Editor │ ├── CharacterEditor.cs.meta │ └── CharacterEditor.cs ├── Character.cs ├── CharacterSpriteSettings.cs └── CharacterSpriteData.cs ├── Custom Attribute Processors ├── Stats.cs ├── Character.cs └── CharacterAttributeProcessor.cs ├── SFX Manager ├── PlaySFXExample.cs ├── SFXClip.cs ├── SFXManager.cs └── SFX.cs ├── Class Value Drawers ├── MyClass.cs └── Editor │ └── MySubClassDrawer.cs ├── Value Resolver ├── ColorIfExample.cs ├── ColorIfAttributeDrawer.cs └── ColorIfAttribute.cs ├── PropertyProcessors ├── SampleClass.cs └── SamplePropertyProcessor.cs ├── The Manager Manager ├── ManagableAttribute.cs └── Editor │ ├── MBManager.cs │ └── GUIUtils.cs ├── Data Manager ├── DataManager.cs └── GUIUtils.cs ├── How I Use Odin ├── PlayerUnitValidator.cs ├── HelpfulButtons.cs ├── UnitUnlocakUpgradeWindow.cs ├── StatsUpgradeWindow.cs ├── ResourceSOCreator.cs └── PatchNotes.cs ├── ScriptableObjectsWithOdin ├── EnemyData.cs ├── Editor │ └── EnemyDataEditor.cs └── EnemyControl.cs ├── Generic Matching ├── NPCBase.cs └── NPCStats.cs ├── CustomGroups └── MyClass.cs └── THE Game Manager └── TheGameManager.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /ScriptableObjects/Readme: -------------------------------------------------------------------------------- 1 | These files are from the "scriptable object" tutorial video. 2 | 3 | Link: https://youtu.be/W5ECIJyoW80 4 | -------------------------------------------------------------------------------- /Inspector Groups/Readme: -------------------------------------------------------------------------------- 1 | These files are from the "Creating Inspector Groups with Odin Inspector" video. The video can be found here: https://youtu.be/9AEEH67G-Vs 2 | -------------------------------------------------------------------------------- /Custom Validator Scripts/Plant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class Plant : MonoBehaviour 6 | { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /First Custom Inspector/Readme.txt: -------------------------------------------------------------------------------- 1 | This folder contains the Game Manager script used in the "How to Create a Custom Inspector with Odin?" 2 | 3 | The video can be found at: https://youtu.be/UXYCTHf6MKY -------------------------------------------------------------------------------- /AttributeDrawers/MyColorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System; 5 | 6 | public class MyColorAttribute : Attribute 7 | { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Composite Attributes/PreFabAssetList.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using System; 3 | 4 | [IncludeMyAttributes] 5 | [AssetList(Path = "Prefabs")] 6 | public class PreFabAssetList : Attribute 7 | { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Shorts/TabGroup.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d68928357abcc2f4eb0de26edd8c30af 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Composite Attributes/PropertyOnly.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using System; 3 | 4 | 5 | [IncludeMyAttributes] 6 | [HideLabel] 7 | [InlineProperty] 8 | public class PropertyOnly : Attribute 9 | { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /ScriptableObjects/EnemyBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public abstract class EnemyBase : ScriptableObject 6 | { 7 | public abstract void DoTurn(); 8 | } 9 | -------------------------------------------------------------------------------- /Shorts/Button Attribute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e95601394d742024897d2588f473e1af 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Shorts/Color Palette.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e4b05335151e4854bb850dc8e67cb65a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Composite Attributes/PropertyKeyWordExample.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using System; 3 | 4 | [IncludeMyAttributes] 5 | [Title("@$property.NiceName")] 6 | public class PropertyKeyWordExample : Attribute 7 | { 8 | 9 | } 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #SirenixTutorialFiles 2 | 3 | A collection of files used in recent (after Dec 19) Devdog Tutorial videos. 4 | 5 | All videos can be found on the Devdog YouTube channel. 6 | 7 | Link: https://www.youtube.com/channel/UCWpLqoOZdE5ZWZNonkBd9eA/ 8 | -------------------------------------------------------------------------------- /Action Resolver/ActionButtonAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class ActionButtonAttribute : Attribute 4 | { 5 | public string action; 6 | 7 | public ActionButtonAttribute (string action) 8 | { 9 | this.action = action; 10 | } 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /Struct Value Drawers/MyClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System; 5 | using Sirenix.OdinInspector; 6 | 7 | public class MyClass : MonoBehaviour 8 | { 9 | [ShowInInspector] 10 | public DateTime myDateTime; 11 | } -------------------------------------------------------------------------------- /Composite Attributes/TextureView.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using System; 3 | 4 | [IncludeMyAttributes] 5 | [PreviewField(Alignment = ObjectFieldAlignment.Center, Height = 100)] 6 | [HideLabel] 7 | [AssetsOnly] 8 | public class TextureView : Attribute 9 | { 10 | 11 | } 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ScriptableObjects/Color SOs/ColorData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(fileName = "ColorData", menuName = "My Game/Color Data")] 6 | public class ColorData : ScriptableObject 7 | { 8 | public Color colorValue; 9 | } 10 | -------------------------------------------------------------------------------- /AttributeDrawers/MyClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class MyClass : MonoBehaviour 6 | { 7 | [Vector2Slider(0,20)] 8 | public Vector2 myVector2; 9 | 10 | [MyColor] 11 | public Color myColor; 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /Custom Validator Scripts/Plant.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 84300fa3fedec8a4bacd049071c208be 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Custom Validator Scripts/TestClass.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b9a587f5cf95594c926c0cbb3715cf7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Shorts/TabGroup/CharacterTabExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac620f805908f954d8de4a6abb20cfb9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Custom Validator Scripts/PlantValidator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dcfa6de806f9a2d4db08d7125519b073 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Shorts/Color Palette/ColorPaletteExamples.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3160a361edb1c384c8fcf4a34dda9f82 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /2D Character Creator/Editor/CharacterEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d4163024fba27c40b9911811df52184 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Custom Attribute Processors/Stats.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [System.Serializable] 7 | public class Stats 8 | { 9 | public float stat1; 10 | public float stat2; 11 | public float stat3; 12 | public float stat4; 13 | } 14 | -------------------------------------------------------------------------------- /Custom Validator Scripts/EmptyStringValidator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17d0795dd5216f14d87bee7de63140cc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Custom Validator Scripts/SpriteSizeAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4e627cf8bd1d144e8198a7a6797863e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /SFX Manager/PlaySFXExample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class PlaySFXExample : MonoBehaviour 7 | { 8 | public SFX sfxToPlay; 9 | 10 | private void Start() 11 | { 12 | sfxToPlay.PlaySFX(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Shorts/Button Attribute/ButtonShortExamples.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 895e6a275f11d7e4e9e98600446d3df0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Composite Attributes/StatsObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class StatsObject : MonoBehaviour 7 | { 8 | [PropertyOnly] 9 | public Stats stats; 10 | 11 | [PreFabAssetList] 12 | public GameObject prefabList; 13 | } 14 | -------------------------------------------------------------------------------- /Custom Validator Scripts/NeedsComponentAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 48abf01db8c02a046aa131e684b8fd59 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Custom Attribute Processors/Character.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class Character : MonoBehaviour 6 | { 7 | public string characterName; 8 | public string description; 9 | public Texture2D icon; 10 | public GameObject prefab; 11 | 12 | public Stats stats; 13 | } 14 | -------------------------------------------------------------------------------- /Class Value Drawers/MyClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | 6 | public class MyClass : MonoBehaviour 7 | { 8 | public MySubClass mySubClass; 9 | } 10 | 11 | [System.Serializable] 12 | public class MySubClass 13 | { 14 | public string text; 15 | public int number; 16 | public Vector3 location; 17 | } -------------------------------------------------------------------------------- /AttributeDrawers/Vector2SliderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System; 5 | 6 | public class Vector2SliderAttribute : Attribute 7 | { 8 | public float minValue; 9 | public float maxValue; 10 | 11 | public Vector2SliderAttribute(float min, float max) 12 | { 13 | this.minValue = min; 14 | this.maxValue = max; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Value Resolver/ColorIfExample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class ColorIfExample : MonoBehaviour 7 | { 8 | [ColorIf("@Color.green", "HasEvenNumberOfCharacters")] 9 | public string greenIfEven; 10 | 11 | private bool HasEvenNumberOfCharacters() 12 | { 13 | return greenIfEven?.Length % 2 == 0; 14 | } 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /Action Resolver/ActionButtonExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Sirenix.OdinInspector; 3 | 4 | public class ActionButtonExample : MonoBehaviour 5 | { 6 | [ActionButton("DoAction")] 7 | public string someString; 8 | 9 | [ActionButton("@UnityEngine.Debug.Log(\"I did an action without a method!!!\")")] 10 | public string anotherString; 11 | 12 | private void DoAction() 13 | { 14 | Debug.Log("I did an action!"); 15 | } 16 | } 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /PropertyProcessors/SampleClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class SampleClass : MonoBehaviour 6 | { 7 | public string top; 8 | public string middle; 9 | public string bottom; 10 | 11 | public int value1 = 1; 12 | public int value2 = 2; 13 | public int value3 = 3; 14 | 15 | } 16 | 17 | public enum MyEnum 18 | { 19 | zero, 20 | one, 21 | two, 22 | three, 23 | four 24 | } 25 | -------------------------------------------------------------------------------- /ScriptableObjects/EnemyData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | [CreateAssetMenu(fileName = "EnemyData", menuName = "My Game/Enemy Data")] 6 | public class EnemyData : ScriptableObject 7 | { 8 | public string enemyName; 9 | public string description; 10 | public GameObject enemyModel; 11 | public int health = 20; 12 | public float speed = 2f; 13 | public float detectRange = 10f; 14 | public int damage = 1; 15 | } 16 | -------------------------------------------------------------------------------- /Inspector Groups/Columns.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class Columns : MonoBehaviour 7 | { 8 | [HorizontalGroup("Base", LabelWidth = 80)] 9 | 10 | [VerticalGroup("Base/Column 1")] 11 | public string a; 12 | 13 | 14 | [VerticalGroup("Base/Column 1")] 15 | public string b; 16 | 17 | [BoxGroup("Base/Column 2")] 18 | public string c; 19 | [BoxGroup("Base/Column 2")] 20 | public string d; 21 | } 22 | -------------------------------------------------------------------------------- /The Manager Manager/ManagableAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | /// 4 | /// An attribute to mark monobehaviors so they 5 | /// can be imported into the MB manager editor window 6 | /// 7 | public class ManageableAttribute : Attribute 8 | { 9 | } 10 | 11 | /// 12 | /// An attribute to mark scriptable objects so they 13 | /// can be imported into the data manager editor window 14 | /// 15 | public class ManageableDataAttribute : Attribute 16 | { 17 | } 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Custom Validator Scripts/TestClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using UnityEngine.EventSystems; 6 | 7 | public class TestClass : MonoBehaviour 8 | { 9 | [NeedsComponent(typeof(Rigidbody))] 10 | public GameObject componentTest; 11 | 12 | [SpriteSize(100)] 13 | public Sprite sprite; 14 | 15 | [SpriteSize(SpriteSize.large)] 16 | public Sprite sprite2; 17 | 18 | //public List plantList = new List(); 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Custom Validator Scripts/EmptyStringValidator.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor.Validation; 2 | 3 | [assembly: RegisterValidator(typeof(EmptyStringValidator))] 4 | 5 | public class EmptyStringValidator : ValueValidator 6 | { 7 | protected override void Validate(ValidationResult result) 8 | { 9 | if (string.IsNullOrEmpty(this.ValueEntry.SmartValue)) 10 | { 11 | result.ResultType = ValidationResultType.Warning; 12 | result.Message = "This string is empty! Are you sure that's correct?"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ScriptableObjects/Color SOs/PanelBackgroundColor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using UnityEngine.UI; 6 | 7 | [RequireComponent(typeof(Image))] 8 | public class PanelBackgroundColor : MonoBehaviour 9 | { 10 | private Image panel; 11 | public ColorData color; 12 | 13 | private void AdjustColor(ColorReference _color) 14 | { 15 | if (panel == null) 16 | panel = this.GetComponent(); 17 | 18 | panel.color = _color.Value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Inspector Groups/Tabs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class Tabs : MonoBehaviour 7 | { 8 | [TabGroup("Tab Group 1", "Tab 1")] 9 | public string A; 10 | [TabGroup("Tab Group 1", "Tab 1")] 11 | public string B; 12 | 13 | [TabGroup("Tab Group 1", "Tab 2")] 14 | public int C; 15 | [TabGroup("Tab Group 1", "Tab 2")] 16 | public int D; 17 | 18 | [BoxGroup("Box Group")] 19 | public int E; 20 | 21 | [BoxGroup("Tab Group 1/Tab 1/Sub Box Group")] 22 | public int F; 23 | } 24 | -------------------------------------------------------------------------------- /SFX Manager/SFXClip.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [CreateAssetMenu(menuName = "New SFX Clip", fileName = "NewSFXClip")] 7 | public class SFXClip : ScriptableObject 8 | { 9 | [Space] 10 | [Title("Audio Clip")] 11 | [Required] 12 | public AudioClip clip; 13 | 14 | [Title("Clip Settings")] 15 | [Range(0f, 1f)] 16 | public float volume = 1f; 17 | [Range(0f, 0.2f)] 18 | public float volumeVariation = 0.05f; 19 | [Range(0f, 2f)] 20 | public float pitch = 1f; 21 | [Range(0f, 0.2f)] 22 | public float pitchVariation = 0.05f; 23 | } 24 | -------------------------------------------------------------------------------- /Shorts/Color Palette/ColorPaletteExamples.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using Sirenix.OdinInspector; 6 | 7 | public class ColorPaletteExamples : MonoBehaviour 8 | { 9 | [OnValueChanged("AssignColor")] 10 | [ColorPalette(PaletteName = "MyColors")] 11 | public Color myColor; 12 | 13 | private SpriteRenderer mySprite; 14 | 15 | //[OnInspectorGUI] 16 | private void AssignColor() 17 | { 18 | if (mySprite == null) 19 | mySprite = this.GetComponent(); 20 | 21 | mySprite.color = myColor; 22 | 23 | //force update in scene view 24 | UnityEditor.SceneView.RepaintAll(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Action Resolver/ActionButtonAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using Sirenix.OdinInspector.Editor.ActionResolvers; 3 | using UnityEngine; 4 | 5 | public class ActionButtonAttributeDrawer : OdinAttributeDrawer 6 | { 7 | private ActionResolver actionResolver; 8 | 9 | protected override void Initialize() 10 | { 11 | this.actionResolver = ActionResolver.Get(this.Property, this.Attribute.action); 12 | } 13 | 14 | protected override void DrawPropertyLayout (GUIContent label) 15 | { 16 | this.actionResolver.DrawError(); 17 | 18 | if(GUILayout.Button("Perform Action")) 19 | { 20 | this.actionResolver.DoActionForAllSelectionIndices(); 21 | } 22 | 23 | this.CallNextDrawer(label); 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /ScriptableObjects/EnemyManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | public class EnemyManager : MonoBehaviour 6 | { 7 | public List enemyList = new List(); 8 | 9 | private void DoEnemyTurns() 10 | { 11 | foreach (EnemyData enemy in enemyList) 12 | { 13 | if(enemy.enemyName == "Bob") 14 | { 15 | //do bob's turn 16 | } 17 | else if (enemy.enemyName == "Suzy") 18 | { 19 | //do suzy's turn 20 | } 21 | else if (enemy.enemyName == "Hank") 22 | { 23 | //do hank's turn 24 | } 25 | else if (enemy.enemyName == "WhyDidIDoItThisWay") 26 | { 27 | //do this guys's turn 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Custom Validator Scripts/NeedsComponentAttribute.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using Sirenix.OdinInspector.Editor.Validation; 4 | 5 | [assembly: RegisterValidator(typeof(NeedsComponentValidator))] 6 | 7 | public class NeedsComponentAttribute : Attribute 8 | { 9 | public Type type; 10 | 11 | public NeedsComponentAttribute(Type type) 12 | { 13 | this.type = type; 14 | } 15 | } 16 | 17 | public class NeedsComponentValidator : AttributeValidator 18 | { 19 | protected override void Validate(ValidationResult result) 20 | { 21 | if (this.ValueEntry.SmartValue == null) 22 | return; 23 | 24 | if (this.ValueEntry.SmartValue.GetComponent(this.Attribute.type) == null) 25 | { 26 | result.ResultType = ValidationResultType.Error; 27 | result.Message = "This Needs a " + this.Attribute.type.Name; 28 | } 29 | } 30 | } 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Struct Value Drawers/Editor/DateTimeDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.Utilities.Editor; 5 | using Sirenix.OdinInspector.Editor; 6 | using System; 7 | 8 | public class DateTimeDrawer : OdinValueDrawer 9 | { 10 | private string display; 11 | private object key = new object(); 12 | 13 | protected override void DrawPropertyLayout(GUIContent label) 14 | { 15 | display = this.ValueEntry.SmartValue.ToString(); 16 | 17 | SirenixEditorGUI.BeginShakeableGroup(key); 18 | 19 | display = SirenixEditorFields.TextField(label, display); 20 | 21 | if(DateTime.TryParse(display, out DateTime result)) 22 | { 23 | this.ValueEntry.SmartValue = result; 24 | } 25 | else 26 | { 27 | SirenixEditorGUI.StartShakingGroup(key); 28 | } 29 | 30 | SirenixEditorGUI.EndShakeableGroup(key); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /The Manager Manager/Editor/MBManager.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using System; 3 | using System.Linq; 4 | using UnityEditor; 5 | 6 | public class MBManager : OdinEditorWindow 7 | { 8 | private static Type[] typesToDisplay = TypeCache.GetTypesWithAttribute() 9 | .OrderBy(m => m.Name) 10 | .ToArray(); 11 | 12 | private Type selectedType; 13 | 14 | [MenuItem("Tools/MB Manager")] 15 | private static void OpenEditor() => GetWindow(); 16 | 17 | protected override void OnGUI() 18 | { 19 | GUIUtils.SelectButtonList(ref selectedType, typesToDisplay); 20 | base.OnGUI(); 21 | } 22 | 23 | protected override object GetTarget() 24 | { 25 | if (selectedType == null && typesToDisplay.Length > 0) 26 | selectedType = typesToDisplay[0]; 27 | 28 | if (selectedType == null) 29 | return null; 30 | else 31 | return FindObjectOfType(selectedType); 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /AttributeDrawers/Editor/Vector2SliderAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.Utilities; 5 | using Sirenix.Utilities.Editor; 6 | using Sirenix.OdinInspector.Editor; 7 | using UnityEditor; 8 | 9 | public class Vector2SliderAttributeDrawer : OdinAttributeDrawer 10 | { 11 | protected override void DrawPropertyLayout(GUIContent label) 12 | { 13 | Rect rect = EditorGUILayout.GetControlRect(); 14 | 15 | if(label != null) 16 | rect = EditorGUI.PrefixLabel(rect, label); 17 | 18 | Vector2 value = this.ValueEntry.SmartValue; 19 | 20 | GUIHelper.PushLabelWidth(20); 21 | value.x = EditorGUI.Slider(rect.AlignLeft(rect.width * 0.5f), "X", value.x, 22 | this.Attribute.minValue, this.Attribute.maxValue); 23 | value.y = EditorGUI.Slider(rect.AlignRight(rect.width * 0.5f), "Y", value.y, 24 | this.Attribute.minValue, this.Attribute.maxValue); 25 | GUIHelper.PopLabelWidth(); 26 | 27 | this.ValueEntry.SmartValue = value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Data Manager/DataManager.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector.Editor; 2 | using System; 3 | using System.Linq; 4 | using UnityEditor; 5 | 6 | public class DataManager : OdinMenuEditorWindow 7 | { 8 | private static Type[] typesToDisplay = TypeCache.GetTypesWithAttribute() 9 | .OrderBy(m => m.Name) 10 | .ToArray(); 11 | 12 | private Type selectedType; 13 | 14 | [MenuItem("Tools/Data Manager")] 15 | private static void OpenEditor() => GetWindow(); 16 | 17 | protected override void OnGUI() 18 | { 19 | //draw menu tree for SOs and other assets 20 | if (GUIUtils.SelectButtonList(ref selectedType, typesToDisplay)) 21 | this.ForceMenuTreeRebuild(); 22 | 23 | base.OnGUI(); 24 | } 25 | 26 | protected override OdinMenuTree BuildMenuTree() 27 | { 28 | var tree = new OdinMenuTree(); 29 | if(selectedType != null) 30 | tree.AddAllAssetsAtPath(selectedType.Name, "Assets/", selectedType, true, true); 31 | return tree; 32 | } 33 | } 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Value Resolver/ColorIfAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Sirenix.OdinInspector.Editor; 3 | using Sirenix.OdinInspector.Editor.ValueResolvers; 4 | using Sirenix.Utilities.Editor; 5 | public class ColorIfAttributeDrawer : OdinAttributeDrawer 6 | { 7 | private ValueResolver colorResolver; 8 | private ValueResolver conditionResolver; 9 | 10 | protected override void Initialize() 11 | { 12 | this.colorResolver = ValueResolver.Get(this.Property, this.Attribute.color, Color.white); 13 | this.conditionResolver = ValueResolver.Get(this.Property, this.Attribute.condition); 14 | } 15 | 16 | protected override void DrawPropertyLayout(GUIContent label) 17 | { 18 | ValueResolver.DrawErrors(this.colorResolver, this.conditionResolver); 19 | 20 | bool condition = this.conditionResolver.GetValue(); 21 | 22 | if(condition) 23 | { 24 | GUIHelper.PushColor(this.colorResolver.GetValue()); 25 | } 26 | 27 | this.CallNextDrawer(label); 28 | 29 | if(condition) 30 | { 31 | GUIHelper.PopColor(); 32 | } 33 | } 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /How I Use Odin/PlayerUnitValidator.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using Sirenix.OdinInspector.Editor.Validation; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | [assembly: RegisterValidationRule(typeof(PlayerUnitValidator), Name = "PlayerUnitValidator")] 7 | 8 | public class PlayerUnitValidator : RootObjectValidator 9 | { 10 | protected override void Validate(ValidationResult result) 11 | { 12 | if (this.Object.gameObject.layer != 7 && this.Object.gameObject.layer != 9) 13 | result.AddError("Player Unit object is not on the player object layer.") 14 | .WithFix("Set Correct Layer =>", () => SetLayerToPlayerUnit(this.Object.gameObject), true); 15 | 16 | if (this.Object.GetComponent() == null) 17 | result.AddError("Player Unit does not have a collider."); 18 | 19 | if (this.Object.GetStat(Stat.sightDistance) <= 0.01f) 20 | result.AddError("Sight distance not set.") 21 | .WithFix(() => this.Object.GetStats().AddStat(Stat.sightDistance, 3f), true); 22 | } 23 | 24 | private void SetLayerToPlayerUnit(GameObject gameObject) 25 | { 26 | gameObject.layer = 7; 27 | } 28 | } 29 | #endif 30 | 31 | 32 | -------------------------------------------------------------------------------- /Inspector Groups/Grouping.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class Grouping : MonoBehaviour 7 | { 8 | [HorizontalGroup("base", Width = 150)] 9 | 10 | [VerticalGroup("base/left")] 11 | [LabelWidth(90)] 12 | [HideLabel, Title("Enemy Name", Bold = false, HorizontalLine = false)] 13 | public string enemyName; 14 | [VerticalGroup("base/left")] 15 | [PreviewField(150)] 16 | [HideLabel] 17 | public Texture2D texture; 18 | 19 | [VerticalGroup("base/right")] 20 | [TextArea(5,5)] 21 | public string description; 22 | 23 | [HorizontalGroup("base/right/lower")] 24 | 25 | [VerticalGroup("base/right/lower/left")] 26 | 27 | 28 | [LabelWidth(50)] 29 | [Range(0,20)] 30 | public float stat1; 31 | 32 | [VerticalGroup("base/right/lower/left")] 33 | 34 | [LabelWidth(50)] 35 | [Range(0,20)] 36 | public float stat2; 37 | 38 | [VerticalGroup("base/right/lower/right")] 39 | 40 | [LabelWidth(50)] 41 | [Range(0,20)] 42 | public float stat3; 43 | [VerticalGroup("base/right/lower/right")] 44 | [LabelWidth(50)] 45 | [Range(0,20)] 46 | public float stat4; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Composite Attributes/Stats.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [System.Serializable] 7 | public class Stats 8 | { 9 | [HorizontalGroup("base", Width = 150)] 10 | 11 | [VerticalGroup("base/left")] 12 | [LabelWidth(90)] 13 | [HideLabel, Title("Enemy Name", Bold = false, HorizontalLine = false)] 14 | public string enemyName; 15 | [VerticalGroup("base/left")] 16 | [PreviewField(150)] 17 | [HideLabel] 18 | public Texture2D texture; 19 | 20 | [VerticalGroup("base/right")] 21 | [TextArea(5,5)] 22 | [LabelText("@$property.Parent")] 23 | public string description; 24 | 25 | [HorizontalGroup("base/right/lower")] 26 | 27 | [VerticalGroup("base/right/lower/left")] 28 | [LabelWidth(50)] 29 | [Range(0,20)] 30 | public float stat1; 31 | [VerticalGroup("base/right/lower/left")] 32 | [LabelWidth(50)] 33 | [Range(0,20)] 34 | public float stat2; 35 | [VerticalGroup("base/right/lower/right")] 36 | [LabelWidth(50)] 37 | [Range(0,20)] 38 | public float stat3; 39 | [VerticalGroup("base/right/lower/right")] 40 | [LabelWidth(50)] 41 | [Range(0,20)] 42 | public float stat4; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /ScriptableObjects/Color SOs/ColorReference.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [System.Serializable] 7 | [InlineProperty] 8 | public class ColorReference 9 | { 10 | [HorizontalGroup("Color Reference",MaxWidth = 100)] 11 | [ValueDropdown("valueList")] 12 | [HideLabel] 13 | public bool useValue = true; 14 | 15 | [ShowIf("useValue", Animate = false)] 16 | [HorizontalGroup("Color Reference")] 17 | [HideLabel] 18 | public Color constantValue; 19 | 20 | [HideIf("useValue", Animate = false)] 21 | [HorizontalGroup("Color Reference")] 22 | [HideLabel] 23 | [Required] 24 | public ColorData variable; 25 | 26 | private ValueDropdownList valueList = new ValueDropdownList() 27 | { 28 | {"Value", true }, 29 | {"Reference",false }, 30 | }; 31 | 32 | public Color Value 33 | { 34 | get 35 | { 36 | return useValue ? constantValue : variable.colorValue; 37 | } 38 | set 39 | { 40 | if (useValue) 41 | constantValue = value; 42 | else 43 | variable.colorValue = value; 44 | } 45 | 46 | 47 | 48 | } 49 | 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /ScriptableObjectsWithOdin/EnemyData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [CreateAssetMenu(fileName = "EnemyData", menuName = "My Game/Enemy Data")] 7 | [InlineEditor] 8 | public class EnemyData : ScriptableObject 9 | { 10 | [BoxGroup("Basic Info")] 11 | [LabelWidth(100)] 12 | public string enemyName; 13 | [BoxGroup("Basic Info")] 14 | [LabelWidth(100)] 15 | [TextArea] 16 | public string description; 17 | 18 | [HorizontalGroup("Game Data", 75)] 19 | [PreviewField(75)] 20 | [HideLabel] 21 | public GameObject enemyModel; 22 | 23 | [VerticalGroup("Game Data/Stats")] 24 | [LabelWidth(100)] 25 | [Range(20,100)] 26 | [GUIColor(0.5f,1f,0.5f)] 27 | public int health = 20; 28 | [VerticalGroup("Game Data/Stats")] 29 | [LabelWidth(100)] 30 | [Range(0.5f,5f)] 31 | [GUIColor(0.3f,0.5f,1f)] 32 | public float speed = 2f; 33 | [VerticalGroup("Game Data/Stats")] 34 | [LabelWidth(100)] 35 | [Range(5,30)] 36 | [GUIColor(1f,1f,0f)] 37 | public float detectRange = 10f; 38 | [VerticalGroup("Game Data/Stats")] 39 | [LabelWidth(100)] 40 | [Range(1,10)] 41 | [GUIColor(0.8f,0.4f,0.4f)] 42 | public int damage = 1; 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /Custom Validator Scripts/SpriteSizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | using Sirenix.OdinInspector.Editor.Validation; 4 | 5 | [assembly: RegisterValidator(typeof(SpriteSizeValidator))] 6 | public class SpriteSizeValidator : AttributeValidator 7 | { 8 | protected override void Validate(ValidationResult result) 9 | { 10 | if (this.ValueEntry.SmartValue == null) 11 | return; 12 | 13 | int size = this.Attribute.size; 14 | int width = this.ValueEntry.SmartValue.texture.width; 15 | 16 | if (width != size) 17 | { 18 | result.ResultType = ValidationResultType.Warning; 19 | result.Message = "The Size of the sprite is NOT the desired size of " + width + " instead it is " + size; 20 | } 21 | } 22 | } 23 | 24 | public class SpriteSizeAttribute : Attribute 25 | { 26 | public int size; 27 | 28 | public SpriteSizeAttribute(int size) 29 | { 30 | this.size = size; 31 | } 32 | 33 | public SpriteSizeAttribute(SpriteSize spriteSize) 34 | { 35 | this.size = (int)spriteSize; 36 | } 37 | } 38 | 39 | public enum SpriteSize 40 | { 41 | small = 128, 42 | medium = 256, 43 | large = 512, 44 | extraLarge = 1024, 45 | huge = 2048 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /How I Use Odin/HelpfulButtons.cs: -------------------------------------------------------------------------------- 1 | 2 | using Sirenix.OdinInspector; 3 | using Sirenix.OdinInspector.Editor; 4 | using UnityEditor; 5 | using UnityEditor.SceneManagement; 6 | 7 | public class HelpfulButtons : OdinEditorWindow 8 | { 9 | [MenuItem("Tools/Helpful Buttons")] 10 | private static void OpenWindow() 11 | { 12 | GetWindow().Show(); 13 | } 14 | 15 | [ButtonGroup] 16 | private void StartScene() 17 | { 18 | LoadScene("Assets/Scenes/AlphaStartScene.unity"); 19 | } 20 | 21 | [ButtonGroup] 22 | private void GameScene() 23 | { 24 | LoadScene("Assets/Scenes/Game Scene.unity"); 25 | } 26 | 27 | [ButtonGroup] 28 | private void TestScene() 29 | { 30 | LoadScene("Assets/Scenes/Testing/Test Scene.unity"); 31 | } 32 | 33 | private void LoadScene(string scenePath) 34 | { 35 | if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) 36 | UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath); 37 | } 38 | 39 | [ButtonGroup] 40 | private void PatchNotes() 41 | { 42 | Selection.activeObject = AssetDatabase.LoadMainAssetAtPath("Assets/Prefabs/Patch Notes.asset"); 43 | } 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /Value Resolver/ColorIfAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class ColorIfAttribute : Attribute 4 | { 5 | public string color; 6 | public string condition; 7 | public string myString; 8 | 9 | public ColorIfAttribute(string color, string condition) 10 | { 11 | this.color = color; 12 | this.condition = condition; 13 | } 14 | } 15 | 16 | //public class ColorIfAttributeDrawer : OdinAttributeDrawer 17 | //{ 18 | // private ValueResolver colorResolver; 19 | // private ValueResolver conditionResolver; 20 | 21 | // protected override void Initialize() 22 | // { 23 | // this.colorResolver = ValueResolver.Get(this.Property, this.Attribute.color, Color.white); 24 | // this.conditionResolver = ValueResolver.Get(this.Property, this.Attribute.condition); 25 | // } 26 | 27 | // protected override void DrawPropertyLayout(GUIContent label) 28 | // { 29 | // ValueResolver.DrawErrors(this.colorResolver, this.conditionResolver); 30 | 31 | // bool condition = this.conditionResolver.GetValue(); 32 | 33 | // if (condition) 34 | // { 35 | // GUIHelper.PushColor(this.colorResolver.GetValue()); 36 | // } 37 | 38 | // this.CallNextDrawer(label); 39 | 40 | // if (condition) 41 | // { 42 | // GUIHelper.PopColor(); 43 | // } 44 | // } 45 | //} -------------------------------------------------------------------------------- /Shorts/Button Attribute/ButtonShortExamples.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using Sirenix.OdinInspector; 4 | 5 | public class ButtonShortExamples : MonoBehaviour 6 | { 7 | public int width; 8 | public int height; 9 | public GameObject gridItem; 10 | private List gridItemsList = new List(); 11 | 12 | [ButtonGroup("GridButton")] 13 | private void GenerateGrid() 14 | { 15 | for (int i = 0; i < width; i++) 16 | { 17 | for (int j = 0; j < height; j++) 18 | { 19 | GameObject newGriditem 20 | = Instantiate(gridItem); 21 | newGriditem.transform.position 22 | = new Vector3(i, 0.25f, j) * 2; 23 | gridItemsList.Add(newGriditem); 24 | } 25 | } 26 | } 27 | 28 | [ButtonGroup("GridButton")] 29 | private void ClearGrid() 30 | { 31 | for (int i = 0; i < gridItemsList.Count; i++) 32 | { 33 | if (Application.isEditor) 34 | DestroyImmediate(gridItemsList[i]); 35 | else 36 | Destroy(gridItemsList[i]); 37 | } 38 | } 39 | 40 | private void SomeGeneric() where T : MonoBehaviour 41 | { 42 | 43 | } 44 | 45 | private void AnotherGeneric() where T : Component 46 | { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /PropertyProcessors/SamplePropertyProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using Sirenix.OdinInspector.Editor; 6 | using System; 7 | 8 | public class SamplePropertyProcessor : OdinPropertyProcessor where T : SampleClass 9 | { 10 | public override void ProcessMemberProperties(List propertyInfos) 11 | { 12 | for (int i = 0; i < propertyInfos.Count; i++) 13 | { 14 | if(propertyInfos[i].PropertyName == "bottom") 15 | { 16 | propertyInfos.Insert(0, propertyInfos[i]); 17 | propertyInfos.RemoveAt(i + 1); 18 | } 19 | } 20 | 21 | propertyInfos.AddDelegate("Print Hello", () => Debug.Log("Hello"), new BoxGroupAttribute("injected")); 22 | 23 | propertyInfos.AddValue("Injected Property", 24 | (ref SampleClass s) => s.value1 + s.value2 + s.value3, 25 | (ref SampleClass s, int sum) => { }, new BoxGroupAttribute("injected")); 26 | 27 | propertyInfos.AddValue("Injected Enum", 28 | (ref SampleClass s) => (MyEnum)s.value1, 29 | (ref SampleClass s, MyEnum myEnum) => s.value1 = (int)myEnum, 30 | new EnumToggleButtonsAttribute(), 31 | new BoxGroupAttribute("injected")); 32 | 33 | propertyInfos.Remove("value1"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /The Manager Manager/Editor/GUIUtils.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.Utilities; 2 | using System; 3 | using UnityEditor; 4 | using UnityEngine; 5 | public static class GUIUtils 6 | { 7 | public static bool SelectButtonList(ref Type selectedType, Type[] typesToDisplay) 8 | { 9 | var rect = GUILayoutUtility.GetRect(0, 25); 10 | 11 | for (int i = 0; i < typesToDisplay.Length; i++) 12 | { 13 | var name = typesToDisplay[i].Name; 14 | var btnRect = rect.Split(i, typesToDisplay.Length); 15 | 16 | if (GUIUtils.SelectButton(btnRect, name, typesToDisplay[i] == selectedType)) 17 | { 18 | selectedType = typesToDisplay[i]; 19 | return true; 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | public static bool SelectButton(Rect rect, string name, bool selected) 26 | { 27 | if (GUI.Button(rect, GUIContent.none, GUIStyle.none)) 28 | return true; 29 | 30 | if (Event.current.type == EventType.Repaint) 31 | { 32 | var style = new GUIStyle(EditorStyles.miniButtonMid); 33 | style.stretchHeight = true; 34 | style.fixedHeight = rect.height; 35 | style.Draw(rect, new GUIContent(name), false, false, selected, false); 36 | } 37 | return false; 38 | } 39 | 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Data Manager/GUIUtils.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.Utilities; 2 | using System; 3 | using UnityEditor; 4 | using UnityEngine; 5 | using Sirenix.Utilities.Editor; 6 | 7 | public static class GUIUtils 8 | { 9 | public static bool SelectButtonList(ref Type selectedType, Type[] typesToDisplay) 10 | { 11 | var rect = GUILayoutUtility.GetRect(0, 25); 12 | 13 | for (int i = 0; i < typesToDisplay.Length; i++) 14 | { 15 | var name = typesToDisplay[i].Name; 16 | var btnRect = rect.Split(i, typesToDisplay.Length); 17 | 18 | if (GUIUtils.SelectButton(btnRect, name, typesToDisplay[i] == selectedType)) 19 | { 20 | selectedType = typesToDisplay[i]; 21 | return true; 22 | } 23 | } 24 | return false; 25 | } 26 | 27 | public static bool SelectButton(Rect rect, string name, bool selected) 28 | { 29 | if (GUI.Button(rect, GUIContent.none, GUIStyle.none)) 30 | return true; 31 | 32 | if (Event.current.type == EventType.Repaint) 33 | { 34 | var style = new GUIStyle(EditorStyles.miniButtonMid); 35 | style.stretchHeight = true; 36 | style.fixedHeight = rect.height; 37 | style.Draw(rect, GUIHelper.TempContent(name), false, false, selected, false); 38 | } 39 | 40 | 41 | return false; 42 | } 43 | 44 | } 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Shorts/TabGroup/CharacterTabExample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class CharacterTabExample : MonoBehaviour 7 | { 8 | [TabGroup("Character Details")] 9 | public CharacterInfo characterInfo; 10 | 11 | [TabGroup("Character Stats")] 12 | public CharacterStats stats; 13 | 14 | [TabGroup("Other")] 15 | public List inventory; 16 | 17 | [TabGroup("Other")] 18 | public List skillList; 19 | } 20 | 21 | [System.Serializable] 22 | public class Skill 23 | { 24 | //[TabGroup("Character Details")] 25 | //[TabGroup("Character Stats")] 26 | //[TabGroup("Other")] 27 | //[TabGroup("Skills")] 28 | public string skillName; 29 | public Sprite skillIcon; 30 | } 31 | 32 | [System.Serializable] 33 | public class CharacterStats 34 | { 35 | [Range(0,20)] 36 | public int strength; 37 | [Range(0,20)] 38 | public int vitality; 39 | [Range(0,20)] 40 | public int intelligence; 41 | [Range(0,20)] 42 | public int dexeterity; 43 | [Range(0,20)] 44 | public int charisma; 45 | } 46 | 47 | [System.Serializable] 48 | public class CharacterInfo 49 | { 50 | public string characterName; 51 | public string characterStory; 52 | public Sprite charcterImage; 53 | } 54 | 55 | [System.Serializable] 56 | public class InventoryObject 57 | { 58 | public Sprite icon; 59 | public string itemName; 60 | public string itemDescription; 61 | public ItemStats itemStats; 62 | } 63 | 64 | [System.Serializable] 65 | public class ItemStats 66 | { 67 | public int cost; 68 | public int weight; 69 | } 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Custom Validator Scripts/PlantValidator.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Sirenix.OdinInspector.Editor.Validation; 3 | using Sirenix.OdinInspector.Editor; 4 | 5 | [assembly: RegisterValidator(typeof(PlantValidator))] 6 | public class PlantValidator : ValueValidator 7 | { 8 | public override bool CanValidateProperty(InspectorProperty property) 9 | { 10 | return property.IsTreeRoot; 11 | } 12 | 13 | protected override void Validate(ValidationResult result) 14 | { 15 | if (this.ValueEntry.SmartValue == null) 16 | return; 17 | 18 | if(this.ValueEntry.SmartValue.transform.childCount == 0) 19 | { 20 | result.ResultType = ValidationResultType.Error; 21 | result.Message = "This Plant Doesn't Have a Child Object"; 22 | } 23 | else 24 | { 25 | Transform child = this.ValueEntry.SmartValue.transform.GetChild(0); 26 | 27 | if (child.GetComponent() == null) 28 | { 29 | result.ResultType = ValidationResultType.Warning; 30 | result.Message = "The plant mesh is missing a collider"; 31 | } 32 | else if (child.GetComponent() == null) 33 | { 34 | result.ResultType = ValidationResultType.Warning; 35 | result.Message = "The plant mesh is missing a rigidbody"; 36 | } 37 | else if(!child.GetComponent().isKinematic) 38 | { 39 | result.ResultType = ValidationResultType.Warning; 40 | result.Message = "The plant rigidbody is not set to isKinematic"; 41 | } 42 | } 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Custom Attribute Processors/CharacterAttributeProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using Sirenix.OdinInspector.Editor; 6 | using Sirenix.Utilities; 7 | using System; 8 | using System.Reflection; 9 | 10 | public class CharacterAttributeProcessor : OdinAttributeProcessor where T : Character 11 | { 12 | public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List attributes) 13 | { 14 | attributes.Add(new LabelWidthAttribute(110)); 15 | 16 | if(member.Name == "icon") 17 | { 18 | attributes.Add(new PropertyOrderAttribute(-1)); 19 | attributes.Add(new HideLabelAttribute()); 20 | attributes.Add(new HorizontalGroupAttribute("Character", 100)); 21 | attributes.Add(new PreviewFieldAttribute(100, ObjectFieldAlignment.Center)); 22 | } 23 | 24 | if(member.Name == "prefab") 25 | { 26 | attributes.Add(new VerticalGroupAttribute("Character/Right")); 27 | } 28 | 29 | if(member.GetReturnType() == typeof(string)) 30 | { 31 | attributes.Add(new VerticalGroupAttribute("Character/Right")); 32 | } 33 | 34 | } 35 | } 36 | 37 | public class StatsAttributeProcessor : OdinAttributeProcessor 38 | { 39 | public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List attributes) 40 | { 41 | attributes.Add(new BoxGroupAttribute("Stats")); 42 | attributes.Add(new RangeAttribute(0, 20)); 43 | } 44 | 45 | public override void ProcessSelfAttributes(InspectorProperty property, List attributes) 46 | { 47 | attributes.Add(new HideLabelAttribute()); 48 | attributes.Add(new SpaceAttribute()); 49 | } 50 | } -------------------------------------------------------------------------------- /How I Use Odin/UnitUnlocakUpgradeWindow.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using Sirenix.OdinInspector.Editor; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | public class UnitUnlocakUpgradeWindow : OdinEditorWindow 7 | { 8 | [MenuItem("Tools/Unlock Unit Upgrade Creator")] 9 | private static void OpenWindow() 10 | { 11 | UnitUnlocakUpgradeWindow window = GetWindow(); 12 | window.Show(); 13 | window.path = PlayerPrefs.GetString("UnlockUnitPath", ""); 14 | } 15 | 16 | private new void OnDestroy() 17 | { 18 | PlayerPrefs.SetString("UnlockUnitPath", path); 19 | base.OnDestroy(); 20 | } 21 | 22 | [FolderPath, SerializeField, Required] 23 | private string path; 24 | 25 | [InlineEditor(Expanded = true)] 26 | public UnitUnlockUpgrade upgrade; 27 | 28 | [GUIColor(0.5f,1f,0.5f)] 29 | [ButtonGroup("")] 30 | private void SaveUpgrade() 31 | { 32 | upgrade.upgradeName = GenerateName(); 33 | upgrade.description = GenerateDescription(); 34 | 35 | if (string.IsNullOrEmpty(upgrade.upgradeName)) 36 | return; 37 | 38 | AssetDatabase.CreateAsset(upgrade, path + "/" + upgrade.upgradeName + ".asset"); 39 | AssetDatabase.SaveAssets(); 40 | AssetDatabase.Refresh(); 41 | NewUpgrade(); 42 | } 43 | 44 | [GUIColor(0.5f,0.5f,1f)] 45 | [ButtonGroup("")] 46 | private void NewUpgrade() 47 | { 48 | upgrade = ScriptableObject.CreateInstance(); 49 | upgrade.cost = new HexGame.Resources.ResourceAmount(HexGame.Resources.ResourceType.Research, 500); 50 | } 51 | 52 | [GUIColor(0.5f, 1f, 0.5f)] 53 | [ButtonGroup("")] 54 | private string GenerateName() 55 | { 56 | string name = "Unlock "; 57 | 58 | name += upgrade.buildingsToUnlock[0].ToNiceString(); 59 | 60 | return name; 61 | } 62 | 63 | private string GenerateDescription() 64 | { 65 | return $"Unlocks {upgrade.buildingsToUnlock[0].ToNiceString()}"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ScriptableObjectsWithOdin/Editor/EnemyDataEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using Sirenix.OdinInspector; 4 | using Sirenix.OdinInspector.Editor; 5 | using Sirenix.Utilities.Editor; 6 | 7 | public class EnemyDataEditor : OdinMenuEditorWindow 8 | { 9 | [MenuItem("Tools/Enemy Data")] 10 | private static void OpenWindow() 11 | { 12 | GetWindow().Show(); 13 | } 14 | 15 | protected override OdinMenuTree BuildMenuTree() 16 | { 17 | var tree = new OdinMenuTree(); 18 | tree.Selection.SupportsMultiSelect = false; 19 | 20 | tree.Add("Create New", new CreateNewEnemyData()); 21 | tree.AddAllAssetsAtPath("Enemy Data", "Assets/Scripts", typeof(EnemyData)); 22 | return tree; 23 | } 24 | 25 | public class CreateNewEnemyData 26 | { 27 | public CreateNewEnemyData() 28 | { 29 | enemyData = ScriptableObject.CreateInstance(); 30 | enemyData.enemyName = "New Enemy Data"; 31 | } 32 | 33 | [InlineEditor(Expanded = true)] 34 | public EnemyData enemyData; 35 | 36 | [Button("Add New Enemy SO")] 37 | private void CreateNewData() 38 | { 39 | AssetDatabase.CreateAsset(enemyData, "Assets/Scripts/" + enemyData.enemyName + ".asset"); 40 | AssetDatabase.SaveAssets(); 41 | } 42 | } 43 | 44 | protected override void OnBeginDrawEditors() 45 | { 46 | OdinMenuTreeSelection selected = this.MenuTree.Selection; 47 | 48 | SirenixEditorGUI.BeginHorizontalToolbar(); 49 | { 50 | GUILayout.FlexibleSpace(); 51 | 52 | if (SirenixEditorGUI.ToolbarButton("Delete Current")) 53 | { 54 | EnemyData asset = selected.SelectedValue as EnemyData; 55 | string path = AssetDatabase.GetAssetPath(asset); 56 | AssetDatabase.DeleteAsset(path); 57 | AssetDatabase.SaveAssets(); 58 | } 59 | 60 | } 61 | SirenixEditorGUI.EndHorizontalToolbar(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ScriptableObjects/EnemyControl.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.AI; 5 | using Sirenix.OdinInspector; 6 | 7 | [RequireComponent(typeof(NavMeshAgent))] 8 | public class EnemyControl : MonoBehaviour 9 | { 10 | private NavMeshAgent navAgent; 11 | private float wanderDistance = 3; 12 | 13 | public EnemyData data; 14 | 15 | private void Start() 16 | { 17 | if (navAgent == null) 18 | navAgent = this.GetComponent(); 19 | 20 | if (data != null) 21 | LoadEnemy(data); 22 | } 23 | 24 | private void LoadEnemy(EnemyData _data) 25 | { 26 | //remove children objects i.e. visuals 27 | foreach (Transform child in this.transform) 28 | { 29 | if (Application.isEditor) 30 | DestroyImmediate(child.gameObject); 31 | else 32 | Destroy(child.gameObject); 33 | } 34 | 35 | //load current enemy visuals 36 | GameObject visuals = Instantiate(data.enemyModel); 37 | visuals.transform.SetParent(this.transform); 38 | visuals.transform.localPosition = Vector3.zero; 39 | visuals.transform.rotation = Quaternion.identity; 40 | 41 | //use stats data to set up enemy 42 | if (navAgent == null) 43 | navAgent = this.GetComponent(); 44 | 45 | this.navAgent.speed = data.speed; 46 | } 47 | 48 | private void Update() 49 | { 50 | if (data == null) 51 | return; 52 | 53 | if (navAgent.remainingDistance < 1f) 54 | GetNewDestination(); 55 | } 56 | 57 | private void GetNewDestination() 58 | { 59 | Vector3 nextDestination = this.transform.position; 60 | nextDestination += wanderDistance * new Vector3(Random.Range(-1f, 1f), 0f, Random.Range(-1f, 1f)).normalized; 61 | 62 | NavMeshHit hit; 63 | if (NavMesh.SamplePosition(nextDestination, out hit, 3f, NavMesh.AllAreas)) 64 | navAgent.SetDestination(hit.position); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /How I Use Odin/StatsUpgradeWindow.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using Sirenix.OdinInspector.Editor; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | public class StatsUpgradeWindow : OdinEditorWindow 7 | { 8 | [MenuItem("Tools/Stats Upgrade Creator")] 9 | private static void OpenWindow() 10 | { 11 | StatsUpgradeWindow window = GetWindow(); 12 | window.Show(); 13 | window.path = PlayerPrefs.GetString("StatsPath", ""); 14 | } 15 | private new void OnDestroy() 16 | { 17 | PlayerPrefs.SetString("StatsPath", path); 18 | base.OnDestroy(); 19 | } 20 | 21 | [FolderPath, SerializeField, Required] 22 | private string path; 23 | 24 | [InlineEditor(Expanded = true)] 25 | public StatsUpgrade upgrade; 26 | 27 | [GUIColor(0.5f,1f,0.5f)] 28 | [ButtonGroup("")] 29 | private void SaveUpgrade() 30 | { 31 | upgrade.upgradeName = GenerateName(); 32 | 33 | if (string.IsNullOrEmpty(upgrade.upgradeName)) 34 | return; 35 | 36 | AssetDatabase.CreateAsset(upgrade, path + "/" + upgrade.upgradeName + ".asset"); 37 | AssetDatabase.SaveAssets(); 38 | AssetDatabase.Refresh(); 39 | NewUpgrade(); 40 | } 41 | 42 | [GUIColor(0.5f,0.5f,1f)] 43 | [ButtonGroup("")] 44 | private void NewUpgrade() 45 | { 46 | upgrade = ScriptableObject.CreateInstance(); 47 | upgrade.cost = new HexGame.Resources.ResourceAmount(HexGame.Resources.ResourceType.Research, 500); 48 | } 49 | 50 | [GUIColor(0.5f, 1f, 0.5f)] 51 | [ButtonGroup("")] 52 | private string GenerateName() 53 | { 54 | string name; 55 | name = upgrade.unitsToUpgrade[0].name; 56 | name = name.Replace("Stats", ""); 57 | 58 | foreach (var upgrade in upgrade.upgradeToApply) 59 | { 60 | if (upgrade.Value > 0) 61 | name += "Plus"; 62 | else 63 | name += "Minus"; 64 | 65 | name += $" {Mathf.Abs(upgrade.Value)} {upgrade.Key}"; 66 | } 67 | 68 | return name; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ScriptableObjectsWithOdin/EnemyControl.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.AI; 5 | using Sirenix.OdinInspector; 6 | 7 | [RequireComponent(typeof(NavMeshAgent))] 8 | public class EnemyControl : MonoBehaviour 9 | { 10 | private NavMeshAgent navAgent; 11 | private float wanderDistance = 3; 12 | 13 | [OnValueChanged("LoadEnemy")] 14 | [InlineEditor] 15 | public EnemyData data; 16 | 17 | private void Start() 18 | { 19 | if (navAgent == null) 20 | navAgent = this.GetComponent(); 21 | 22 | if (data != null) 23 | LoadEnemy(data); 24 | } 25 | 26 | private void LoadEnemy(EnemyData _data) 27 | { 28 | //remove children objects i.e. visuals 29 | foreach (Transform child in this.transform) 30 | { 31 | if (Application.isEditor) 32 | DestroyImmediate(child.gameObject); 33 | else 34 | Destroy(child.gameObject); 35 | } 36 | 37 | //load current enemy visuals 38 | GameObject visuals = Instantiate(data.enemyModel); 39 | visuals.transform.SetParent(this.transform); 40 | visuals.transform.localPosition = Vector3.zero; 41 | visuals.transform.rotation = Quaternion.identity; 42 | 43 | //use stats data to set up enemy 44 | if (navAgent == null) 45 | navAgent = this.GetComponent(); 46 | 47 | this.navAgent.speed = data.speed; 48 | } 49 | 50 | private void Update() 51 | { 52 | if (data == null) 53 | return; 54 | 55 | if (navAgent.remainingDistance < 1f) 56 | GetNewDestination(); 57 | } 58 | 59 | private void GetNewDestination() 60 | { 61 | Vector3 nextDestination = this.transform.position; 62 | nextDestination += wanderDistance * new Vector3(Random.Range(-1f, 1f), 0f, Random.Range(-1f, 1f)).normalized; 63 | 64 | NavMeshHit hit; 65 | if (NavMesh.SamplePosition(nextDestination, out hit, 3f, NavMesh.AllAreas)) 66 | navAgent.SetDestination(hit.position); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /AttributeDrawers/Editor/MyColorAttributeDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.Utilities; 5 | using Sirenix.Utilities.Editor; 6 | using Sirenix.OdinInspector.Editor; 7 | using UnityEditor; 8 | 9 | public class MyColorAttributeDrawer : OdinAttributeDrawer 10 | { 11 | protected override void DrawPropertyLayout(GUIContent label) 12 | { 13 | Rect rect = EditorGUILayout.GetControlRect(); 14 | Color tempColor = this.ValueEntry.SmartValue; 15 | string hexCode = ColorUtility.ToHtmlStringRGB(tempColor); 16 | 17 | if (label != null) 18 | rect = EditorGUI.PrefixLabel(rect, label); 19 | 20 | rect = EditorGUILayout.GetControlRect(); 21 | tempColor = SirenixEditorFields.ColorField(rect.AlignLeft(rect.width * 0.75f), tempColor); 22 | 23 | //hexcode 24 | hexCode = SirenixEditorFields.TextField(rect.AlignRight(rect.width * 0.25f), "#" + hexCode); 25 | if(ColorUtility.TryParseHtmlString(hexCode, out tempColor)) 26 | { 27 | this.ValueEntry.SmartValue = tempColor; 28 | } 29 | 30 | //rgb values 31 | rect = EditorGUILayout.GetControlRect(); 32 | 33 | GUIHelper.PushLabelWidth(15); 34 | tempColor.r = EditorGUI.Slider(rect.AlignLeft(rect.width * 0.3f), "R", tempColor.r, 0f, 1f); 35 | tempColor.g = EditorGUI.Slider(rect.AlignCenter(rect.width * 0.3f), "G", tempColor.g, 0f, 1f); 36 | tempColor.b = EditorGUI.Slider(rect.AlignRight(rect.width * 0.3f), "B", tempColor.b, 0f, 1f); 37 | GUIHelper.PopLabelWidth(); 38 | 39 | //hsv values 40 | Color.RGBToHSV(tempColor, out float h, out float s, out float v); 41 | rect = EditorGUILayout.GetControlRect(); 42 | 43 | GUIHelper.PushLabelWidth(15); 44 | h = EditorGUI.Slider(rect.AlignLeft(rect.width * 0.3f), "H", h, 0f, 1f); 45 | s = EditorGUI.Slider(rect.AlignCenter(rect.width * 0.3f), "S", s, 0f, 1f); 46 | v = EditorGUI.Slider(rect.AlignRight(rect.width * 0.3f), "V", v, 0f, 1f); 47 | GUIHelper.PopLabelWidth(); 48 | 49 | tempColor = Color.HSVToRGB(h, s, v); 50 | 51 | this.ValueEntry.SmartValue = tempColor; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /2D Character Creator/Character.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | public class Character : MonoBehaviour 7 | { 8 | [OnValueChanged("SetSprites")] 9 | [InlineEditor(InlineEditorObjectFieldModes.Foldout, Expanded = true)] 10 | [OnValueChanged("SetSprites", true)] //will only update if SO changed in character inspector 11 | [TabGroup("Sprites")] 12 | public CharacterSpriteData spriteData; 13 | 14 | //Sprite Renderers 15 | //these correspond to the sprites in the SO 16 | 17 | [Title("Head")] 18 | [TabGroup("Sprite Renderers")] 19 | public SpriteRenderer head; 20 | [TabGroup("Sprite Renderers")] 21 | public SpriteRenderer face; 22 | [TabGroup("Sprite Renderers")] 23 | public SpriteRenderer beard; 24 | [TabGroup("Sprite Renderers")] 25 | public SpriteRenderer hair; 26 | [TabGroup("Sprite Renderers")] 27 | public SpriteRenderer hat; 28 | 29 | [Title("Body")] 30 | [TabGroup("Sprite Renderers")] 31 | public SpriteRenderer body; 32 | [TabGroup("Sprite Renderers")] 33 | public SpriteRenderer rightHand; 34 | [TabGroup("Sprite Renderers")] 35 | public SpriteRenderer leftHand; 36 | [TabGroup("Sprite Renderers")] 37 | public SpriteRenderer rightLeg; 38 | [TabGroup("Sprite Renderers")] 39 | public SpriteRenderer leftLeg; 40 | 41 | [Title("Weapons")] 42 | [TabGroup("Sprite Renderers")] 43 | public SpriteRenderer rightHandItem; 44 | [TabGroup("Sprite Renderers")] 45 | public SpriteRenderer leftHandItem; 46 | 47 | private void SetSprites() 48 | { 49 | if (spriteData == null) 50 | return; 51 | 52 | body.sprite = spriteData.body; 53 | head.sprite = spriteData.head; 54 | face.sprite = spriteData.face; 55 | beard.sprite = spriteData.beard; 56 | hair.sprite = spriteData.hair; 57 | hat.sprite = spriteData.hat; 58 | rightHand.sprite = spriteData.rightHand; 59 | rightHandItem.sprite = spriteData.rightHandItem; 60 | leftHand.sprite = spriteData.leftHand; 61 | leftHandItem.sprite = spriteData.leftHandItem; 62 | rightLeg.sprite = spriteData.rightLeg; 63 | leftLeg.sprite = spriteData.leftLeg; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SFX Manager/SFXManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System.Linq; 5 | using Sirenix.OdinInspector; 6 | 7 | public class SFXManager : MonoBehaviour 8 | { 9 | private static SFXManager _instance; 10 | public static SFXManager instance 11 | { 12 | get 13 | { 14 | if (_instance == null) 15 | _instance = FindObjectOfType(); 16 | 17 | return _instance; 18 | } 19 | } 20 | 21 | [HorizontalGroup("AudioSource")] 22 | [SerializeField] 23 | private AudioSource defaultAudioSource; 24 | 25 | [TabGroup("UI")] 26 | [AssetList(Path = "/Audio/UI SFX", AutoPopulate = true)] 27 | public List uiSFX; 28 | [TabGroup("Ambient")] 29 | [AssetList(Path = "/Audio/Ambient SFX", AutoPopulate = true)] 30 | public List ambientSFX; 31 | [TabGroup("Weapons")] 32 | [AssetList(Path = "/Audio/Weapon SFX", AutoPopulate = true)] 33 | public List weaponSFX; 34 | 35 | public static void PlaySFX(SFXClip sfx, bool waitToFinish = true, AudioSource audioSource = null) 36 | { 37 | if (audioSource == null) 38 | audioSource = SFXManager.instance.defaultAudioSource; 39 | 40 | if (audioSource == null) 41 | { 42 | Debug.LogError("You forgot to add a default audio source!"); 43 | return; 44 | } 45 | 46 | if (!audioSource.isPlaying || !waitToFinish) 47 | { 48 | audioSource.clip = sfx.clip; 49 | audioSource.volume = sfx.volume + Random.Range(-sfx.volumeVariation, sfx.volumeVariation); 50 | audioSource.pitch = sfx.pitch + Random.Range(-sfx.pitchVariation, sfx.pitchVariation); 51 | audioSource.Play(); 52 | } 53 | } 54 | 55 | [HorizontalGroup("AudioSource")] 56 | [ShowIf("@defaultAudioSource == null")] 57 | [GUIColor(1f,0.5f,0.5f,1f)] 58 | [Button] 59 | private void AddAudioSource() 60 | { 61 | defaultAudioSource = this.gameObject.GetComponent(); 62 | 63 | if (defaultAudioSource == null) 64 | defaultAudioSource = this.gameObject.AddComponent(); 65 | } 66 | 67 | public enum SFXType 68 | { 69 | UI, 70 | Ambient, 71 | Weapons 72 | } 73 | } 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Generic Matching/NPCBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.Utilities.Editor; 5 | using Sirenix.OdinInspector.Editor; 6 | using UnityEditor; 7 | 8 | public class NPCBase : MonoBehaviour 9 | { 10 | public BaseStats stats; 11 | 12 | public MagicStats magicStats; 13 | 14 | public List NPCStatsList = new List(); 15 | } 16 | 17 | //public interface IResettable 18 | //{ 19 | // void Reset(); 20 | //} 21 | 22 | //public class AddBoxToStatsDrawer : OdinValueDrawer where T : BaseStats 23 | //{ 24 | // protected override void DrawPropertyLayout(GUIContent label) 25 | // { 26 | // SirenixEditorGUI.BeginBox(); 27 | // this.CallNextDrawer(label); 28 | // SirenixEditorGUI.EndBox(); 29 | // } 30 | //} 31 | 32 | //public class ResetContextMenuDrawer : OdinValueDrawer, IDefinesGenericMenuItems where T : IResettable 33 | //{ 34 | 35 | // protected override void Initialize() 36 | // { 37 | // this.SkipWhenDrawing = true; 38 | // } 39 | 40 | // public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) 41 | // { 42 | // genericMenu.AddItem(new GUIContent("Reset Stats"), false, Reset); 43 | // } 44 | 45 | // private void Reset() 46 | // { 47 | // this.Property.RecordForUndo("Resetting values using IResettable"); 48 | 49 | // foreach (var value in this.ValueEntry.Values) 50 | // { 51 | // value.Reset(); 52 | // } 53 | // } 54 | //} 55 | 56 | //public class ResetListDrawr : OdinValueDrawer, IDefinesGenericMenuItems where TList : IList 57 | // where TElement : IResettable 58 | //{ 59 | // protected override void Initialize() 60 | // { 61 | // this.SkipWhenDrawing = true; 62 | // } 63 | 64 | // public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) 65 | // { 66 | // genericMenu.AddItem(new GUIContent("Reset List"), false, ResetList); 67 | // } 68 | 69 | // private void ResetList() 70 | // { 71 | // this.Property.RecordForUndo("Resetting List using IResettable"); 72 | 73 | // foreach (var list in this.ValueEntry.Values) 74 | // { 75 | // foreach (var element in list) 76 | // { 77 | // element.Reset(); 78 | // } 79 | // } 80 | // } 81 | //} 82 | -------------------------------------------------------------------------------- /Generic Matching/NPCStats.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using Sirenix.Utilities.Editor; 4 | using Sirenix.OdinInspector.Editor; 5 | using UnityEditor; 6 | 7 | [System.Serializable] 8 | public class BaseStats : IResettable 9 | { 10 | public int health; 11 | public int gold; 12 | 13 | public void Reset() 14 | { 15 | health = 100; 16 | gold = 100; 17 | } 18 | } 19 | 20 | [System.Serializable] 21 | public class MagicStats : BaseStats 22 | { 23 | public int mana; 24 | public int manaRecharge; 25 | } 26 | 27 | public interface IResettable 28 | { 29 | void Reset(); 30 | } 31 | 32 | public class AddBoxToStatsDrawer : OdinValueDrawer where T : BaseStats 33 | { 34 | protected override void DrawPropertyLayout(GUIContent label) 35 | { 36 | SirenixEditorGUI.BeginBox(); 37 | this.CallNextDrawer(label); 38 | SirenixEditorGUI.EndBox(); 39 | } 40 | } 41 | 42 | public class ResetContextMenuDrawer : OdinValueDrawer, IDefinesGenericMenuItems where T : IResettable 43 | { 44 | protected override void Initialize() 45 | { 46 | this.SkipWhenDrawing = true; 47 | } 48 | 49 | public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) 50 | { 51 | genericMenu.AddItem(new GUIContent("Reset Stats"), false, Reset); 52 | } 53 | 54 | private void Reset() 55 | { 56 | this.Property.RecordForUndo("Reset Stats with IResettable"); 57 | 58 | foreach (var value in this.ValueEntry.Values) 59 | { 60 | value.Reset(); 61 | } 62 | } 63 | } 64 | 65 | public class ResetListDrawer : OdinValueDrawer, IDefinesGenericMenuItems where TList : IList 66 | where TElement : IResettable 67 | { 68 | 69 | protected override void Initialize() 70 | { 71 | this.SkipWhenDrawing = true; 72 | } 73 | 74 | public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) 75 | { 76 | genericMenu.AddItem(new GUIContent("Reset List"), false, ResetList); 77 | } 78 | 79 | private void ResetList() 80 | { 81 | this.Property.RecordForUndo("Reset List with IResettable"); 82 | 83 | foreach (var list in this.ValueEntry.Values) 84 | { 85 | foreach (var element in list) 86 | { 87 | element.Reset(); 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Class Value Drawers/Editor/MySubClassDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.Utilities.Editor; 5 | using Sirenix.OdinInspector.Editor; 6 | using Sirenix.Utilities; 7 | using UnityEditor; 8 | 9 | 10 | public class MySubClassDrawer : OdinValueDrawer 11 | { 12 | private InspectorProperty text; 13 | private InspectorProperty number; 14 | private InspectorProperty location; 15 | 16 | protected override void Initialize() 17 | { 18 | text = this.Property.Children["text"]; 19 | number = this.Property.Children["number"]; 20 | location = this.Property.Children["location"]; 21 | } 22 | 23 | protected override void DrawPropertyLayout(GUIContent label) 24 | { 25 | Rect rect = EditorGUILayout.GetControlRect(); 26 | 27 | if (label != null) 28 | rect = EditorGUI.PrefixLabel(rect, label); 29 | 30 | rect = EditorGUILayout.GetControlRect(); 31 | 32 | GUIHelper.PushLabelWidth(75); 33 | text.ValueEntry.WeakSmartValue = SirenixEditorFields.TextField(rect.Split(0, 2), 34 | "Text", (string)text.ValueEntry.WeakSmartValue); 35 | number.ValueEntry.WeakSmartValue = SirenixEditorFields.IntField(rect.Split(1, 2), 36 | "Number", (int)number.ValueEntry.WeakSmartValue); 37 | location.Draw(); 38 | GUIHelper.PopLabelWidth(); 39 | } 40 | } 41 | 42 | public class MySubClassDrawer_WrongWay : OdinValueDrawer 43 | { 44 | private InspectorProperty text; 45 | private InspectorProperty number; 46 | private InspectorProperty location; 47 | 48 | protected override void Initialize() 49 | { 50 | text = this.Property.Children["text"]; 51 | number = this.Property.Children["number"]; 52 | location = this.Property.Children["location"]; 53 | } 54 | 55 | protected override void DrawPropertyLayout(GUIContent label) 56 | { 57 | string text = this.ValueEntry.SmartValue.text; 58 | int number = this.ValueEntry.SmartValue.number; 59 | Vector3 location = this.ValueEntry.SmartValue.location; 60 | 61 | Rect rect = EditorGUILayout.GetControlRect(); 62 | 63 | if (label != null) 64 | rect = EditorGUI.PrefixLabel(rect, label); 65 | 66 | rect = EditorGUILayout.GetControlRect(); 67 | 68 | GUIHelper.PushLabelWidth(75); 69 | text = SirenixEditorFields.TextField(rect.Split(0, 2), "Text", text); 70 | number = SirenixEditorFields.IntField(rect.Split(1, 2), "Number", number); 71 | location = SirenixEditorFields.Vector3Field("Location", location); 72 | GUIHelper.PopLabelWidth(); 73 | 74 | this.ValueEntry.SmartValue.text = text; 75 | this.ValueEntry.SmartValue.number = number; 76 | this.ValueEntry.SmartValue.location = location; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /SFX Manager/SFX.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | [System.Serializable] 7 | public class SFX 8 | { 9 | [LabelText("SFX Type")] 10 | [LabelWidth(100)] 11 | [OnValueChanged("SFXChange")] 12 | [InlineButton("PlaySFX")] 13 | public SFXManager.SFXType sfxType = SFXManager.SFXType.UI; 14 | 15 | [LabelText("$sfxLabel")] 16 | [LabelWidth(100)] 17 | [ValueDropdown("SFXType")] 18 | [OnValueChanged("SFXChange")] 19 | [InlineButton("SelectSFX")] 20 | public SFXClip sfxToPlay; 21 | private string sfxLabel = "SFX"; 22 | 23 | [SerializeField] 24 | private bool showSettings = false; 25 | 26 | [ShowIf("showSettings")] 27 | [SerializeField] 28 | private bool editSettings = false; 29 | 30 | [InlineEditor(InlineEditorObjectFieldModes.Hidden)] 31 | [ShowIf("showSettings")] 32 | [EnableIf("editSettings")] 33 | [SerializeField] 34 | private SFXClip _sfxBase; 35 | 36 | [Title("Audio Source")] 37 | [ShowIf("showSettings")] 38 | [EnableIf("editSettings")] 39 | [SerializeField] 40 | private bool waitToPlay = true; 41 | 42 | [ShowIf("showSettings")] 43 | [EnableIf("editSettings")] 44 | [SerializeField] 45 | private bool useDefault = true; 46 | 47 | [DisableIf("useDefault")] 48 | [ShowIf("showSettings")] 49 | [EnableIf("editSettings")] 50 | [SerializeField] 51 | private AudioSource audiosource; 52 | 53 | private void SFXChange() 54 | { 55 | //keep the label up to date 56 | sfxLabel = sfxType.ToString() + " SFX"; 57 | 58 | //keep the displayed "SFX clip" up to date 59 | _sfxBase = sfxToPlay; 60 | } 61 | 62 | 63 | private void SelectSFX() 64 | { 65 | UnityEditor.Selection.activeObject = sfxToPlay; 66 | } 67 | 68 | //Get's list of SFX from manager, used in the inspector 69 | private List SFXType() 70 | { 71 | List sfxList; 72 | 73 | switch (sfxType) 74 | { 75 | case SFXManager.SFXType.UI: 76 | sfxList = SFXManager.instance.uiSFX; 77 | break; 78 | case SFXManager.SFXType.Ambient: 79 | sfxList = SFXManager.instance.ambientSFX; 80 | break; 81 | case SFXManager.SFXType.Weapons: 82 | sfxList = SFXManager.instance.weaponSFX; 83 | break; 84 | default: 85 | sfxList = SFXManager.instance.uiSFX; 86 | break; 87 | } 88 | 89 | return sfxList; 90 | } 91 | 92 | public void PlaySFX() 93 | { 94 | if (useDefault || audiosource == null) 95 | SFXManager.PlaySFX(sfxToPlay, waitToPlay, null); 96 | else 97 | SFXManager.PlaySFX(sfxToPlay, waitToPlay, audiosource); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /CustomGroups/MyClass.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using Sirenix.OdinInspector.Editor; 6 | using Sirenix.Utilities.Editor; 7 | using System; 8 | 9 | public class MyClass : MonoBehaviour 10 | { 11 | 12 | 13 | [ColorFoldoutGroup("group1", 1f,0f,0f)] 14 | public int first = 1; 15 | [ColorFoldoutGroup("group1")] 16 | public int second = 2; 17 | [ColorFoldoutGroup("group1")] 18 | public int third = 3; 19 | 20 | [ColorFoldoutGroup("group2", 0f, 1f, 0f)] 21 | public string down; 22 | [ColorFoldoutGroup("group2")] 23 | public string up; 24 | [ColorFoldoutGroup("group2")] 25 | public string strange; 26 | 27 | [ColorFoldoutGroup("group3", 0f, 0f, 0.7f)] 28 | public Vector3 charm = new Vector3(1,0,0); 29 | [ColorFoldoutGroup("group3")] 30 | public Vector3 bottom = new Vector3(0, -1, 0); 31 | [ColorFoldoutGroup("group3")] 32 | public Vector3 top = new Vector3(0, 0, 0); 33 | } 34 | 35 | public class ColorFoldoutGroupAttribute : PropertyGroupAttribute 36 | { 37 | public float R, G, B, A; 38 | 39 | public ColorFoldoutGroupAttribute(string path) : base (path) 40 | { 41 | 42 | } 43 | 44 | public ColorFoldoutGroupAttribute(string path, float r, float g, float b, float a = 1f) : base(path) 45 | { 46 | this.R = r; 47 | this.G = g; 48 | this.B = b; 49 | this.A = a; 50 | } 51 | 52 | protected override void CombineValuesWith(PropertyGroupAttribute other) 53 | { 54 | var otherAttr = (ColorFoldoutGroupAttribute)other; 55 | 56 | this.R = Math.Max(otherAttr.R, this.R); 57 | this.G = Math.Max(otherAttr.G, this.G); 58 | this.B = Math.Max(otherAttr.B, this.B); 59 | this.A = Math.Max(otherAttr.A, this.A); 60 | } 61 | } 62 | 63 | public class ColorFoldoutGroupAttributeDrawer : OdinGroupDrawer 64 | { 65 | private LocalPersistentContext isExpanded; 66 | 67 | protected override void Initialize() 68 | { 69 | this.isExpanded = this.GetPersistentValue("ColorFoldoutGroupAttributeDrawer.isExpanded", 70 | GeneralDrawerConfig.Instance.ExpandFoldoutByDefault); 71 | } 72 | 73 | protected override void DrawPropertyLayout(GUIContent label) 74 | { 75 | GUIHelper.PushColor(new Color(this.Attribute.R, this.Attribute.G, this.Attribute.B, this.Attribute.A)); 76 | SirenixEditorGUI.BeginBox(); 77 | SirenixEditorGUI.BeginBoxHeader(); 78 | GUIHelper.PopColor(); 79 | 80 | this.isExpanded.Value = SirenixEditorGUI.Foldout(this.isExpanded.Value, label); 81 | SirenixEditorGUI.EndBoxHeader(); 82 | 83 | if (SirenixEditorGUI.BeginFadeGroup(this, this.isExpanded.Value)) 84 | { 85 | for (int i = 0; i < this.Property.Children.Count; i++) 86 | { 87 | this.Property.Children[i].Draw(); 88 | } 89 | } 90 | SirenixEditorGUI.EndFadeGroup(); 91 | SirenixEditorGUI.EndBox(); 92 | } 93 | } -------------------------------------------------------------------------------- /How I Use Odin/ResourceSOCreator.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using Sirenix.OdinInspector.Editor; 3 | using UnityEditor; 4 | using UnityEngine; 5 | using HexGame.Resources; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | public class ResourceSOCreator : OdinEditorWindow 10 | { 11 | [MenuItem("Tools/Resource Creator")] 12 | private static void OpenWindow() 13 | { 14 | ResourceSOCreator window = GetWindow(); 15 | window.Show(); 16 | window.path = PlayerPrefs.GetString("Resource SO Path", ""); 17 | } 18 | private new void OnDestroy() 19 | { 20 | PlayerPrefs.SetString("Resource SO Path", path); 21 | base.OnDestroy(); 22 | } 23 | 24 | [SerializeField] 25 | [OnValueChanged("FindTemplate")] 26 | private ResourceType resourceType; 27 | 28 | [FolderPath, SerializeField, Required] 29 | private string path; 30 | 31 | [InlineEditor(Expanded = true)] 32 | public ResourceTemplate resource; 33 | 34 | [GUIColor(0.5f,1f,0.5f)] 35 | [ButtonGroup("")] 36 | private void SaveResourceTemplate() 37 | { 38 | if (string.IsNullOrEmpty(resource.resourceName)) 39 | return; 40 | 41 | if(!AssetDatabase.Contains(resource)) 42 | AssetDatabase.CreateAsset(resource, path + "/" + resource.resourceName + ".asset"); 43 | AssetDatabase.SaveAssets(); 44 | AssetDatabase.Refresh(); 45 | NewUpgrade(); 46 | } 47 | 48 | [GUIColor(0.5f,0.5f,1f)] 49 | [ButtonGroup("")] 50 | private void NewUpgrade() 51 | { 52 | resource = ScriptableObject.CreateInstance(); 53 | } 54 | 55 | [GUIColor(0.5f, 1f, 0.5f)] 56 | [ButtonGroup("")] 57 | private void CreateRemainingTypes() 58 | { 59 | List templates = HelperFunctions.GetScriptableObjects(path); 60 | 61 | foreach (var rt in System.Enum.GetValues(typeof(ResourceType))) 62 | { 63 | ResourceTemplate resourceTemplate = templates.Where(x => x.type == (ResourceType)rt).FirstOrDefault(); 64 | if(resourceTemplate == null) 65 | { 66 | NewUpgrade(); 67 | resource.type = (ResourceType)rt; 68 | SaveResourceTemplate(); 69 | } 70 | } 71 | } 72 | 73 | private void FindTemplate() 74 | { 75 | if (resource != null) 76 | SaveResourceTemplate(); 77 | 78 | Debug.Log("Looking for template"); 79 | List templates = HelperFunctions.GetScriptableObjects(path); 80 | ResourceTemplate resourceTemplate = null; 81 | 82 | if(templates != null && templates.Count > 0) 83 | resourceTemplate = templates.Where(x => x.type == resourceType).FirstOrDefault(); 84 | 85 | if(resourceTemplate == null) 86 | { 87 | NewUpgrade(); 88 | resource.type = (ResourceType)resourceType; 89 | } 90 | else 91 | { 92 | resource = resourceTemplate; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /How I Use Odin/PatchNotes.cs: -------------------------------------------------------------------------------- 1 | using Sirenix.OdinInspector; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | [CreateAssetMenu(fileName = "Patch Notes", menuName = "Create New Patch Notes")] 8 | public class PatchNotes : SerializedScriptableObject 9 | { 10 | [SerializeField] 11 | public Dictionary patchNotes = new Dictionary(); 12 | 13 | [Title("Add New Notes")] 14 | [SerializeField] 15 | [OnValueChanged("LoadNotesToEdit")] 16 | private float version; 17 | [TextArea(10, 20)] 18 | [SerializeField] 19 | [OnValueChanged("AddNotes")] 20 | private string notes; 21 | [SerializeField] 22 | [TextArea(5, 10)] 23 | public string knownIssues; 24 | 25 | public NoteContainer GetNotes(float version) 26 | { 27 | patchNotes.TryGetValue(version, out NoteContainer notes); 28 | return notes; 29 | } 30 | public void SaveNotes(float version, string note) 31 | { 32 | patchNotes.TryGetValue(version, out NoteContainer notes); 33 | if (notes == null) 34 | patchNotes.Add(version, new NoteContainer(version, note)); 35 | else 36 | notes.notes = note; 37 | } 38 | 39 | [GUIColor(0.6f, 1f, 0.6f)] 40 | [ButtonGroup("Buttons")] 41 | private void AddNotes() 42 | { 43 | SaveNotes(version, notes); 44 | } 45 | 46 | [GUIColor(1f, 0.6f, 0.6f)] 47 | [ButtonGroup("Buttons")] 48 | private void ResetRead() 49 | { 50 | foreach (KeyValuePair note in patchNotes) 51 | ES3.Save(note.Key.ToString() + " has been read", false, "patchnotes.es3"); 52 | } 53 | public void SetLatestAsRead() 54 | { 55 | ES3.Save(GetLatestVersion().ToString() + " has been read", true, "patchnotes.es3"); 56 | } 57 | public bool IsLatestRead() 58 | { 59 | if (!ES3.FileExists("patchnotes.es3")) 60 | return false; 61 | 62 | return ES3.Load(GetLatestVersion().ToString() + " has been read", "patchnotes.es3"); 63 | } 64 | private void LoadNotesToEdit() 65 | { 66 | if (patchNotes.TryGetValue(version, out NoteContainer notes)) 67 | this.notes = notes.notes; 68 | } 69 | public float GetLatestVersion() 70 | { 71 | float latestVersion = 0f; 72 | 73 | foreach (KeyValuePair note in patchNotes) 74 | { 75 | if (note.Key > latestVersion) 76 | latestVersion = note.Key; 77 | } 78 | 79 | return latestVersion; 80 | } 81 | public ReadOnlyCollection GetAllNotes(bool newestFirst = true) 82 | { 83 | List notes = new List(); 84 | foreach (var version in patchNotes.Keys) 85 | { 86 | notes.Add(GetNotes(version)); 87 | } 88 | 89 | if (newestFirst) 90 | notes = notes.OrderByDescending(x => x.version).ToList(); 91 | 92 | return notes.AsReadOnly(); 93 | } 94 | [SerializeField] 95 | public class NoteContainer 96 | { 97 | public NoteContainer(float version, string notes) 98 | { 99 | this.version = version; 100 | this.notes = notes; 101 | } 102 | 103 | public float version; 104 | [TextArea] 105 | public string notes; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /First Custom Inspector/GameManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using Sirenix.OdinInspector; 4 | 5 | public class GameManager : MonoBehaviour 6 | { 7 | [BoxGroup("Game State Info")] 8 | [EnumToggleButtons] 9 | [OnValueChanged("StateChange")] 10 | [ShowInInspector] 11 | public static GameState gameState; 12 | 13 | [BoxGroup("Game State Info")] 14 | [ShowInInspector] 15 | public static int turnsRemaining = 3; 16 | 17 | //UI elements 18 | [TabGroup("UI")] 19 | [SceneObjectsOnly] 20 | [Required] 21 | [InlineButton("SelectCanvas","Select")] 22 | public Canvas startButtons; 23 | [TabGroup("UI")] 24 | [SceneObjectsOnly,Required] 25 | [InlineButton("SelectCanvas","Select")] 26 | public Canvas pauseMenu; 27 | [TabGroup("UI")] 28 | [SceneObjectsOnly,Required] 29 | [InlineButton("SelectCanvas","Select")] 30 | public Canvas HUD; 31 | 32 | //background music 33 | [TabGroup("Music")] 34 | public AudioSource musicSource; 35 | 36 | [Space] 37 | [ShowInInspector] 38 | [ValueDropdown("musicList")] 39 | [TabGroup("Music")] 40 | [InlineButton("PlayMusic")] 41 | private AudioClip currentMusicClip; 42 | [TabGroup("Music")] 43 | [InlineEditor(InlineEditorModes.SmallPreview)] 44 | public List musicList; 45 | 46 | //sfx 47 | [TabGroup("SFX")] 48 | public AudioSource sfxSource; 49 | [TabGroup("SFX")] 50 | [InlineButton("PlaySFX","Test")] 51 | public AudioClip uiClick; 52 | [TabGroup("SFX")] 53 | [InlineButton("PlaySFX","Test")] 54 | public AudioClip weaponShoot; 55 | [TabGroup("SFX")] 56 | [InlineButton("PlaySFX","Test")] 57 | public AudioClip weaponHit; 58 | [TabGroup("SFX")] 59 | [InlineButton("PlaySFX","Test")] 60 | public AudioClip enemySpawn; 61 | 62 | //enemies 63 | [TabGroup("Enemies", "Enemy Data")] 64 | [AssetsOnly] 65 | public GameObject enemyPrefab; 66 | [TabGroup("Enemies", "Enemy Data")] 67 | [InlineEditor(InlineEditorModes.GUIOnly)] 68 | [AssetsOnly] 69 | public List enemyList; 70 | 71 | //spawn points 72 | [TabGroup("Enemies","Spawn Points")] 73 | [SceneObjectsOnly] 74 | public List spawnPoints; 75 | 76 | private void PlaySFX(AudioClip sfx) 77 | { 78 | if (sfxSource != null && !sfxSource.isPlaying) 79 | sfxSource.PlayOneShot(sfx); 80 | } 81 | 82 | public void PlayMusic(AudioClip music) 83 | { 84 | if (musicSource != null && music != null) 85 | { 86 | musicSource.clip = music; 87 | musicSource.Play(); 88 | } 89 | } 90 | 91 | [Button(ButtonSizes.Medium)] 92 | [TabGroup("Enemies","Enemy Data")] 93 | [GUIColor(0.6f,1f,0.6f)] 94 | public void SpawnRandomEnemy() 95 | { 96 | if (enemyList.Count == 0 || spawnPoints.Count == 0) 97 | return; 98 | 99 | GameObject enemyToSpawn = Instantiate(enemyPrefab); 100 | 101 | //inject data 102 | EnemyData data = enemyList[Random.Range(0, enemyList.Count)]; 103 | enemyToSpawn.GetComponent().SetEnemyData(data); 104 | 105 | //set location 106 | enemyToSpawn.transform.position = spawnPoints[Random.Range(0, spawnPoints.Count)].position; 107 | } 108 | 109 | public void StateChange() 110 | { 111 | switch (gameState) 112 | { 113 | case GameState.startScene: 114 | break; 115 | case GameState.gamePlay: 116 | break; 117 | case GameState.paused: 118 | break; 119 | case GameState.complete: 120 | break; 121 | default: 122 | break; 123 | } 124 | } 125 | 126 | private void SelectCanvas(Canvas _object) 127 | { 128 | if(_object) 129 | UnityEditor.Selection.activeObject = _object.gameObject; 130 | } 131 | } 132 | 133 | public enum GameState 134 | { 135 | startScene, 136 | gamePlay, 137 | paused, 138 | complete 139 | } 140 | -------------------------------------------------------------------------------- /2D Character Creator/Editor/CharacterEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using Sirenix.OdinInspector; 5 | using Sirenix.OdinInspector.Editor; 6 | using Sirenix.Utilities.Editor; 7 | 8 | public class CharacterEditor : OdinMenuEditorWindow 9 | { 10 | private static CharacterSpriteSettings sprites; 11 | 12 | [MenuItem("Tools/2D Character Creator")] 13 | public static void OpenWindow() 14 | { 15 | GetWindow().Show(); 16 | if (sprites == null) 17 | sprites = AssetDatabase.LoadAssetAtPath("Assets/Scripts/2D Character Creator/SpriteSettings.asset", typeof(CharacterSpriteSettings)) as CharacterSpriteSettings; 18 | } 19 | 20 | protected override OdinMenuTree BuildMenuTree() 21 | { 22 | var tree = new OdinMenuTree(); 23 | tree.Selection.SupportsMultiSelect = false; 24 | 25 | tree.Add("Create New", new CreateNewCharcacterData()); 26 | tree.AddAllAssetsAtPath("Enemy Data", "Assets/Scripts/2D Character Creator/Characters", typeof(CharacterSpriteData)); 27 | if(sprites != null) 28 | tree.Add("Set Up Sprites", sprites); 29 | return tree; 30 | } 31 | 32 | protected override void OnBeginDrawEditors() 33 | { 34 | OdinMenuTreeSelection selected = this.MenuTree.Selection; 35 | 36 | SirenixEditorGUI.BeginHorizontalToolbar(); 37 | { 38 | GUILayout.FlexibleSpace(); 39 | 40 | if (SirenixEditorGUI.ToolbarButton("Delete Current")) 41 | { 42 | CharacterSpriteData asset = selected.SelectedValue as CharacterSpriteData; 43 | string path = AssetDatabase.GetAssetPath(asset); 44 | AssetDatabase.DeleteAsset(path); 45 | AssetDatabase.SaveAssets(); 46 | } 47 | } 48 | SirenixEditorGUI.EndHorizontalToolbar(); 49 | } 50 | 51 | public class CreateNewCharcacterData 52 | { 53 | public CreateNewCharcacterData() 54 | { 55 | characterSpriteData = ScriptableObject.CreateInstance(); 56 | characterSpriteData.characterName = "New Character Data"; 57 | } 58 | 59 | [InlineEditor(Expanded = true)] 60 | public CharacterSpriteData characterSpriteData; 61 | 62 | [GUIColor(0.7f,1f,0.7f)] 63 | [ButtonGroup("CreateButtons")] 64 | private void CreateCharacterData() 65 | { 66 | AssetDatabase.CreateAsset(characterSpriteData, "Assets/Scripts/2D Character Creator/Characters/" + characterSpriteData.characterName + ".asset"); 67 | AssetDatabase.SaveAssets(); 68 | } 69 | 70 | [GUIColor(0.7f,0.7f,1f)] 71 | [ButtonGroup("CreateButtons")] 72 | private void RandomizeSprites() 73 | { 74 | CharacterSpriteSettings sprites = AssetDatabase.LoadAssetAtPath("Assets/Scripts/2D Character Creator/SpriteSettings.asset", typeof(CharacterSpriteSettings)) as CharacterSpriteSettings; 75 | 76 | if (sprites == null) 77 | return; 78 | 79 | characterSpriteData.body = sprites.bodySprites[Random.Range(0, sprites.bodySprites.Count)]; 80 | characterSpriteData.head = sprites.headSprites[Random.Range(0, sprites.headSprites.Count)]; 81 | characterSpriteData.face = sprites.faceSprites[Random.Range(0, sprites.faceSprites.Count)]; 82 | characterSpriteData.beard = sprites.beardSprites[Random.Range(0, sprites.beardSprites.Count)]; 83 | characterSpriteData.hair = sprites.hairSprites[Random.Range(0, sprites.hairSprites.Count)]; 84 | characterSpriteData.hat = sprites.hatSprites[Random.Range(0, sprites.hatSprites.Count)]; 85 | characterSpriteData.rightHand = sprites.rightHandSprites[Random.Range(0, sprites.rightHandSprites.Count)]; 86 | characterSpriteData.rightHandItem = sprites.weaponSprites[Random.Range(0, sprites.weaponSprites.Count)]; 87 | characterSpriteData.leftHand = sprites.leftHandSprites[Random.Range(0, sprites.leftHandSprites.Count)]; 88 | characterSpriteData.leftHandItem = sprites.weaponSprites[Random.Range(0, sprites.weaponSprites.Count)]; 89 | characterSpriteData.rightLeg = sprites.rightLegSprites[Random.Range(0, sprites.rightLegSprites.Count)]; 90 | characterSpriteData.leftLeg = sprites.leftLegSprites[Random.Range(0, sprites.leftLegSprites.Count)]; 91 | } 92 | } 93 | } 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /2D Character Creator/CharacterSpriteSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | 6 | /// 7 | /// This holds all the project wide sprite data 8 | /// these fields can be accessed in the editor 9 | /// and the Character Data SO 10 | /// 11 | 12 | [CreateAssetMenu(fileName = "SpriteSettings", menuName = "Create Sprite Settings SO")] 13 | public class CharacterSpriteSettings : ScriptableObject 14 | { 15 | [Title("Asset Locations")] 16 | [FolderPath] 17 | public List spriteFolders; 18 | [FolderPath] 19 | public List otherSpriteFolders; 20 | 21 | [Title("Head")] 22 | [SerializeField] 23 | private string headIdentifier; 24 | public List headSprites = new List(); 25 | [SerializeField] 26 | private string faceIdentifier; 27 | public List faceSprites = new List(); 28 | [SerializeField] 29 | private string beardIdentifier; 30 | public List beardSprites = new List(); 31 | [SerializeField] 32 | private string hairIdentifier; 33 | public List hairSprites = new List(); 34 | [SerializeField] 35 | private string hatIdentifier; 36 | public List hatSprites = new List(); 37 | 38 | [Title("Body")] 39 | [SerializeField] 40 | private string bodyIdentifier; 41 | public List bodySprites = new List(); 42 | [SerializeField] 43 | private string rightHandIdentifier; 44 | public List rightHandSprites = new List(); 45 | [SerializeField] 46 | private string leftHandIdentifier; 47 | public List leftHandSprites = new List(); 48 | [SerializeField] 49 | private string rightLegIdentifier; 50 | public List rightLegSprites = new List(); 51 | [SerializeField] 52 | private string leftLegIdentifier; 53 | public List leftLegSprites = new List(); 54 | 55 | [Title("Weapons")] 56 | [SerializeField] 57 | private string weaponIndentifier; 58 | [VerticalGroup("Character/Weapons")] 59 | public List weaponSprites = new List(); 60 | 61 | [Button] 62 | public void FindSpritesInAssets() 63 | { 64 | if (spriteFolders == null || spriteFolders.Count == 0) 65 | return; 66 | 67 | FindSpritesAddToList(bodyIdentifier, bodySprites); 68 | FindSpritesAddToList(headIdentifier, headSprites); 69 | FindSpritesAddToList(faceIdentifier, faceSprites); 70 | FindSpritesAddToList(beardIdentifier, beardSprites); 71 | FindSpritesAddToList(hairIdentifier, hairSprites); 72 | FindSpritesAddToList(hatIdentifier, hatSprites); 73 | FindSpritesAddToList(rightHandIdentifier, rightHandSprites); 74 | FindSpritesAddToList(leftHandIdentifier, leftHandSprites); 75 | FindSpritesAddToList(rightLegIdentifier, rightLegSprites); 76 | FindSpritesAddToList(leftLegIdentifier, leftLegSprites); 77 | 78 | if (otherSpriteFolders == null || otherSpriteFolders.Count == 0) 79 | return; 80 | 81 | FindUnidentifiedSpritesAddToList(weaponSprites); 82 | } 83 | 84 | private void FindSpritesAddToList(string identifier, List spriteList) 85 | { 86 | spriteList.Clear(); 87 | string[] folders = spriteFolders.ToArray(); 88 | string[] spriteGUIDs = UnityEditor.AssetDatabase.FindAssets(identifier + " t:sprite", folders); 89 | 90 | foreach (string GUID in spriteGUIDs) 91 | { 92 | string spritePath = UnityEditor.AssetDatabase.GUIDToAssetPath(GUID); 93 | Sprite foundSprite = UnityEditor.AssetDatabase.LoadAssetAtPath(spritePath); 94 | 95 | if (foundSprite != null) 96 | spriteList.Add(foundSprite); 97 | } 98 | } 99 | 100 | private void FindUnidentifiedSpritesAddToList(List spriteList) 101 | { 102 | spriteList.Clear(); 103 | string[] folders = otherSpriteFolders.ToArray(); 104 | string[] spriteGUIDs = UnityEditor.AssetDatabase.FindAssets("t:sprite", folders); 105 | 106 | foreach (string GUID in spriteGUIDs) 107 | { 108 | string spritePath = UnityEditor.AssetDatabase.GUIDToAssetPath(GUID); 109 | Sprite foundSprite = UnityEditor.AssetDatabase.LoadAssetAtPath(spritePath); 110 | 111 | if (foundSprite != null) 112 | spriteList.Add(foundSprite); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /2D Character Creator/CharacterSpriteData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Sirenix.OdinInspector; 5 | using Sirenix.Utilities.Editor; 6 | using Sirenix.OdinInspector.Editor; 7 | 8 | /// 9 | /// This holds all the sprite data for the character 10 | /// 11 | 12 | 13 | [CreateAssetMenu(fileName = "New Character Sprites", menuName = "Create New Character Sprite SO")] 14 | public class CharacterSpriteData : SerializedScriptableObject 15 | { 16 | [LabelWidth(125)] 17 | public string characterName; 18 | 19 | //sprites 20 | [Title("Head")] 21 | [HorizontalGroup("Character", LabelWidth = 100)] 22 | [VerticalGroup("Character/Head")] 23 | [ValueDropdown("@GetBodySprites(spriteSettings.hatSprites)")] 24 | public Sprite hat; 25 | [VerticalGroup("Character/Head")] 26 | [ValueDropdown("@GetBodySprites(spriteSettings.hairSprites)")] 27 | public Sprite hair; 28 | [VerticalGroup("Character/Head")] 29 | [ValueDropdown("@GetBodySprites(spriteSettings.headSprites)")] 30 | public Sprite head; 31 | [VerticalGroup("Character/Head")] 32 | [ValueDropdown("@GetBodySprites(spriteSettings.faceSprites)")] 33 | public Sprite face; 34 | [VerticalGroup("Character/Head")] 35 | [ValueDropdown("@GetBodySprites(spriteSettings.beardSprites)")] 36 | public Sprite beard; 37 | 38 | [Title("Body")] 39 | [VerticalGroup("Character/Body")] 40 | [ValueDropdown("@GetBodySprites(spriteSettings.bodySprites)")] 41 | public Sprite body; 42 | [VerticalGroup("Character/Body")] 43 | [ValueDropdown("@GetBodySprites(spriteSettings.rightHandSprites)")] 44 | public Sprite rightHand; 45 | [VerticalGroup("Character/Body")] 46 | [ValueDropdown("@GetBodySprites(spriteSettings.leftHandSprites)")] 47 | public Sprite leftHand; 48 | [VerticalGroup("Character/Body")] 49 | [ValueDropdown("@GetBodySprites(spriteSettings.rightLegSprites)")] 50 | public Sprite rightLeg; 51 | [VerticalGroup("Character/Body")] 52 | [ValueDropdown("@GetBodySprites(spriteSettings.leftLegSprites)")] 53 | public Sprite leftLeg; 54 | 55 | 56 | [Title("Weapons")] 57 | [VerticalGroup("Character/Weapons")] 58 | [ValueDropdown("@GetBodySprites(spriteSettings.weaponSprites)")] 59 | public Sprite rightHandItem; 60 | [VerticalGroup("Character/Weapons")] 61 | [ValueDropdown("@GetBodySprites(spriteSettings.weaponSprites)")] 62 | public Sprite leftHandItem; 63 | 64 | private static CharacterSpriteSettings spriteSettings; 65 | 66 | //I'm leaving this in as a simpler visualization option 67 | //it also shows up one more Odin feature 68 | 69 | //[SerializeField] 70 | //[TableMatrix(SquareCells = true)] 71 | //private Sprite[,] spriteArray; 72 | 73 | //private void OnValidate() 74 | //{ 75 | // DrawCharacterInArray(); 76 | //} 77 | 78 | //[OnInspectorInit] 79 | //public void DrawCharacterInArray() 80 | //{ 81 | // spriteArray = new Sprite[5, 4] 82 | // { 83 | // {null,null,rightHandItem,null}, 84 | // {null,beard,rightHand,rightLeg}, 85 | // {hat,face,body,null}, 86 | // {null,head,leftHand,leftLeg}, 87 | // {null,null,leftHandItem,null} 88 | // }; 89 | //} 90 | 91 | [OnInspectorInit] 92 | private void GetSpriteSettings() 93 | { 94 | if (spriteSettings == null) 95 | spriteSettings = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/Scripts/2D Character Creator/SpriteSettings.asset", typeof(CharacterSpriteSettings)) as CharacterSpriteSettings; 96 | } 97 | 98 | private IEnumerable GetBodySprites(List spriteList) 99 | { 100 | List dropDownItemList = new List(); 101 | 102 | dropDownItemList.Add(new ValueDropdownItem("none", null)); //adding a null value to dropdown 103 | 104 | for (int i = 0; i < spriteList.Count; i++) 105 | { 106 | ValueDropdownItem dropdownItem = new ValueDropdownItem(spriteList[i].name + " " + i, spriteList[i]); 107 | dropDownItemList.Add(dropdownItem); 108 | } 109 | 110 | return dropDownItemList; 111 | } 112 | 113 | [OnInspectorGUI] 114 | private void DrawPreview() 115 | { 116 | var rect = GUILayoutUtility.GetRect(300, 200); 117 | Rect newRect; 118 | 119 | if (rightHand != null) //all the if's seem horrible 120 | { 121 | newRect = PositionSprite(rect, -10, 120, rightHand); 122 | GUI.DrawTexture(newRect, rightHand.texture, ScaleMode.ScaleToFit); 123 | } 124 | 125 | if (leftLeg != null) 126 | { 127 | newRect = PositionSprite(rect, 10, 150, leftLeg); 128 | GUI.DrawTexture(newRect, leftLeg.texture, ScaleMode.ScaleToFit); 129 | } 130 | 131 | if (rightLeg != null) 132 | { 133 | newRect = PositionSprite(rect, -10, 150, rightLeg); 134 | GUI.DrawTexture(newRect, rightLeg.texture, ScaleMode.ScaleToFit); 135 | } 136 | 137 | if (body != null) 138 | { 139 | newRect = PositionSprite(rect, -5, 135, body); 140 | GUI.DrawTexture(newRect, body.texture, ScaleMode.ScaleToFit); 141 | } 142 | 143 | if (head != null) 144 | { 145 | newRect = PositionSprite(rect, 0, 105, head); 146 | GUI.DrawTexture(newRect, head.texture, ScaleMode.ScaleToFit); 147 | } 148 | 149 | if (face != null) 150 | { 151 | newRect = PositionSprite(rect, 0, 65, face); 152 | GUI.DrawTexture(newRect, face.texture, ScaleMode.ScaleToFit); 153 | } 154 | 155 | if (hair != null) 156 | { 157 | newRect = PositionSprite(rect, 0, 100, hair); 158 | GUI.DrawTexture(newRect, hair.texture, ScaleMode.ScaleToFit); 159 | } 160 | 161 | if (hat != null) 162 | { 163 | newRect = PositionSprite(rect, 0, 105, hat); 164 | GUI.DrawTexture(newRect, hat.texture, ScaleMode.ScaleToFit); 165 | } 166 | 167 | if (leftHand != null) 168 | { 169 | newRect = PositionSprite(rect, 10, 120, leftHand); 170 | GUI.DrawTexture(newRect, leftHand.texture, ScaleMode.ScaleToFit); 171 | } 172 | 173 | if (rightHandItem != null) 174 | { 175 | newRect = PositionSprite(rect, -30, 135, rightHandItem); 176 | GUI.DrawTexture(newRect, rightHandItem.texture, ScaleMode.ScaleToFit); 177 | } 178 | 179 | if (leftHandItem != null) 180 | { 181 | newRect = PositionSprite(rect, 12, 145, leftHandItem); 182 | GUI.DrawTexture(newRect, leftHandItem.texture, ScaleMode.ScaleToFit); 183 | } 184 | } 185 | 186 | private Rect PositionSprite(Rect rect, float x, float y, Sprite sprite) 187 | { 188 | float width = sprite.texture.width; 189 | float height = sprite.texture.height; 190 | 191 | float xPos = rect.x + x - sprite.pivot.x;//no width needed? 192 | xPos += rect.width / 2; //centering 193 | float yPos = rect.y + y + sprite.pivot.y - height; //why does the height matter? 194 | 195 | return new Rect(xPos, yPos, width, height); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /THE Game Manager/TheGameManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | using Sirenix.OdinInspector; 6 | using Sirenix.OdinInspector.Editor; 7 | using Sirenix.Utilities.Editor; 8 | 9 | public class TheGameManager : OdinMenuEditorWindow 10 | { 11 | [OnValueChanged("StateChange")] 12 | [LabelText("Manager View")] 13 | [LabelWidth(100f)] 14 | [EnumToggleButtons] 15 | [ShowInInspector] 16 | private ManagerState managerState; 17 | private int enumIndex = 0; 18 | private bool treeRebuild = false; 19 | 20 | private DrawUniverse drawUniverse = new DrawUniverse(); 21 | private DrawNPC drawNPC = new DrawNPC(); 22 | private DrawSFX drawSFX = new DrawSFX(); 23 | 24 | private DrawSelected drawModules = new DrawSelected(); 25 | private DrawSelected drawColors = new DrawSelected(); 26 | private DrawSelected drawItems = new DrawSelected(); 27 | private DrawSelected drawRecipes = new DrawSelected(); 28 | 29 | //paths to SOs in project 30 | private string modulePath = "Assets/Prefabs/Ships/Module/ModuleData"; 31 | private string colorPath = "Assets/Resources/ColorData"; 32 | private string itemPath = "Assets/Resources/Items"; 33 | private string recipePath = "Assets/Scripts/Industry/Recipe Data"; 34 | 35 | [MenuItem("Tools/The Game Manager")] 36 | public static void OpenWindow() 37 | { 38 | GetWindow().Show(); 39 | } 40 | 41 | private void StateChange() 42 | { 43 | treeRebuild = true; 44 | } 45 | 46 | protected override void Initialize() 47 | { 48 | drawModules.SetPath(modulePath); 49 | drawColors.SetPath(colorPath); 50 | drawItems.SetPath(itemPath); 51 | drawRecipes.SetPath(recipePath); 52 | 53 | drawUniverse.FindMyObject(); 54 | drawNPC.FindMyObject(); 55 | drawSFX.FindMyObject(); 56 | } 57 | 58 | protected override void OnGUI() 59 | { 60 | if(treeRebuild && Event.current.type == EventType.Layout) 61 | { 62 | ForceMenuTreeRebuild(); 63 | treeRebuild = false; 64 | } 65 | 66 | SirenixEditorGUI.Title("The Game Manager", "Because every hobby game is overscoped", TextAlignment.Center, true); 67 | EditorGUILayout.Space(); 68 | 69 | switch (managerState) 70 | { 71 | 72 | case ManagerState.modules: 73 | case ManagerState.items: 74 | case ManagerState.recipes: 75 | case ManagerState.color: 76 | DrawEditor(enumIndex); 77 | break; 78 | default: 79 | break; 80 | } 81 | EditorGUILayout.Space(); 82 | 83 | base.OnGUI(); 84 | } 85 | 86 | protected override void DrawEditors() 87 | { 88 | switch (managerState) 89 | { 90 | case ManagerState.universe: 91 | DrawEditor(enumIndex); 92 | break; 93 | case ManagerState.modules: 94 | drawModules.SetSelected(this.MenuTree.Selection.SelectedValue); 95 | break; 96 | case ManagerState.npc: 97 | DrawEditor(enumIndex); 98 | break; 99 | case ManagerState.items: 100 | drawItems.SetSelected(this.MenuTree.Selection.SelectedValue); 101 | break; 102 | case ManagerState.recipes: 103 | drawRecipes.SetSelected(this.MenuTree.Selection.SelectedValue); 104 | break; 105 | case ManagerState.color: 106 | drawColors.SetSelected(this.MenuTree.Selection.SelectedValue); 107 | break; 108 | case ManagerState.sfx: 109 | DrawEditor(enumIndex); 110 | break; 111 | default: 112 | break; 113 | } 114 | 115 | DrawEditor((int)managerState); 116 | } 117 | 118 | protected override IEnumerable GetTargets() 119 | { 120 | List targets = new List(); 121 | targets.Add(drawUniverse); 122 | targets.Add(drawModules); 123 | targets.Add(drawNPC); 124 | targets.Add(drawItems); 125 | targets.Add(drawRecipes); 126 | targets.Add(drawColors); 127 | targets.Add(drawSFX); 128 | targets.Add(base.GetTarget()); 129 | 130 | enumIndex = targets.Count - 1; 131 | 132 | return targets; 133 | } 134 | 135 | protected override void DrawMenu() 136 | { 137 | switch (managerState) 138 | { 139 | 140 | case ManagerState.modules: 141 | case ManagerState.items: 142 | case ManagerState.recipes: 143 | case ManagerState.color: 144 | base.DrawMenu(); 145 | break; 146 | default: 147 | break; 148 | } 149 | } 150 | 151 | protected override OdinMenuTree BuildMenuTree() 152 | { 153 | OdinMenuTree tree = new OdinMenuTree(); 154 | 155 | switch (managerState) 156 | { 157 | 158 | case ManagerState.modules: 159 | tree.AddAllAssetsAtPath("Module Data", modulePath, typeof(ModuleData)); 160 | break; 161 | case ManagerState.items: 162 | tree.AddAllAssetsAtPath("Item Data", itemPath, typeof(OpenInventory.ItemData)); 163 | break; 164 | case ManagerState.recipes: 165 | tree.AddAllAssetsAtPath("Recipes", recipePath, typeof(RecipeBase)); 166 | break; 167 | case ManagerState.color: 168 | tree.AddAllAssetsAtPath("Color Data", colorPath, typeof(ColorData)); 169 | break; 170 | default: 171 | break; 172 | } 173 | 174 | return tree; 175 | } 176 | 177 | public enum ManagerState 178 | { 179 | universe, 180 | modules, 181 | npc, 182 | items, 183 | recipes, 184 | color, 185 | sfx 186 | } 187 | } 188 | 189 | public class DrawSelected where T : ScriptableObject 190 | { 191 | [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] 192 | public T selected; 193 | 194 | [LabelWidth(100)] 195 | [PropertyOrder(-1)] 196 | [ColorGroupAttribute("CreateNew", 1f,1f,1f)] 197 | [HorizontalGroup("CreateNew/Horizontal")] 198 | public string nameForNew; 199 | 200 | private string path; 201 | 202 | [HorizontalGroup("CreateNew/Horizontal")] 203 | [GUIColor(0.7f,0.7f,1f)] 204 | [Button] 205 | public void CreateNew() 206 | { 207 | if (nameForNew == "") 208 | return; 209 | 210 | T newItem = ScriptableObject.CreateInstance(); 211 | //newItem.name = "New " + typeof(T).ToString(); 212 | 213 | if (path == "") 214 | path = "Assets/"; 215 | 216 | AssetDatabase.CreateAsset(newItem, path + "\\" + nameForNew + ".asset"); 217 | AssetDatabase.SaveAssets(); 218 | 219 | nameForNew = ""; 220 | } 221 | 222 | [HorizontalGroup("CreateNew/Horizontal")] 223 | [GUIColor(1f, 0.7f, 0.7f)] 224 | [Button] 225 | public void DeleteSelected() 226 | { 227 | if(selected != null) 228 | { 229 | string _path = AssetDatabase.GetAssetPath(selected); 230 | AssetDatabase.DeleteAsset(_path); 231 | AssetDatabase.SaveAssets(); 232 | } 233 | } 234 | 235 | public void SetSelected(object item) 236 | { 237 | var attempt = item as T; 238 | if (attempt != null) 239 | this.selected = attempt; 240 | } 241 | 242 | public void SetPath(string path) 243 | { 244 | this.path = path; 245 | } 246 | } 247 | 248 | public class DrawSceneObject where T : MonoBehaviour 249 | { 250 | [Title("Universe Creator")] 251 | [ShowIf("@myObject != null")] 252 | [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] 253 | public T myObject; 254 | 255 | public void FindMyObject() 256 | { 257 | if (myObject == null) 258 | myObject = GameObject.FindObjectOfType(); 259 | } 260 | 261 | [ShowIf("@myObject != null")] 262 | [GUIColor(0.7f,1f,0.7f)] 263 | [ButtonGroup("Top Button", -1000)] 264 | private void SelectSceneObject() 265 | { 266 | if (myObject != null) 267 | Selection.activeGameObject = myObject.gameObject; 268 | } 269 | 270 | [ShowIf("@myObject == null")] 271 | [Button] 272 | private void CreateManagerObject() 273 | { 274 | GameObject newManager = new GameObject(); 275 | newManager.name = "New " + typeof(T).ToString(); 276 | myObject = newManager.AddComponent(); 277 | } 278 | } 279 | 280 | public class DrawUniverse : DrawSceneObject 281 | { 282 | [ShowIf("@myObject != null")] 283 | [GUIColor(0.7f,1f,1f)] 284 | [ButtonGroup("Top Button")] 285 | private void SomeUniverseFunction1() 286 | { 287 | 288 | } 289 | 290 | [ShowIf("@myObject != null")] 291 | [GUIColor(0.7f, 0.7f, 1f)] 292 | [ButtonGroup("Top Button")] 293 | private void SomeUniverseFunction2() 294 | { 295 | 296 | } 297 | } 298 | 299 | public class DrawNPC : DrawSceneObject 300 | { 301 | 302 | } 303 | 304 | public class DrawSFX : DrawSceneObject 305 | { 306 | 307 | } --------------------------------------------------------------------------------