added many classes and update old one with new features from new project

This commit is contained in:
Martin Johnson 2022-05-27 14:12:56 -04:00
parent a20d66e8e6
commit efdd2742db
88 changed files with 41798 additions and 302 deletions

View File

@ -0,0 +1,62 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
namespace Canvases.Components
{
public class ButtonUIComponent : UISelectableComponentBase
{
public event Action OnClick;
[Header("Association"), SerializeField, Required]
private Button button;
[Header("Configuration"), SerializeField]
private ButtonNavigationType buttonNavigationType;
private enum ButtonNavigationType
{
Forward,
Backward
}
public Color Color
{
set => button.image.color = value;
}
public bool Enabled
{
set => button.interactable = value;
}
public void Select() => button.Select();
protected override void SetSelectableGameObject() => Selectable = button;
private void Start()
{
Debug.Assert(button, $"A {nameof(button)} must be assigned to a {nameof(ButtonUIComponent)}");
button.onClick.AddListener(OnButtonClicked);
}
protected override void OnDestroy()
{
button.onClick.RemoveListener(OnButtonClicked);
base.OnDestroy();
}
private void OnButtonClicked()
{
// PlayClickedSound();
OnClick?.Invoke();
}
private void OnValidate()
{
if (!button)
button = GetComponent<Button>();
}
}
}

View File

@ -0,0 +1,37 @@
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
namespace Canvases.Components
{
// ReSharper disable once InconsistentNaming Reason: UI should be capitalized
public class ImageUIComponent : UIComponentBase
{
[Header("Association")][Required]
[SerializeField] private Image image;
private Color initialColor;
private void Awake() => initialColor = image.color;
private void Start() => Debug.Assert(image, $"A {nameof(image)} must be assigned to a {nameof(ImageUIComponent)}");
public void ResetColor() => image.color = initialColor;
public Sprite Sprite
{
set => image.sprite = value;
}
public Color Color
{
set => image.color = value;
}
private void OnValidate()
{
if (!image)
image = GetComponent<Image>();
}
}
}

View File

@ -0,0 +1,151 @@
using System;
using System.Collections;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
namespace Canvases.Components
{
// ReSharper disable once InconsistentNaming Reason: UI should be capitalized
public class SliderUIComponent : UISelectableComponentBase
{
public event Action<float> OnValueChanged;
[Header("Association"), SerializeField, Required]
private Slider slider;
[SerializeField] private Image backgroundImage;
[SerializeField] private Image fillImage;
[SerializeField] private Image frame;
[Header("Animation"), SerializeField] private bool isChangeValueAnimated;
[SerializeField] [Range(0f, 1f)] private float animationSpeed = 0.05f;
[SerializeField] private Color valueIncreaseColor = Color.green;
[SerializeField] private Color valueDecreaseColor = Color.red;
private float targetValue;
private float currentValue;
private Coroutine animationRoutine;
private Color initialColor = Color.black;
public float Value
{
get => currentValue;
set
{
if (isChangeValueAnimated)
{
targetValue = value;
StartAnimation();
}
else
{
currentValue = value;
slider.value = value;
}
}
}
public float MaxValue
{
get => slider.maxValue;
set => slider.maxValue = value;
}
public Color BackgroundColor
{
set
{
if (backgroundImage != null)
{
backgroundImage.color = value;
}
}
}
public Color FillColor
{
set
{
if (fillImage != null)
{
initialColor = value;
fillImage.color = initialColor;
}
}
}
public Color FrameColor
{
set
{
if (frame != null)
{
frame.color = value;
}
}
}
private void Start()
{
Debug.Assert(slider, $"A {nameof(slider)} must be assigned to a {nameof(SliderUIComponent)}");
slider.onValueChanged.AddListener(OnSliderChanged);
}
protected override void SetSelectableGameObject() => Selectable = slider;
protected override void OnDestroy()
{
slider.onValueChanged.RemoveListener(OnSliderChanged);
base.OnDestroy();
}
private void StartAnimation()
{
if (animationRoutine != null)
StopCoroutine(animationRoutine);
animationRoutine = StartCoroutine(AnimateValueChange(targetValue > currentValue));
}
private IEnumerator AnimateValueChange(bool isValueChangePositive)
{
if (isValueChangePositive)
{
fillImage.color = valueIncreaseColor;
while (currentValue < targetValue)
{
currentValue += animationSpeed * Time.deltaTime;
slider.value = currentValue;
yield return null;
}
}
else
{
fillImage.color = valueDecreaseColor;
while (currentValue > targetValue)
{
currentValue -= animationSpeed * Time.deltaTime;
slider.value = currentValue;
yield return null;
}
}
currentValue = targetValue;
slider.value = currentValue;
fillImage.color = initialColor;
}
private void OnSliderChanged(float value) => OnValueChanged?.Invoke(value);
private void OnValidate()
{
if (!slider)
slider = GetComponent<Slider>();
}
}
}

View File

