Skip to content

Instantly share code, notes, and snippets.

@INeatFreak
Forked from aarthificial/Optional.cs
Last active May 19, 2024 09:48
Show Gist options
  • Save INeatFreak/e01763f844336792ebe07c1cd1b6d018 to your computer and use it in GitHub Desktop.
Save INeatFreak/e01763f844336792ebe07c1cd1b6d018 to your computer and use it in GitHub Desktop.
using System;
using UnityEngine;
#if UNITY_2020_1_OR_NEWER
// good to go
#else
#error "Optional<T> plugin requires Unity 2020.1 or above for it to work. On below versions serializing fields of generic types was not possible."
#endif
[Serializable]
public struct Optional<T>
{
[SerializeField] bool enabled;
[SerializeField] T value;
public bool Enabled => enabled;
public T Value => value;
public Optional(T initialValue)
{
enabled = true;
value = initialValue;
// Value state based toggling approach:
// if (value == null) {
// enabled = false;
// } else {
// enabled = !value.Equals(default(T));
// }
}
public Optional(T initialValue, bool enabled)
{
this.enabled = enabled;
value = initialValue;
}
// conversion operators
public static implicit operator Optional<T>(T v) {
return new Optional<T>(v);
}
public static implicit operator T(Optional<T> o) {
return o.Value;
}
// for if statements
public static implicit operator bool(Optional<T> o) {
return o.enabled;
}
// equal operators
public static bool operator ==(Optional<T> lhs, Optional<T> rhs) {
if (lhs.value is null) {
if (rhs.value is null) {
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles the case of null on right side.
return lhs.value.Equals(rhs.value);
}
public static bool operator !=(Optional<T> lhs, Optional<T> rhs) {
return !(lhs == rhs);
}
public override bool Equals(object obj) {
// return base.Equals(obj);
return value.Equals(obj);
}
public override int GetHashCode() {
// return base.GetHashCode();
return value.GetHashCode();
}
public override string ToString() => value.ToString();
}
using UnityEngine;
public class OptionalDemo : MonoBehaviour
{
[Header(" [ Floats ] ")]
[SerializeField] Optional<float> optionalFloat = 100.95f;
[Header(" [ Float Ranges ] ")]
// this won't work as Range attribute check for field Type and theres no way to support custom classes
[SerializeField][Range(0,500)] Optional<float> optionalRange = 1.5986f;
[Header(" [ Integers ] ")]
[SerializeField] Optional<int> optionalInt = 50;
[SerializeField] Optional<int> optionalInt2;
[Header(" [ Transforms ] ")]
[SerializeField] Optional<Transform> optionalTransform = null;
[SerializeField] Optional<Transform> optionalTransform2 = null;
private void Start() {
optionalFloat += 1.1f;
if (optionalInt == optionalInt2)
Debug.Log("optionalInt == optionalInt2");
if (optionalTransform == optionalTransform2)
Debug.Log("optionalTransform == optionalTransform2");
if (optionalTransform == null) {
Debug.Log("optionalTransform is null");
} else {
optionalTransform = null;
}
if (optionalInt == null) {
Debug.Log("optionalInt can't be null. Something is wrong!");
}
if (optionalInt) {
Debug.Log("optionalInt is enabled.");
}
// optionalFloat = optionalTransform;
}
}
using UnityEditor;
using UnityEngine;
namespace Editor
{
[CustomPropertyDrawer(typeof(Optional<>))]
public class OptionalPropertyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var valueProperty = property.FindPropertyRelative("value");
return EditorGUI.GetPropertyHeight(valueProperty);
}
public override void OnGUI(
Rect position,
SerializedProperty property,
GUIContent label
)
{
var valueProperty = property.FindPropertyRelative("value");
var enabledProperty = property.FindPropertyRelative("enabled");
EditorGUI.BeginProperty(position, label, property);
position.width -= 24;
EditorGUI.BeginDisabledGroup(!enabledProperty.boolValue);
EditorGUI.PropertyField(position, valueProperty, label, true);
EditorGUI.EndDisabledGroup();
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
position.x += position.width + 24;
position.width = position.height = EditorGUI.GetPropertyHeight(enabledProperty);
position.x -= position.width;
EditorGUI.PropertyField(position, enabledProperty, GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
}
@Aryazaky
Copy link

Thank you!

@INeatFreak
Copy link
Author

Thank you!

You're welcome!

@Daniel-Pham831
Copy link

Daniel-Pham831 commented May 19, 2024

Actually you can pass the attributes down to the value inside Optional.
I did make a comment at the aarthificial's gist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment