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
ImageInstanceLayer.cs
1using System.Drawing;
8using SkiaSharp;
9
11
108{
109 private readonly Random _rng = new();
110 private readonly SKPaint _paint = new()
111 {
112 IsAntialias = true,
113 BlendMode = SKBlendMode.SrcOver
114 };
115
116 private long _instanceLastTick;
117
121 public List<ImageInstance> Instances { get; } = new();
122
160 public Func<Rectangle, Random, IEnumerable<ImageInstance>>? Initializer { get; set; }
161
171 public Func<ImageInstance, Rectangle, bool>? ShouldRecycle { get; set; }
172
201 public Func<ImageInstance, Rectangle, Random, ImageInstance>? RecycleInstance { get; set; }
202
225 public Action<ImageInstance, float>? UpdateInstance { get; set; }
226
235 View view,
236 Rectangle screenBounds,
237 string? nickname = null)
238 : base(renderSurfaceHost,
240 sceneLayer: null,
241 view: view,
242 screenBounds: screenBounds,
243 worldBounds: null,
244 name: nickname)
245 {
246 }
247
256 SceneLayer sceneLayer,
257 Rectangle worldBounds,
258 string? nickname = null)
259 : base(renderSurfaceHost,
261 sceneLayer: sceneLayer,
262 view: null,
263 screenBounds: null,
264 worldBounds: worldBounds,
265 name: nickname)
266 {
267 }
268
281 View view,
282 Rectangle screenBounds,
283 Func<Rectangle, Random, IEnumerable<ImageInstance>>? initializer,
284 Func<ImageInstance, Rectangle, bool>? shouldRecycle = null,
285 Func<ImageInstance, Rectangle, Random, ImageInstance>? recycleInstance = null,
286 Action<ImageInstance, float>? updateInstance = null,
287 string? nickname = null)
288 : this(renderSurfaceHost, view, screenBounds, nickname)
289 {
290 Initializer = initializer;
291 ShouldRecycle = shouldRecycle;
292 RecycleInstance = recycleInstance;
293 UpdateInstance = updateInstance;
294
296 }
297
311 SceneLayer sceneLayer,
312 Rectangle worldBounds,
313 Func<Rectangle, Random, IEnumerable<ImageInstance>>? initializer,
314 Func<ImageInstance, Rectangle, bool>? shouldRecycle = null,
315 Func<ImageInstance, Rectangle, Random, ImageInstance>? recycleInstance = null,
316 Action<ImageInstance, float>? updateInstance = null,
317 string? nickname = null)
318 : this(renderSurfaceHost, sceneLayer, worldBounds, nickname)
319 {
320 Initializer = initializer;
321 ShouldRecycle = shouldRecycle;
322 RecycleInstance = recycleInstance;
323 UpdateInstance = updateInstance;
324
326 }
327
332 {
333 Instances.Clear();
334
335 var bounds = Mode == DirectDrawingMode.SceneLayer
337 : ScreenBounds;
338
339 if (Initializer is not null)
340 Instances.AddRange(Initializer(bounds, _rng));
341
342 ForceRefresh();
343 }
344
350 public override void Update(long tick)
351 {
352 if (tick <= _lastTick)
353 return;
354
355 float dt = 0f;
356
357 if (_instanceLastTick > 0)
358 dt = HighResTimer.GetDuration(_instanceLastTick, tick);
359
360 _instanceLastTick = tick;
361
362 if (dt <= 0f)
363 {
364 base.Update(tick);
365 return;
366 }
367
368 var bounds = Mode == DirectDrawingMode.SceneLayer
370 : ScreenBounds;
371
372 for (int i = 0; i < Instances.Count; i++)
373 {
374 var instance = Instances[i];
375 var oldRefreshBounds = GetRefreshBounds(instance);
376
377 instance.Bounds = new RectangleF(
378 instance.Bounds.X + (instance.VelocityX * dt),
379 instance.Bounds.Y + (instance.VelocityY * dt),
380 instance.Bounds.Width,
381 instance.Bounds.Height);
382
383 instance.Rotation += instance.AngularVelocity * dt;
384
385 UpdateInstance?.Invoke(instance, dt);
386
387 if (ShouldRecycle is not null &&
388 RecycleInstance is not null &&
389 ShouldRecycle(instance, bounds))
390 {
391 var recycled = RecycleInstance(instance, bounds, _rng);
392 Instances[i] = recycled;
393
394 AddRefreshRect(oldRefreshBounds);
395 AddRefreshRect(GetRefreshBounds(recycled));
396 }
397 else
398 {
399 AddRefreshRect(oldRefreshBounds);
400 AddRefreshRect(GetRefreshBounds(instance));
401 }
402 }
403
404 base.Update(tick);
405 }
406
412 protected override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
413 {
414 var canvas = backbuffer.Canvas;
415
416 Rectangle srcBounds = Mode == DirectDrawingMode.SceneLayer
418 : ScreenBounds;
419
420 float sx = srcBounds.Width > 0
421 ? destRectScreen.Width / srcBounds.Width
422 : 1f;
423
424 float sy = srcBounds.Height > 0
425 ? destRectScreen.Height / srcBounds.Height
426 : 1f;
427
428 canvas.Save();
429 canvas.ClipRect(destRectScreen.ToSKRect());
430
431 foreach (var instance in Instances)
432 {
433 _paint.Color = instance.Tint;
434
435 var dst = new SKRect(
436 destRectScreen.Left + ((instance.Bounds.Left - srcBounds.Left) * sx),
437 destRectScreen.Top + ((instance.Bounds.Top - srcBounds.Top) * sy),
438 destRectScreen.Left + ((instance.Bounds.Right - srcBounds.Left) * sx),
439 destRectScreen.Top + ((instance.Bounds.Bottom - srcBounds.Top) * sy));
440
441 if (instance.Rotation != 0f)
442 {
443 float cx = (dst.Left + dst.Right) * 0.5f;
444 float cy = (dst.Top + dst.Bottom) * 0.5f;
445
446 canvas.Save();
447 canvas.RotateDegrees(instance.Rotation, cx, cy);
448 canvas.DrawBitmap(instance.Bitmap, dst, _paint);
449 canvas.Restore();
450 }
451 else
452 {
453 canvas.DrawBitmap(instance.Bitmap, dst, _paint);
454 }
455 }
456
457 canvas.Restore();
458 }
459
464 protected override void Dispose(bool disposing)
465 {
466 if (disposing)
467 _paint.Dispose();
468
469 base.Dispose(disposing);
470 }
471
476 private void AddRefreshRect(RectangleF rect)
477 {
478 const float pad = 4f;
479
480 var expanded = Rectangle.Ceiling(RectangleF.Inflate(rect, pad, pad));
481
482 if (Mode == DirectDrawingMode.SceneLayer)
483 {
484 SceneLayer!.RefreshQueue.AddWorldRect(expanded);
485 return;
486 }
487
488 foreach (var sceneLayer in RenderSurfaceHost.Scene.SceneLayers)
489 sceneLayer.RefreshQueue.AddViewScreenRect(View!, sceneLayer, expanded);
490 }
491
497 private static RectangleF GetRefreshBounds(ImageInstance instance)
498 {
499 if (instance.Rotation == 0f && instance.AngularVelocity == 0f)
500 return instance.Bounds;
501
502 float cx = instance.Bounds.Left + (instance.Bounds.Width * 0.5f);
503 float cy = instance.Bounds.Top + (instance.Bounds.Height * 0.5f);
504
505 float halfWidth = instance.Bounds.Width * 0.5f;
506 float halfHeight = instance.Bounds.Height * 0.5f;
507 float halfDiagonal = MathF.Sqrt((halfWidth * halfWidth) + (halfHeight * halfHeight));
508
509 return new RectangleF(
510 cx - halfDiagonal,
511 cy - halfDiagonal,
512 halfDiagonal * 2f,
513 halfDiagonal * 2f);
514 }
515}
View? View
Gets the view to which this direct drawing is attached, or null if the drawing is in scene-layer mode...
long _lastTick
The last engine tick value used for delta-time calculations in Update.
RenderSurfaceHostBase RenderSurfaceHost
Gets the render surface host that manages this direct drawing's rendering pipeline.
SceneLayer? SceneLayer
Gets the scene layer to which this direct drawing is attached, or null if the drawing is in view mode...
Rectangle WorldBounds
Gets or sets the world-space bounds of this direct drawing in pixels.
DirectDrawingMode Mode
Gets the drawing mode that determines how this direct drawing is positioned and transformed.
Rectangle ScreenBounds
Gets or sets the screen-space bounds of this direct drawing in pixels.
DirectDrawingMovableBase(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, SceneLayer? sceneLayer, View? view, Rectangle? screenBounds, Rectangle? worldBounds, string? name=null)
Initializes a new instance of the DirectDrawingMovableBase class with integrated movement capabilitie...
override void Dispose(bool disposing)
Disposes managed resources.
List< ImageInstance > Instances
Gets the collection of image instances managed by this layer.
override void Update(long tick)
Updates all instances, applying built-in motion, optional custom updates, and optional recycling beha...
override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
Renders all image instances to the backbuffer.
Action< ImageInstance, float >? UpdateInstance
Gets or sets an optional callback invoked after the built-in motion update for each instance.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, Func< Rectangle, Random, IEnumerable< ImageInstance > >? initializer, Func< ImageInstance, Rectangle, bool >? shouldRecycle=null, Func< ImageInstance, Rectangle, Random, ImageInstance >? recycleInstance=null, Action< ImageInstance, float >? updateInstance=null, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class and assigns optional hooks.
void InitializeInstances()
Clears the current instance collection and rebuilds it using Initializer, if one is assigned.
Func< ImageInstance, Rectangle, bool >? ShouldRecycle
Gets or sets an optional callback used to determine whether an instance should be recycled.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, Func< Rectangle, Random, IEnumerable< ImageInstance > >? initializer, Func< ImageInstance, Rectangle, bool >? shouldRecycle=null, Func< ImageInstance, Rectangle, Random, ImageInstance >? recycleInstance=null, Action< ImageInstance, float >? updateInstance=null, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class for scene-layer rendering and assigns opti...
Func< ImageInstance, Rectangle, Random, ImageInstance >? RecycleInstance
Gets or sets an optional callback used to create a replacement instance when recycling occurs.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class for scene-layer rendering.
Func< Rectangle, Random, IEnumerable< ImageInstance > >? Initializer
Gets or sets an optional callback used to populate the initial set of instances.
Represents a base class for managing a graphical backbuffer, and is the in-memory surface where rende...
SKCanvas Canvas
Gets the SkiaSharp canvas used for drawing operations on this backbuffer.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
Scene Scene
Gets the source Scenes.Scene used for rendering operations.
ReadOnlyCollection< SceneLayer > SceneLayers
Gets a read-only collection of all layers in this scene, in their current order.
Definition Scene.cs:267
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.
@ SceneLayer
Performs direct drawing within a scene layer using world-space coordinates.