Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
EffectsManager.cs
1using System.Collections.ObjectModel;
6
7namespace Gondwana.Effects;
8
12public sealed class EffectsManager : IDisposable
13{
14 private readonly object _sync = new();
15 private readonly List<DisplayEffect> _activeEffects = [];
16 private readonly RenderSurfaceHostBase _host;
17 private long? _lastTick;
18 private bool _disposed;
19
20 internal EffectsManager(RenderSurfaceHostBase host) =>
21 _host = host ?? throw new ArgumentNullException(nameof(host));
22
24 public ReadOnlyCollection<DisplayEffect> ActiveEffects
25 {
26 get
27 {
28 lock (_sync)
29 return _activeEffects.ToList().AsReadOnly();
30 }
31 }
32
38 public TEffect Run<TEffect>(View target, TEffect effect)
39 where TEffect : DisplayEffect => RunCore(target, effect);
40
46 public TEffect Run<TEffect>(SceneLayer target, TEffect effect)
47 where TEffect : DisplayEffect => RunCore(target, effect);
48
51 public void Cancel(DisplayEffect effect)
52 {
53 ArgumentNullException.ThrowIfNull(effect);
54
55 bool removed;
56 lock (_sync)
57 removed = _activeEffects.Remove(effect);
58
59 if (!removed)
60 return;
61
62 effect.CancelInternal(restoreState: true);
63 Invalidate(effect.Target);
64 }
65
67 public void CancelAll()
68 {
69 DisplayEffect[] snapshot;
70
71 lock (_sync)
72 {
73 snapshot = _activeEffects.ToArray();
74 _activeEffects.Clear();
75 }
76
77 foreach (var effect in snapshot)
78 {
79 effect.CancelInternal(restoreState: true);
80 Invalidate(effect.Target);
81 }
82 }
83
84 internal void Update(long tick)
85 {
86 if (!_lastTick.HasValue)
87 {
88 _lastTick = tick;
89 return;
90 }
91
92 float deltaSeconds = HighResTimer.GetDuration(_lastTick.Value, tick);
93 _lastTick = tick;
94 Advance(deltaSeconds);
95 }
96
97 internal void Advance(float deltaSeconds)
98 {
99 DisplayEffect[] snapshot;
100
101 lock (_sync)
102 snapshot = _activeEffects.ToArray();
103
104 foreach (var effect in snapshot)
105 {
106 if (!OwnsTarget(effect.Target))
107 {
108 RemoveAndCancel(effect, restoreState: false);
109 continue;
110 }
111
112 bool finished = effect.AdvanceInternal(deltaSeconds);
113 Invalidate(effect.Target);
114
115 if (finished)
116 {
117 lock (_sync)
118 _activeEffects.Remove(effect);
119 }
120 }
121 }
122
123 internal void Invalidate(object target)
124 {
125 if (target is View or SceneLayer)
126 _host.Scene.FullRefreshNeeded = true;
127 }
128
129 private TEffect RunCore<TEffect>(object target, TEffect effect)
130 where TEffect : DisplayEffect
131 {
132 ObjectDisposedException.ThrowIf(_disposed, this);
133 ArgumentNullException.ThrowIfNull(target);
134 ArgumentNullException.ThrowIfNull(effect);
135
136 if (!OwnsTarget(target))
137 throw new ArgumentException(
138 "The effect target is not owned by this render surface host.",
139 nameof(target));
140
141 if (!effect.SupportsTarget(target))
142 throw new ArgumentException(
143 $"{effect.GetType().Name} does not support {target.GetType().Name} targets.",
144 nameof(effect));
145
146 DisplayEffect? replaced;
147
148 lock (_sync)
149 {
150 if (effect.Status != EffectStatus.Pending)
151 throw new InvalidOperationException("An effect instance can only be run once.");
152
153 replaced = _activeEffects.FirstOrDefault(
154 active => ReferenceEquals(active.Target, target)
155 && active.Channel == effect.Channel);
156
157 if (replaced is not null)
158 _activeEffects.Remove(replaced);
159
160 _activeEffects.Add(effect);
161 }
162
163 // Preserve the current presentation value when replacing an effect so the
164 // new effect can continue from it without a one-frame reset.
165 replaced?.CancelInternal(restoreState: false);
166
167 try
168 {
169 effect.StartInternal(this, target);
170 Invalidate(target);
171 }
172 catch
173 {
174 lock (_sync)
175 _activeEffects.Remove(effect);
176
177 throw;
178 }
179
180 if (effect.Status != EffectStatus.Running)
181 {
182 lock (_sync)
183 _activeEffects.Remove(effect);
184 }
185
186 return effect;
187 }
188
189 private bool OwnsTarget(object target) => target switch
190 {
191 View view => _host.ViewManager.Views.Contains(view),
192 SceneLayer layer => ReferenceEquals(layer.Scene, _host.Scene)
193 && _host.Scene.SceneLayers.Contains(layer),
194 _ => false
195 };
196
197 private void RemoveAndCancel(DisplayEffect effect, bool restoreState)
198 {
199 lock (_sync)
200 _activeEffects.Remove(effect);
201
202 effect.CancelInternal(restoreState);
203 }
204
206 public void Dispose()
207 {
208 if (_disposed)
209 return;
210
211 _disposed = true;
212 CancelAll();
213 }
214}
Base class for transient effects applied to a View or SceneLayer.
ReadOnlyCollection< DisplayEffect > ActiveEffects
Gets a snapshot of the effects that are currently running.
void CancelAll()
Cancels every running effect owned by this manager.
void Cancel(DisplayEffect effect)
Cancels a running effect and restores its original presentation state.
TEffect Run< TEffect >(View target, TEffect effect)
Starts an effect targeting a View owned by this render surface.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
Represents a rendered view of a scene, combining a camera position with a viewport configuration to c...
Definition View.cs:14
Represents a single layer within a Scene, containing a grid of tiles and supporting various coordinat...
Definition SceneLayer.cs:40
Scene Scene
Gets or sets the parent scene that contains this layer.
Scene()
Initializes a new instance of the Scene class with an empty layer collection.
Definition Scene.cs:112
@ SceneLayer
Performs direct drawing within a scene layer using world-space coordinates.
EffectStatus
Describes the current lifecycle state of a display effect.
@ View
Displays camera position, viewport zoom, and viewport dimensions.