Skip to content

Commit 4b7a8ab

Browse files
committed
version 1.0.0
1 parent 6bde8a1 commit 4b7a8ab

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

Attributes/BigNumberAttribute.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Could be placed within any folder in Assets (except "Editor").
2+
using UnityEngine;
3+
4+
/// <summary> Use it to make large numbers appear more readable without use of exponents. </summary>
5+
public class BigNumberAttribute : PropertyAttribute {}

Editor/BigNumberDrawer.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Must be placed within a folder named "Editor"
2+
using System;
3+
using System.Globalization;
4+
using System.Text;
5+
using UnityEditor;
6+
using UnityEngine;
7+
8+
/// <summary> Use it to make large numbers appear more readable without use of exponents.
9+
/// <para>Supported types: int, long, float, double</para></summary>
10+
[CustomPropertyDrawer(typeof(BigNumberAttribute))]
11+
public class BigNumberDrawer : PropertyDrawer
12+
{
13+
//Use those to tweak for your taste.
14+
#region Options
15+
16+
/// <summary> Symbol used to separate groups of digits inside a number. </summary>
17+
public const string NumberSeparator = " "; // Can be anything except '.', since period is used as decimal separator in Unity.
18+
19+
/// <summary> If number is equals or bigger than this value, an abbreviated version of this number would appear in label. </summary>
20+
public const int ShowAbbreviationMin = 10_000;
21+
22+
/// <summary> Amount of digits shown after '.' in abbreviated values. </summary>
23+
public const int DecimalPlacesInAbbreviated = 0;
24+
25+
#endregion
26+
27+
private static readonly NumberFormatInfo SeparatorFormat = new NumberFormatInfo { NumberGroupSeparator = NumberSeparator, NumberDecimalDigits = 0 };
28+
29+
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
30+
{
31+
string toText = string.Empty;
32+
double number = 0;
33+
34+
switch (property.type)
35+
{
36+
case "int":
37+
number = property.intValue;
38+
39+
break;
40+
case "long":
41+
number = property.longValue;
42+
43+
break;
44+
case "float":
45+
number = property.floatValue;
46+
47+
break;
48+
case "double":
49+
number = property.doubleValue;
50+
break;
51+
}
52+
53+
toText = AddSeparators(number);
54+
55+
if (Math.Abs(number) >= ShowAbbreviationMin) label.text = $"{label.text} ({Abbreviate(number, DecimalPlacesInAbbreviated)})";
56+
57+
58+
EditorGUI.BeginProperty(position, label, property);
59+
toText = EditorGUI.TextField(position, label, toText);
60+
EditorGUI.EndProperty();
61+
62+
63+
string parsable = toText.Replace(NumberSeparator, string.Empty).Replace(" ", string.Empty);
64+
try
65+
{
66+
double value = double.Parse(parsable, CultureInfo.InvariantCulture);
67+
68+
switch (property.type)
69+
{
70+
case "int":
71+
value = Math.Clamp(value, int.MinValue, int.MaxValue);
72+
property.intValue = (int)value;
73+
74+
break;
75+
case "long":
76+
value = Math.Clamp(value, long.MinValue, long.MaxValue);
77+
property.longValue = (long)value;
78+
79+
break;
80+
case "float":
81+
value = Math.Clamp(value, float.MinValue, float.MaxValue);
82+
property.floatValue = (float)value;
83+
84+
break;
85+
case "double":
86+
property.doubleValue = value;
87+
88+
break;
89+
}
90+
property.serializedObject.ApplyModifiedProperties();
91+
}
92+
catch(System.Exception)
93+
{
94+
//Any formatting errors will be ignored, Unity will automatically revert to last correct input.
95+
}
96+
}
97+
98+
private string AddSeparators(double value)
99+
{
100+
StringBuilder sb = new StringBuilder();
101+
102+
sb.Append(value.ToString("N", SeparatorFormat));
103+
104+
if((decimal)value % 1 != 0) //Adding separators for the decimal part.
105+
{
106+
string decimals = ((decimal)value % 1).ToString().Remove(0, 2);
107+
sb.Append('.');
108+
109+
for (int i = 0; i < decimals.Length; i++)
110+
{
111+
if (i != 0 && i % 3 == 0) sb.Append(NumberSeparator);
112+
113+
sb.Append(decimals[i]);
114+
}
115+
}
116+
return sb.ToString();
117+
}
118+
119+
/// <summary> Abbreviates a number into more pleasant to read (123 456 789 123 -> 123.45B) </summary>
120+
/// <param name="decimalPlaces"> Amount of digits after point. </param>
121+
public static string Abbreviate(double number, int decimalPlaces = 2)
122+
{
123+
bool negative = number < 0;
124+
number = Math.Abs(number);
125+
126+
string numberString = number.ToString();
127+
128+
foreach (NumberSuffix suffix in Enum.GetValues(typeof(NumberSuffix)))
129+
{
130+
// Assign the amount of digits to base 10.
131+
double currentDigitValue = 1 * Math.Pow(10, (int)suffix * 3);
132+
133+
string suffixText = Enum.GetName(typeof(NumberSuffix), (int)suffix);
134+
if (suffix == 0) { suffixText = string.Empty; }
135+
136+
// Set the return value to a rounded value with the suffix.
137+
if (number >= currentDigitValue)
138+
{
139+
numberString = $"{Math.Round(number / currentDigitValue, decimalPlaces, MidpointRounding.ToEven)} {suffixText}";
140+
}
141+
}
142+
if (negative) numberString = numberString.Insert(0, "-");
143+
144+
return numberString;
145+
}
146+
147+
/// <summary> Suffixes for numbers based on how many digits they have left of the decimal point. </summary>
148+
/// <remarks> Must be ordered from small to large. </remarks>
149+
private enum NumberSuffix
150+
{
151+
/// <summary> Thousand = 1_000 </summary>
152+
K = 1,
153+
/// <summary> Million = 1_000_000 </summary>
154+
M = 2,
155+
/// <summary> Billion = 1_000_000_000 </summary>
156+
B = 3,
157+
/// <summary> Trillion = 1_000_000_000_000 </summary>
158+
T = 4,
159+
/// <summary> Quadrillion = 1_000_000_000_000_000</summary>
160+
Q = 5
161+
}
162+
}

0 commit comments

Comments
 (0)