Skip to content

Commit 224e28c

Browse files
committed
feat: add fly in animation to message feed messaged
1 parent ba5336d commit 224e28c

File tree

1 file changed

+119
-51
lines changed

1 file changed

+119
-51
lines changed

Assets/Scripts/Gameplay/UI/MessageFeed.cs

Lines changed: 119 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using UnityEngine;
45
using UnityEngine.UIElements;
@@ -8,16 +9,15 @@
89
using Unity.BossRoom.Gameplay.Messages;
910
using Unity.BossRoom.Infrastructure;
1011
using VContainer;
11-
using System.Linq;
1212

1313
public class MessageFeed : MonoBehaviour
1414
{
1515
[SerializeField]
1616
UIDocument doc;
1717

18-
List<Message> m_messages;
18+
List<Message> m_Messages;
1919

20-
VisualElement messageContainer;
20+
VisualElement m_MessageContainer;
2121

2222
DisposableGroup m_Subscriptions;
2323

@@ -106,15 +106,15 @@ void Start()
106106
templateLabel.style.display = DisplayStyle.None;
107107
templateBox.style.display = DisplayStyle.None;
108108

109-
m_messages = new List<Message>();
109+
m_Messages = new List<Message>();
110110

111111
// Create a container for all messages
112-
messageContainer = new VisualElement();
113-
messageContainer.style.flexDirection = FlexDirection.Column; // Arrange messages vertically
112+
m_MessageContainer = new VisualElement();
113+
m_MessageContainer.style.flexDirection = FlexDirection.Column; // Arrange messages vertically
114114

115115
// make sure other visual elements don't get pushed down by the message container
116-
messageContainer.style.position = Position.Absolute;
117-
doc.rootVisualElement.Add(messageContainer);
116+
m_MessageContainer.style.position = Position.Absolute;
117+
doc.rootVisualElement.Add(m_MessageContainer);
118118
}
119119

120120
void OnDestroy()
@@ -125,72 +125,140 @@ void OnDestroy()
125125
}
126126
}
127127

128-
static void StartFadeout(Message message, float opacity)
128+
void Update()
129129
{
130-
message.messageBox.style.opacity = opacity;
131-
message.messageBox.schedule.Execute(() =>
130+
foreach (var m in m_Messages)
132131
{
133-
opacity -= 0.01f;
134-
message.messageBox.style.opacity = opacity;
135-
if (opacity <= 0)
132+
if (m.isShown)
136133
{
137-
message.messageBox.style.display = DisplayStyle.None;
138-
message.startTime = 0;
134+
// Check if a message should begin fading out
135+
if (Time.realtimeSinceStartup - m.startTime > 5 && m.messageBox.style.opacity == 1)
136+
{
137+
StartFadeout(m, 1f);
138+
m.isShown = false;
139+
}
139140
}
140-
}).Every((long)0.1f).Until(() => opacity <= 0);
141+
}
141142
}
142143

143144
void ShowMessage(string message)
144145
{
145-
// Limit maximum number of active messages
146-
int maxMessages = 10;
147-
if (m_messages.Count(m => m.isShown) >= maxMessages)
146+
const float messageHeight = 40f; // Approximate height of a message (adjust based on UI)
147+
const float verticalSpacing = 10f; // Spacing between stacked messages
148+
149+
// Reuse or create a new message
150+
Message newMessage = null;
151+
152+
foreach (var m in m_Messages)
148153
{
149-
// Find the oldest active message and start fading it out
150-
var oldestMessage = m_messages.FirstOrDefault(m => m.isShown && m.startTime > 0);
151-
if (oldestMessage != null)
154+
if (!m.isShown)
152155
{
153-
StartFadeout(oldestMessage, 1f);
154-
oldestMessage.isShown = false;
156+
// Reuse the hidden message
157+
newMessage = m;
158+
break;
155159
}
156160
}
157161

158-
// Reuse or create a new message
159-
foreach (var m in m_messages)
162+
if (newMessage == null)
160163
{
161-
if (!m.isShown)
164+
// Create a new message if no reusable messages are available
165+
var newBox = new VisualElement();
166+
newBox.AddToClassList("messageBox");
167+
newBox.style.position = Position.Absolute; // Explicitly position it
168+
// Position the new message box below the last message
169+
//newBox.style.top = m_MessageContainer.childCount * (messageHeight + verticalSpacing);
170+
171+
var newLabel = new Label();
172+
newLabel.AddToClassList("message");
173+
newBox.Add(newLabel);
174+
175+
newMessage = new Message()
162176
{
163-
m.isShown = true;
164-
m.Label.text = message;
165-
m.messageBox.style.display = DisplayStyle.Flex;
166-
m.startTime = Time.realtimeSinceStartup;
177+
isShown = true,
178+
startTime = Time.realtimeSinceStartup,
179+
messageBox = newBox,
180+
Label = newLabel
181+
};
167182

168-
return;
169-
}
183+
m_MessageContainer.Add(newBox); // Add the message box to the parent container
184+
m_Messages.Add(newMessage); // Add to the list of messages
170185
}
171186