@ -0,0 +1,43 @@
using Sirenix.OdinInspector;
using TMPro;
using UnityEngine;
namespace Canvases.Components
{
// ReSharper disable once InconsistentNaming Reason: UI should be capitalized
public class TextUIComponent : UIComponentBase
{
[Header("Association"), Required, SerializeField]
private TMP_Text text;
private string initialText;
public string Text
{
get => text.text;
set => text.text = string.IsNullOrEmpty(value) ? string.Empty : value;
}
public Color Color
{
get => text.color;
set => text.color = value;
}
private void Awake() => initialText = text.text;
private void Start() => Debug.Assert(text, $"A {nameof(text)} must be assigned to a {nameof(TMP_Text)}");
public void ResetText() => text.text = initialText;
public void AddText(string textToAdd) => text.text += textToAdd;
public void EraseText() => text.text = string.Empty;
private void OnValidate()
{
if (!text)
text = GetComponent<TMP_Text>();
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;
namespace Canvases.Components
{
// ReSharper disable once InconsistentNaming Reason: UI should be capitalized
public class ToggleUIComponent : UISelectableComponentBase
{
public event Action<bool> OnValueChanged;
[Header("Association"), Required, SerializeField]
private Toggle toggle;
public bool IsOn => toggle.isOn;
private void Start()
{
Debug.Assert(toggle, $"A {nameof(toggle)} must be assigned to a {nameof(Toggle)}");
toggle.onValueChanged.AddListener(OnToggleChanged);
}
protected override void OnDestroy()
{
toggle.onValueChanged.RemoveListener(OnToggleChanged);
base.OnDestroy();
}
protected override void SetSelectableGameObject() => Selectable = toggle;
public void Invert() => toggle.isOn = !toggle.isOn;
private void OnToggleChanged(bool value) => OnValueChanged?.Invoke(value);
private void OnValidate()
{
if (!toggle)
toggle = GetComponent<Toggle>();
}
}
}

View File

@ -0,0 +1,17 @@
using UnityEngine;
using Utilities.Extensions;
namespace Canvases.Components
{
// ReSharper disable once InconsistentNaming Reason: UI should be capitalized
public abstract class UIComponentBase : MonoBehaviour
{
public void Show() => gameObject.Show();
public void Hide() => gameObject.Hide();
public void ToggleDisplay() => gameObject.SetActive(!gameObject.activeInHierarchy);
public void SetActive(bool active) => gameObject.SetActive(active);
}
}

View File

@ -0,0 +1,52 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Utilities.Extensions;
namespace Canvases.Components
{
public abstract class UISelectableComponentBase : UIComponentBase
{
public event Action OnSelected;
protected Selectable Selectable = null;
private EventTrigger.Entry selectEventTriggerEntry;
private CanvasGroup parentCanvasGroup;
private void Awake()
{
SetSelectableGameObject();
AddSelectEventTrigger();
parentCanvasGroup = gameObject.GetComponentInParents<CanvasGroup>();
}
protected abstract void SetSelectableGameObject();
private void AddSelectEventTrigger()
{
EventTrigger eventTrigger = Selectable.gameObject.GetOrAddComponent<EventTrigger>();
selectEventTriggerEntry = new EventTrigger.Entry
{
eventID = EventTriggerType.Select
};
selectEventTriggerEntry.callback.AddListener(OnSelectableSelected);
eventTrigger.triggers.Add(selectEventTriggerEntry);
}
private void OnSelectableSelected(BaseEventData data)
{
if (!Selectable.interactable || parentCanvasGroup != null && !parentCanvasGroup.interactable)
return;
// Play select sound
OnSelected?.Invoke();
}
protected virtual void OnDestroy()
{
selectEventTriggerEntry?.callback.RemoveListener(OnSelectableSelected);
}
}
}

View File

@ -0,0 +1,204 @@
using System;
using Canvases.Components;
using UnityEngine;
using UnityEngine.InputSystem;
using Utilities;
using Utilities.Extensions;
namespace Canvases.Menu.Rebind
{
public class RebindActionUI : MonoBehaviour
{
public event Action<string, string> UpdateBindingUIEvent;
[SerializeField] private TextUIComponent actionLabel;
[SerializeField] private RebindReferences mainBindingReferences;
[SerializeField] private RebindReferences altBindingReferences;
private TextUIComponent rebindOverlay;
private InputAction action;
private InputActionRebindingExtensions.RebindingOperation rebindOperation;
public void ResetToDefault()
{
action.actionMap.Disable();
action.RemoveBindingOverride(mainBindingReferences.Index);
action.RemoveBindingOverride(altBindingReferences.Index);
UpdateBindingDisplay();
action.actionMap.Enable();
}
private void StartInteractiveRebind(int index)
{
action.actionMap.Disable();
PerformInteractiveRebind(index);
action.actionMap.Enable();
}
private void PerformInteractiveRebind(int bindingIndex)
{
rebindOperation?.Cancel(); // Will null out rebindOperation.
void CleanUp(InputActionRebindingExtensions.RebindingOperation rebindingOperation)
{
if (rebindOverlay != null)
{
rebindOverlay.SetActive(false);
}
UpdateBindingDisplay();
rebindOperation?.Dispose();
rebindOperation = null;
}
// Configure the rebind.
rebindOperation = action.PerformInteractiveRebinding(bindingIndex)
.OnCancel(CleanUp)
.OnComplete(CleanUp);
// If it's a part binding, show the name of the part in the UI.
string partName = string.Empty;
if (action.bindings[bindingIndex].isPartOfComposite)
partName = $"Binding '{action.bindings[bindingIndex].name}'. ";
// Bring up rebind overlay, if we have one.
if (rebindOverlay != null)
{
rebindOverlay.Show();
string text = !string.IsNullOrEmpty(rebindOperation.expectedControlType)
? $"{partName}Waiting for {rebindOperation.expectedControlType} input..."
: $"{partName}Waiting for input...";
rebindOverlay.Text =text;
}
// If we have no rebind overlay and no callback but we have a binding text label,
// temporarily set the binding text label to "<Waiting>".
if (rebindOverlay == null && mainBindingReferences.Text != null)
mainBindingReferences.Text.Text = "<Waiting...>";
rebindOperation.Start();
}
protected void OnEnable()
{
mainBindingReferences.Button.OnClick += () => StartInteractiveRebind(mainBindingReferences.Index);
altBindingReferences.Button.OnClick += () => StartInteractiveRebind(altBindingReferences.Index);
}
protected void OnDisable()
{
rebindOperation?.Dispose();
rebindOperation = null;
}
private void UpdateActionLabel()
{
if (actionLabel == null) return;
string actionLabelText = action.bindings[mainBindingReferences.Index].isPartOfComposite
? action.bindings[mainBindingReferences.Index].name.Capitalize()
: action.name;
if (actionLabelText.Equals("Dash")) actionLabelText = "Tackle";
actionLabel.Text = actionLabelText;
}
public void UpdateBindingDisplay(bool shouldCallEvent = true)
{
string mainDisplayString = string.Empty;
string altDisplayString = string.Empty;
string deviceLayoutName = string.Empty;
string mainControlPath = string.Empty;
string altControlPath = string.Empty;
if (action != null)
{
if (mainBindingReferences.Index != -1)
mainDisplayString = action.GetBindingDisplayString(mainBindingReferences.Index,
out deviceLayoutName,
out mainControlPath);
if (altBindingReferences.Index != -1)
altDisplayString = action.GetBindingDisplayString(altBindingReferences.Index, out _,
out altControlPath);
}
UpdateDuplicateText(mainBindingReferences, mainDisplayString);
UpdateDuplicateText(altBindingReferences, altDisplayString);
Sprite mainIcon = BindingsIconsUtil.GetSprite(deviceLayoutName, mainControlPath);
Sprite altIcon = BindingsIconsUtil.GetSprite(deviceLayoutName, altControlPath);
DisplayIcon(mainBindingReferences, mainIcon);
DisplayIcon(altBindingReferences, altIcon);
if (shouldCallEvent)
{
UpdateBindingUIEvent?.Invoke(deviceLayoutName, mainControlPath);
}
}
private void DisplayIcon(RebindReferences reference, Sprite mainIcon)
{
if (mainIcon != null)
{
reference.Text.Hide();
reference.Image.Sprite = mainIcon;
reference.Image.Show();
}
else
{
reference.Text.Show();
reference.Image.Hide();
}
}
private void UpdateDuplicateText(RebindReferences reference, string mainDisplayString)
{
if (reference.Text == null) return;
reference.Text.Text = mainDisplayString == "Delta" ? "Mouse" : mainDisplayString;
bool mainDuplicate = CheckDuplicateBindings(reference.Index);
reference.Button.Color = mainDuplicate ? Color.red : Color.white;
}
private bool CheckDuplicateBindings(int bindingIndex)
{
if (action == null) return false;
InputBinding newBinding = action.bindings[bindingIndex];
if (newBinding.effectivePath.IsNullOrEmpty()) return false;
foreach (InputBinding binding in action.actionMap.bindings)
{
if (binding.action == newBinding.action) continue;
if (binding.effectivePath == newBinding.effectivePath) return true;
}
if (!action.bindings[0].isComposite) return false;
for (int i = 1; i < action.bindings.Count; i++)
{
if (i == bindingIndex) continue;
if (action.bindings[i].effectivePath == newBinding.effectivePath)
{
return true;
}
}
return false;
}
public void Initialize(int bindingIndex, InputAction inputAction, TextUIComponent overlay,
Action<string, string> onUpdateBindingUIEvent)
{
mainBindingReferences.Index = bindingIndex;
altBindingReferences.Index = bindingIndex + 1;
action = inputAction;
rebindOverlay = overlay;
UpdateBindingUIEvent += onUpdateBindingUIEvent;
UpdateActionLabel();
UpdateBindingDisplay();
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using Canvases.Components;
using UnityEngine;
namespace Canvases.Menu.Rebind
{
[Serializable]
public class RebindReferences
{
public TextUIComponent Text;
public ButtonUIComponent Button;
public ImageUIComponent Image;
[HideInInspector] public int Index = -1;
}
}

View File

@ -0,0 +1,112 @@
using System.Collections.Generic;
using Canvases.Components;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;
using Utilities;
using Utilities.Extensions;
namespace Canvases.Menu.Rebind
{
public class RebindUI : MonoBehaviour
{
[SerializeField] private ImageUIComponent background;
[SerializeField] private GameObject rebindMenuContent;
[SerializeField] private TextUIComponent currentControlScheme;
[SerializeField] private ButtonUIComponent resetAllButton;
[SerializeField] private RectTransform scrollRectContent;
[SerializeField] private TextUIComponent rebindOverlayText;
[SerializeField] private RebindActionUI rebindPrefab;
[SerializeField] private ButtonUIComponent applyButton;
private PlayerInputAction playerInputActionRef;
private RebindActionUI firstButton;
private readonly List<RebindActionUI> rebindUIs = new List<RebindActionUI>();
private bool isInitialized;
private void Start()
{
// Bind to OnInputDeviceChanged
}
private void OnEnable()
{
applyButton.OnClick += OnApplyPressed;
}
private void OnDisable()
{
applyButton.OnClick -= OnApplyPressed;
}
private void OnDestroy()
{
// Unbind to OnInputDeviceChanged
}
private void OnApplyPressed()
{
// Save changes
// Hide
}
private void OnResetAll()
{
playerInputActionRef.RemoveAllBindingOverrides();
UpdateAllRebindUI();
// UpdateBindings
}
private void AddBindingsButton(string deviceName)
{
currentControlScheme.Text = $"< {deviceName.ToUpper()} >";
ReadOnlyArray<InputAction> inputActions = playerInputActionRef.Player.Get().actions;
bool first = true;
foreach (var inputAction in inputActions)
{
foreach (var mainBinding in BindingsIconsUtil.GetRelevantMainBindings(inputAction, deviceName))
{
SpawnButton(inputAction, mainBinding, first);
first = false;
}
}
}
private void SpawnButton(InputAction inputAction, int mainBindingIndex, bool first)
{
RebindActionUI actionButton = Instantiate(rebindPrefab, scrollRectContent);
rebindUIs.Add(actionButton);
actionButton.name = $"Rebind UI for {inputAction.name}";
actionButton.Initialize(mainBindingIndex, inputAction, rebindOverlayText, OnUpdateBindingUIEvent);
if (first)
{
firstButton = actionButton;
}
}
private void OnUpdateBindingUIEvent(string deviceLayoutName, string mainControlPath)
{
UpdateAllRebindUI();
// UpdateBindings
}
private void UpdateAllRebindUI()
{
foreach (RebindActionUI rebindActionUI in rebindUIs)
{
rebindActionUI.UpdateBindingDisplay(false);
}
}
private void OnInputDeviceChanged(string newDevice)
{
rebindUIs.Clear();
scrollRectContent.DestroyChildren();
AddBindingsButton(newDevice);
}
}
}

45
Common/Settings.cs Normal file
View File

@ -0,0 +1,45 @@
using System.Linq;
using UnityEngine;
namespace Common
{
public class SettingsSystem : PersistentSingleton<SettingsSystem>
{
private const string SETTINGS_FOLDER_PATH = "Settings";
public static GameSettings GameSettings => Instance.GameSetting;
private GameSettings gameSetting;
private GameSettings GameSetting => gameSetting;
protected override void Awake()
{
base.Awake();
LoadSettings(out gameSetting);
}
private void LoadSettings<T>(out T memberToInitialize) where T : ScriptableObject
{
T[] loadedSettingsList = Resources.LoadAll<T>(SETTINGS_FOLDER_PATH);
Debug.Assert(loadedSettingsList.Any(),
$"An object of type {typeof(T).Name} should be in the folder {SETTINGS_FOLDER_PATH}");
if (loadedSettingsList.Length > 1)
Debug.LogWarning(
$"More than one object of type {typeof(T).Name} was found in the folder {SETTINGS_FOLDER_PATH}. Taking the first one.");
memberToInitialize = loadedSettingsList.First();
}
private void LoadAllSettings<T>(out T[] memberToInitialize) where T : ScriptableObject
{
T[] loadedSettingsList = Resources.LoadAll<T>(SETTINGS_FOLDER_PATH);
Debug.Assert(loadedSettingsList.Any(),
$"An object of type {typeof(T).Name} should be in the folder {SETTINGS_FOLDER_PATH}");
memberToInitialize = loadedSettingsList;
}
}
}

125
Common/Singleton.cs Normal file
View File

@ -0,0 +1,125 @@
using System;
using UnityEngine;
using Utilities.Extensions;
namespace Common
{
public abstract class AbstractSingleton<T> : MonoBehaviour where T : AbstractSingleton<T>
{
protected static T SharedInstance;
public static bool HasInstance => SharedInstance != null;
protected virtual void Awake()
{
try
{
SharedInstance = (T) Convert.ChangeType(this, typeof(T));
}
catch (InvalidCastException)
{
Debug.Assert(false, "Singleton's T type should be the derived class.");
}
}
protected void DestroyInstance()
{
if (gameObject.GetNumberOfComponents() == 1)
{
Destroy(gameObject);
}
else
{
Destroy(this);
}
}
protected virtual void OnApplicationQuit()
{
SharedInstance = null;
DestroyInstance();
}
}
/// <summary>
/// Singleton class that can be loaded and unloaded with scenes.
/// </summary>
public abstract class Singleton<T> : AbstractSingleton<T> where T : Singleton<T>
{
public static T Instance
{
get
{
Debug.Assert(HasInstance, $"Trying to access a script of type {typeof(T).Name} that is not in the scene.");
return SharedInstance;
}
}
protected override void Awake()
{
if (HasInstance)
{
Debug.LogWarning($"New instance of type {typeof(T).Name} detected. " +
"This new instance is becoming the default instance.");
SharedInstance = (T) Convert.ChangeType(this, typeof(T));;
return;
}
base.Awake();
}
protected virtual void OnDestroy()
{
if (SharedInstance == this)
{
SharedInstance = null;
}
}
}
/// <summary>
/// Singleton class that is lazy loaded once and is only unloaded when the game exits.
/// </summary>
public abstract class PersistentSingleton<T> : AbstractSingleton<T> where T : PersistentSingleton<T>
{
public static T Instance
{
get
{
CreateIfDoesNotExist();
return SharedInstance;
}
}
public static void CreateIfDoesNotExist()
{
if (!HasInstance && Application.isPlaying)
{
CreateInstance();
}
}
private static void CreateInstance()
{
var instanceGameObject = new GameObject(typeof(T).Name);
instanceGameObject.AddComponent<T>();
}
protected override void Awake()
{
if (HasInstance)
{
Debug.Log($"Two or more instances of a singleton of type {typeof(T).Name} were found in the scene. " +
"The new instance of the singleton trying to register will be removed.");
DestroyInstance();
return;
}
DontDestroyOnLoadUtils.Add(gameObject);
base.Awake();
}
}
}

61
Common/Timer.cs Normal file
View File

@ -0,0 +1,61 @@
using System;
namespace Common
{
public class Timer
{
public event Action OnTimeOver;
public bool IsRunning { get; private set; }
public float CurrentTime { get; private set; }
private readonly float startingTime;
private readonly bool isCountDown;
private bool isEnabled;
public Timer(float startTime, bool countDown = true)
{
IsRunning = false;
isEnabled = true;
startingTime = startTime;
isCountDown = countDown;
Init();
}
private void Init()
{
CurrentTime = startingTime;
}
public void Tick(float dt)
{
if (!IsRunning || !isEnabled) return;
CurrentTime += isCountDown ? -dt : dt;
if (CurrentTime < 0)
{
CurrentTime = 0;
IsRunning = false;
isEnabled = false;
OnTimeOver?.Invoke();
}
}
public void Reset()
{
isEnabled = true;
CurrentTime = isCountDown ? startingTime : 0;
}
public void Start() => IsRunning = true;
public void Pause() => IsRunning = false;
public void Stop()
{
IsRunning = false;
isEnabled = false;
CurrentTime = 0;
}
}
}

44
Common/TimerWrapper.cs Normal file
View File

@ -0,0 +1,44 @@
using UnityEngine;
using UnityEngine.Events;
namespace Common
{
public class TimerWrapper : MonoBehaviour
{
public UnityAction OnTimerOver;
[SerializeField, Min(0)] private float startTime;
[SerializeField] private bool countDown = true;
[SerializeField] private bool startOnAwake;
[SerializeField] private bool startOnStart;
private Timer timer;
public bool IsRunning => timer.IsRunning;
public float CurrentTime => timer.CurrentTime;
private void Awake()
{
timer = new Timer(startTime, countDown);
timer.OnTimeOver += OnTimeOver;
if (startOnAwake) timer.Start();
}
private void Start()
{
if (startOnStart) timer.Start();
}
private void Update()
{
timer.Tick(Time.deltaTime);
}
public void StartTimer() => timer.Start();
public void PauseTimer() => timer.Pause();
public void StopTimer() => timer.Stop();
public void ResetTimer() => timer.Reset();
private void OnTimeOver() => OnTimerOver?.Invoke();
}
}

View File

@ -1,77 +0,0 @@
using System;
using UnityEngine;
namespace Core
{
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T _instance;
public static T Instance
{
get
{
if (!HasInstance)
{
Debug.LogWarning(
$"Trying to access a singleton of type {typeof(T).Name} that is not in the scene. " +
"If you are trying to see if the Instance exists, you should use the HasInstance static property instead.");
}
return _instance;
}
}
private bool _destroyedByScript;
public static bool HasInstance => _instance != null;
protected virtual void Awake()
{
_destroyedByScript = false;
if (HasInstance)
{
Debug.Log($"Two or more instances of a singleton of type {typeof(T).Name} were found in the scene. " +
"The new instance of the singleton trying to register will be removed.");
DestroyInstance();
return;
}
try
{
_instance = (T) Convert.ChangeType(this, typeof(T));
}
catch (InvalidCastException)
{
Debug.Assert(false, "Singleton's T type should be the derived class.");
}
}
private void DestroyInstance()
{
_destroyedByScript = true;
if (gameObject.GetNumberOfComponents() == 1)
{
Destroy(gameObject);
}
else
{
Destroy(this);
}
}
protected virtual void OnDestroy()
{
if (_instance == this)
{
_instance = null;
}
else if (!_destroyedByScript)
{
Debug.LogWarning($"Instance of {typeof(T).Name} deleted, but was not the singleton instance.");
}
}
}
}

View File

@ -1,49 +0,0 @@
using UnityEngine;
using UnityEngine.Events;
namespace Core
{
public class Timer : MonoBehaviour
{
public UnityAction onTimeOver;
public bool IsRunning { get; private set; }
public float CurrentTime { get; private set; }
[SerializeField] private float _startingTime = 600;
[SerializeField] private bool _isCountDown;
private void Start()
{
CurrentTime = _startingTime;
}
private void Update()
{
if (IsRunning)
{
if (_isCountDown)
{
if (CurrentTime > 0)
{
CurrentTime -= Time.deltaTime;
}
else
{
CurrentTime = 0;
IsRunning = false;
onTimeOver?.Invoke();
}
}
else
{
CurrentTime += Time.deltaTime;
}
}
}
public void StartTimer() => IsRunning = true;
public void StopTimer() => IsRunning = false;
}
}

133
Editor/SceneAutoLoader.cs Normal file
View File

@ -0,0 +1,133 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
/// <summary>
/// Scene auto loader.
/// </summary>
/// <description>
/// This class adds a File > Scene Autoload menu containing options to select
/// a "master scene" enable it to be auto-loaded when the user presses play
/// in the editor. When enabled, the selected scene will be loaded on play,
/// then the original scene will be reloaded on stop.
///
/// Based on an idea on this thread:
/// http://forum.unity3d.com/threads/157502-Executing-first-scene-in-build-settings-when-pressing-play-button-in-editor
/// </description>
[InitializeOnLoad]
public static class SceneAutoLoader
{
// Static constructor binds a playmode-changed callback.
// [InitializeOnLoad] above makes sure this gets executed.
static SceneAutoLoader()
{
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
// Menu items to select the "master" scene and control whether or not to load it.
[MenuItem("File/Scene Autoload/Select Master Scene...")]
private static void SelectMasterScene()
{
string masterScene = EditorUtility.OpenFilePanel("Select Master Scene", Application.dataPath, "unity");
masterScene = masterScene.Replace(Application.dataPath, "Assets"); //project relative instead of absolute path
if (!string.IsNullOrEmpty(masterScene))
{
MasterScene = masterScene;
LoadMasterOnPlay = true;
}
}
[MenuItem("File/Scene Autoload/Load Master On Play", true)]
private static bool ShowLoadMasterOnPlay()
{
return !LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Load Master On Play")]
private static void EnableLoadMasterOnPlay()
{
LoadMasterOnPlay = true;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play", true)]
private static bool ShowDontLoadMasterOnPlay()
{
return LoadMasterOnPlay;
}
[MenuItem("File/Scene Autoload/Don't Load Master On Play")]
private static void DisableLoadMasterOnPlay()
{
LoadMasterOnPlay = false;
}
// Play mode change callback handles the scene load/reload.
private static void OnPlayModeChanged(PlayModeStateChange state)
{
if (!LoadMasterOnPlay)
{
return;
}
if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
{
// User pressed play -- autoload master scene.
PreviousScene = SceneManager.GetActiveScene().path;
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
try
{
EditorSceneManager.OpenScene(MasterScene);
}
catch
{
Debug.LogError($"error: scene not found: {MasterScene}");
EditorApplication.isPlaying = false;
}
}
else
{
// User cancelled the save operation -- cancel play as well.
EditorApplication.isPlaying = false;
}
}
// isPlaying check required because cannot OpenScene while playing
if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
{
// User pressed stop -- reload previous scene.
try
{
EditorSceneManager.OpenScene(PreviousScene);
}
catch
{
Debug.LogError($"error: scene not found: {PreviousScene}");
}
}
}
// Properties are remembered as editor preferences.
private const string EDITOR_PREF_LOAD_MASTER_ON_PLAY = "SceneAutoLoader.LoadMasterOnPlay";
private const string EDITOR_PREF_MASTER_SCENE = "SceneAutoLoader.MasterScene";
private const string EDITOR_PREF_PREVIOUS_SCENE = "SceneAutoLoader.PreviousScene";
private static bool LoadMasterOnPlay
{
get => EditorPrefs.GetBool(EDITOR_PREF_LOAD_MASTER_ON_PLAY, false);
set => EditorPrefs.SetBool(EDITOR_PREF_LOAD_MASTER_ON_PLAY, value);
}
private static string MasterScene
{
get => EditorPrefs.GetString(EDITOR_PREF_MASTER_SCENE, "Master.unity");
set => EditorPrefs.SetString(EDITOR_PREF_MASTER_SCENE, value);
}
private static string PreviousScene
{
get => EditorPrefs.GetString(EDITOR_PREF_PREVIOUS_SCENE, SceneManager.GetActiveScene().path);
set => EditorPrefs.SetString(EDITOR_PREF_PREVIOUS_SCENE, value);
}
}

View File

@ -1,19 +0,0 @@
using UnityEngine;
public static class GameObjectExtensions
{
public static int GetNumberOfComponents(this GameObject gameObject)
{
return gameObject.GetComponentsInChildren<Component>().Length - 1;
}
public static void Show(this GameObject gameObject)
{
gameObject.SetActive(true);
}
public static void Hide(this GameObject gameObject)
{
gameObject.SetActive(false);
}
}

View File

@ -1,45 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool<T> where T : Object
{
private readonly Queue<T> _pool;
private readonly List<T> _prefab;
public ObjectPool(IEnumerable<T> poolPrefab)
{
_pool = new Queue<T>();
_prefab = new List<T>();
_prefab.AddRange(poolPrefab);
}
/*public ObjectPool(IEnumerable<T> prefabs)
{
_pool = new Queue<T>();
_prefab.AddRange(prefabs);
}*/
public void Pool(T objectToPool)
{
_pool.Enqueue(objectToPool);
}
public T DePool()
{
if (_pool.Count > 0)
{
return _pool.Dequeue();
}
var randomNumber = Random.Range(0, _prefab.Count);
return Object.Instantiate(_prefab[randomNumber]);
}
public void PoolAll(IEnumerable<T> listOfObject)
{
foreach (var obj in listOfObject)
{
Pool(obj);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,202 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Sirenix.Serialization.Config</name>
</assembly>
<members>
<member name="T:Sirenix.Serialization.CustomLogger">
<summary>
A helper class for quickly and easily defining custom loggers.
</summary>
<seealso cref="T:Sirenix.Serialization.ILogger" />
</member>
<member name="M:Sirenix.Serialization.CustomLogger.#ctor(System.Action{System.String},System.Action{System.String},System.Action{System.Exception})">
<summary>
Not yet documented.
</summary>
</member>
<member name="M:Sirenix.Serialization.CustomLogger.LogWarning(System.String)">
<summary>
Not yet documented.
</summary>
</member>
<member name="M:Sirenix.Serialization.CustomLogger.LogError(System.String)">
<summary>
Not yet documented.
</summary>
</member>
<member name="M:Sirenix.Serialization.CustomLogger.LogException(System.Exception)">
<summary>
Not yet documented.
</summary>
</member>
<member name="T:Sirenix.Serialization.DataFormat">
<summary>
Specifies a data format to read and write in.
</summary>
</member>
<member name="F:Sirenix.Serialization.DataFormat.Binary">
<summary>
A custom packed binary format. This format is most efficient and almost allocation-free,
but its serialized data is not human-readable.
</summary>
</member>
<member name="F:Sirenix.Serialization.DataFormat.JSON">
<summary>
A JSON format compliant with the json specification found at "http://www.json.org/".
<para />
This format has rather sluggish performance and allocates frightening amounts of string garbage.
</summary>
</member>
<member name="F:Sirenix.Serialization.DataFormat.Nodes">
<summary>
A format that does not serialize to a byte stream, but to a list of data nodes in memory
which can then be serialized by Unity.
<para />
This format is highly inefficient, and is primarily used for ensuring that Unity assets
are mergeable by individual values when saved in Unity's text format. This makes
serialized values more robust and data recovery easier in case of issues.
<para />
This format is *not* recommended for use in builds.
</summary>
</member>
<member name="T:Sirenix.Serialization.DefaultLoggers">
<summary>
Defines default loggers for serialization and deserialization. This class and all of its loggers are thread safe.
</summary>
</member>
<member name="P:Sirenix.Serialization.DefaultLoggers.DefaultLogger">
<summary>
Not yet documented.
</summary>
</member>
<member name="P:Sirenix.Serialization.DefaultLoggers.UnityLogger">
<summary>
Not yet documented.
</summary>
</member>
<member name="T:Sirenix.Serialization.ErrorHandlingPolicy">
<summary>
The policy for handling errors during serialization and deserialization.
</summary>
</member>
<member name="F:Sirenix.Serialization.ErrorHandlingPolicy.Resilient">
<summary>
Attempts will be made to recover from errors and continue serialization. Data may become invalid.
</summary>
</member>
<member name="F:Sirenix.Serialization.ErrorHandlingPolicy.ThrowOnErrors">
<summary>
Exceptions will be thrown when errors are logged.
</summary>
</member>
<member name="F:Sirenix.Serialization.ErrorHandlingPolicy.ThrowOnWarningsAndErrors">
<summary>
Exceptions will be thrown when warnings or errors are logged.
</summary>
</member>
<member name="T:Sirenix.Serialization.GlobalSerializationConfig">
<summary>
Not yet documented.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.ODIN_SERIALIZATION_CAUTIONARY_WARNING_TEXT">
<summary>
Text for the cautionary serialization warning shown in the inspector.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.ODIN_SERIALIZATION_CAUTIONARY_WARNING_BUTTON_TEXT">
<summary>
Text for the hide button for the cautionary serialization warning shown in the inspector.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.ODIN_PREFAB_CAUTIONARY_WARNING_BUTTON_TEXT">
<summary>
Text for the hide button for the cautionary prefab warning shown in the inspector.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.HideSerializationCautionaryMessage">
<summary>
Whether the user has chosen to hide the cautionary serialization warning.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.HideOdinSerializeAttributeWarningMessages">
<summary>
Whether the user has chosen to hide the warning messages related to the OdinSerialize attribute.
</summary>
</member>
<member name="F:Sirenix.Serialization.GlobalSerializationConfig.HideNonSerializedShowInInspectorWarningMessages">
<summary>
Whether the user has chosen to hide the warning messages related to the SerializeField and ShowInInspector attributes on non-serialized members.
</summary>
</member>
<member name="P:Sirenix.Serialization.GlobalSerializationConfig.Logger">
<summary>
Not yet documented.
</summary>
</member>
<member name="P:Sirenix.Serialization.GlobalSerializationConfig.EditorSerializationFormat">
<summary>
Not yet documented.
</summary>
</member>
<member name="P:Sirenix.Serialization.GlobalSerializationConfig.BuildSerializationFormat">
<summary>
Not yet documented.
</summary>
</member>
<member name="P:Sirenix.Serialization.GlobalSerializationConfig.LoggingPolicy">
<summary>
Not yet documented.
</summary>
</member>
<member name="P:Sirenix.Serialization.GlobalSerializationConfig.ErrorHandlingPolicy">
<summary>
Not yet documented.
</summary>
</member>
<member name="T:Sirenix.Serialization.ILogger">
<summary>
Implements methods for logging warnings, errors and exceptions during serialization and deserialization.
</summary>
</member>
<member name="M:Sirenix.Serialization.ILogger.LogWarning(System.String)">
<summary>
Logs a warning.
</summary>
<param name="warning">The warning to log.</param>
</member>
<member name="M:Sirenix.Serialization.ILogger.LogError(System.String)">
<summary>
Logs an error.
</summary>
<param name="error">The error to log.</param>
</member>
<member name="M:Sirenix.Serialization.ILogger.LogException(System.Exception)">
<summary>
Logs an exception.
</summary>
<param name="exception">The exception to log.</param>
</member>
<member name="T:Sirenix.Serialization.LoggingPolicy">
<summary>
The policy for which level of logging to do during serialization and deserialization.
</summary>
</member>
<member name="F:Sirenix.Serialization.LoggingPolicy.LogErrors">
<summary>
Not yet documented.
</summary>
</member>
<member name="F:Sirenix.Serialization.LoggingPolicy.LogWarningsAndErrors">
<summary>
Not yet documented.
</summary>
</member>
<member name="F:Sirenix.Serialization.LoggingPolicy.Silent">
<summary>
Not yet documented.
</summary>
</member>
</members>
</doc>

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
<linker>
<assembly fullname="Sirenix.OdinInspector.Attributes" preserve="all"/>
<assembly fullname="Sirenix.Serialization.Config" preserve="all"/>
<assembly fullname="Sirenix.Serialization" preserve="all"/>
<assembly fullname="Sirenix.Utilities" preserve="all"/>
</linker>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,13 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -262940062, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: OdinPathLookup
m_EditorClassIdentifier:

View File

@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1726182683, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: AOTGenerationConfig
m_EditorClassIdentifier:
automateBeforeBuilds: 0
deleteDllAfterBuilds: 1
AutomateForAllAOTPlatforms: 1
automateForPlatforms: 0900000014000000
lastScan: 0
supportSerializedTypes: []

View File

@ -0,0 +1,136 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 772478971, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: ColorPaletteManager
m_EditorClassIdentifier:
colorPalettes:
- name: Country
showAlpha: 0
colors:
- {r: 0.776, g: 0.651, b: 0.349, a: 1}
- {r: 0.863, g: 0.761, b: 0.631, a: 1}
- {r: 0.91, g: 0.831, b: 0.686, a: 1}
- {r: 0.961, g: 0.902, b: 0.788, a: 1}
- {r: 0.753, g: 0.714, b: 0.667, a: 1}
- {r: 0.478, g: 0.573, b: 0.431, a: 1}
- {r: 0.314, g: 0.427, b: 0.31, a: 1}
- {r: 0.596, g: 0.345, b: 0.235, a: 1}
- {r: 0.545, g: 0.329, b: 0.318, a: 1}
- {r: 0.647, g: 0.204, b: 0.227, a: 1}
- {r: 0.435, g: 0.161, b: 0.063, a: 1}
- {r: 0.357, g: 0.333, b: 0.278, a: 1}
- {r: 0.976, g: 0.98, b: 0.961, a: 1}
- {r: 0.165, g: 0.271, b: 0.11, a: 1}
- name: Beach
showAlpha: 0
colors:
- {r: 0.996, g: 0.906, b: 0.459, a: 1}
- {r: 0.314, g: 0.592, b: 0.035, a: 1}
- {r: 0.486, g: 0.953, b: 0.875, a: 1}
- {r: 0.996, g: 0.82, b: 0.212, a: 1}
- {r: 1, g: 0.769, b: 0.165, a: 1}
- {r: 0.804, g: 0.835, b: 0.753, a: 1}
- {r: 1, g: 0.769, b: 0.165, a: 1}
- {r: 1, g: 0.702, b: 0.063, a: 1}
- {r: 1, g: 0.898, b: 0.569, a: 1}
- name: Fall
showAlpha: 0
colors:
- {r: 0.82, g: 0.722, b: 0.318, a: 1}
- {r: 0.537, g: 0.192, b: 0.153, a: 1}
- {r: 0.996, g: 0.812, b: 0.012, a: 1}
- {r: 1, g: 0.431, b: 0.02, a: 1}
- {r: 0.937, g: 0.267, b: 0.094, a: 1}
- {r: 0.42, g: 0.212, b: 0.18, a: 1}
- {r: 0.992, g: 0.651, b: 0.004, a: 1}
- {r: 0.89, g: 0.353, b: 0.086, a: 1}
- {r: 1, g: 0.443, b: 0.004, a: 1}
- {r: 0.682, g: 0.275, b: 0.137, a: 1}
- {r: 0.306, g: 0.231, b: 0.114, a: 1}
- {r: 0.384, g: 0.416, b: 0.082, a: 1}
- {r: 0.165, g: 0.157, b: 0.008, a: 1}
- {r: 0.906, g: 0.635, b: 0.227, a: 1}
- {r: 0.82, g: 0.722, b: 0.318, a: 1}
- {r: 0.745, g: 0.435, b: 0.031, a: 1}
- {r: 0.765, g: 0.682, b: 0.569, a: 1}
- {r: 0.18, g: 0.149, b: 0.075, a: 1}
- {r: 0.702, g: 0.451, b: 0.059, a: 1}
- name: Passion
showAlpha: 0
colors:
- {r: 0.925, g: 0.682, b: 0.624, a: 1}
- {r: 0.188, g: 0.114, b: 0.224, a: 1}
- {r: 0.349, g: 0.11, b: 0.231, a: 1}
- {r: 0.435, g: 0.267, b: 0.357, a: 1}
- name: Sepia
showAlpha: 0
colors:
- {r: 0.353, g: 0.098, b: 0.02, a: 1}
- {r: 0.663, g: 0.188, b: 0.114, a: 1}
- {r: 0.906, g: 0.643, b: 0.082, a: 1}
- {r: 0.996, g: 0.839, b: 0.322, a: 1}
- {r: 0.486, g: 0.392, b: 0.02, a: 1}
- {r: 0.294, g: 0.235, b: 0.012, a: 1}
- name: Floral
showAlpha: 0
colors:
- {r: 0.855, g: 0.518, b: 0.412, a: 1}
- {r: 0.827, g: 0.294, b: 0.333, a: 1}
- {r: 0.737, g: 0.118, b: 0.208, a: 1}
- {r: 0.549, g: 0.149, b: 0.235, a: 1}
- {r: 0.949, g: 0.925, b: 0.784, a: 1}
- {r: 0.945, g: 0.882, b: 0.69, a: 1}
- {r: 0.871, g: 0.812, b: 0.698, a: 1}
- {r: 0.4, g: 0.196, b: 0.243, a: 1}
- {r: 0.271, g: 0.157, b: 0.227, a: 1}
- name: Underwater
showAlpha: 0
colors:
- {r: 0.663, g: 0.416, b: 0.733, a: 1}
- {r: 0.2, g: 0.6, b: 0.698, a: 1}
- {r: 0.11, g: 0.49, b: 0.698, a: 1}
- {r: 0.439, g: 0.627, b: 0.227, a: 1}
- {r: 0, g: 0.357, b: 0.604, a: 1}
- {r: 0.067, g: 0.271, b: 0.353, a: 1}
- name: Breeze
showAlpha: 0
colors:
- {r: 0.706, g: 1, b: 0, a: 1}
- {r: 0.651, g: 1, b: 0.404, a: 1}
- {r: 0.122, g: 1, b: 0.514, a: 1}
- {r: 0.216, g: 0.894, b: 0.961, a: 1}
- {r: 0.4, g: 1, b: 0.882, a: 1}
- {r: 0.027, g: 0.792, b: 0.8, a: 1}
- name: Clovers
showAlpha: 0
colors:
- {r: 0.431, g: 0.549, b: 0.102, a: 1}
- {r: 0.671, g: 0.714, b: 0.071, a: 1}
- {r: 0.969, g: 0.949, b: 0.831, a: 1}
- {r: 0.886, g: 0.902, b: 0.702, a: 1}
- {r: 0.753, g: 0.824, b: 0.627, a: 1}
- {r: 0.404, g: 0.6, b: 0.4, a: 1}
- name: Tropical
showAlpha: 0
colors:
- {r: 0.953, g: 0.647, b: 0.804, a: 1}
- {r: 0.965, g: 0.741, b: 0.871, a: 1}
- {r: 0.949, g: 0.549, b: 0.643, a: 1}
- {r: 0.992, g: 0.659, b: 0.498, a: 1}
- {r: 0.976, g: 0.792, b: 0.729, a: 1}
- {r: 0.984, g: 0.855, b: 0.725, a: 1}
- {r: 0.259, g: 0.882, b: 0.663, a: 1}
- {r: 0.349, g: 0.753, b: 0.78, a: 1}
- {r: 0.725, g: 0.976, b: 0.91, a: 1}
- {r: 0.647, g: 0.745, b: 0.957, a: 1}
- {r: 0.725, g: 0.863, b: 0.973, a: 1}
- {r: 0.89, g: 0.945, b: 0.996, a: 1}

View File

@ -0,0 +1,14 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -645759843, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: GeneralDrawerConfig
m_EditorClassIdentifier:

View File

@ -0,0 +1,15 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 188390376, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: ImportSettingsConfig
m_EditorClassIdentifier:
automateBeforeBuild: 1

View File

@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1137305049, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: InspectorConfig
m_EditorClassIdentifier:
enableOdinInInspector: 1
defaultEditorBehaviour: 11
processMouseMoveInInspector: 1
drawingConfig:
configs: []

View File

@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -228747253, guid: a4865f1ab4504ed8a368670db22f409c, type: 3}
m_Name: OdinModuleConfig
m_EditorClassIdentifier:
configurations:
- ID: Unity.Mathematics
ActivationSettings: 0
ModuleTogglingSettings: 0
ModuleUpdateSettings: 0

View File

@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1549551891, guid: 74721b9f0af448f5ae2e91102a1a5edd, type: 3}
m_Name: GlobalSerializationConfig
m_EditorClassIdentifier:
HideSerializationCautionaryMessage: 1
HidePrefabCautionaryMessage: 0
HideOdinSerializeAttributeWarningMessages: 0
HideNonSerializedShowInInspectorWarningMessages: 0
buildSerializationFormat: 0
editorSerializationFormat: 2
loggingPolicy: 0
errorHandlingPolicy: 0

View File

@ -0,0 +1,883 @@
//-----------------------------------------------------------------------
// <copyright file="MathematicsDrawers.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Sirenix.OdinInspector.Modules.UnityMathematics.Editor
{
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
public sealed class MatrixFloat2x2Processor : MatrixProcessor<float2x2> { }
public sealed class MatrixFloat3x2Processor : MatrixProcessor<float3x2> { }
public sealed class MatrixFloat4x2Processor : MatrixProcessor<float4x2> { }
public sealed class MatrixFloat2x3Processor : MatrixProcessor<float2x3> { }
public sealed class MatrixFloat3x3Processor : MatrixProcessor<float3x3> { }
public sealed class MatrixFloat4x3Processor : MatrixProcessor<float4x3> { }
public sealed class MatrixFloat2x4Processor : MatrixProcessor<float2x4> { }
public sealed class MatrixFloat3x4Processor : MatrixProcessor<float3x4> { }
public sealed class MatrixFloat4x4Processor : MatrixProcessor<float4x4> { }
public sealed class MatrixDouble2x2Processor : MatrixProcessor<double2x2> { }
public sealed class MatrixDouble3x2Processor : MatrixProcessor<double3x2> { }
public sealed class MatrixDouble4x2Processor : MatrixProcessor<double4x2> { }
public sealed class MatrixDouble2x3Processor : MatrixProcessor<double2x3> { }
public sealed class MatrixDouble3x3Processor : MatrixProcessor<double3x3> { }
public sealed class MatrixDouble4x3Processor : MatrixProcessor<double4x3> { }
public sealed class MatrixDouble2x4Processor : MatrixProcessor<double2x4> { }
public sealed class MatrixDouble3x4Processor : MatrixProcessor<double3x4> { }
public sealed class MatrixDouble4x4Processor : MatrixProcessor<double4x4> { }
public sealed class MatrixBool2x2Processor : MatrixProcessor<bool2x2> { }
public sealed class MatrixBool3x2Processor : MatrixProcessor<bool3x2> { }
public sealed class MatrixBool4x2Processor : MatrixProcessor<bool4x2> { }
public sealed class MatrixBool2x3Processor : MatrixProcessor<bool2x3> { }
public sealed class MatrixBool3x3Processor : MatrixProcessor<bool3x3> { }
public sealed class MatrixBool4x3Processor : MatrixProcessor<bool4x3> { }
public sealed class MatrixBool2x4Processor : MatrixProcessor<bool2x4> { }
public sealed class MatrixBool3x4Processor : MatrixProcessor<bool3x4> { }
public sealed class MatrixBool4x4Processor : MatrixProcessor<bool4x4> { }
public sealed class MatrixInt2x2Processor : MatrixProcessor<int2x2> { }
public sealed class MatrixInt3x2Processor : MatrixProcessor<int3x2> { }
public sealed class MatrixInt4x2Processor : MatrixProcessor<int4x2> { }
public sealed class MatrixInt2x3Processor : MatrixProcessor<int2x3> { }
public sealed class MatrixInt3x3Processor : MatrixProcessor<int3x3> { }
public sealed class MatrixInt4x3Processor : MatrixProcessor<int4x3> { }
public sealed class MatrixInt2x4Processor : MatrixProcessor<int2x4> { }
public sealed class MatrixInt3x4Processor : MatrixProcessor<int3x4> { }
public sealed class MatrixInt4x4Processor : MatrixProcessor<int4x4> { }
public sealed class MatrixUInt2x2Processor : MatrixProcessor<uint2x2> { }
public sealed class MatrixUInt3x2Processor : MatrixProcessor<uint3x2> { }
public sealed class MatrixUInt4x2Processor : MatrixProcessor<uint4x2> { }
public sealed class MatrixUInt2x3Processor : MatrixProcessor<uint2x3> { }
public sealed class MatrixUInt3x3Processor : MatrixProcessor<uint3x3> { }
public sealed class MatrixUInt4x3Processor : MatrixProcessor<uint4x3> { }
public sealed class MatrixUInt2x4Processor : MatrixProcessor<uint2x4> { }
public sealed class MatrixUInt3x4Processor : MatrixProcessor<uint3x4> { }
public sealed class MatrixUInt4x4Processor : MatrixProcessor<uint4x4> { }
public sealed class DisableUnityMatrixDrawerAttribute : Attribute { }
public abstract class MatrixProcessor<T> : OdinAttributeProcessor<T>
{
public override void ProcessSelfAttributes(InspectorProperty property, List<Attribute> attributes)
{
attributes.GetOrAddAttribute<InlinePropertyAttribute>();
attributes.GetOrAddAttribute<DisableUnityMatrixDrawerAttribute>();
}
public override void ProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member, List<Attribute> attributes)
{
attributes.Add(new HideLabelAttribute());
attributes.Add(new MatrixChildAttribute());
}
}
public class DisableUnityMatrixDrawerAttributeDrawer : OdinAttributeDrawer<DisableUnityMatrixDrawerAttribute>
{
protected override void Initialize()
{
this.SkipWhenDrawing = true;
var chain = this.Property.GetActiveDrawerChain().BakedDrawerArray;
for (int i = 0; i < chain.Length; i++)
{
var type = chain[i].GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(UnityPropertyDrawer<,>) && type.GetGenericArguments()[0].Name == "MatrixDrawer")
{
chain[i].SkipWhenDrawing = true;
break;
}
}
}
}
public class MatrixChildAttribute : Attribute { }
public class Bool2Drawer : OdinValueDrawer<bool2>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 100;
if (label != null)
{
GUILayout.Space(3); // Ugh, better than nothing
}
var options = GUILayoutOptions.Height(EditorGUIUtility.singleLineHeight);
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
EditorGUILayout.EndVertical();
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class Bool3Drawer : OdinValueDrawer<bool3>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 100;
if (label != null)
{
GUILayout.Space(3); // Ugh, better than nothing
}
var options = GUILayoutOptions.Height(EditorGUIUtility.singleLineHeight);
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
EditorGUILayout.EndVertical();
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class Bool4Drawer : OdinValueDrawer<bool4>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 100;
if (label != null)
{
GUILayout.Space(3); // Ugh, better than nothing
}
var options = GUILayoutOptions.Height(EditorGUIUtility.singleLineHeight);
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(options);
this.ValueEntry.Property.Children[3].Draw(showLabels ? GUIHelper.TempContent("W") : null);
EditorGUILayout.EndVertical();
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class Float2Drawer : OdinValueDrawer<float2>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector2(val.x, val.y));
val = new float2(vec.x, vec.y);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
float2 value = (float2)property.ValueEntry.WeakSmartValue;
var vec = new Vector2(value.x, value.y);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0)"), vec == Vector2.zero, () => SetVector(property, Vector2.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1)"), vec == Vector2.one, () => SetVector(property, Vector2.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0)"), vec == Vector2.right, () => SetVector(property, Vector2.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0)"), vec == Vector2.left, () => SetVector(property, Vector2.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1)"), vec == Vector2.up, () => SetVector(property, Vector2.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1)"), vec == Vector2.down, () => SetVector(property, Vector2.down));
}
private void SetVector(InspectorProperty property, Vector2 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new float2(value.x, value.y);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((float2)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Float3Drawer : OdinValueDrawer<float3>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector3(val.x, val.y, val.z));
val = new float3(vec.x, vec.y, vec.z);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
float3 value = (float3)property.ValueEntry.WeakSmartValue;
var vec = new Vector3(value.x, value.y, value.z);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0, 0)"), vec == Vector3.zero, () => SetVector(property, Vector3.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1, 1)"), vec == Vector3.one, () => SetVector(property, Vector3.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0, 0)"), vec == Vector3.right, () => SetVector(property, Vector3.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0, 0)"), vec == Vector3.left, () => SetVector(property, Vector3.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1, 0)"), vec == Vector3.up, () => SetVector(property, Vector3.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1, 0)"), vec == Vector3.down, () => SetVector(property, Vector3.down));
genericMenu.AddItem(new GUIContent("Forward", "Set the vector property to (0, 0, 1)"), vec == Vector3.forward, () => SetVector(property, Vector3.forward));
genericMenu.AddItem(new GUIContent("Back", "Set the vector property to (0, 0, -1)"), vec == Vector3.back, () => SetVector(property, Vector3.back));
}
private void SetVector(InspectorProperty property, Vector3 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new float3(value.x, value.y, value.z);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((float3)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Float4Drawer : OdinValueDrawer<float4>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector4(val.x, val.y, val.z, val.w));
val = new float4(vec.x, vec.y, vec.z, vec.w);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
this.ValueEntry.Property.Children[3].Draw(showLabels ? GUIHelper.TempContent("W") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
float4 value = (float4)property.ValueEntry.WeakSmartValue;
var vec = new Vector4(value.x, value.y, value.z, value.w);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0, 0, 0)"), vec == Vector4.zero, () => SetVector(property, Vector3.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1, 1, 1)"), vec == Vector4.one, () => SetVector(property, Vector4.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0, 0, 0)"), (Vector3)vec == Vector3.right, () => SetVector(property, Vector3.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0, 0, 0)"), (Vector3)vec == Vector3.left, () => SetVector(property, Vector3.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1, 0, 0)"), (Vector3)vec == Vector3.up, () => SetVector(property, Vector3.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1, 0, 0)"), (Vector3)vec == Vector3.down, () => SetVector(property, Vector3.down));
genericMenu.AddItem(new GUIContent("Forward", "Set the vector property to (0, 0, 1, 0)"), (Vector3)vec == Vector3.forward, () => SetVector(property, Vector3.forward));
genericMenu.AddItem(new GUIContent("Back", "Set the vector property to (0, 0, -1, 0)"), (Vector3)vec == Vector3.back, () => SetVector(property, Vector3.back));
}
private void SetVector(InspectorProperty property, Vector4 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new float4(value.x, value.y, value.z, value.w);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((float4)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Double2Drawer : OdinValueDrawer<double2>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector2((float)val.x, (float)val.y));
val = new double2(vec.x, vec.y);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
double2 value = (double2)property.ValueEntry.WeakSmartValue;
var vec = new Vector2((float)value.x, (float)value.y);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0)"), vec == Vector2.zero, () => SetVector(property, Vector2.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1)"), vec == Vector2.one, () => SetVector(property, Vector2.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0)"), vec == Vector2.right, () => SetVector(property, Vector2.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0)"), vec == Vector2.left, () => SetVector(property, Vector2.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1)"), vec == Vector2.up, () => SetVector(property, Vector2.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1)"), vec == Vector2.down, () => SetVector(property, Vector2.down));
}
private void SetVector(InspectorProperty property, Vector2 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new double2(value.x, value.y);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((double2)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Double3Drawer : OdinValueDrawer<double3>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector3((float)val.x, (float)val.y, (float)val.z));
val = new double3(vec.x, vec.y, vec.z);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
double3 value = (double3)property.ValueEntry.WeakSmartValue;
var vec = new Vector3((float)value.x, (float)value.y, (float)value.z);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0, 0)"), vec == Vector3.zero, () => SetVector(property, Vector3.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1, 1)"), vec == Vector3.one, () => SetVector(property, Vector3.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0, 0)"), vec == Vector3.right, () => SetVector(property, Vector3.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0, 0)"), vec == Vector3.left, () => SetVector(property, Vector3.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1, 0)"), vec == Vector3.up, () => SetVector(property, Vector3.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1, 0)"), vec == Vector3.down, () => SetVector(property, Vector3.down));
genericMenu.AddItem(new GUIContent("Forward", "Set the vector property to (0, 0, 1)"), vec == Vector3.forward, () => SetVector(property, Vector3.forward));
genericMenu.AddItem(new GUIContent("Back", "Set the vector property to (0, 0, -1)"), vec == Vector3.back, () => SetVector(property, Vector3.back));
}
private void SetVector(InspectorProperty property, Vector3 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new double3(value.x, value.y, value.z);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((double3)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Double4Drawer : OdinValueDrawer<double4>, IDefinesGenericMenuItems
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
// Slide rect
{
var val = this.ValueEntry.SmartValue;
EditorGUI.BeginChangeCheck();
var vec = SirenixEditorFields.VectorPrefixSlideRect(labelRect, new Vector4((float)val.x, (float)val.y, (float)val.z, (float)val.w));
val = new double4(vec.x, vec.y, vec.z, vec.w);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = val;
}
}
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
this.ValueEntry.Property.Children[3].Draw(showLabels ? GUIHelper.TempContent("W") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
double4 value = (double4)property.ValueEntry.WeakSmartValue;
var vec = new Vector4((float)value.x, (float)value.y, (float)value.z, (float)value.w);
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Normalize"), Mathf.Approximately(vec.magnitude, 1f), () => NormalizeEntries(property));
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0, 0, 0)"), vec == Vector4.zero, () => SetVector(property, Vector3.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1, 1, 1)"), vec == Vector4.one, () => SetVector(property, Vector4.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0, 0, 0)"), (Vector3)vec == Vector3.right, () => SetVector(property, Vector3.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0, 0, 0)"), (Vector3)vec == Vector3.left, () => SetVector(property, Vector3.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1, 0, 0)"), (Vector3)vec == Vector3.up, () => SetVector(property, Vector3.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1, 0, 0)"), (Vector3)vec == Vector3.down, () => SetVector(property, Vector3.down));
genericMenu.AddItem(new GUIContent("Forward", "Set the vector property to (0, 0, 1, 0)"), (Vector3)vec == Vector3.forward, () => SetVector(property, Vector3.forward));
genericMenu.AddItem(new GUIContent("Back", "Set the vector property to (0, 0, -1, 0)"), (Vector3)vec == Vector3.back, () => SetVector(property, Vector3.back));
}
private void SetVector(InspectorProperty property, Vector4 value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = new double4(value.x, value.y, value.z, value.w);
}
});
}
private void NormalizeEntries(InspectorProperty property)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = math.normalizesafe((double4)property.ValueEntry.WeakValues[i]);
}
});
}
}
public class Int2Drawer : OdinValueDrawer<int2>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class Int3Drawer : OdinValueDrawer<int3>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class Int4Drawer : OdinValueDrawer<int4>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
this.ValueEntry.Property.Children[3].Draw(showLabels ? GUIHelper.TempContent("W") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class UInt2Drawer : OdinValueDrawer<uint2>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class UInt3Drawer : OdinValueDrawer<uint3>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
public class UInt4Drawer : OdinValueDrawer<uint4>
{
private bool isMatrixChild;
protected override void Initialize()
{
this.isMatrixChild = this.Property.GetAttribute<MatrixChildAttribute>() != null;
}
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
Rect contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
var showLabels = !this.isMatrixChild && SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
this.ValueEntry.Property.Children[3].Draw(showLabels ? GUIHelper.TempContent("W") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
}
#endif
}

View File

@ -0,0 +1,11 @@
{
"name": "Sirenix.OdinInspector.Modules.UnityMathematics",
"references": [ "Unity.Mathematics", "Sirenix.OdinInspector.Attributes", "Sirenix.OdinInspector.Editor", "Sirenix.Utilities", "Sirenix.Utilities.Editor" ],
"includePlatforms": [ "Editor" ],
"excludePlatforms": [],
"allowUnsafeCode": true,
"autoReferenced": true,
"overrideReferences": false,
"precompiledReferences": [ "Sirenix.Utilities.dll", "Sirenix.Utilities.Editor.dll", "Sirenix.OdinInspector.Attributes.dll", "Sirenix.OdinInspector.Editor.dll", "Sirenix.Serialization.dll" ],
"defineConstraints": []
}

View File

@ -0,0 +1,8 @@
ManifestVersion: 1
ModuleID: Unity.Mathematics
ModuleVersion: 1.0.1.0
ModuleFiles:
MathematicsDrawers.cs
MathematicsDrawers.cs.meta
Sirenix.OdinInspector.Modules.UnityMathematics.asmdef
Sirenix.OdinInspector.Modules.UnityMathematics.asmdef.meta

View File

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------
// <copyright file="AssemblyImportSettingsAutomation.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_5_6_OR_NEWER
namespace Sirenix.OdinInspector.Editor
{
using System.IO;
using System.Collections.Generic;
using Sirenix.Serialization.Utilities.Editor;
using Sirenix.Utilities;
using UnityEditor;
using UnityEditor.Build;
#if UNITY_2018_1_OR_NEWER
using UnityEditor.Build.Reporting;
#endif
public class AssemblyImportSettingsAutomation :
#if UNITY_2018_1_OR_NEWER
IPreprocessBuildWithReport
#else
IPreprocessBuild
#endif
{
public int callbackOrder { get { return -1500; } }
private static void ConfigureImportSettings()
{
if (EditorOnlyModeConfig.Instance.IsEditorOnlyModeEnabled() || ImportSettingsConfig.Instance.AutomateBeforeBuild == false)
{
return;
}
var assemblyDir = new DirectoryInfo(SirenixAssetPaths.SirenixAssembliesPath).FullName;
var projectAssetsPath = Directory.GetCurrentDirectory().TrimEnd('\\', '/');
var isPackage = PathUtilities.HasSubDirectory(new DirectoryInfo(projectAssetsPath), new DirectoryInfo(assemblyDir)) == false;
var aotDirPath = assemblyDir + "NoEmitAndNoEditor/";
var jitDirPath = assemblyDir + "NoEditor/";
var aotDir = new DirectoryInfo(aotDirPath);
var jitDir = new DirectoryInfo(jitDirPath);
var aotAssemblies = new List<string>();
var jitAssemblies = new List<string>();
foreach (var file in aotDir.GetFiles("*.dll"))
{
string path = file.FullName;
if (isPackage)
{
path = SirenixAssetPaths.SirenixAssembliesPath.TrimEnd('\\', '/') + "/" + path.Substring(assemblyDir.Length);
}
else
{
path = path.Substring(projectAssetsPath.Length + 1);
}
aotAssemblies.Add(path);
}
foreach (var file in jitDir.GetFiles("*.dll"))
{
string path = file.FullName;
if (isPackage)
{
path = SirenixAssetPaths.SirenixAssembliesPath.TrimEnd('\\', '/') + "/" + path.Substring(assemblyDir.Length);
}
else
{
path = path.Substring(projectAssetsPath.Length + 1);
}
jitAssemblies.Add(path);
}
AssetDatabase.StartAssetEditing();
try
{
var platform = EditorUserBuildSettings.activeBuildTarget;
if (AssemblyImportSettingsUtilities.IsJITSupported(
platform,
AssemblyImportSettingsUtilities.GetCurrentScriptingBackend(),
AssemblyImportSettingsUtilities.GetCurrentApiCompatibilityLevel()))
{
ApplyImportSettings(platform, aotAssemblies.ToArray(), OdinAssemblyImportSettings.ExcludeFromAll);
ApplyImportSettings(platform, jitAssemblies.ToArray(), OdinAssemblyImportSettings.IncludeInBuildOnly);
}
else
{
ApplyImportSettings(platform, aotAssemblies.ToArray(), OdinAssemblyImportSettings.IncludeInBuildOnly);
ApplyImportSettings(platform, jitAssemblies.ToArray(), OdinAssemblyImportSettings.ExcludeFromAll);
}
}
finally
{
AssetDatabase.StopAssetEditing();
}
}
private static void ApplyImportSettings(BuildTarget platform, string[] assemblyPaths, OdinAssemblyImportSettings importSettings)
{
for (int i = 0; i < assemblyPaths.Length; i++)
{
AssemblyImportSettingsUtilities.SetAssemblyImportSettings(platform, assemblyPaths[i], importSettings);
}
}
#if UNITY_2018_1_OR_NEWER
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
{
ConfigureImportSettings();
}
#else
void IPreprocessBuild.OnPreprocessBuild(BuildTarget target, string path)
{
ConfigureImportSettings();
}
#endif
}
}
#endif // UNITY_EDITOR && UNITY_5_6_OR_NEWER

View File

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------
// <copyright file="BuildAOTAutomation.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_5_6_OR_NEWER
namespace Sirenix.Serialization.Internal
{
using Sirenix.Serialization;
using UnityEditor;
using UnityEditor.Build;
using System.IO;
using System;
#if UNITY_2018_1_OR_NEWER
using UnityEditor.Build.Reporting;
#endif
#if UNITY_2018_1_OR_NEWER
public class PreBuildAOTAutomation : IPreprocessBuildWithReport
#else
public class PreBuildAOTAutomation : IPreprocessBuild
#endif
{
public int callbackOrder { get { return -1000; } }
public void OnPreprocessBuild(BuildTarget target, string path)
{
if (AOTGenerationConfig.Instance.ShouldAutomationGeneration(target))
{
AOTGenerationConfig.Instance.ScanProject();
AOTGenerationConfig.Instance.GenerateDLL();
}
}
#if UNITY_2018_1_OR_NEWER
public void OnPreprocessBuild(BuildReport report)
{
this.OnPreprocessBuild(report.summary.platform, report.summary.outputPath);
}
#endif
}
#if UNITY_2018_1_OR_NEWER
public class PostBuildAOTAutomation : IPostprocessBuildWithReport
#else
public class PostBuildAOTAutomation : IPostprocessBuild
#endif
{
public int callbackOrder { get { return -1000; } }
public void OnPostprocessBuild(BuildTarget target, string path)
{
if (AOTGenerationConfig.Instance.DeleteDllAfterBuilds && AOTGenerationConfig.Instance.ShouldAutomationGeneration(target))
{
Directory.Delete(AOTGenerationConfig.Instance.AOTFolderPath, true);
File.Delete(AOTGenerationConfig.Instance.AOTFolderPath.TrimEnd('/', '\\') + ".meta");
AssetDatabase.Refresh();
}
}
#if UNITY_2018_1_OR_NEWER
public void OnPostprocessBuild(BuildReport report)
{
this.OnPostprocessBuild(report.summary.platform, report.summary.outputPath);
}
#endif
}
}
#endif // UNITY_EDITOR && UNITY_5_6_OR_NEWER

View File

@ -0,0 +1,121 @@
//-----------------------------------------------------------------------
// <copyright file="EnsureOdinInspectorDefine.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR
namespace Sirenix.Utilities
{
using System;
using System.Linq;
using UnityEditor;
/// <summary>
/// Defines the ODIN_INSPECTOR symbol.
/// </summary>
internal static class EnsureOdinInspectorDefine
{
private static readonly string[] DEFINES = new string[] { "ODIN_INSPECTOR", "ODIN_INSPECTOR_3" };
[InitializeOnLoadMethod]
private static void EnsureScriptingDefineSymbol()
{
var currentTarget = EditorUserBuildSettings.selectedBuildTargetGroup;
if (currentTarget == BuildTargetGroup.Unknown)
{
return;
}
var definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTarget).Trim();
var defines = definesString.Split(';');
bool changed = false;
foreach (var define in DEFINES)
{
if (defines.Contains(define) == false)
{
if (definesString.EndsWith(";", StringComparison.InvariantCulture) == false)
{
definesString += ";";
}
definesString += define;
changed = true;
}
}
if (changed)
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTarget, definesString);
}
}
}
//
// If you have a project where only some users have Odin, and you want to utilize the ODIN_INSPECTOR
// define symbol. Then, in order to only define the symbol for those with Odin, you can delete this script,
// which prevent ODIN_INSPECTOR from being added to the Unity's player settings.
//
// And instead automatically add the ODIN_INSPECTOR define to an mcs.rsp file if Odin exists using the script below.
// You can then ignore the mcs.rsp file in source control.
//
// Remember to manually remove the ODIN_INSPECTOR define symbol in player settings after removing this script.
//
// static class AddOdinInspectorDefineIfOdinExist
// {
// private const string ODIN_MCS_DEFINE = "-define:ODIN_INSPECTOR";
//
// [InitializeOnLoadMethod]
// private static void AddOrRemoveOdinDefine()
// {
// var addDefine = AppDomain.CurrentDomain.GetAssemblies().Any(x => x.FullName.StartsWith("Sirenix.OdinInspector.Editor"));
//
// #if ODIN_INSPECTOR
// var hasDefine = true;
// #else
// var hasDefine = false;
// #endif
//
// if (addDefine == hasDefine)
// {
// return;
// }
//
// var mcsPath = Path.Combine(Application.dataPath, "mcs.rsp");
// var hasMcsFile = File.Exists(mcsPath);
//
// if (addDefine)
// {
// var lines = hasMcsFile ? File.ReadAllLines(mcsPath).ToList() : new List<string>();
// if (!lines.Any(x => x.Trim() == ODIN_MCS_DEFINE))
// {
// lines.Add(ODIN_MCS_DEFINE);
// File.WriteAllLines(mcsPath, lines.ToArray());
// AssetDatabase.Refresh();
// }
// }
// else if (hasMcsFile)
// {
// var linesWithoutOdinDefine = File.ReadAllLines(mcsPath).Where(x => x.Trim() != ODIN_MCS_DEFINE).ToArray();
//
// if (linesWithoutOdinDefine.Length == 0)
// {
// // Optional - Remove the mcs file instead if it doesn't contain any lines.
// File.Delete(mcsPath);
// }
// else
// {
// File.WriteAllLines(mcsPath, linesWithoutOdinDefine);
// }
//
// AssetDatabase.Refresh();
// }
// }
// }
}
#endif // UNITY_EDITOR

View File

@ -0,0 +1,208 @@
//-----------------------------------------------------------------------
// <copyright file="FixBrokenUnityObjectWrapperDrawer.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_2018_3_OR_NEWER
#pragma warning disable
namespace Sirenix.OdinInspector.Editor.Drawers
{
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using System.Linq;
using UnityEditor;
using UnityEngine;
[DrawerPriority(0.001, 0, 0)]
public class FixBrokenUnityObjectWrapperDrawer<T> : OdinValueDrawer<T>, IDefinesGenericMenuItems
where T : UnityEngine.Component
{
private const string AUTO_FIX_PREFS_KEY = "TemporarilyBrokenUnityObjectWrapperDrawer.autoFix";
private bool isBroken = false;
private T realWrapperInstance;
private bool allowSceneViewObjects;
private static bool autoFix;
protected override void Initialize()
{
this.allowSceneViewObjects = this.ValueEntry.Property.GetAttribute<AssetsOnlyAttribute>() == null;
autoFix = EditorPrefs.HasKey(AUTO_FIX_PREFS_KEY);
}
protected override void DrawPropertyLayout(GUIContent label)
{
if (!(this.ValueEntry.ValueState == PropertyValueState.NullReference || this.ValueEntry.ValueState == PropertyValueState.ReferenceValueConflict))
{
this.CallNextDrawer(label);
return;
}
if (Event.current.type == EventType.Layout)
{
this.isBroken = false;
var count = this.ValueEntry.ValueCount;
for (int i = 0; i < count; i++)
{
var component = this.ValueEntry.Values[i];
if (ComponentIsBroken(component, ref this.realWrapperInstance))
{
this.isBroken = true;
break;
}
}
if (this.isBroken && autoFix)
{
this.isBroken = false;
for (int i = 0; i < this.ValueEntry.ValueCount; i++)
{
T fixedComponent = null;
if (ComponentIsBroken(this.ValueEntry.Values[i], ref fixedComponent) && fixedComponent)
{
(this.ValueEntry as IValueEntryActualValueSetter<T>).SetActualValue(i, fixedComponent);
}
}
this.ValueEntry.Update();
}
}
if (!this.isBroken)
{
this.CallNextDrawer(label);
return;
}
var rect = EditorGUILayout.GetControlRect(label != null);
var btnRect = rect.AlignRight(20);
var controlRect = rect.SetXMax(btnRect.xMin - 5);
object newInstance = null;
EditorGUI.BeginChangeCheck();
{
if (this.ValueEntry.BaseValueType.IsInterface)
{
newInstance = SirenixEditorFields.PolymorphicObjectField(controlRect,
label,
this.realWrapperInstance,
this.ValueEntry.BaseValueType,
this.allowSceneViewObjects);
}
else
{
newInstance = SirenixEditorFields.UnityObjectField(
controlRect,
label,
this.realWrapperInstance,
this.ValueEntry.BaseValueType,
this.allowSceneViewObjects) as Component;
}
}
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.WeakSmartValue = newInstance;
}
if (GUI.Button(btnRect, " ", EditorStyles.miniButton))
{
var popup = new FixBrokenUnityObjectWrapperPopup(this.ValueEntry);
OdinEditorWindow.InspectObjectInDropDown(popup, 300);
}
if (Event.current.type == EventType.Repaint)
{
GUI.DrawTexture(btnRect, EditorIcons.ConsoleWarnicon, ScaleMode.ScaleToFit);
}
}
private static bool ComponentIsBroken(T component, ref T realInstance)
{
var uObj = component;
var oObj = (object)uObj;
if (oObj != null && uObj == null)
{
var instanceId = uObj.GetInstanceID();
if (AssetDatabase.Contains(instanceId))
{
var path = AssetDatabase.GetAssetPath(instanceId);
var realWrapper = AssetDatabase.LoadAllAssetsAtPath(path).FirstOrDefault(n => n.GetInstanceID() == instanceId) as T;
if (realWrapper)
{
realInstance = realWrapper;
return true;
}
}
}
return false;
}
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
if (EditorPrefs.HasKey(AUTO_FIX_PREFS_KEY))
{
genericMenu.AddItem(new GUIContent("Disable auto-fix of broken prefab instance references"), false, (x) =>
{
EditorPrefs.DeleteKey(AUTO_FIX_PREFS_KEY);
autoFix = false;
}, null);
}
}
[TypeInfoBox("This asset reference is temporarily broken until the next reload, because of an error in Unity where the C# wrapper object of a prefab asset is destroyed when changes are made to that prefab asset. This error has been reported to Unity.\n\nMeanwhile, Odin can fix this for you by getting a new, valid wrapper object from the asset database and replacing the broken wrapper instance with the new one.")]
private class FixBrokenUnityObjectWrapperPopup
{
private IPropertyValueEntry<T> valueEntry;
public FixBrokenUnityObjectWrapperPopup(IPropertyValueEntry<T> valueEntry)
{
this.valueEntry = valueEntry;
}
[HorizontalGroup, Button(ButtonSizes.Large)]
public void FixItThisTime()
{
for (int i = 0; i < this.valueEntry.ValueCount; i++)
{
var localI = i;
T fixedComponent = null;
if (ComponentIsBroken(this.valueEntry.Values[i], ref fixedComponent) && fixedComponent)
{
this.valueEntry.Property.Tree.DelayActionUntilRepaint(() =>
{
(this.valueEntry as IValueEntryActualValueSetter<T>).SetActualValue(localI, fixedComponent);
});
}
}
if (GUIHelper.CurrentWindow)
{
EditorApplication.delayCall += GUIHelper.CurrentWindow.Close;
}
}
[HorizontalGroup, Button(ButtonSizes.Large)]
public void FixItAlways()
{
EditorPrefs.SetBool(AUTO_FIX_PREFS_KEY, true);
autoFix = true;
if (GUIHelper.CurrentWindow)
{
EditorApplication.delayCall += GUIHelper.CurrentWindow.Close;
}
}
}
}
}
#endif

View File

@ -0,0 +1,15 @@
{
"name": "Sirenix.OdinInspector.CompatibilityLayer.Editor",
"references": [
"Sirenix.OdinInspector.CompatibilityLayer"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"autoReferenced": true,
"overrideReferences": false,
"precompiledReferences": [],
"defineConstraints": []
}

View File

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------
// <copyright file="SyncListDrawer.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && !UNITY_2019_1_OR_NEWER
#pragma warning disable 0618
namespace Sirenix.OdinInspector.Editor.Drawers
{
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
/// <summary>
/// SyncList property drawer.
/// </summary>
[DrawerPriority(0, 0, 2)]
public class SyncListDrawer<TList, TElement> : OdinValueDrawer<TList> where TList : SyncList<TElement>
{
private LocalPersistentContext<bool> visible;
protected override void Initialize()
{
this.visible = this.Property.Context.GetPersistent(this, "expanded", GeneralDrawerConfig.Instance.OpenListsByDefault);
}
/// <summary>
/// Draws the property.
/// </summary>
protected override void DrawPropertyLayout(GUIContent label)
{
var entry = this.ValueEntry;
var property = entry.Property;
int minCount = int.MaxValue;
int maxCount = 0;
for (int i = 0; i < entry.ValueCount; i++)
{
if (entry.Values[i].Count > maxCount)
{
maxCount = entry.Values[i].Count;
}
if (entry.Values[i].Count < minCount)
{
minCount = entry.Values[i].Count;
}
}
SirenixEditorGUI.BeginHorizontalToolbar();
this.visible.Value = SirenixEditorGUI.Foldout(this.visible.Value, GUIHelper.TempContent("SyncList " + label.text + " [" + typeof(TList).Name + "]"));
EditorGUILayout.LabelField(GUIHelper.TempContent(minCount == maxCount ? (minCount == 0 ? "Empty" : minCount + " items") : minCount + " (" + maxCount + ") items"), SirenixGUIStyles.RightAlignedGreyMiniLabel);
SirenixEditorGUI.EndHorizontalToolbar();
if (SirenixEditorGUI.BeginFadeGroup(this.visible, this.visible.Value))
{
GUIHelper.PushGUIEnabled(false);
SirenixEditorGUI.BeginVerticalList();
{
var elementLabel = new GUIContent();
for (int i = 0; i < maxCount; i++)
{
SirenixEditorGUI.BeginListItem();
elementLabel.text = "Item " + i;
if (i < minCount)
{
property.Children[i].Draw(elementLabel);
}
else
{
EditorGUILayout.LabelField(elementLabel, SirenixEditorGUI.MixedValueDashChar);
}
SirenixEditorGUI.EndListItem();
}
}
SirenixEditorGUI.EndVerticalList();
GUIHelper.PopGUIEnabled();
}
SirenixEditorGUI.EndFadeGroup();
}
}
}
#endif // UNITY_EDITOR && !UNITY_2019_1_OR_NEWER

View File

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------
// <copyright file="SyncVarAttributeDrawer.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && !UNITY_2019_1_OR_NEWER
#pragma warning disable 0618
namespace Sirenix.OdinInspector.Editor.Drawers
{
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
/// <summary>
/// SyncVar attribute drawer.
/// </summary>
public class SyncVarAttributeDrawer : OdinAttributeDrawer<SyncVarAttribute>
{
/// <summary>
/// Draws the property.
/// </summary>
protected override void DrawPropertyLayout(GUIContent label)
{
GUILayout.BeginHorizontal();
{
GUILayout.BeginVertical();
{
this.CallNextDrawer(label);
}
GUILayout.EndVertical();
GUILayout.Label("SyncVar", EditorStyles.miniLabel, GUILayoutOptions.Width(52f));
}
GUILayout.EndHorizontal();
}
}
}
#endif // UNITY_EDITOR && !UNITY_2019_1_OR_NEWER

View File

@ -0,0 +1,73 @@
//-----------------------------------------------------------------------
// <copyright file="Vector2IntMinMaxAttributeDrawer.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_2017_2_OR_NEWER
namespace Sirenix.OdinInspector.Editor.Drawers
{
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.ValueResolvers;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using System.Reflection;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Draws Vector2Int properties marked with <see cref="MinMaxSliderAttribute"/>.
/// </summary>
public class Vector2IntMinMaxAttributeDrawer : OdinAttributeDrawer<MinMaxSliderAttribute, Vector2Int>
{
private ValueResolver<float> minGetter;
private ValueResolver<float> maxGetter;
private ValueResolver<Vector2Int> vector2IntMinMaxGetter;
/// <summary>
/// Initializes the drawer by resolving any optional references to members for min/max value.
/// </summary>
protected override void Initialize()
{
// Min member reference.
this.minGetter = ValueResolver.Get<float>(this.Property, this.Attribute.MinValueGetter, this.Attribute.MinValue);
this.maxGetter = ValueResolver.Get<float>(this.Property, this.Attribute.MaxValueGetter, this.Attribute.MaxValue);
// Min max member reference.
if (this.Attribute.MinMaxValueGetter != null)
{
this.vector2IntMinMaxGetter = ValueResolver.Get<Vector2Int>(this.Property, this.Attribute.MinMaxValueGetter);
}
}
/// <summary>
/// Draws the property.
/// </summary>
protected override void DrawPropertyLayout(GUIContent label)
{
ValueResolver.DrawErrors(this.minGetter, this.maxGetter, this.vector2IntMinMaxGetter);
// Get the range of the slider from the attribute or from member references.
Vector2 range;
if (this.vector2IntMinMaxGetter != null && !this.vector2IntMinMaxGetter.HasError)
{
range = (Vector2)this.vector2IntMinMaxGetter.GetValue();
}
else
{
range.x = this.minGetter.GetValue();
range.y = this.maxGetter.GetValue();
}
EditorGUI.BeginChangeCheck();
Vector2 value = SirenixEditorFields.MinMaxSlider(label, (Vector2)this.ValueEntry.SmartValue, range, this.Attribute.ShowFields);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = new Vector2Int((int)value.x, (int)value.y);
}
}
}
}
#endif // UNITY_EDITOR && UNITY_2017_2_OR_NEWER

View File

@ -0,0 +1,141 @@
//-----------------------------------------------------------------------
// <copyright file="VectorIntDrawers.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_2017_2_OR_NEWER
namespace Sirenix.OdinInspector.Editor.Drawers
{
using Utilities.Editor;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Vector2Int proprety drawer.
/// </summary>
public sealed class Vector2IntDrawer : OdinValueDrawer<Vector2Int>, IDefinesGenericMenuItems
{
/// <summary>
/// Draws the property.
/// </summary>
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
var contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
EditorGUI.BeginChangeCheck();
var val = SirenixEditorFields.VectorPrefixSlideRect(labelRect, (Vector2)this.ValueEntry.SmartValue);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = new Vector2Int((int)val.x, (int)val.y);
}
var showLabels = SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
Vector2Int value = (Vector2Int)property.ValueEntry.WeakSmartValue;
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0)"), value == Vector2Int.zero, () => SetVector(property, Vector2Int.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1)"), value == Vector2Int.one, () => SetVector(property, Vector2Int.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0)"), value == Vector2Int.right, () => SetVector(property, Vector2Int.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0)"), value == Vector2Int.left, () => SetVector(property, Vector2Int.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1)"), value == Vector2Int.up, () => SetVector(property, Vector2Int.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1)"), value == Vector2Int.down, () => SetVector(property, Vector2Int.down));
}
private void SetVector(InspectorProperty property, Vector2Int value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
for (int i = 0; i < property.ValueEntry.ValueCount; i++)
{
property.ValueEntry.WeakValues[i] = value;
}
});
}
}
/// <summary>
/// Vector3Int property drawer.
/// </summary>
public sealed class Vector3IntDrawer : OdinValueDrawer<Vector3Int>, IDefinesGenericMenuItems
{
/// <summary>
/// Draws the property.
/// </summary>
protected override void DrawPropertyLayout(GUIContent label)
{
Rect labelRect;
var contentRect = SirenixEditorGUI.BeginHorizontalPropertyLayout(label, out labelRect);
{
EditorGUI.BeginChangeCheck();
var val = SirenixEditorFields.VectorPrefixSlideRect(labelRect, (Vector3)this.ValueEntry.SmartValue);
if (EditorGUI.EndChangeCheck())
{
this.ValueEntry.SmartValue = new Vector3Int((int)val.x, (int)val.y, (int)val.z);
}
var showLabels = SirenixEditorFields.ResponsiveVectorComponentFields && contentRect.width >= 185;
GUIHelper.PushLabelWidth(SirenixEditorFields.SingleLetterStructLabelWidth);
this.ValueEntry.Property.Children[0].Draw(showLabels ? GUIHelper.TempContent("X") : null);
this.ValueEntry.Property.Children[1].Draw(showLabels ? GUIHelper.TempContent("Y") : null);
this.ValueEntry.Property.Children[2].Draw(showLabels ? GUIHelper.TempContent("Z") : null);
GUIHelper.PopLabelWidth();
}
SirenixEditorGUI.EndHorizontalPropertyLayout();
}
/// <summary>
/// Populates the generic menu for the property.
/// </summary>
public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu)
{
Vector3Int value = (Vector3Int)property.ValueEntry.WeakSmartValue;
if (genericMenu.GetItemCount() > 0)
{
genericMenu.AddSeparator("");
}
genericMenu.AddItem(new GUIContent("Zero", "Set the vector to (0, 0, 0)"), value == Vector3Int.zero, () => SetVector(property, Vector3Int.zero));
genericMenu.AddItem(new GUIContent("One", "Set the vector to (1, 1, 1)"), value == Vector3Int.one, () => SetVector(property, Vector3Int.one));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent("Right", "Set the vector to (1, 0, 0)"), value == Vector3Int.right, () => SetVector(property, Vector3Int.right));
genericMenu.AddItem(new GUIContent("Left", "Set the vector to (-1, 0, 0)"), value == Vector3Int.left, () => SetVector(property, Vector3Int.left));
genericMenu.AddItem(new GUIContent("Up", "Set the vector to (0, 1, 0)"), value == Vector3Int.up, () => SetVector(property, Vector3Int.up));
genericMenu.AddItem(new GUIContent("Down", "Set the vector to (0, -1, 0)"), value == Vector3Int.down, () => SetVector(property, Vector3Int.down));
genericMenu.AddItem(new GUIContent("Forward", "Set the vector property to (0, 0, 1)"), value == new Vector3Int(0, 0, 1), () => SetVector(property, new Vector3Int(0, 0, 1)));
genericMenu.AddItem(new GUIContent("Back", "Set the vector property to (0, 0, -1)"), value == new Vector3Int(0, 0, -1), () => SetVector(property, new Vector3Int(0, 0, -1)));
}
private void SetVector(InspectorProperty property, Vector3Int value)
{
property.Tree.DelayActionUntilRepaint(() =>
{
property.ValueEntry.WeakSmartValue = value;
});
}
}
}
#endif // UNITY_EDITOR && UNITY_2017_2_OR_NEWER

