ui system improved

This commit is contained in:
Hubert Mattusch 2023-05-14 01:04:34 +02:00
parent c0923cc884
commit 7aecbeb240
40 changed files with 519 additions and 119 deletions

54
KeyBasedFactory.cs Normal file
View File

@ -0,0 +1,54 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace NEG.Utils
{
public class KeyBasedFactory<T1, T2>
{
[PublicAPI]
protected Dictionary<T1, Type> data;
public KeyBasedFactory()
{
data = new Dictionary<T1, Type>();
}
public void FireRegistration()
{
ScanAssembly(typeof(T2).Assembly);
if(typeof(T2).Assembly.GetType().Assembly == typeof(T2).Assembly)
return;
ScanAssembly(typeof(T2).Assembly.GetType().Assembly);
}
private static void ScanAssembly(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var methodFields =
type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < methodFields.Length; i++)
{
if (Attribute.GetCustomAttribute(methodFields[i], typeof(FactoryRegistration)) != null)
{
methodFields[i].Invoke(null, Array.Empty<object>());
}
}
}
}
public void Register(T1 key, Type type) => data.Add(key, type);
public T2 CreateInstance(T1 key, params object[] args) => (T2)Activator.CreateInstance(data[key], args);
}
[AttributeUsage(AttributeTargets.Method)]
public class FactoryRegistration : Attribute
{
public FactoryRegistration() { }
}
}

3
KeyBasedFactory.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2e0d3df1ddd34209bfb7262b4e51abfe
timeCreated: 1683917310

View File

