Skip to content

Commit 12dbb62

Browse files
Merge pull request #13 from CoderGamester/develop
Release 0.7.0
2 parents 9679c5e + 8e81df5 commit 12dbb62

14 files changed

+434
-32
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ All notable changes to this package will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
66

7+
## [0.7.0] - 2021-03-12
8+
9+
- Added *NonDrawingView* to have an Image without a renderer to not add additional draw calls.
10+
- Added *SafeAreaHelperView* to add the possibility for the *RectTransform* to adjust himself to the screen notches
11+
- Added *AnimatedUiPresenter* to play animation on enter or closing
12+
- Added the possibility to add *Layers* externally into the *UiService*
13+
14+
**Changed**:
15+
- Now *Canvas* are single *GameObjects* that can be controlled outside of the *UiService*
16+
17+
**Fixed**:
18+
- Fixed the issue when setting data on *UiPresenterData* not being invoked
19+
720
## [0.6.1] - 2020-09-24
821

922
- Updated dependency packages

Editor/NonDrawingViewEditor.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using FirstLight.UiService;
2+
using UnityEditor;
3+
using UnityEditor.UI;
4+
using UnityEngine;
5+
6+
// ReSharper disable once CheckNamespace
7+
8+
namespace FirstLightEditor.UiService
9+
{
10+
/// <summary>
11+
/// <see cref="NonDrawingView"/> custom inspector
12+
/// </summary>
13+
[CanEditMultipleObjects, CustomEditor(typeof(NonDrawingView), false)]
14+
public class NonDrawingViewEditor : GraphicEditor
15+
{
16+
public override void OnInspectorGUI ()
17+
{
18+
serializedObject.Update();
19+
EditorGUILayout.PropertyField(m_Script, new GUILayoutOption[0]);
20+
21+
// skipping AppearanceControlsGUI
22+
RaycastControlsGUI();
23+
serializedObject.ApplyModifiedProperties();
24+
}
25+
}
26+
}