View File

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------
// <copyright file="VectorIntPropertyResolvers.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR && UNITY_2017_2_OR_NEWER
namespace Sirenix.OdinInspector.Editor.Drawers
{
using UnityEngine;
public sealed class Vector2IntResolver : BaseMemberPropertyResolver<Vector2Int>
{
protected override InspectorPropertyInfo[] GetPropertyInfos()
{
return new InspectorPropertyInfo[]
{
InspectorPropertyInfo.CreateValue("x", 0, this.Property.ValueEntry.SerializationBackend,
new GetterSetter<Vector2Int, int>(
getter: (ref Vector2Int vec) => vec.x,
setter: (ref Vector2Int vec, int value) => vec.x = value)),
InspectorPropertyInfo.CreateValue("y", 0, this.Property.ValueEntry.SerializationBackend,
new GetterSetter<Vector2Int, int>(
getter: (ref Vector2Int vec) => vec.y,
setter: (ref Vector2Int vec, int value) => vec.y = value)),
};
}
}
public sealed class Vector3IntResolver : BaseMemberPropertyResolver<Vector3Int>
{
protected override InspectorPropertyInfo[] GetPropertyInfos()
{
return new InspectorPropertyInfo[]
{
InspectorPropertyInfo.CreateValue("x", 0, this.Property.ValueEntry.SerializationBackend,
new GetterSetter<Vector3Int, int>(
getter: (ref Vector3Int vec) => vec.x,
setter: (ref Vector3Int vec, int value) => vec.x = value)),
InspectorPropertyInfo.CreateValue("y", 0, this.Property.ValueEntry.SerializationBackend,
new GetterSetter<Vector3Int, int>(
getter: (ref Vector3Int vec) => vec.y,
setter: (ref Vector3Int vec, int value) => vec.y = value)),
InspectorPropertyInfo.CreateValue("z", 0, this.Property.ValueEntry.SerializationBackend,
new GetterSetter<Vector3Int, int>(
getter: (ref Vector3Int vec) => vec.z,
setter: (ref Vector3Int vec, int value) => vec.z = value)),
};
}
}
}
#endif // UNITY_EDITOR && UNITY_2017_2_OR_NEWER

View File

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------
// <copyright file="SerializedNetworkBehaviour.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if !UNITY_2019_1_OR_NEWER
#pragma warning disable 0618
namespace Sirenix.OdinInspector
{
using Sirenix.Serialization;
using UnityEngine;
using UnityEngine.Networking;
/// <summary>
/// A Unity NetworkBehaviour which is serialized by the Sirenix serialization system.
/// Please note that Odin's custom serialization only works for non-synced variables - [SyncVar] and SyncLists still have the same limitations.
/// </summary>
[ShowOdinSerializedPropertiesInInspector]
public abstract class SerializedNetworkBehaviour : NetworkBehaviour, ISerializationCallbackReceiver, ISupportsPrefabSerialization
{
[SerializeField, HideInInspector]
private SerializationData serializationData;
SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
this.OnAfterDeserialize();
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
this.OnBeforeSerialize();
}
/// <summary>
/// Invoked after deserialization has taken place.
/// </summary>
protected virtual void OnAfterDeserialize()
{
}
/// <summary>
/// Invoked before serialization has taken place.
/// </summary>
protected virtual void OnBeforeSerialize()
{
}
#if UNITY_EDITOR
[HideInTables]
[OnInspectorGUI, PropertyOrder(int.MinValue)]
private void InternalOnInspectorGUI()
{
EditorOnlyModeConfigUtility.InternalOnInspectorGUI(this);
}
#endif
}
}
#endif // UNITY_2019_1_OR_NEWER

View File

@ -0,0 +1,11 @@
{
"name": "Sirenix.OdinInspector.CompatibilityLayer",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"autoReferenced": true,
"overrideReferences": false,
"precompiledReferences": [],
"defineConstraints": []
}

View File

@ -0,0 +1,80 @@
#if UNITY_2017_2_OR_NEWER
//-----------------------------------------------------------------------
// <copyright file="VectorIntFormatters.cs" company="Sirenix IVS">
// Copyright (c) Sirenix IVS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
[assembly: Sirenix.Serialization.RegisterFormatter(typeof(Sirenix.Serialization.Vector2IntFormatter))]
[assembly: Sirenix.Serialization.RegisterFormatter(typeof(Sirenix.Serialization.Vector3IntFormatter))]
namespace Sirenix.Serialization
{
using UnityEngine;
/// <summary>
/// Custom formatter for the <see cref="Vector2Int"/> type.
/// </summary>
/// <seealso cref="Sirenix.Serialization.MinimalBaseFormatter{UnityEngine.Vector2Int}" />
public class Vector2IntFormatter : MinimalBaseFormatter<Vector2Int>
{
private static readonly Serializer<int> Serializer = Serialization.Serializer.Get<int>();
/// <summary>
/// Reads into the specified value using the specified reader.
/// </summary>
/// <param name="value">The value to read into.</param>
/// <param name="reader">The reader to use.</param>
protected override void Read(ref Vector2Int value, IDataReader reader)
{
value.x = Vector2IntFormatter.Serializer.ReadValue(reader);
value.y = Vector2IntFormatter.Serializer.ReadValue(reader);
}
/// <summary>
/// Writes from the specified value using the specified writer.
/// </summary>
/// <param name="value">The value to write from.</param>
/// <param name="writer">The writer to use.</param>
protected override void Write(ref Vector2Int value, IDataWriter writer)
{
Vector2IntFormatter.Serializer.WriteValue(value.x, writer);
Vector2IntFormatter.Serializer.WriteValue(value.y, writer);
}
}
/// <summary>
/// Custom formatter for the <see cref="Vector3Int"/> type.
/// </summary>
/// <seealso cref="Sirenix.Serialization.MinimalBaseFormatter{UnityEngine.Vector3Int}" />
public class Vector3IntFormatter : MinimalBaseFormatter<Vector3Int>
{
private static readonly Serializer<int> Serializer = Serialization.Serializer.Get<int>();
/// <summary>
/// Reads into the specified value using the specified reader.
/// </summary>
/// <param name="value">The value to read into.</param>
/// <param name="reader">The reader to use.</param>
protected override void Read(ref Vector3Int value, IDataReader reader)
{
value.x = Vector3IntFormatter.Serializer.ReadValue(reader);
value.y = Vector3IntFormatter.Serializer.ReadValue(reader);
value.z = Vector3IntFormatter.Serializer.ReadValue(reader);
}
/// <summary>
/// Writes from the specified value using the specified writer.
/// </summary>
/// <param name="value">The value to write from.</param>
/// <param name="writer">The writer to use.</param>
protected override void Write(ref Vector3Int value, IDataWriter writer)
{
Vector3IntFormatter.Serializer.WriteValue(value.x, writer);
Vector3IntFormatter.Serializer.WriteValue(value.y, writer);
Vector3IntFormatter.Serializer.WriteValue(value.z, writer);
}
}
}
#endif

View File

@ -0,0 +1,35 @@
------------------------------------ Getting Started ------------------------------------
Open up the Getting Started guide from "Tools > Odin Inspector > Getting Started."
------------------------------------- Helpful Links -------------------------------------
Tutorials: https://odininspector.com/tutorials
API Documentaion: https://odininspector.com/documentation
Roadmap: https://odininspector.com/roadmap
Release Notes: https://odininspector.com/patch-notes
Issue Tracker: https://bitbucket.org/sirenix/odin-inspector
--------------------------------- Community and Support ---------------------------------
If you have any issues, suggestions or want advice, then you're more than welcome
to join us on Discord, or reach out to us by any other means.
Support: https://odininspector.com/support
Community Addons: https://odininspector.com/community-tools
Discord: https://discord.gg/AgDmStu
-------------------------------------- Thank you! ---------------------------------------
We really hope you like using Odin. Be sure to leave a review on the Asset Store,
that helps us out a lot!
Leave a review: https://assetstore.unity.com/packages/tools/utilities/odin-inspector-and-serializer-89041
Odin Inspector is published and developed by Sirenix.
Sirenix: Https://sirenix.net

View File

