Last active
October 31, 2022 19:28
-
-
Save nomnomab/d2d5b1841bcb3992cb88ea0c1b5133cb to your computer and use it in GitHub Desktop.
A visual representation of two fill objects in a single container.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.CompilerServices; | |
using UnityEngine; | |
using UnityEngine.Events; | |
using UnityEngine.UI; | |
[ExecuteAlways] | |
[DisallowMultipleComponent] | |
[RequireComponent(typeof(RectTransform))] | |
public class PercentageFill : LayoutGroup { | |
[SerializeField] private RectTransform _leftBar; | |
[SerializeField] private RectTransform _rightBar; | |
[SerializeField] private Slider.Direction _direction = Slider.Direction.LeftToRight; | |
[SerializeField] private Vector2 _range = new Vector2(0, 100f); | |
[SerializeField] private float _value = 20; | |
public UnityEvent<float> onValueChanged; | |
#if UNITY_EDITOR | |
protected override void OnValidate() { | |
MapValue(); | |
if (!Application.isPlaying) { | |
SetDirty(); | |
return; | |
} | |
SetDirtyEmit(); | |
} | |
#endif | |
private void SetDirtyEmit() { | |
SetDirty(); | |
onValueChanged?.Invoke(_value); | |
} | |
public void SetValue(float newValue) { | |
_value = newValue; | |
MapValue(); | |
SetDirtyEmit(); | |
} | |
public void SetValueWithoutNotify(float newValue) { | |
_value = newValue; | |
MapValue(); | |
SetDirty(); | |
} | |
public void SetNormalizedValue(float percent) { | |
_value = Mathf.Lerp(_range.x, _range.y, percent); | |
MapValue(); | |
SetDirtyEmit(); | |
} | |
public void SetNormalizedValueWithoutNotify(float percent) { | |
_value = Mathf.Lerp(_range.x, _range.y, percent); | |
MapValue(); | |
SetDirty(); | |
} | |
private void MapValue() { | |
_value = Mathf.Clamp(_value, _range.x, _range.y); | |
} | |
public float GetValue(Side side) { | |
float left = _value; | |
float right = _range.y - _value; | |
return side switch { | |
Side.Left => _direction == Slider.Direction.LeftToRight ? left : right, | |
Side.Right => _direction == Slider.Direction.TopToBottom ? left : right, | |
_ => throw new ArgumentOutOfRangeException(nameof(side), side, null) | |
}; | |
} | |
public float GetNormalizedValue(Side side) { | |
float value = GetValue(side); | |
return CalculatePercentage(value); | |
} | |
public Slider.Direction GetDirection() { | |
return _direction; | |
} | |
public override void CalculateLayoutInputHorizontal() { | |
base.CalculateLayoutInputHorizontal(); | |
if (!_leftBar || !_rightBar) { | |
return; | |
} | |
if (!_direction.HasFlag(Slider.Direction.LeftToRight) && !_direction.HasFlag(Slider.Direction.RightToLeft)) { | |
return; | |
} | |
(float leftWidth, float rightWidth) = CalculateWidth(0); | |
RectTransform leftChild = _leftBar; | |
RectTransform rightChild = _rightBar; | |
if (_direction == Slider.Direction.RightToLeft) { | |
(leftChild, rightChild) = (rightChild, leftChild); | |
} | |
SetChildAlongAxis(leftChild, 0, 0, leftWidth); | |
SetChildAlongAxis(rightChild, 0, leftWidth, rightWidth); | |
SetChildAlongAxis(leftChild, 1, 0, rectTransform!.rect.height); | |
SetChildAlongAxis(rightChild, 1, 0, rectTransform!.rect.height); | |
} | |
public override void CalculateLayoutInputVertical() { | |
if (!_leftBar || !_rightBar) { | |
return; | |
} | |
if (!_direction.HasFlag(Slider.Direction.TopToBottom) && !_direction.HasFlag(Slider.Direction.BottomToTop)) { | |
return; | |
} | |
(float leftWidth, float rightWidth) = CalculateWidth(1); | |
RectTransform leftChild = _leftBar; | |
RectTransform rightChild = _rightBar; | |
if (_direction == Slider.Direction.BottomToTop) { | |
(leftChild, rightChild) = (rightChild, leftChild); | |
} | |
SetChildAlongAxis(leftChild, 1, 0, leftWidth); | |
SetChildAlongAxis(rightChild, 1, leftWidth, rightWidth); | |
SetChildAlongAxis(leftChild, 0, 0, rectTransform!.rect.width); | |
SetChildAlongAxis(rightChild, 0, 0, rectTransform!.rect.width); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private float CalculatePercentage(float value) { | |
return (value - _range.x) / (_range.y - _range.x); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private (float left, float right) CalculateWidth(int axis) { | |
float width = axis switch { | |
0 => rectTransform!.rect.width, | |
1 => rectTransform!.rect.height, | |
_ => throw new NotImplementedException($"Axis {axis} is not available") | |
}; | |
float percentage = Mathf.Clamp01(CalculatePercentage(_value)); | |
return (width * percentage, width * (1f - percentage)); | |
} | |
public override void SetLayoutHorizontal() { } | |
public override void SetLayoutVertical() { } | |
public enum Side { | |
Left, | |
Right | |
} | |
} | |
#if UNITY_EDITOR | |
[UnityEditor.CustomEditor(typeof(PercentageFill))] | |
public class PercentageFillEditor : UnityEditor.Editor { | |
public override void OnInspectorGUI() { | |
UnityEditor.SerializedProperty iter = serializedObject.FindProperty("_leftBar"); | |
UnityEditor.SerializedProperty rangeProperty = serializedObject.FindProperty("_range"); | |
using (var changeScope = new UnityEditor.EditorGUI.ChangeCheckScope()) { | |
// first field | |
UnityEditor.EditorGUILayout.PropertyField(iter, true); | |
// rest | |
while (iter.Next(false)) { | |
if (iter.name == "_value") { | |
Vector2 range = rangeProperty.vector2Value; | |
iter.floatValue = UnityEditor.EditorGUILayout.Slider(iter.displayName, iter.floatValue, range.x, range.y); | |
continue; | |
} | |
UnityEditor.EditorGUILayout.PropertyField(iter, true); | |
} | |
if (!changeScope.changed) { | |
return; | |
} | |
serializedObject.ApplyModifiedProperties(); | |
} | |
} | |
} | |
#endif |
Author
nomnomab
commented
Oct 31, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment