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
Camera.cs
1using System.Drawing;
2using System.Numerics;
6
8
15public sealed class Camera
16{
17 private readonly Scene _scene;
18 private PointF _positionPx = new(0, 0);
19 private Func<PointF>? _followWorldPx;
20 private bool _hardFollow;
21 private SceneLayer? _followLayer;
22 private int _followAxis; // 0 = both, 1 = screen X, 2 = screen Y
23 private PointF _followAnchor = new(0.5f, 0.5f);
24 private SceneLayer? TopologyLayer => _followLayer ?? _scene.SceneLayers.FirstOrDefault(layer => layer.Visible);
25
26 // Explicit pan-to-target (camera upper-left) state.
27 // This is used by PanTo and is independent of the "follow" target.
28 private PointF? _panTargetUpperLeftPx;
29 private float _panLerpPerSecond;
30
36 public PointF PositionPx
37 {
38 get => _positionPx;
39 private set
40 {
41 if (_positionPx != value)
42 {
43 _positionPx = value;
44
45 if (_scene is not null)
46 _scene.FullRefreshNeeded = true;
47 }
48 }
49 }
50
58 public RectangleF WorldBoundsPx { get; set; } = RectangleF.Empty;
59
65 public Rectangle DeadZonePx { get; set; } = Rectangle.Empty;
66
72 public float FollowLerpPerSecond { get; set; } = 8f;
73
74 internal Camera(Scene scene) => _scene = scene ?? throw new ArgumentNullException(nameof(scene));
75
80 internal Func<SizeF> GetVisibleWorldSizePx { get; set; } = () => new SizeF(1280, 720);
81
82 #region Camera movement methods
83
92 public void SnapTo(PointF worldUpperLeftPx)
93 {
94 PositionPx = ClampToWorldBounds(worldUpperLeftPx);
95 }
96
105 public void CenterOn(PointF worldCenterPx)
106 {
107 var vis = GetVisibleWorldSizePx();
108 SnapTo(new PointF(worldCenterPx.X - vis.Width * 0.5f,
109 worldCenterPx.Y - vis.Height * 0.5f));
110 }
111
120 public void CenterOnGrid(SceneLayer layer, int col, int row)
121 {
122 // Anchor (top-left of tile)
123 var anchor = layer.GridToWorldPx(new PointF(col, row));
124 float tileCenterX = anchor.X + layer.TileWidth * 0.5f;
125 float tileCenterY = anchor.Y + layer.TileHeight * 0.5f;
126
127 var vis = GetVisibleWorldSizePx();
128
129 // Camera is top-left, so subtract half the visible size to center that point.
130 var camTopLeft = new PointF(
131 tileCenterX - vis.Width * 0.5f,
132 tileCenterY - vis.Height * 0.5f);
133
134 SnapTo(camTopLeft);
135 }
136
148 public void PanCenterTo(PointF worldCenterPx, float speed)
149 {
150 FollowLerpPerSecond = speed;
151
152 // For center-based pans, we treat the target as a "point of interest"
153 // and let the follow/dead-zone logic convert it to a camera UL.
154 Follow(() => worldCenterPx, hardFollow: false);
155 }
156
165 public void AnimateCenterOnGrid(SceneLayer layer, int col, int row, float speed)
166 {
167 // Compute world center of the tile and reuse PanCenterTo.
168 var anchor = layer.GridToWorldPx(new PointF(col, row));
169 float tileCenterX = anchor.X + layer.TileWidth * 0.5f;
170 float tileCenterY = anchor.Y + layer.TileHeight * 0.5f;
171
172 PanCenterTo(new PointF(tileCenterX, tileCenterY), speed);
173 }
174
187 public void PanTo(PointF worldTopLeftPx, float speed)
188 {
189 // Cancel any center-based follow when we take direct manual control.
190 _followAxis = 0;
191 _followLayer = null;
192 _followWorldPx = null;
193 _hardFollow = false;
194
195 if (speed <= 0f)
196 {
197 _panTargetUpperLeftPx = null;
198 SnapTo(worldTopLeftPx);
199 return;
200 }
201
202 _panLerpPerSecond = speed;
203 _panTargetUpperLeftPx = ClampToWorldBounds(worldTopLeftPx);
204 }
205
220 public void PanCenterToOnce(PointF worldCenterPx, float speed)
221 {
222 // Compute the desired camera upper-left such that worldCenterPx
223 // ends up in the middle of the visible region.
224 var vis = GetVisibleWorldSizePx();
225
226 var desiredUpperLeft = new PointF(
227 worldCenterPx.X - vis.Width * 0.5f,
228 worldCenterPx.Y - vis.Height * 0.5f);
229
230 // Delegate to the existing pan-to-upper-left logic (which also clamps).
231 PanTo(desiredUpperLeft, speed);
232 }
233
247 public void PanToOverDuration(PointF worldTopLeftPx, float durationSeconds)
248 {
249 if (durationSeconds <= 0f)
250 {
251 // Degenerate case — behave like SnapTo().
252 _panTargetUpperLeftPx = null;
253 SnapTo(worldTopLeftPx);
254 return;
255 }
256
257 // In an exponential smoothing system:
258 // pos(t) = target + (pos0 - target) * exp(-k * t)
259 //
260 // To reach ~99% of target position in T seconds:
261 // exp(-k*T) = 0.01 → k = -ln(0.01) / T
262 //
263 // This produces a visually nice, predictable time-to-arrive.
264 float k = -(float)Math.Log(0.01f) / durationSeconds;
265
266 // Cancel following and start a timed pan using your existing machinery.
267 _followAxis = 0;
268 _followLayer = null;
269 _followWorldPx = null;
270 _hardFollow = false;
271
272 _panLerpPerSecond = k;
273 _panTargetUpperLeftPx = ClampToWorldBounds(worldTopLeftPx);
274 }
275
289 public void PanCenterToOverDuration(PointF worldCenterPx, float durationSeconds)
290 {
291 // Compute the desired camera upper-left such that worldCenterPx
292 // ends up in the middle of the visible region.
293 var vis = GetVisibleWorldSizePx();
294
295 var desiredUpperLeft = new PointF(
296 worldCenterPx.X - vis.Width * 0.5f,
297 worldCenterPx.Y - vis.Height * 0.5f);
298
299 // Delegate to the duration-based UL pan logic.
300 PanToOverDuration(desiredUpperLeft, durationSeconds);
301 }
302
317 public void PanToGridOverDuration(SceneLayer layer, int col, int row, float durationSeconds)
318 {
319 if (layer is null) throw new ArgumentNullException(nameof(layer));
320
321 // Same pattern as AnimateCenterOnGrid: get the tile's world-space anchor
322 // (top-left), then offset by half the tile size to get its visual center.
323 var anchor = layer.GridToWorldPx(new PointF(col, row));
324 float tileCenterX = anchor.X + layer.TileWidth * 0.5f;
325 float tileCenterY = anchor.Y + layer.TileHeight * 0.5f;
326
327 // Then pan so that tile center ends up at the center of the view
328 // over the requested duration.
329 PanCenterToOverDuration(new PointF(tileCenterX, tileCenterY), durationSeconds);
330 }
331
338 public void PanBy(PointF deltaPx)
339 {
340 SnapTo(new PointF(PositionPx.X + deltaPx.X,
341 PositionPx.Y + deltaPx.Y));
342 }
343
347 internal void CancelPan()
348 {
349 _panTargetUpperLeftPx = null;
350 _panLerpPerSecond = 0f;
351 }
352
368 public void Follow(Func<PointF> getWorldPixel, bool hardFollow = false)
369 {
370 FollowAt(getWorldPixel, new PointF(0.5f, 0.5f), hardFollow);
371 }
372
396 public void FollowAt(Func<PointF> getWorldPixel, PointF viewportAnchor, bool hardFollow = false)
397 {
398 ArgumentNullException.ThrowIfNull(getWorldPixel);
399 ValidateViewportAnchor(viewportAnchor, nameof(viewportAnchor));
400
401 _followAxis = 0;
402 _followLayer = null;
403 _followWorldPx = getWorldPixel;
404 _followAnchor = viewportAnchor;
405 _hardFollow = hardFollow;
406 }
407
416 public void FollowTo(PointF worldPointOfInterestPx)
417 {
418 // Freeze the value so the camera lerps toward a fixed point.
419 Follow(() => worldPointOfInterestPx);
420 }
421
430 public void FollowCentered(IMovableOnSceneLayer target, float speed = -1f, bool hard = false)
431 {
432 FollowAt(target, new PointF(0.5f, 0.5f), speed, hard);
433 }
434
450 public void FollowAt(IMovableOnSceneLayer target, PointF viewportAnchor, float speed = -1f, bool hard = false)
451 {
452 ArgumentNullException.ThrowIfNull(target);
453 ValidateViewportAnchor(viewportAnchor, nameof(viewportAnchor));
454
455 if (speed > 0f)
456 FollowLerpPerSecond = speed;
457
458 FollowAt(() =>
459 {
460 var layer = target.SceneLayer;
461 var pos = target.GetPosition();
462
463 return target.PositionSpace == MovementSpace.Grid
464 ? GetCenteredTile(layer, pos)
465 : new PointF(pos.X, pos.Y);
466 },
467 viewportAnchor,
468 hard);
469 _followLayer = target.SceneLayer;
470 }
471
479 public void FollowCenteredX(IMovableOnSceneLayer target, float speed = -1f, bool hard = false)
480 {
481 FollowAtX(target, 0.5f, speed, hard);
482 }
483
499 public void FollowAtX(IMovableOnSceneLayer target, float horizontalAnchor, float speed = -1f, bool hard = false)
500 {
501 ArgumentNullException.ThrowIfNull(target);
502 ValidateNormalizedAnchor(horizontalAnchor, nameof(horizontalAnchor));
503
504 if (speed > 0f)
505 FollowLerpPerSecond = speed;
506
507 FollowAt(() =>
508 {
509 var layer = target.SceneLayer;
510 var pos = target.GetPosition();
511
512 float worldCenterX;
513 if (target.PositionSpace == MovementSpace.Grid)
514 {
515 var anchor = layer.GridToWorldPx(new PointF(pos.X, pos.Y));
516 worldCenterX = anchor.X + layer.TileWidth * 0.5f;
517 }
518 else
519 {
520 worldCenterX = pos.X;
521 }
522
523 var vis = GetVisibleWorldSizePx();
524 return new PointF(
525 worldCenterX,
526 PositionPx.Y + vis.Height * 0.5f);
527 },
528 new PointF(horizontalAnchor, 0.5f),
529 hard);
530 _followLayer = target.SceneLayer;
531 _followAxis = 1;
532 }
533
541 public void FollowCenteredY(IMovableOnSceneLayer target, float speed = -1f, bool hard = false)
542 {
543 FollowAtY(target, 0.5f, speed, hard);
544 }
545
561 public void FollowAtY(IMovableOnSceneLayer target, float verticalAnchor, float speed = -1f, bool hard = false)
562 {
563 ArgumentNullException.ThrowIfNull(target);
564 ValidateNormalizedAnchor(verticalAnchor, nameof(verticalAnchor));
565
566 if (speed > 0f)
567 FollowLerpPerSecond = speed;
568
569 FollowAt(() =>
570 {
571 var layer = target.SceneLayer;
572 var pos = target.GetPosition();
573
574 float worldCenterY;
575 if (target.PositionSpace == MovementSpace.Grid)
576 {
577 var anchor = layer.GridToWorldPx(new PointF(pos.X, pos.Y));
578 worldCenterY = anchor.Y + layer.TileHeight * 0.5f;
579 }
580 else
581 {
582 worldCenterY = pos.Y;
583 }
584
585 var vis = GetVisibleWorldSizePx();
586 return new PointF(
587 PositionPx.X + vis.Width * 0.5f,
588 worldCenterY);
589 },
590 new PointF(0.5f, verticalAnchor),
591 hard);
592 _followLayer = target.SceneLayer;
593 _followAxis = 2;
594 }
595
600 public void ClearFollow()
601 {
602 _followAxis = 0;
603 _followLayer = null;
604 _followWorldPx = null;
605 _followAnchor = new PointF(0.5f, 0.5f);
606 _panTargetUpperLeftPx = null;
607 _hardFollow = false;
608 }
609
610 #endregion Camera movement methods
611
612 internal void Update(float dtSeconds)
613 {
614 // 1) Explicit pan-to-UL (PanTo) takes priority over center-follow.
615 if (_panTargetUpperLeftPx is { } panTarget)
616 {
617 var clamped = ClampToWorldBounds(panTarget);
618
619 if (_hardFollow || _panLerpPerSecond <= 0f)
620 {
621 PositionPx = clamped;
622 _panTargetUpperLeftPx = null;
623 }
624 else
625 {
626 float t = 1f - (float)Math.Exp(-_panLerpPerSecond * Math.Max(0f, dtSeconds));
627 var newPos = new PointF(
628 PositionPx.X + (clamped.X - PositionPx.X) * t,
629 PositionPx.Y + (clamped.Y - PositionPx.Y) * t);
630
631 PositionPx = newPos;
632
633 // Close enough → snap and finish.
634 if (Math.Abs(newPos.X - clamped.X) < 0.5f &&
635 Math.Abs(newPos.Y - clamped.Y) < 0.5f)
636 {
637 PositionPx = clamped;
638 _panTargetUpperLeftPx = null;
639 }
640 }
641
642 // If we're actively panning, don't also run center-follow this frame.
643 if (_panTargetUpperLeftPx is not null)
644 return;
645 }
646
647 // 2) Center-follow / dead-zone logic.
648 if (_followWorldPx is null)
649 return;
650
651 var target = _followWorldPx();
652 if (TopologyLayer is { } layer && (layer.WrapHorizontally || layer.WrapVertically))
653 {
654 var visible = GetVisibleWorldSizePx();
655 var followReference = new PointF(
656 PositionPx.X + visible.Width * _followAnchor.X,
657 PositionPx.Y + visible.Height * _followAnchor.Y);
658 var period = layer.GetPeriod();
659 if (_followAxis == 1)
660 {
661 target = NearestWrappedByAxis(period, target, followReference, followX: true);
662 target.Y = followReference.Y;
663 }
664 else if (_followAxis == 2)
665 {
666 target = NearestWrappedByAxis(period, target, followReference, followX: false);
667 target.X = followReference.X;
668 }
669 else
670 {
671 target = period.Nearest(target, followReference);
672 }
673 }
674 var desiredUL = DesiredUpperLeftToContainTarget(target);
675 if (_hardFollow || FollowLerpPerSecond <= 0f)
676 {
677 PositionPx = ClampToWorldBounds(desiredUL);
678 }
679 else
680 {
681 float t = 1f - (float)Math.Exp(-FollowLerpPerSecond * Math.Max(0f, dtSeconds));
682 var clamped = ClampToWorldBounds(desiredUL);
683 PositionPx = new PointF(
684 PositionPx.X + (clamped.X - PositionPx.X) * t,
685 PositionPx.Y + (clamped.Y - PositionPx.Y) * t);
686 }
687 }
688
689 #region private methods
690
691 private PointF DesiredUpperLeftToContainTarget(PointF targetWorldPx)
692 {
693 var vis = GetVisibleWorldSizePx();
694 if (DeadZonePx == Rectangle.Empty)
695 return new PointF(
696 targetWorldPx.X - vis.Width * _followAnchor.X,
697 targetWorldPx.Y - vis.Height * _followAnchor.Y);
698
699 var viewWorld = new RectangleF(PositionPx.X, PositionPx.Y, vis.Width, vis.Height);
700 var dzWorld = new RectangleF(viewWorld.X + DeadZonePx.X,
701 viewWorld.Y + DeadZonePx.Y,
702 DeadZonePx.Width, DeadZonePx.Height);
703
704 if (dzWorld.Contains(targetWorldPx))
705 return PositionPx;
706
707 float newX = PositionPx.X;
708 float newY = PositionPx.Y;
709
710 if (targetWorldPx.X < dzWorld.Left)
711 newX -= dzWorld.Left - targetWorldPx.X;
712
713 if (targetWorldPx.X > dzWorld.Right)
714 newX += targetWorldPx.X - dzWorld.Right;
715
716 if (targetWorldPx.Y < dzWorld.Top)
717 newY -= dzWorld.Top - targetWorldPx.Y;
718
719 if (targetWorldPx.Y > dzWorld.Bottom)
720 newY += targetWorldPx.Y - dzWorld.Bottom;
721
722 return new PointF(newX, newY);
723 }
724
725 private static PointF NearestWrappedByAxis(LayerPeriod period, PointF point, PointF reference, bool followX)
726 {
727 var delta = new PointF(reference.X - point.X, reference.Y - point.Y);
728 var coefficients = period.Coefficients(delta);
729 int baseColumn = (int)Math.Round(coefficients.X);
730 int baseRow = (int)Math.Round(coefficients.Y);
731
732 int minColumn = period.WrapColumns ? baseColumn - 4 : 0;
733 int maxColumn = period.WrapColumns ? baseColumn + 4 : 0;
734 int minRow = period.WrapRows ? baseRow - 4 : 0;
735 int maxRow = period.WrapRows ? baseRow + 4 : 0;
736
737 var best = period.Nearest(point, reference);
738 float bestAxisDistance = followX ? Math.Abs(best.X - reference.X) : Math.Abs(best.Y - reference.Y);
739 float bestDistanceSquared = (best.X - reference.X) * (best.X - reference.X) + (best.Y - reference.Y) * (best.Y - reference.Y);
740
741 for (int c = minColumn; c <= maxColumn; c++)
742 for (int r = minRow; r <= maxRow; r++)
743 {
744 var offset = period.Offset(c, r);
745 var candidate = new PointF(point.X + offset.X, point.Y + offset.Y);
746 float axisDistance = followX ? Math.Abs(candidate.X - reference.X) : Math.Abs(candidate.Y - reference.Y);
747 float distanceSquared = (candidate.X - reference.X) * (candidate.X - reference.X) +
748 (candidate.Y - reference.Y) * (candidate.Y - reference.Y);
749 if (axisDistance < bestAxisDistance ||
750 (axisDistance == bestAxisDistance && distanceSquared < bestDistanceSquared))
751 {
752 best = candidate;
753 bestAxisDistance = axisDistance;
754 bestDistanceSquared = distanceSquared;
755 }
756 }
757
758 return best;
759 }
760
761 private PointF ClampToWorldBounds(PointF ul)
762 {
763 if (WorldBoundsPx == RectangleF.Empty)
764 return ul;
765
766 var vis = GetVisibleWorldSizePx();
767 if (TopologyLayer is { } layer && (layer.WrapHorizontally || layer.WrapVertically))
768 {
769 var period = layer.GetPeriod();
770 var position = period.Coefficients(ul);
771 PointF[] Corners(RectangleF rect) => new[] {
772 period.Coefficients(new(rect.Left, rect.Top)), period.Coefficients(new(rect.Right, rect.Top)),
773 period.Coefficients(new(rect.Left, rect.Bottom)), period.Coefficients(new(rect.Right, rect.Bottom)) };
774 var bounds = Corners(WorldBoundsPx);
775 var viewport = Corners(new(0, 0, vis.Width, vis.Height));
776 float lowX = bounds.Min(p => p.X) - viewport.Min(p => p.X);
777 float lowY = bounds.Min(p => p.Y) - viewport.Min(p => p.Y);
778 float highX = Math.Max(lowX, bounds.Max(p => p.X) - viewport.Max(p => p.X));
779 float highY = Math.Max(lowY, bounds.Max(p => p.Y) - viewport.Max(p => p.Y));
780 return period.Offset(layer.WrapHorizontally ? position.X : Math.Clamp(position.X, lowX, highX),
781 layer.WrapVertically ? position.Y : Math.Clamp(position.Y, lowY, highY));
782 }
783 float minX = WorldBoundsPx.Left;
784 float minY = WorldBoundsPx.Top;
785 float maxX = WorldBoundsPx.Right - vis.Width;
786 float maxY = WorldBoundsPx.Bottom - vis.Height;
787
788 if (maxX < minX)
789 maxX = minX;
790
791 if (maxY < minY)
792 maxY = minY;
793
794 return new PointF(
795 Math.Clamp(ul.X, minX, maxX),
796 Math.Clamp(ul.Y, minY, maxY));
797 }
798
799 private static void ValidateViewportAnchor(PointF viewportAnchor, string paramName)
800 {
801 ValidateNormalizedAnchor(viewportAnchor.X, paramName);
802 ValidateNormalizedAnchor(viewportAnchor.Y, paramName);
803 }
804
805 private static void ValidateNormalizedAnchor(float value, string paramName)
806 {
807 if (!float.IsFinite(value) || value < 0f || value > 1f)
808 throw new ArgumentOutOfRangeException(paramName, value, "Camera follow anchors must be finite values from 0 through 1.");
809 }
810
818 private static PointF GetCenteredTile(SceneLayer layer, Vector2 gridPos)
819 {
820 var anchor = layer.GridToWorldPx(new PointF(gridPos.X, gridPos.Y));
821 return new PointF(
822 anchor.X + layer.TileWidth * 0.5f,
823 anchor.Y + layer.TileHeight * 0.5f);
824 }
825
826 #endregion private methods
827}
Represents a world-space camera for a Scene. The camera tracks a world-space upper-left position in p...
Definition Camera.cs:16
void PanCenterToOverDuration(PointF worldCenterPx, float durationSeconds)
Smoothly pans the camera so that the specified world-space point becomes the visual center of the vie...
Definition Camera.cs:289
void PanToGridOverDuration(SceneLayer layer, int col, int row, float durationSeconds)
Smoothly pans the camera so that the specified grid tile becomes the visual center of the view over a...
Definition Camera.cs:317
void FollowAtY(IMovableOnSceneLayer target, float verticalAnchor, float speed=-1f, bool hard=false)
Smoothly follows an IMovable target vertically while keeping its center at a normalized vertical posi...
Definition Camera.cs:561
Rectangle DeadZonePx
Size of the dead zone, in pixels, around the center of the view where the follow target is allowed to...
Definition Camera.cs:65
float FollowLerpPerSecond
Controls how quickly the camera moves toward its follow target when smooth follow is enabled....
Definition Camera.cs:72
void FollowCentered(IMovableOnSceneLayer target, float speed=-1f, bool hard=false)
Makes the camera follow an IMovable-on-SceneLayer so that the object stays centered in the view....
Definition Camera.cs:430
void SnapTo(PointF worldUpperLeftPx)
Instantly moves the camera to the specified world-space position, interpreted as the upper-left corne...
Definition Camera.cs:92
void AnimateCenterOnGrid(SceneLayer layer, int col, int row, float speed)
Smoothly pans the camera until the specified grid tile is centered in the view. Uses the given follow...
Definition Camera.cs:165
void FollowCenteredY(IMovableOnSceneLayer target, float speed=-1f, bool hard=false)
Smoothly follows an IMovable target, centering it vertically only. Horizontal camera position is left...
Definition Camera.cs:541
void Follow(Func< PointF > getWorldPixel, bool hardFollow=false)
Configures the camera to follow a dynamically supplied world-space target position....
Definition Camera.cs:368
void FollowAtX(IMovableOnSceneLayer target, float horizontalAnchor, float speed=-1f, bool hard=false)
Smoothly follows an IMovable target horizontally while keeping its center at a normalized horizontal ...
Definition Camera.cs:499
void PanCenterToOnce(PointF worldCenterPx, float speed)
Smoothly pans the camera so that the specified world-space point becomes the visual center of the vie...
Definition Camera.cs:220
PointF PositionPx
Gets or sets the camera's world-space position in pixels, interpreted as the upper-left corner of the...
Definition Camera.cs:37
void ClearFollow()
Clears any active follow or pan targets; the camera remains at its current position until moved again...
Definition Camera.cs:600
void CenterOnGrid(SceneLayer layer, int col, int row)
Instantly centers the camera on the specified tile in the given SceneLayer. The tile's visual center ...
Definition Camera.cs:120
void CenterOn(PointF worldCenterPx)
Instantly repositions the camera so that the given world-space point appears at the center of the vie...
Definition Camera.cs:105
void PanCenterTo(PointF worldCenterPx, float speed)
Smoothly pans the camera so that the given world-space point ends up at the center of the visible vie...
Definition Camera.cs:148
void FollowAt(Func< PointF > getWorldPixel, PointF viewportAnchor, bool hardFollow=false)
Configures the camera to follow a dynamically supplied world-space target while keeping that target a...
Definition Camera.cs:396
void FollowCenteredX(IMovableOnSceneLayer target, float speed=-1f, bool hard=false)
Smoothly follows an IMovable target, centering it horizontally only. Vertical camera position is left...
Definition Camera.cs:479
void PanToOverDuration(PointF worldTopLeftPx, float durationSeconds)
Smoothly pans the camera so that its upper-left pixel reaches the specified world-space target over a...
Definition Camera.cs:247
void FollowTo(PointF worldPointOfInterestPx)
Starts smooth camera movement toward a fixed world-space "point ofinterest" (typically a center point...
Definition Camera.cs:416
void FollowAt(IMovableOnSceneLayer target, PointF viewportAnchor, float speed=-1f, bool hard=false)
Makes the camera follow an IMovable-on-SceneLayer while keeping the object's center at a normalized p...
Definition Camera.cs:450
RectangleF WorldBoundsPx
World-space rectangle (in pixels) that the camera is allowed to move within. Clamping logic uses this...
Definition Camera.cs:58
void PanTo(PointF worldTopLeftPx, float speed)
Smoothly pans the camera toward a world-space top-left position for the view, using the given follow ...
Definition Camera.cs:187
void PanBy(PointF deltaPx)
Instantly pans the camera by the given offset in world-space pixels. This adds the offset to the curr...
Definition Camera.cs:338
Represents a single layer within a Scene, containing a grid of tiles and supporting various coordinat...
Definition SceneLayer.cs:40
int TileHeight
Gets or sets the height of each tile in this layer, measured in pixels.
PointF GridToWorldPx(PointF grid)
Converts a grid coordinate (col,row) into the projection-defined world-space pixel anchor for that ti...
int TileWidth
Gets or sets the width of each tile in this layer, measured in pixels.
Represents a game scene containing multiple layers of tiles, sprites, and game objects....
Definition Scene.cs:34
Implemented by grid-space IMovable objects that belong to a specific Scenes.SceneLayer.
SceneLayer SceneLayer
Gets the Scenes.SceneLayer that this movable object belongs to.
Vector2 GetPosition()
Get the current position in the mover's PositionSpace.
MovementSpace PositionSpace
Which unit system this mover uses for its position.
Definition IMovable.cs:14
@ Rectangle
Particles are spawned uniformly within a rectangular area. This is the default distribution.
MovementSpace
Specifies the coordinate system used for movement calculations and updates.
@ Grid
Movement is in a scene's grid/tile coordinate system.