1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
using UnityEngine.EventSystems;
namespace UnityEngine.UI
{
[RequireComponent(typeof(Canvas))]
[ExecuteInEditMode]
[AddComponentMenu("Layout/Canvas Scaler", 101)]
public class CanvasScaler : UIBehaviour
{
public enum ScaleMode { ConstantPixelSize, ScaleWithScreenSize, ConstantPhysicalSize }
[Tooltip("Determines how UI elements in the Canvas are scaled.")]
[SerializeField] private ScaleMode m_UiScaleMode = ScaleMode.ConstantPixelSize;
public ScaleMode uiScaleMode { get { return m_UiScaleMode; } set { m_UiScaleMode = value; } }
[Tooltip("If a sprite has this 'Pixels Per Unit' setting, then one pixel in the sprite will cover one unit in the UI.")]
[SerializeField] protected float m_ReferencePixelsPerUnit = 100;
public float referencePixelsPerUnit { get { return m_ReferencePixelsPerUnit; } set { m_ReferencePixelsPerUnit = value; } }
// Constant Pixel Size settings
[Tooltip("Scales all UI elements in the Canvas by this factor.")]
[SerializeField] protected float m_ScaleFactor = 1;
public float scaleFactor { get { return m_ScaleFactor; } set { m_ScaleFactor = Mathf.Max(0.01f, value); } }
// Scale With Screen Size settings
public enum ScreenMatchMode { MatchWidthOrHeight = 0, Expand = 1, Shrink = 2 }
[Tooltip("The resolution the UI layout is designed for. If the screen resolution is larger, the UI will be scaled up, and if it's smaller, the UI will be scaled down. This is done in accordance with the Screen Match Mode.")]
[SerializeField] protected Vector2 m_ReferenceResolution = new Vector2(800, 600);
public Vector2 referenceResolution
{
get
{
return m_ReferenceResolution;
}
set
{
m_ReferenceResolution = value;
const float k_MinimumResolution = 0.00001f;
if (m_ReferenceResolution.x > -k_MinimumResolution && m_ReferenceResolution.x < k_MinimumResolution) m_ReferenceResolution.x = k_MinimumResolution * Mathf.Sign(m_ReferenceResolution.x);
if (m_ReferenceResolution.y > -k_MinimumResolution && m_ReferenceResolution.y < k_MinimumResolution) m_ReferenceResolution.y = k_MinimumResolution * Mathf.Sign(m_ReferenceResolution.y);
}
}
[Tooltip("A mode used to scale the canvas area if the aspect ratio of the current resolution doesn't fit the reference resolution.")]
[SerializeField] protected ScreenMatchMode m_ScreenMatchMode = ScreenMatchMode.MatchWidthOrHeight;
public ScreenMatchMode screenMatchMode { get { return m_ScreenMatchMode; } set { m_ScreenMatchMode = value; } }
[Tooltip("Determines if the scaling is using the width or height as reference, or a mix in between.")]
[Range(0, 1)]
[SerializeField] protected float m_MatchWidthOrHeight = 0;
public float matchWidthOrHeight { get { return m_MatchWidthOrHeight; } set { m_MatchWidthOrHeight = value; } }
// The log base doesn't have any influence on the results whatsoever, as long as the same base is used everywhere.
private const float kLogBase = 2;
// Constant Physical Size settings
public enum Unit { Centimeters, Millimeters, Inches, Points, Picas }
[Tooltip("The physical unit to specify positions and sizes in.")]
[SerializeField] protected Unit m_PhysicalUnit = Unit.Points;
public Unit physicalUnit { get { return m_PhysicalUnit; } set { m_PhysicalUnit = value; } }
[Tooltip("The DPI to assume if the screen DPI is not known.")]
[SerializeField] protected float m_FallbackScreenDPI = 96;
public float fallbackScreenDPI { get { return m_FallbackScreenDPI; } set { m_FallbackScreenDPI = value; } }
[Tooltip("The pixels per inch to use for sprites that have a 'Pixels Per Unit' setting that matches the 'Reference Pixels Per Unit' setting.")]
[SerializeField] protected float m_DefaultSpriteDPI = 96;
public float defaultSpriteDPI { get { return m_DefaultSpriteDPI; } set { m_DefaultSpriteDPI = Mathf.Max(1, value); } }
// World Canvas settings
[Tooltip("The amount of pixels per unit to use for dynamically created bitmaps in the UI, such as Text.")]
[SerializeField] protected float m_DynamicPixelsPerUnit = 1;
public float dynamicPixelsPerUnit { get { return m_DynamicPixelsPerUnit; } set { m_DynamicPixelsPerUnit = value; } }
// General variables
private Canvas m_Canvas;
[System.NonSerialized]
private float m_PrevScaleFactor = 1;
[System.NonSerialized]
private float m_PrevReferencePixelsPerUnit = 100;
protected CanvasScaler() {}
protected override void OnEnable()
{
base.OnEnable();
m_Canvas = GetComponent<Canvas>();
Handle();
}
protected override void OnDisable()
{
SetScaleFactor(1);
SetReferencePixelsPerUnit(100);
base.OnDisable();
}
protected virtual void Update()
{
Handle();
}
protected virtual void Handle()
{
if (m_Canvas == null || !m_Canvas.isRootCanvas)
return;
if (m_Canvas.renderMode == RenderMode.WorldSpace)
{
HandleWorldCanvas();
return;
}
switch (m_UiScaleMode)
{
case ScaleMode.ConstantPixelSize: HandleConstantPixelSize(); break;
case ScaleMode.ScaleWithScreenSize: HandleScaleWithScreenSize(); break;
case ScaleMode.ConstantPhysicalSize: HandleConstantPhysicalSize(); break;
}
}
protected virtual void HandleWorldCanvas()
{
SetScaleFactor(m_DynamicPixelsPerUnit);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
}
protected virtual void HandleConstantPixelSize()
{
SetScaleFactor(m_ScaleFactor);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
}
protected virtual void HandleScaleWithScreenSize()
{
Vector2 screenSize = new Vector2(Screen.width, Screen.height);
// Multiple display support only when not the main display. For display 0 the reported
// resolution is always the desktops resolution since its part of the display API,
// so we use the standard none multiple display method. (case 741751)
int displayIndex = m_Canvas.targetDisplay;
if (displayIndex > 0 && displayIndex < Display.displays.Length)
{
Display disp = Display.displays[displayIndex];
screenSize = new Vector2(disp.renderingWidth, disp.renderingHeight);
}
float scaleFactor = 0;
switch (m_ScreenMatchMode)
{
case ScreenMatchMode.MatchWidthOrHeight:
{
// We take the log of the relative width and height before taking the average.
// Then we transform it back in the original space.
// the reason to transform in and out of logarithmic space is to have better behavior.
// If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.
// In normal space the average would be (0.5 + 2) / 2 = 1.25
// In logarithmic space the average is (-1 + 1) / 2 = 0
float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
break;
}
case ScreenMatchMode.Expand:
{
scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
break;
}
case ScreenMatchMode.Shrink:
{
scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
break;
}
}
SetScaleFactor(scaleFactor);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
}
protected virtual void HandleConstantPhysicalSize()
{
float currentDpi = Screen.dpi;
float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi);
float targetDPI = 1;
switch (m_PhysicalUnit)
{
case Unit.Centimeters: targetDPI = 2.54f; break;
case Unit.Millimeters: targetDPI = 25.4f; break;
case Unit.Inches: targetDPI = 1; break;
case Unit.Points: targetDPI = 72; break;
case Unit.Picas: targetDPI = 6; break;
}
SetScaleFactor(dpi / targetDPI);
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI);
}
protected void SetScaleFactor(float scaleFactor)
{
if (scaleFactor == m_PrevScaleFactor)
return;
m_Canvas.scaleFactor = scaleFactor;
m_PrevScaleFactor = scaleFactor;
}
protected void SetReferencePixelsPerUnit(float referencePixelsPerUnit)
{
if (referencePixelsPerUnit == m_PrevReferencePixelsPerUnit)
return;
m_Canvas.referencePixelsPerUnit = referencePixelsPerUnit;
m_PrevReferencePixelsPerUnit = referencePixelsPerUnit;
}
#if UNITY_EDITOR
protected override void OnValidate()
{
m_ScaleFactor = Mathf.Max(0.01f, m_ScaleFactor);
m_DefaultSpriteDPI = Mathf.Max(1, m_DefaultSpriteDPI);
}
#endif
}
}
|