Editor/NonDrawingViewEditor.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/AnimatedUiPresenter.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System.Threading.Tasks;
2+
using UnityEngine;
3+
4+
// ReSharper disable CheckNamespace
5+
6+
namespace GameLovers.UiService
7+
{
8+
/// <inheritdoc />
9+
/// <remarks>
10+
/// Allows this Presenter to have an intro and outro animation when opened and closed to provide feedback and joy for players.
11+
/// </remarks>
12+
[RequireComponent(typeof(CanvasGroup))]
13+
public abstract class AnimatedUiPresenter : UiCloseActivePresenter
14+
{
15+
[SerializeField] protected CanvasGroup _canvasGroup;
16+
[SerializeField] protected Animation _animation;
17+
[SerializeField] protected AnimationClip _introAnimationClip;
18+
[SerializeField] protected AnimationClip _outroAnimationClip;
19+
20+
private void OnValidate()
21+
{
22+
_canvasGroup = _canvasGroup ? _canvasGroup : GetComponent<CanvasGroup>();
23+
24+
Debug.Assert(_animation != null, $"Presenter {gameObject.name} does not have a referenced Animation");
25+
OnEditorValidate();
26+
}
27+
28+
protected override async void OnClosed()
29+
{
30+
_animation.clip = _outroAnimationClip;
31+
_animation.Play();
32+
33+
await Task.Delay(Mathf.RoundToInt(_animation.clip.length * 1000));
34+
35+
if (gameObject != null)
36+
{
37+
gameObject.SetActive(false);
38+
OnClosedCompleted();
39+
}
40+
}
41+
42+
protected override async void OnOpened()
43+
{
44+
_canvasGroup.alpha = 0;
45+
_animation.clip = _introAnimationClip;
46+
_animation.Play();
47+
48+
await Task.Yield();
49+
50+
_canvasGroup.alpha = 1;
51+
52+
await Task.Delay(Mathf.RoundToInt(_animation.clip.length * 1000));
53+
54+
if (gameObject != null)
55+
{
56+
OnOpenedCompleted();
57+
}
58+
}
59+
60+
/// <summary>
61+
/// Called in the end of this object MonoBehaviour's OnValidate() -> <see cref="OnValidate"/>.
62+
/// Override this method to have your custom extra validation.
63+
/// </summary>
64+
/// <remarks>
65+
/// This is Editor only call.
66+
/// </remarks>
67+
protected virtual void OnEditorValidate() { }
68+
69+
/// <summary>
70+
/// Called in the end of this object's <see cref="OnOpened"/>.
71+
/// Override this method to have your custom extra execution when the presenter is opened.
72+
/// </summary>
73+
protected virtual void OnOpenedCompleted() { }
74+
75+
/// <summary>
76+
/// Called in the end of this object's <see cref="OnClosed"/>.
77+
/// Override this method to have your custom extra execution when the presenter is closed.
78+
/// </summary>
79+
protected virtual void OnClosedCompleted() { }
80+
}
81+
82+
/// <inheritdoc />
83+
/// <remarks>
84+
/// Allows this Presenter to have an intro and outro animation when opened and closed to provide feedback and joy for players.
85+
/// </remarks>
86+
[RequireComponent(typeof(Animation), typeof(CanvasGroup))]
87+
public abstract class AnimatedUiPresenterData<T> : UiCloseActivePresenterData<T> where T : struct
88+
{
89+
[SerializeField] protected Animation _animation;
90+
[SerializeField] protected AnimationClip _introAnimationClip;
91+
[SerializeField] protected AnimationClip _outroAnimationClip;
92+
93+
private void OnValidate()
94+
{
95+
Debug.Assert(_animation != null, $"Presenter {gameObject.name} does not have a referenced Animation");
96+
OnEditorValidate();
97+
}
98+
99+
protected override async void OnOpened()
100+
{
101+
_animation.clip = _introAnimationClip;
102+
_animation.Play();
103+
104+
await Task.Delay(Mathf.RoundToInt(_animation.clip.length * 1000));
105+
106+
if (gameObject != null)
107+
{
108+
OnOpenedCompleted();
109+
}
110+
}
111+
112+
protected override async void OnClosed()
113+
{
114+
_animation.clip = _outroAnimationClip;
115+
_animation.Play();
116+
117+
await Task.Delay(Mathf.RoundToInt(_animation.clip.length * 1000));
118+
119+
if (gameObject != null)
120+
{
121+
gameObject.SetActive(false);
122+
OnClosedCompleted();
123+
}
124+
}
125+
126+
127+
/// <inheritdoc cref="AnimatedUiPresenter.OnEditorValidate"/>
128+
protected virtual void OnEditorValidate() { }
129+
130+
/// <inheritdoc cref="AnimatedUiPresenter.OnOpenedCompleted"/>
131+
protected virtual void OnOpenedCompleted() { }
132+
133+
/// <inheritdoc cref="AnimatedUiPresenter.OnClosedCompleted"/>
134+
protected virtual void OnClosedCompleted() { }
135+
}
136+
}
137+

Runtime/AnimatedUiPresenter.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/IUiService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ namespace GameLovers.UiService
1414
public interface IUiService
1515
{
1616
/// <summary>
17-
/// Requests the <see cref="Canvas"/> of the given <paramref name="layer"/>
17+
/// Requests the root <see cref="GameObject"/> of the given <paramref name="layer"/>
1818
/// </summary>
19-
Canvas GetLayer(int layer);
19+
GameObject GetLayer(int layer);
2020

2121
/// <summary>
2222
/// Adds the given UI <paramref name="config"/> to the service

Runtime/NonDrawingView.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using UnityEngine.UI;
2+
3+
// ReSharper disable CheckNamespace
4+
5+
namespace FirstLight.UiService
6+
{
7+
/// <summary>
8+
/// A concrete subclass of the Unity UI `Graphic` class that just skips drawing.
9+
/// Useful for providing a raycast target without actually drawing anything.
10+
/// </summary>
11+
public class NonDrawingView : Graphic
12+
{
13+
public override void SetMaterialDirty() { }
14+
public override void SetVerticesDirty() { }
15+
16+
/// <summary>
17+
/// Probably not necessary since the chain of calls
18+
/// `Rebuild()`->`UpdateGeometry()`->`DoMeshGeneration()`->`OnPopulateMesh()` won't happen.
19+
/// So here really just as a fail-safe.
20+
/// </summary>
21+
protected override void OnPopulateMesh(VertexHelper vh)
22+
{
23+
vh.Clear();
24+
}
25+
}
26+
}

Runtime/NonDrawingView.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/SafeAreaHelperView.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using System;
2+
using UnityEngine;
3+
using UnityEngine.UI;
4+
5+
// ReSharper disable CheckNamespace
6+
7+
namespace FirstLight.UiService
8+
{
9+
/// <summary>
10+
/// This view helper translate anchored views based on device safe area (screens witch a notch)
11+
/// </summary>
12+
[RequireComponent(typeof(RectTransform))]
13+
public class SafeAreaHelperView : MonoBehaviour
14+
{
15+
private const float _floatTolerance = 0.01f;
16+
17+
[SerializeField] private RectTransform _rectTransform;
18+
[SerializeField] private bool _ignoreWidth = true;
19+
[SerializeField] private bool _onUpdate = false;
20+
[SerializeField] private Vector2 _refResolution;
21+
22+
private Vector2 _initAnchoredPosition;
23+
private Rect _resolution;
24+
private Rect _safeArea;
25+
26+
internal void OnValidate()
27+
{
28+
_rectTransform = _rectTransform ? _rectTransform : GetComponent<RectTransform>();
29+
_refResolution = transform.root.GetComponent<CanvasScaler>().referenceResolution;
30+
_initAnchoredPosition = _rectTransform.anchoredPosition;
31+
}
32+
33+
private void Awake()
34+
{
35+
_initAnchoredPosition = _rectTransform.anchoredPosition;
36+
_resolution = new Rect(0,0, Screen.currentResolution.width, Screen.currentResolution.height);
37+
_safeArea = Screen.safeArea;
38+
}
39+
40+
private void OnEnable()
41+
{
42+
UpdatePositions();
43+
}
44+
45+
private void Update()
46+
{
47+
if (_onUpdate)
48+
{
49+
UpdatePositions();
50+
}
51+
}
52+
53+
internal void UpdatePositions()
54+
{
55+
var anchorMax = _rectTransform.anchorMax;
56+
var anchorMin = _rectTransform.anchorMin;
57+
var anchoredPosition = _initAnchoredPosition;
58+
59+
#if UNITY_EDITOR
60+
// Because Unity Device Simulator and Game View have different screen resolution configs and sometimes use Desktop resolution
61+
_safeArea = Screen.safeArea;
62+
_resolution = new Rect(0, 0, Screen.width, Screen.height);
63+
_resolution = _resolution == _safeArea ? _resolution : new Rect(0,0, Screen.currentResolution.width, Screen.currentResolution.height);
64+
#endif
65+
66+
if (_safeArea == _resolution)
67+
{
68+
return;
69+
}
70+
71+
// Check if anchored to top or bottom
72+
if (Math.Abs(anchorMax.y - anchorMin.y) < _floatTolerance)
73+
{
74+
// bottom
75+
if (anchorMax.y < _floatTolerance)
76+
{
77+
anchoredPosition.y += (_safeArea.yMin - _resolution.yMin) * _refResolution.y / _resolution.height;
78+
}
79+
else // top
80+
{
81+
anchoredPosition.y += (_safeArea.yMax - _resolution.yMax) * _refResolution.y / _resolution.height;
82+
}
83+
}
84+
85+
// Check if anchored to left or right
86+
if (!_ignoreWidth && Math.Abs(anchorMax.x - anchorMin.x) < _floatTolerance)
87+
{
88+
// left
89+
if (anchorMax.x < _floatTolerance)
90+
{
91+
anchoredPosition.x += (_safeArea.xMin - _resolution.xMin) * _refResolution.x / _resolution.width;
92+
}
93+
else // right
94+
{
95+
anchoredPosition.x += (_safeArea.xMax - _resolution.xMax) * _refResolution.x / _resolution.width;
96+
}
97+
}
98+
99+
_rectTransform.anchoredPosition = anchoredPosition;
100+
}
101+
}
102+
103+
#if UNITY_EDITOR
104+
[UnityEditor.CustomEditor(typeof(SafeAreaHelperView))]
105+
public class SafeAreaHelperViewEditor : UnityEditor.Editor
106+
{
107+
public override void OnInspectorGUI()
108+
{
109+
DrawDefaultInspector();
110+
111+
if (GUILayout.Button("Update Anchored Data"))
112+
{
113+
var view = (SafeAreaHelperView) target;
114+
115+
view.OnValidate();
116+
}
117+
118+
if (GUILayout.Button("Update Anchored View"))
119+
{
120+
var view = (SafeAreaHelperView) target;
121+
122+
view.UpdatePositions();
123+
}
124+
}
125+
}
126+
#endif
127+
}

0 commit comments

Comments
 (0)