172-
// Create a new message container
173-
var newBox = new VisualElement();
174-
newBox.AddToClassList("messageBox");
187+
// Set the properties of the reused or new message
188+
newMessage.isShown = true;
189+
newMessage.Label.text = message;
190+
newMessage.startTime = Time.realtimeSinceStartup;
191+
newMessage.messageBox.style.opacity = 1;
192+
newMessage.messageBox.style.display = DisplayStyle.Flex;
193+
194+
// Start a fly-in animation
195+
StartCoroutine(FlyInWithBounce(newMessage.messageBox, -300, 50, 0.2f, 0.2f));
196+
}
197+
198+
IEnumerator FlyInWithBounce(VisualElement element, float startLeft, float targetLeft, float duration, float bounceDuration)
199+
{
200+
float elapsedTime = 0;
201+
element.style.opacity = 0;
202+
203+
// Main fly-in animation (linear movement from off-screen)
204+
while (elapsedTime < duration)
205+
{
206+
elapsedTime += Time.deltaTime;
207+
float t = elapsedTime / duration; // Normalized time (0 to 1)
175208

176-
var newLabel = new Label();
177-
newLabel.text = message;
178-
newLabel.AddToClassList("message");
209+
// Linearly interpolate left position
210+
float newLeft = Mathf.Lerp(startLeft, targetLeft, t);
211+
element.style.left = newLeft;
179212

180-
newBox.Add(newLabel);
213+
// Gradually fade in
214+
element.style.opacity = t;
181215

182-
var newMessage = new Message()
216+
yield return null;
217+
}
218+
219+
// Ensure the message is at the target final position
220+
element.style.left = targetLeft;
221+
element.style.opacity = 1;
222+
223+
// Bounce Animation: Overshoot to the right and come back
224+
float overshootAmount = 20;
225+
float bounceElapsedTime = 0;
226+
227+
while (bounceElapsedTime < bounceDuration)
183228
{
184-
isShown = true,
185-
startTime = Time.realtimeSinceStartup,
186-
messageBox = newBox,
187-
Label = newLabel
188-
};
189-
190-
messageContainer.Add(newBox);
191-
m_messages.Add(newMessage);
229+
bounceElapsedTime += Time.deltaTime;
230+
float t = bounceElapsedTime / bounceDuration;
231+
232+
// Use a simple sine easing for the bounce effect
233+
float bounceT = Mathf.Sin(t * Mathf.PI);
234+
235+
// Interpolate between targetLeft and overshoot position
236+
float bounceLeft = Mathf.Lerp(targetLeft, targetLeft + overshootAmount, bounceT);
237+
238+
element.style.left = bounceLeft;
239+
yield return null;
240+
}
241+
242+
// Finally snap back to the exact target position
243+
element.style.left = targetLeft;
244+
}
245+
246+
static void StartFadeout(Message message, float opacity)
247+
{
248+
message.messageBox.schedule.Execute(() =>
249+
{
250+
opacity -= 0.01f;
251+
message.messageBox.style.opacity = opacity;
252+
253+
if (opacity <= 0)
254+
{
255+
// Once faded out fully, hide the message and reset state
256+
message.messageBox.style.display = DisplayStyle.None;
257+
message.isShown = false;
258+
message.startTime = 0;
259+
}
260+
}).Every((long)0.1f).Until(() => opacity <= 0);
192261
}
193-
194262

195263
class Message
196264
{

0 commit comments

Comments
 (0)