@ -2,19 +2,62 @@ Unity Utility
Contains a growing list of utility used in prototypes and game development
AudioEvent (ScriptableObject)
SimpleAudioEvent (ScriptableObject)
AudioEventInspector (CustomInspector)
ObjectPool
GenericPool
RangedFloat (CustomTypes)
MinMaxRangeAttribute (Attributes)
RangedFloatDrawer (PropertyDrawer)
UI
components
Base
AudioEvent
AudioEvent
SimpleAudioEvent
---
Canvases
Components
Button
Image
Slider
Text
Toggle
UIBase
UISelectableBase
Menu
RebindActionUI
RebindReferences
RebindUI
---
Common
Settings
Singleton
Timer
TimerWrapper
---
Editor
Custom Inspector
AudioEventInspector
SaveManagerInspector
Property drawers
RangedFloatDrawer
SceneAutoLoader
---
Plugins
Odin inspector
---
RangedFloat
MinMaxRangeAttribute
RangedFloat
---
SaveSystem
GameData template
ISaveData
SaveManager
---
Utilities
Extensions
Collection
GameObject
InputAction
String
Transform
Vector
BindingsIcons
DontDestroyOnLoad
Helpers
SerializableDictionary

View File

@ -1,21 +0,0 @@
using AudioEvent;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace UI
{
public class ButtonUIComponent : UIComponentBase
{
[SerializeField] private Button _button;
[SerializeField] private SimpleAudioEvent _clickSound;
[SerializeField] private AudioSource _audioSource;
private void Awake() => _button.onClick.AddListener(() => _clickSound.Play(_audioSource));
public void OnClick(UnityAction callback) => _button.onClick.AddListener(callback);
private void OnDestroy() => _button.onClick.RemoveAllListeners();
}
}

View File

@ -1,25 +0,0 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
namespace UI
{
public class SliderUIComponent : UIComponentBase
{
[SerializeField] private Slider _slider;
public void SetValue(float value, float max)
{
_slider.maxValue = max;
_slider.value = value;
}
public void SetValue(float value) => _slider.value = value;
public float GetValue() => _slider.value;
public void OnValueChanged(UnityAction<float> callBack) => _slider.onValueChanged.AddListener(callBack);
private void OnDestroy() => _slider.onValueChanged?.RemoveAllListeners();
}
}

View File

@ -1,22 +0,0 @@
using TMPro;
using UnityEngine;
namespace UI
{
public class TextUIComponent : UIComponentBase
{
[SerializeField] private TMP_Text _text;
private string _initialText;
private void Awake() => _initialText = _text.text;
public void SetText(string newText) => _text.text = string.IsNullOrEmpty(newText) ? string.Empty : newText;
public void ResetText() => _text.text = _initialText;
public void AddText(string textToAdd) => _text.text += textToAdd;
public void HideText() => _text.text = string.Empty;
}
}

View File

@ -1,28 +0,0 @@
using UnityEngine;
namespace UI
{
public class UIComponentBase : MonoBehaviour
{
public void Show()
{
gameObject.Show();
}
public void Hide()
{
gameObject.Hide();
}
public void ToggleDisplay()
{
gameObject.SetActive(!gameObject.activeInHierarchy);
}
public void SetActive(bool active)
{
gameObject.SetActive(active);
}
}
}

View File

@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.Linq;
using Scriptables;
using UnityEngine;
using UnityEngine.InputSystem;
using static UnityEngine.InputSystem.InputSystem;
namespace Utilities
{
public static class BindingsIconsUtil
{
private static GamepadIcons xbox;
private static GamepadIcons ps4;
private static MouseKeyboardIcons mouseKeyboard;
private static void Init()
{
xbox = Resources.Load<GamepadIcons>("InputSystem/GamePadIcons_Xbox");
ps4 = Resources.Load<GamepadIcons>("InputSystem/GamePadIcons_PS");
mouseKeyboard = Resources.Load<MouseKeyboardIcons>("InputSystem/MouseKeyboardIcons");
}
public static Sprite GetSprite(string deviceLayoutName, string mainControlPath)
{
if (xbox == null) Init();
if (string.IsNullOrEmpty(deviceLayoutName) || string.IsNullOrEmpty(mainControlPath))
return null;
Sprite icon = default;
if (IsFirstLayoutBasedOnSecond(deviceLayoutName, "DualShockGamepad"))
{
icon = ps4.GetSprite(mainControlPath);
}
else if (IsFirstLayoutBasedOnSecond(deviceLayoutName, "Gamepad"))
{
icon = xbox.GetSprite(mainControlPath);
}
else if (IsFirstLayoutBasedOnSecond(deviceLayoutName, "Keyboard") || IsFirstLayoutBasedOnSecond(deviceLayoutName, "Mouse"))
{
icon = mouseKeyboard.GetSprite(mainControlPath);
}
return icon;
}
public static Sprite GetSprite(InputAction inputAction, string deviceName)
{
if (inputAction == null || string.IsNullOrEmpty(deviceName))
return null;
var mainBindings = GetRelevantMainBindings(inputAction, deviceName, true);
if (!mainBindings.Any())
return null;
var mainBinding = mainBindings.First();
inputAction.GetBindingDisplayString(mainBinding, out var deviceLayoutName, out var mainControlPath);
return GetSprite(deviceLayoutName, mainControlPath);
}
// So we don't create an instance each time the next method is called
private static readonly List<int> RelevantMainBindings = new List<int>();
public static List<int> GetRelevantMainBindings(InputAction inputAction, string deviceName, bool checkUnrebindableBindings = false)
{
RelevantMainBindings.Clear();
if (inputAction.bindings.Count >= 4)
{
if (inputAction.bindings[0].isComposite)
{
if (deviceName == "Gamepad")
{
RelevantMainBindings.Add(inputAction.bindings.Count - 2);
}
else
{
for (int i = 1; i < inputAction.bindings.Count - 2; i += 2)
{
RelevantMainBindings.Add(i);
}
}
}
else
{
RelevantMainBindings.Add(deviceName == "Gamepad" ? 2 : 0);
}
}
else if (inputAction.bindings.Count >= 2 && checkUnrebindableBindings)
{
RelevantMainBindings.Add(deviceName == "Gamepad" ? 1 : 0);
}
return RelevantMainBindings.ToList();
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Utilities
{
public static class DontDestroyOnLoadUtils
{
private static readonly List<GameObject> ManagedObjects = new List<GameObject>();
public static void Add(GameObject gameObject)
{
ManagedObjects.Add(gameObject);
Object.DontDestroyOnLoad(gameObject);
}
public static void DestroyAll(Func<GameObject, bool> filter = null)
{
foreach (GameObject managedObject in ManagedObjects.ToList())
{
if (filter != null && !filter.Invoke(managedObject))
continue;
Object.DestroyImmediate(managedObject);
}
ManagedObjects.Clear();
}
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Utilities.Extensions
{
public static class CollectionExtensions
{
/// <summary>
/// Returns a random element from the enumerable.
/// </summary>
/// <param name="thisIEnumerable">The enumerable to return a random element from.</param>
/// <typeparam name="T">The type of the enumerable.</typeparam>
/// <returns>A random element from the list, or the default type value if the list is empty.</returns>
public static T RandomElement<T>(this IEnumerable<T> thisIEnumerable)
{
T[] thisArray = thisIEnumerable as T[] ?? thisIEnumerable.ToArray();
if (!thisArray.Any())
return default;
int randomIndex = Random.Range(0, thisArray.Length);
return thisArray[randomIndex];
}
}
}

View File

@ -0,0 +1,125 @@
#if FUSION_WEAVER
using Fusion;
#endif
using UnityEngine;
namespace Utilities.Extensions
{
public static class GameObjectExtensions
{
public static int GetNumberOfComponents(this GameObject gameObject) =>
gameObject.GetComponentsInChildren<Component>().Length - 1;
public static void Show(this GameObject gameObject) => gameObject.SetActive(true);
public static void Hide(this GameObject gameObject) => gameObject.SetActive(false);
public static bool IsVisible(this GameObject gameObject) => gameObject.activeSelf;
/// <summary>
/// First, look in the parent if it has the component, then the current gameObject
/// This is done because entities have their collider a level lower than their parent game object with contains
/// most of their scripts.
/// </summary>
public static T GetComponentInEntity<T>(this GameObject gameObject) where T : Component
{
T component = gameObject.GetComponentInParent<T>();
if (!component)
{
component = gameObject.GetComponent<T>();
}
if (!component)
{
component = gameObject.GetComponentInChildren<T>();
}
if (!component)
{
component = gameObject.transform.parent.GetComponentInChildren<T>();
}
return component;
}
public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
{
if (!gameObject.TryGetComponent(out T component))
{
component = gameObject.AddComponent<T>();
}
return component;
}
public static T GetComponentInParents<T>(this GameObject gameObject) where T : Component
{
T parentComponent = null;
GameObject currentParent = gameObject;
GameObject root = gameObject.GetRoot();
while (currentParent != root && currentParent.GetComponent<T>() == null && parentComponent == null)
{
currentParent = currentParent.GetParent();
parentComponent = currentParent.GetComponent<T>();
}
return parentComponent;
}
#if FUSION_WEAVER
public static T GetComponentInEntity<T>(this NetworkObject gameObject) where T : Component
{
return gameObject.gameObject.GetComponentInEntity<T>();
}
#endif
public static GameObject GetParent(this GameObject gameObject)
{
var parent = gameObject.transform.parent;
return parent ? parent.gameObject : gameObject;
}
public static GameObject GetRoot(this GameObject gameObject)
{
var root = gameObject.transform.root;
return root ? root.gameObject : gameObject;
}
public static bool CompareEntities(this GameObject gameObject, GameObject other)
{
return other == gameObject ||
other.GetParent() == gameObject ||
other.GetParent() == gameObject.GetParent() ||
other == gameObject.GetParent();
}
public static bool HasTag(this GameObject gameObject)
{
return !gameObject.CompareTag("Untagged");
}
public static bool AssignTagIfDoesNotHaveIt(this GameObject gameObject, string tag)
{
if (!gameObject.HasTag())
gameObject.tag = tag;
return gameObject.CompareTag(tag);
}
public static bool HasLayer(this GameObject gameObject)
{
return gameObject.layer != 0;
}
public static bool AssignLayerIfDoesNotHaveIt(this GameObject gameObject, int layer)
{
if (!gameObject.HasLayer())
gameObject.layer = layer;
return gameObject.layer == layer;
}
}
}

View File

@ -0,0 +1,12 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace Utilities.Extensions
{
public static class InputActionExtensions
{
public static bool ReadBool(this InputAction action) => action.ReadValue<float>() != 0;
public static float ReadFloat(this InputAction action) => action.ReadValue<float>();
public static Vector2 ReadV2(this InputAction action) => action.ReadValue<Vector2>();
}
}

View File

@ -0,0 +1,15 @@
namespace Utilities.Extensions
{
public static class StringExtensions
{
public static string Capitalize(this string input) =>
input switch
{
null => string.Empty,
"" => string.Empty,
_ => input[0].ToString().ToUpper() + input.Substring(1)
};
public static bool IsNullOrEmpty(this string input) => string.IsNullOrEmpty(input);
}
}

View File

@ -0,0 +1,15 @@
using UnityEngine;
namespace Utilities.Extensions
{
public static class TransformExtensions
{
public static void DestroyChildren(this Transform t)
{
foreach (Transform child in t)
{
Object.Destroy(child.gameObject);
}
}
}
}

View File

@ -0,0 +1,58 @@
using UnityEngine;
namespace Utilities.Extensions
{
public static class VectorExtensions
{
/// <summary>
/// Vector 2 from vector 3's x and y components
/// </summary>
public static Vector2 ToV2(this Vector3 vector) => vector.XY();
/// <summary>
/// Vector 2 from vector 3's x and z components
/// </summary>
public static Vector2 FlatV3ToV2(this Vector3 vector) => vector.XZ();
/// <summary>
/// Vector 3 removing the y component
/// </summary>
public static Vector3 Flat(this Vector3 vector) => new Vector3(vector.x, 0, vector.z);
/// <summary>
/// Vector 3 from vector 2's x becoming x and y becoming z
/// </summary>
public static Vector3 V2ToFlatV3(this Vector2 vector) => new Vector3(vector.x, 0, vector.y);
public static Vector2 XY(this Vector3 vector) => new Vector2(vector.x, vector.y);
public static Vector2 XZ(this Vector3 vector) => new Vector2(vector.x, vector.z);
public static Vector2 YX(this Vector3 vector) => new Vector2(vector.y, vector.x);
public static Vector2 YZ(this Vector3 vector) => new Vector2(vector.y, vector.z);
public static Vector2 ZX(this Vector3 vector) => new Vector2(vector.z, vector.x);
public static Vector2 ZY(this Vector3 vector) => new Vector2(vector.z, vector.y);
public static Vector3Int ToVector3Int(this Vector3 input) =>
new Vector3Int((int) input.x, (int) input.y, (int) input.z);
public static float SqrDistance(Vector3 a, Vector3 b)
{
float num1 = a.x - b.x;
float num2 = a.y - b.y;
float num3 = a.z - b.z;
return (float) (num1 * (double) num1 + num2 * (double) num2 + num3 * (double) num3);
}
public static float SqrDistanceWith(this Vector3 a, Vector3 b)
{
return SqrDistance(a, b);
}
// Inspired of reefwirrax's answer on https://answers.unity.com/questions/532297/rotate-a-vector-around-a-certain-point.html
public static Vector3 RotateAround(this Vector3 point, Vector3 pivot, Vector3 angles) {
var dir = point - pivot;
dir = Quaternion.Euler(angles) * dir;
point = dir + pivot;
return point;
}
}
}

18
Utilities/Helpers.cs Normal file
View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using UnityEngine;
namespace Utilities
{
public static class Helpers
{
private static readonly Dictionary<float, WaitForSeconds> WaitDictionary =
new Dictionary<float, WaitForSeconds>();
public static WaitForSeconds GetWait(float time)
{
if (WaitDictionary.TryGetValue(time, out var wait)) return wait;
WaitDictionary[time] = new WaitForSeconds(time);
return WaitDictionary[time];
}
}
}