@ -1,7 +1,7 @@
{ {
"name": "NEG.UI", "name": "NEG.UI",
"rootNamespace": "", "rootNamespace": "",
"references": [], "references": ["GUID:3c4294719a93e3c4e831a9ff0c261e8a"],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,

View File

@ -2,6 +2,7 @@ using JetBrains.Annotations;
using NEG.UI.Area; using NEG.UI.Area;
using NEG.UI.Popup; using NEG.UI.Popup;
using NEG.UI.Window; using NEG.UI.Window;
using NEG.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;

View File

@ -1,72 +1,92 @@
using FMOD.Studio; using KBCore.Refs;
using FMODUnity; using NEG.UI.UnityUi.Buttons.Reaction;
using NEG.UI.UnityUi.Buttons.Settings;
using System; using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
namespace NEG.UI.UnityUi.Buttons namespace NEG.UI.UnityUi.Buttons
{ {
[RequireComponent(typeof(ButtonSerializeFields))] [DefaultExecutionOrder(-1)]
[RequireComponent(typeof(Button))]
public class BaseButton : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler public class BaseButton : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler
{ {
public delegate void SelectionHandler(bool isSilent);
/// <summary>
/// is silent
/// </summary>
public event SelectionHandler OnSelected;
public event SelectionHandler OnDeselected;
public event Action OnButtonPressed; public event Action OnButtonPressed;
public bool Interactable { get => serializeFields.Button.interactable; set => serializeFields.Button.interactable = value; } public bool Interactable { get => button.interactable; set => button.interactable = value; }
[SerializeField] public TMP_Text Text => text;
protected ButtonSerializeFields serializeFields;
private bool isHovered; private bool isHovered;
public virtual void OnSelect(BaseEventData eventData) [SerializeField, Self] private Button button;
{ [SerializeField, Child(Flag.Optional)] private TMP_Text text;
if (serializeFields.Text) [SerializeField, Child(Flag.Optional)] private Image icon;
serializeFields.Text.color = serializeFields.SelectedTextColor;
}
public void OnDeselect(BaseEventData eventData) [SerializeField] private ButtonSettings groupButtonSettings;
{ //[SerializeField, Self(Flag.Optional)] private ButtonSettingOverride overrideSettings;
if (serializeFields.Text)
serializeFields.Text.color = serializeFields.DeselectedTextColor;
}
public void OnPointerEnter(PointerEventData eventData) private Dictionary<string, ButtonElementBehaviour> behaviours = new Dictionary<string, ButtonElementBehaviour>();
{
isHovered = true; public virtual void OnSelect(BaseEventData eventData) => OnSelected?.Invoke(eventData is SilentEventData);
if (serializeFields.Text)
serializeFields.Text.color = serializeFields.SelectedTextColor; public void OnDeselect(BaseEventData eventData) => OnDeselected?.Invoke(eventData is SilentEventData);
}
public void OnPointerEnter(PointerEventData eventData) => EventSystem.current.SetSelectedGameObject(gameObject);
public void OnPointerExit(PointerEventData eventData) public void OnPointerExit(PointerEventData eventData)
{ {
isHovered = false; if(EventSystem.current.currentSelectedGameObject == gameObject)
if (serializeFields.Text) EventSystem.current.SetSelectedGameObject(null);
serializeFields.Text.color = serializeFields.DeselectedTextColor;
} }
public void SetText(string text) public void SetText(string txt)
{ {
if(serializeFields == null) if(text == null)
return; return;
if(serializeFields.Text == null) text.text = txt;
}
public void AddOrOverrideSetting(SettingData data)
{
if (behaviours.TryGetValue(data.Key, out var setting))
{
setting.ChangeData(data);
return; return;
serializeFields.Text.text = text; }
behaviours.Add("ChangeTextColor", MonoUiManager.Instance.BehavioursFactory.CreateInstance(data.Key, this, data));
}
public void RemoveSetting(string key)
{
if (!behaviours.TryGetValue(key, out var setting))
{
Debug.LogError($"Behaviour with key {key} was not found");
return;
}
setting.Dispose();
behaviours.Remove(key);
} }
protected virtual void Awake() protected virtual void Awake()
{ {
if(serializeFields == null) button.onClick.AddListener(OnClicked);
serializeFields = GetComponent<ButtonSerializeFields>(); groupButtonSettings.Apply(this);
serializeFields.Button.onClick.AddListener(OnClicked);
OnDeselect(null);
} }
private void OnValidate() private void Start() => OnDeselect(null);
{
if(serializeFields == null) private void OnValidate() => this.ValidateRefs();
serializeFields = GetComponent<ButtonSerializeFields>();
}
protected virtual void OnClicked() protected virtual void OnClicked()
{ {

View File

@ -1,3 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 3a250ef2d0c34e7396a16fc5eddbdb01 guid: 3a250ef2d0c34e7396a16fc5eddbdb01
timeCreated: 1670777213 MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -1
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,33 +0,0 @@
using FMODUnity;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace NEG.UI.UnityUi.Buttons
{
public class ButtonSerializeFields : MonoBehaviour
{
[field: SerializeField]
public Button Button { get; private set; }
[field: SerializeField]
public TMP_Text Text { get; private set; }
[field: SerializeField]
public Color SelectedTextColor { get; private set; }
[field: SerializeField]
public Color DeselectedTextColor { get; private set; }
[field: SerializeField]
public EventReference HoverEventRef { get; private set; }
[field: SerializeField]
public EventReference ClickEventRef { get; private set; }
private void OnValidate()
{
if(Button == null)
Button = GetComponent<Button>();
if (Text == null)
Text = GetComponentInChildren<TMP_Text>();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3f8c6cf4cf18463c86ec1165c61c79b2
timeCreated: 1670777232

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c431f6df68cb4163b9764b6a87abd31a
timeCreated: 1683915618

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 953c3353e3af44258625fe607ede632b
timeCreated: 1683915598

View File

@ -0,0 +1,22 @@
using NEG.UI.UnityUi.Buttons.Settings;
using System;
using UnityEngine.EventSystems;
namespace NEG.UI.UnityUi.Buttons.Reaction
{
public abstract class ButtonElementBehaviour : IDisposable
{
protected SettingData baseData;
protected readonly BaseButton button;
public ButtonElementBehaviour(BaseButton baseButton, SettingData settingData)
{
button = baseButton;
baseData = settingData;
}
public virtual void ChangeData(SettingData newData) => baseData = newData;
public abstract void Dispose();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5e3decad6424cb288eff3e6f7e0d28e
timeCreated: 1683919740

View File

@ -0,0 +1,46 @@
using NEG.UI.UnityUi.Buttons.Settings;
using NEG.Utils;
using UnityEngine;
using UnityEngine.EventSystems;
namespace NEG.UI.UnityUi.Buttons.Reaction
{
public class ChangeTextColorBehaviour : ButtonElementBehaviour
{
private ColorData data;
public ChangeTextColorBehaviour(BaseButton baseButton, ColorData data) : base(baseButton, data)
{
if (baseButton.Text == null)
{
Debug.LogError("Button without text was provided");
return;
}
baseButton.OnSelected += OnButtonSelected;
baseButton.OnDeselected += OnButtonDeselected;
ChangeData(data);
}
public override void ChangeData(SettingData newData)
{
base.ChangeData(newData);
Debug.Assert(newData is ColorData, "newData is not ColorData");
data = (ColorData)newData;
}
public override void Dispose()
{
button.OnSelected -= OnButtonSelected;
button.OnDeselected -= OnButtonDeselected;
}
[FactoryRegistration]
private static void RegisterInFactory() =>
MonoUiManager.Instance.BehavioursFactory.Register("ChangeTextColor", typeof(ChangeTextColorBehaviour));
private void OnButtonSelected(bool _) => button.Text.color = data.SelectedColor;
private void OnButtonDeselected(bool _) => button.Text.color = data.DeselectedColor;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2079225d6e34001ae85f74a0a418d68
timeCreated: 1683919878

View File

@ -0,0 +1,24 @@
using NEG.UI.UnityUi.Buttons.Settings;
using NEG.Utils;
#if FMOD
namespace NEG.UI.UnityUi.Buttons.Reaction
{
public class SimpleSoundBehaviour : ButtonElementBehaviour
{
public SimpleSoundBehaviour(BaseButton baseButton, FmodSoundData data) : base(baseButton, data)
{
//TODO: use silnet to not play sound
}
[FactoryRegistration]
private static void RegisterInFactory() =>
MonoUiManager.Instance.BehavioursFactory.Register("SimpleSound", typeof(SimpleSoundBehaviour));
public override void ChangeData(SettingData newData) => throw new System.NotImplementedException();
public override void Dispose() => throw new System.NotImplementedException();
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d75c0d86eeab47a1a6340f0b03b83de0
timeCreated: 1684002680

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d25aba738e174616bcab9bf2d52a3ed1
timeCreated: 1683398272

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace NEG.UI.UnityUi.Buttons.Settings
{
public class ButtonSettings : MonoBehaviour
{
[SerializeField] private List<SettingData> settingDatas = new();
public void Apply(BaseButton button)
{
foreach (var setting in settingDatas)
{
setting.Apply(button);
}
}
[ContextMenu("Refresh")]
public void Refresh()
{
settingDatas.Clear();
var components = GetComponents<SettingData>();
foreach (var data in components)
{
settingDatas.Add(data);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b5292008faae496ab9028ad1faa0a3ba
timeCreated: 1683398713

View File

@ -0,0 +1,10 @@
using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Settings
{
public class ColorData : SettingData
{
[field: SerializeField] public Color SelectedColor { get; private set; } = Color.black;
[field: SerializeField] public Color DeselectedColor { get; private set; } = Color.black;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cec5286cd31b4819981e244b1adb977e
timeCreated: 1684001181

View File

@ -0,0 +1,14 @@
#if FMOD
using FMODUnity;
using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Settings
{
public class FmodSoundData : SettingData
{
[field: SerializeField] public EventReference SelectedSound { get; private set; }
[field: SerializeField] public EventReference ClickedSound { get; private set; }
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8e6a7b10c07d40408ee9492c931e3ec9
timeCreated: 1684001979

View File

@ -0,0 +1,7 @@
namespace NEG.UI.UnityUi.Buttons.Settings
{
public class RemoveFeature : SettingData
{
public override void Apply(BaseButton button) => button.RemoveSetting(Key);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b44cdc2e0164ebdb8f6da348d653e77
timeCreated: 1684001264

View File

@ -0,0 +1,28 @@
using KBCore.Refs;
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace NEG.UI.UnityUi.Buttons.Settings
{
public abstract class SettingData : MonoBehaviour
{
[field: SerializeField] public string Key { get; private set; }
[SerializeField, Self(Flag.Optional)] private BaseButton attachedButton;
public virtual void Apply(BaseButton button) => button.AddOrOverrideSetting(this);
private void Awake()
{
if(attachedButton != null)
Apply(attachedButton);
}
private void OnValidate()
{
this.ValidateRefs();
if (attachedButton == null && TryGetComponent(out ButtonSettings settings))
settings.Refresh();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49b3fccda2b4485193eada1b3611ea40
timeCreated: 1684001079

View File

@ -0,0 +1,10 @@
using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Settings
{
public class SpriteData : SettingData
{
[field: SerializeField] public Sprite SelectedSprite { get; private set; }
[field: SerializeField] public Sprite DeselectedSprite { get; private set; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aeba07ffa68b4674bc7025ba4f328562
timeCreated: 1684001906

View File

@ -0,0 +1,82 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
namespace NEG.UI.UnityUi
{
public enum SelectionSource
{
Pointer,
Direction
}
public class UiInputModule { }
public class DefaultInputModule : UiInputModule
{
public SelectionSource CurrentSelectionSource { get; private set; }
public DefaultInputModule()
{
var defaultActions = new DefaultInputActions();
InputActionReference.Create(defaultActions.UI.Navigate).action.performed += (ctx) => OnSelectionChangeStarted();
defaultActions.Enable();
if (Gamepad.current != null)
SetDirectionInput();
else
SetPointerInput();
//var keyboardAction = new InputAction(binding: "/<Keyboard>/*");
//keyboardAction.performed += (context) => CurrentInputSource = EInputSource.Keyboard;
//keyboardAction.Enable();
var gamepadAction = new InputAction(binding: "/<Gamepad>/*");
gamepadAction.performed += (context) => OnSelectionChangeStarted();
gamepadAction.Enable();
var mouseAction = new InputAction(binding: "/<Mouse>/*");
mouseAction.performed += (context) =>
{
if(CurrentSelectionSource == SelectionSource.Pointer)
return;
SetPointerInput();
};
mouseAction.Enable();
}
private void OnSelectionChangeStarted()
{
if(CurrentSelectionSource == SelectionSource.Direction)
return;
SetDirectionInput();
}
private void SetDirectionInput()
{
CurrentSelectionSource = SelectionSource.Direction;
Cursor.visible = false;
}
private void SetPointerInput()
{
CurrentSelectionSource = SelectionSource.Pointer;
Cursor.visible = true;
if (EventSystem.current.currentInputModule != null)
{
var module = (InputSystemUIInputModule)EventSystem.current.currentInputModule;
var result = module.GetLastRaycastResult(0);
var data = new PointerEventData(EventSystem.current);
for (var current = result.gameObject.transform;
current != null;
current = current.parent)
{
ExecuteEvents.Execute(current.gameObject, data, ExecuteEvents.pointerEnterHandler);
}
//EventSystem.current.SetSelectedGameObject(result.gameObject);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f6cd55755dda4f71a4257fac75cb902d
timeCreated: 1684009354

View File

@ -1,43 +1,61 @@
using NEG.UI.Area; using NEG.UI.Area;
using NEG.UI.Popup; using NEG.UI.Popup;
using NEG.UI.UnityUi.Buttons.Reaction;
using NEG.UI.UnityUi.Buttons.Settings;
using NEG.UI.UnityUi.Popup; using NEG.UI.UnityUi.Popup;
using NEG.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Assertions; using UnityEngine.Assertions;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace NEG.UI.UnityUi namespace NEG.UI.UnityUi
{ {
/// <summary> /// <summary>
/// Implements ui using UnityUI and Unity Event System with New Input System. /// Implements ui using UnityUI and Unity Event System with New Input System.
/// <para>You have to provide prefabs with addresses:</para> /// <para>You have to provide prefabs within resources:</para>
/// <para> - NEG/UI/PopupCanvas - prefab with canvas to create popups (will be created on every scene)</para> /// <para> - UI/PopupCanvas - prefab with canvas to create popups (will be created on every scene)</para>
/// <para> - NEG/UI/DefaultPopupPrefab - prefab of default popup with 2 options (has to have <see cref="MonoDefaultPopup"/> component)</para> /// <para> - UI/DefaultPopupPrefab - prefab of default popup with 2 options (has to have <see cref="MonoDefaultPopup"/> component)</para>
/// </summary> /// </summary>
public class MonoUiManager : UiManager public class MonoUiManager : UiManager
{ {
//TODO: use default unity selection //TODO: use default unity selection
//TODO: window snaping to slots //TODO: window snaping to slots
public static new MonoUiManager Instance { get; private set; }
public KeyBasedFactory<string, ButtonElementBehaviour> BehavioursFactory { get; private set; }
private readonly MonoDefaultPopup defaultPopupPrefab; private readonly MonoDefaultPopup defaultPopupPrefab;
private readonly GameObject canvasPrefab; private readonly GameObject canvasPrefab;
public MonoUiManager(IArea startArea) : base(startArea) private UiInputModule inputModule;
public MonoUiManager(IArea startArea, Type inputModuleType) : base(startArea)
{ {
var prefabs = Instance = this;
Addressables.LoadAssetsAsync<GameObject>(new List<string>() { "NEG/UI/PopupCanvas", "NEG/UI/DefaultPopupPrefab" }, (_) => { }, Addressables.MergeMode.Union).WaitForCompletion();
Assert.AreEqual(prefabs.Count, 2, "No prefabs was provided. Please check MonoUiManager class documentation"); var popupCanvas = Resources.Load<GameObject>("UI/PopupCanvas");
Assert.IsNotNull(prefabs[0].GetComponent<Canvas>()); var defaultPopup = Resources.Load<GameObject>("UI/DefaultPopupPrefab");
Assert.IsNotNull(prefabs[1].GetComponent<MonoDefaultPopup>()); //Addressables.LoadAssetsAsync<GameObject>(new List<string>() { "NEG/UI/PopupCanvas", "NEG/UI/DefaultPopupPrefab" }, (_) => { }, Addressables.MergeMode.Union).WaitForCompletion();
canvasPrefab = prefabs[0]; Assert.IsNotNull(popupCanvas,"No canvas prefab was provided. Please check MonoUiManager class documentation");
defaultPopupPrefab = prefabs[1].GetComponent<MonoDefaultPopup>(); Assert.IsNotNull(defaultPopup,"No popup prefab was provided. Please check MonoUiManager class documentation");
Assert.IsNotNull(popupCanvas.GetComponent<Canvas>());
Assert.IsNotNull(defaultPopup.GetComponent<MonoDefaultPopup>());
canvasPrefab = popupCanvas;
defaultPopupPrefab = defaultPopup.GetComponent<MonoDefaultPopup>();
SpawnDefaultPopup(); SpawnDefaultPopup();
SceneManager.activeSceneChanged += (_, _) => SpawnDefaultPopup(); SceneManager.activeSceneChanged += (_, _) => SpawnDefaultPopup();
BehavioursFactory = new KeyBasedFactory<string, ButtonElementBehaviour>();
BehavioursFactory.FireRegistration();
inputModule = (UiInputModule)Activator.CreateInstance(inputModuleType);
} }
private void SpawnDefaultPopup() private void SpawnDefaultPopup()

View File

@ -5,10 +5,11 @@
"GUID:343deaaf83e0cee4ca978e7df0b80d21", "GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:7361f1d9c43da6649923760766194746", "GUID:7361f1d9c43da6649923760766194746",
"GUID:6055be8ebefd69e48b49212b09b47b2f", "GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:23eed6c2401dca1419d1ebd180e58c5a",
"GUID:33759803a11f4d538227861a78aba30b",
"GUID:0c752da273b17c547ae705acf0f2adf2", "GUID:0c752da273b17c547ae705acf0f2adf2",
"GUID:9e24947de15b9834991c9d8411ea37cf", "GUID:3c4294719a93e3c4e831a9ff0c261e8a",
"GUID:84651a3751eca9349aac36a66bba901b", "GUID:75469ad4d38634e559750d17036d5f7c"
"GUID:23eed6c2401dca1419d1ebd180e58c5a"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@ -0,0 +1,11 @@
using UnityEngine.EventSystems;
namespace NEG.UI.UnityUi
{
public class SilentEventData : BaseEventData
{
public SilentEventData(EventSystem eventSystem) : base(eventSystem)
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 718df112d05a4270bad895acce9dfa87
timeCreated: 1684008766

View File

@ -21,6 +21,8 @@ namespace NEG.UI.UnityUi.Window
[SerializeField] private List<MonoWindowSlot> windowSlots; [SerializeField] private List<MonoWindowSlot> windowSlots;
[SerializeField] private WindowController controller; [SerializeField] private WindowController controller;
[SerializeField] private GameObject defaultSelectedItem;
public void SetOpenedState(IWindowSlot parentSlot) public void SetOpenedState(IWindowSlot parentSlot)
{ {
gameObject.SetActive(true); gameObject.SetActive(true);
@ -48,6 +50,8 @@ namespace NEG.UI.UnityUi.Window
{ {
if (controller == null) if (controller == null)
controller = GetComponent<WindowController>(); controller = GetComponent<WindowController>();
if(defaultSelectedItem == null)
Debug.LogWarning("Window should have default selected item set");
} }
public void OpenWindow(IWindow window, object data = null) public void OpenWindow(IWindow window, object data = null)

View File

@ -15,7 +15,7 @@ namespace NEG.Utils
/// <param name="type">Interface type.</param> /// <param name="type">Interface type.</param>
public RequireInterfaceAttribute(System.Type type) public RequireInterfaceAttribute(System.Type type)
{ {
this.requiredType = type; requiredType = type;
} }
} }
} }

View File

@ -1,11 +1,15 @@
using System; using JetBrains.Annotations;
using System;
namespace NEG.Utils.Timing namespace NEG.Utils.Timing
{ {
public class AutoTimeMachine public class AutoTimeMachine
{ {
[PublicAPI]
public double Interval { get; set; } public double Interval { get; set; }
public Action Action { get; set; }
[PublicAPI]
public Action Action { get; }
private readonly TimeMachine machine; private readonly TimeMachine machine;

View File

@ -5,66 +5,61 @@ namespace NEG.Utils.Timing
{ {
public class TimeMachine public class TimeMachine
{ {
private double time; private double timeInternal;
public TimeMachine() public TimeMachine()
{ {
time = 0; timeInternal = 0;
} }
/// <summary> /// <summary>
/// Adds time into the TimeMachine /// Adds time into the TimeMachine
/// </summary> /// </summary>
/// <param name="time">Amount of time to be added</param> /// <param name="time">Amount of time to be added</param>
public void Accumulate(double time) public void Accumulate(double time) => timeInternal += time;
{
this.time += time;
}
/// <summary> /// <summary>
/// Retrieves given amount of time from the TimeMachine /// Retrieves given amount of time from the TimeMachine
/// </summary> /// </summary>
/// <param name="maxTime"></param> /// <param name="maxTime"></param>
/// <returns>Amount of time retrievend</returns> /// <returns>Amount of time retrieved</returns>
public double Retrieve(double maxTime) public double Retrieve(double maxTime)
{ {
if(!double.IsFinite(maxTime)) if(!double.IsFinite(maxTime))
{ {
double timeRetrieved = time; double timeRetrieved = timeInternal;
time = 0; timeInternal = 0;
return timeRetrieved; return timeRetrieved;
} }
double timeLeft = time - maxTime; double timeLeft = timeInternal - maxTime;
time = Math.Max(timeLeft, 0); timeInternal = Math.Max(timeLeft, 0);
return Math.Min(maxTime + timeLeft, maxTime); return Math.Min(maxTime + timeLeft, maxTime);
} }
/// <summary> /// <summary>
/// Attempts to retrieves given amount of time from the TimeMachine <br/> /// Attempts to retrieves given amount of time from the TimeMachine <br/>
/// If there is enough <paramref name="time"/> accumulated in this machine subtructs that amount and returns true, otherwise returns false /// If there is enough <paramref name="time"/> accumulated in this machine subtracts that amount and returns true, otherwise returns false
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
public bool TryRetrieve(double time) public bool TryRetrieve(double time)
{ {
if (this.time >= time) if (!(timeInternal >= time))
{
this.time -= time;
return true;
}
return false; return false;
timeInternal -= time;
return true;
} }
/// <summary> /// <summary>
/// Result is equivalent to calling <see cref="TryRetrieve(double)"/> as many times as possible, but is faster for larger <paramref name="limit"/> values /// Result is equivalent to calling <see cref="TryRetrieve(double)"/> as many times as possible, but is faster for larger <paramref name="limit"/> values
/// </summary> /// </summary>
/// <param name="interval">Single unit of warp time, must be positive/param> /// <param name="interval">Single unit of warp time, must be positive</param>
/// <param name="limit">Maximum amount of warps, must be positive</param> /// <param name="limit">Maximum amount of warps, must be positive</param>
/// <returns>Amount of warps</returns> /// <returns>Amount of warps</returns>
public int RetrieveAll(double interval, int limit = int.MaxValue) public int RetrieveAll(double interval, int limit = int.MaxValue)
{ {
int result = Mathf.FloorToInt((float)(time / interval)); int result = Mathf.FloorToInt((float)(timeInternal / interval));
result = Math.Clamp(result, 0, limit); result = Math.Clamp(result, 0, limit);
time -= result * interval; timeInternal -= result * interval;
return result; return result;
} }
} }