37 private string? _serializedSceneId;
38 private string? _serializedSceneLayerId;
40#pragma warning disable IDE0051
45 [JsonProperty(
"SceneId")]
46 private string? SerializedSceneId
48 get => !ReferenceEquals(_sceneLayer,
SceneLayer.Empty)
49 ? _sceneLayer.Scene?.ID ?? _serializedSceneId
51 set => _serializedSceneId = value;
57 [JsonProperty(
"SceneLayerId")]
58 private string? SerializedSceneLayerId
62 : _serializedSceneLayerId;
63 set => _serializedSceneLayerId = value;
70 [JsonProperty(
"SceneLayer")]
79 _serializedSceneId = value.
Scene?.
ID;
80 _serializedSceneLayerId = value.ID;
84#pragma warning restore IDE0051
90 private Size _renderSize;
91 private float _rotation;
93 internal bool _pendingDispose;
95 [JsonProperty(
"SceneLayerCoordinates")]
96 private PointF _sceneLayerCoordinates;
111 protected internal Sprite(
SceneLayer sceneLayer, Frame frame)
115 SpriteManager.Instance.DefaultCollisionProfile)
125 protected internal Sprite(
128 string collisionProfileName)
130 _sceneLayer = sceneLayer
131 ??
throw new ArgumentNullException(
133 "Sprite must be attached to a SceneLayer.");
135 animator =
new Animator(
this);
144 MovementState.ForSceneLayer(),
149 collisionGroup: CollisionMasks.None,
150 collidesWith: CollisionMasks.None));
155 _renderSize = SpriteManager.Instance.SizeNewSpritesToSceneLayer
156 ?
new Size(_sceneLayer.TileWidth, _sceneLayer.TileHeight)
162 SpriteManager.Instance.AddSprite(
this);
170 internal Sprite(Sprite sprite,
SceneLayer sceneLayer)
172 ArgumentNullException.ThrowIfNull(sprite);
174 _sceneLayer = sceneLayer
175 ??
throw new ArgumentNullException(
177 "Sprite must be attached to a SceneLayer.");
179 animator =
new Animator(
this);
181 frame = sprite.frame;
183 _horizAlign = sprite._horizAlign;
184 _vertAlign = sprite._vertAlign;
185 _nudgeX = sprite._nudgeX;
186 _nudgeY = sprite._nudgeY;
187 _renderSize = sprite._renderSize;
188 _rotation = sprite._rotation;
190 visible = sprite.visible;
191 _sceneLayerCoordinates = sprite.SceneLayerCoordinates;
195 MovementState.ForSceneLayer(),
200 collisionGroup: CollisionMasks.None,
201 collidesWith: CollisionMasks.None));
207 SpriteManager.Instance.AddSprite(
this);
216 private void OnDeserialized(StreamingContext context)
218 animator =
new Animator(
this);
224 if (!ReferenceEquals(_sceneLayer,
SceneLayer.Empty))
226 InitializeDeserializedRuntimeBindings();
227 SpriteManager.Instance.AddSprite(
this);
235 internal string? SerializedSceneIdForBinding =>
236 !ReferenceEquals(_sceneLayer,
SceneLayer.Empty)
237 ? _sceneLayer.Scene?.ID ?? _serializedSceneId
238 : _serializedSceneId;
244 internal string? SerializedSceneLayerIdForBinding =>
245 !ReferenceEquals(_sceneLayer,
SceneLayer.Empty)
247 : _serializedSceneLayerId;
253 internal void RebindSceneLayerAfterDeserialization(
SceneLayer sceneLayer)
255 ArgumentNullException.ThrowIfNull(sceneLayer);
257 if (ReferenceEquals(_sceneLayer, sceneLayer) &&
Movement is not
null)
263 _sceneLayer = sceneLayer;
264 _serializedSceneId = sceneLayer.
Scene?.
ID;
265 _serializedSceneLayerId = sceneLayer.
ID;
267 InitializeDeserializedRuntimeBindings();
270 private void InitializeDeserializedRuntimeBindings()
274 MovementState.ForSceneLayer(),
279 collisionGroup: CollisionMasks.None,
280 collidesWith: CollisionMasks.None));
285 RefreshCollisionProfile();
300 new(_sceneLayerCoordinates.X, _sceneLayerCoordinates.Y);
308 PointF oldCoord = _sceneLayerCoordinates;
311 _sceneLayerCoordinates =
new PointF(pos.X, pos.Y);
314 Rectangle movementWorldRect = Rectangle.Union(oldDraw, newDraw);
315 movementWorldRect.Inflate(5, 5);
317 _sceneLayer.RefreshQueue.AddWorldRect(movementWorldRect);
336 if (!
float.IsFinite(value))
337 throw new ArgumentOutOfRangeException(nameof(value),
"Rotation must be a finite number of degrees.");
339 float normalized = value % 360f;
343 if (_rotation.Equals(normalized))
347 _rotation = normalized;
368 GetRotatedBounds(renderRectScreen,
Rotation);
373 RectangleF destRectScreen)
377 base.Draw(backbuffer, destRectScreen);
381 float centerX = destRectScreen.Left + destRectScreen.Width * 0.5f;
382 float centerY = destRectScreen.Top + destRectScreen.Height * 0.5f;
383 var canvas = backbuffer.
Canvas;
388 canvas.RotateDegrees(
Rotation, centerX, centerY);
389 base.Draw(backbuffer, destRectScreen);
405 if (dx == 0 && dy == 0)
409 var translatedRect = originalRect;
410 translatedRect.X += dx;
411 translatedRect.Y += dy;
413 var originalSceneCoord =
414 GetSceneLayerCoordsFromSpriteWorldRect(originalRect);
415 var translatedSceneCoord =
416 GetSceneLayerCoordsFromSpriteWorldRect(translatedRect);
418 var sceneDelta =
new Vector2(
419 translatedSceneCoord.X - originalSceneCoord.X,
420 translatedSceneCoord.Y - originalSceneCoord.Y);
432 Movement.ZeroVelocityComponent(cancelX, cancelY);
445 get => base.CurrentFrame;
449 base.CurrentFrame = value;
475 if (_sceneLayer !=
null)
497 if (_sceneLayer !=
null)
519 if (_sceneLayer !=
null)
541 if (_sceneLayer !=
null)
563 if (_sceneLayer !=
null)
582 Point pxlPt = _sceneLayer.CoordinateSystem
583 .GetAnchorPixelAtSceneLayerCoordinates(
590 pxlPt.X += (_sceneLayer.TileWidth - _renderSize.Width) / 2;
593 pxlPt.X += _sceneLayer.TileWidth - _renderSize.Width;
600 pxlPt.Y += (_sceneLayer.TileHeight - _renderSize.Height) / 2;
603 pxlPt.Y += _sceneLayer.TileHeight - _renderSize.Height;
610 return new Rectangle(pxlPt, _renderSize);
633 zOrder = Math.Max(1, value);
638 internal PointF GetSceneLayerCoordsFromSpriteWorldRect(Rectangle worldRectPx)
646 worldRectPx.X -= (_sceneLayer.TileWidth - worldRectPx.Width) / 2;
649 worldRectPx.X -= _sceneLayer.TileWidth - worldRectPx.Width;
656 worldRectPx.Y -= (_sceneLayer.TileHeight - worldRectPx.Height) / 2;
659 worldRectPx.Y -= _sceneLayer.TileHeight - worldRectPx.Height;
663 return _sceneLayer.CoordinateSystem.GetSceneLayerCoordinatesAtPixel(
665 worldRectPx.Location);
671 _pendingDispose =
true;
674 internal void DisposeImmediate()
676 GC.SuppressFinalize(
this);
679 if (_sceneLayer !=
null)
689 private void InvalidateVisualChange(Rectangle oldBounds, Rectangle newBounds)
691 Rectangle dirtyBounds = Rectangle.Union(oldBounds, newBounds);
692 dirtyBounds.Inflate(2, 2);
693 _sceneLayer.RefreshQueue.AddWorldRect(dirtyBounds);
697 private static Rectangle GetRotatedBounds(Rectangle rect,
float degrees)
699 RectangleF bounds = GetRotatedBounds(
700 new RectangleF(rect.X, rect.Y, rect.Width, rect.Height),
703 return Rectangle.FromLTRB(
704 (
int)MathF.Floor(bounds.Left),
705 (
int)MathF.Floor(bounds.Top),
706 (
int)MathF.Ceiling(bounds.Right),
707 (
int)MathF.Ceiling(bounds.Bottom));
710 private static RectangleF GetRotatedBounds(RectangleF rect,
float degrees)
712 if (rect.IsEmpty || degrees % 360f == 0f)
715 float radians = degrees * MathF.PI / 180f;
716 float sin = MathF.Abs(MathF.Sin(radians));
717 float cos = MathF.Abs(MathF.Cos(radians));
718 float width = rect.Width * cos + rect.Height * sin;
719 float height = rect.Width * sin + rect.Height * cos;
720 float centerX = rect.Left + rect.Width * 0.5f;
721 float centerY = rect.Top + rect.Height * 0.5f;
723 return new RectangleF(
724 centerX - width * 0.5f,
725 centerY - height * 0.5f,