Compare commits

...

2 Commits

Author SHA1 Message Date
Ian Burgmyer 046bbf4320 Added some basic collision properties to sprites. 5 years ago
Ian Burgmyer b8edb976f7 Unified the drawing routines. 5 years ago
  1. 2
      DotSDL/Graphics/Background.cs
  2. 11
      DotSDL/Graphics/Canvas.cs
  3. 147
      DotSDL/Graphics/SdlWindow.cs
  4. 52
      DotSDL/Graphics/Sprite.cs

2
DotSDL/Graphics/Background.cs

@ -30,7 +30,7 @@ namespace DotSDL.Graphics {
// TODO: Might be able to make this faster by leveraging memcpy(). Need to benchmark it. // TODO: Might be able to make this faster by leveraging memcpy(). Need to benchmark it.
unsafe { unsafe {
var copySize = Width * Height * 4; // Width x Height at 4 bytes per pixel. var copySize = Width * Height * 4; // Width x Height at 4 bytes per pixel.
Render.LockTexture(Texture, IntPtr.Zero, out var pixels, out var _); Render.LockTexture(Texture, IntPtr.Zero, out var pixels, out _);
Buffer.MemoryCopy(GetCanvasPointer().ToPointer(), pixels, copySize, copySize); Buffer.MemoryCopy(GetCanvasPointer().ToPointer(), pixels, copySize, copySize);
Render.UnlockTexture(Texture); Render.UnlockTexture(Texture);
} }

11
DotSDL/Graphics/Canvas.cs

@ -153,6 +153,17 @@ namespace DotSDL.Graphics {
} }
} }
/// <summary>
/// <c>true</c> if the <see cref="Canvas"/> should be drawn to the screen, otherwise <c>false</c>.
/// </summary>
public bool Shown { get; set; } = true;
/// <summary>
/// The order in which the canvas is drawn. Lower numbered <see cref="Canvas"/> instances are drawn first
/// and will appear on the bottom. This number can be negative.
/// </summary>
public int ZOrder { get; set; }
/// <summary> /// <summary>
/// Sets the section of the <see cref="Canvas"/> that should be drawn. If the size values are set to 0, the /// Sets the section of the <see cref="Canvas"/> that should be drawn. If the size values are set to 0, the
/// <see cref="Canvas"/> will fill as much of its containing object as possible. /// <see cref="Canvas"/> will fill as much of its containing object as possible.

147
DotSDL/Graphics/SdlWindow.cs

@ -24,6 +24,8 @@ namespace DotSDL.Graphics {
private uint _nextVideoUpdate; private uint _nextVideoUpdate;
private uint _nextGameUpdate; private uint _nextGameUpdate;
private List<Canvas> _drawList = new List<Canvas>();
private ScalingQuality _scalingQuality = ScalingQuality.Nearest; private ScalingQuality _scalingQuality = ScalingQuality.Nearest;
/// <summary>Gets the background layer of this window. This is equivalent to accessing Layers[0].</summary> /// <summary>Gets the background layer of this window. This is equivalent to accessing Layers[0].</summary>
@ -191,6 +193,7 @@ namespace DotSDL.Graphics {
BlendMode = BlendMode.None BlendMode = BlendMode.None
} }
}; };
Background.ZOrder = int.MinValue;
Background.CreateTexture(); Background.CreateTexture();
CameraView = new Rectangle( CameraView = new Rectangle(
@ -240,20 +243,29 @@ namespace DotSDL.Graphics {
Render.SetRenderTarget(_renderer, _texture); Render.SetRenderTarget(_renderer, _texture);
// Blit the background canvases to the target texture. // Sort all of the canvases, then draw them.
foreach(var canvas in Layers) { _drawList.Clear();
canvas.Clipping.Position = CameraView.Position; _drawList.AddRange(Layers.Where(l => l.Shown).ToArray());
canvas.Clipping.Size = CameraView.Size; _drawList.AddRange(Sprites.Where(s => s.Shown).ToArray());
canvas.UpdateTexture();
unsafe { foreach(var canvas in _drawList.OrderBy(layer => layer.ZOrder)) {
var canvasClippingRect = canvas.Clipping.SdlRect; switch(canvas) {
Render.RenderCopy(_renderer, canvas.Texture, new IntPtr(&canvasClippingRect), IntPtr.Zero); case Sprite sprite:
DrawSprite(sprite);
break;
default:
canvas.Clipping.Position = CameraView.Position;
canvas.Clipping.Size = CameraView.Size;
canvas.UpdateTexture();
unsafe {
var canvasClippingRect = canvas.Clipping.SdlRect;
Render.RenderCopy(_renderer, canvas.Texture, new IntPtr(&canvasClippingRect), IntPtr.Zero);
}
break;
} }
} }
// Plot sprites on top of the background layer.
if(Sprites.Count > 0) DrawSprites();
Render.SetRenderTarget(_renderer, IntPtr.Zero); Render.SetRenderTarget(_renderer, IntPtr.Zero);
Render.RenderCopy(_renderer, _texture, IntPtr.Zero, IntPtr.Zero); Render.RenderCopy(_renderer, _texture, IntPtr.Zero, IntPtr.Zero);
@ -306,61 +318,58 @@ namespace DotSDL.Graphics {
} }
/// <summary> /// <summary>
/// Plots the sprites stored in <see cref="Sprites"/> to the screen. Please note that this method is called by /// Plots a sprite to the screen. Please note that this method is called by DotSDL's drawing
/// DotSDL's drawing routines and does not need to be called manually. Additionally, this method will not be /// routines and does not need to be called manually. Additionally, this method will never be
/// called if there are no sprites defined. You usually do not need to override this method. /// called if there are no active sprites. You usually do not need to override this method.
/// </summary> /// </summary>
public virtual void DrawSprites() { public virtual void DrawSprite(Sprite sprite) {
Render.SetRenderTarget(_renderer, _texture); var srcRect = sprite.Clipping.SdlRect;
foreach(var sprite in Sprites.Where(e => e.Shown).OrderBy(e => e.ZOrder)) { var drawSize = sprite.DrawSize;
var srcRect = sprite.Clipping.SdlRect;
var drawSize = sprite.DrawSize; Rectangle dest;
Point rotationCenter;
Rectangle dest; if(sprite.CoordinateSystem == CoordinateSystem.ScreenSpace) {
Point rotationCenter; dest = new Rectangle(sprite.Position, drawSize);
if(sprite.CoordinateSystem == CoordinateSystem.ScreenSpace) { rotationCenter = sprite.RotationCenter;
dest = new Rectangle(sprite.Position, drawSize); } else {
rotationCenter = sprite.RotationCenter; // Create a set of world coordinates based on the position of the camera
} else { // and this sprite.
// Create a set of world coordinates based on the position of the camera var relPosition = new Point(sprite.Position - CameraView.Position);
// and this sprite. var screenPosition = new Point(
var relPosition = new Point(sprite.Position - CameraView.Position); (int)((float)relPosition.X / CameraView.Size.X * RenderWidth),
var screenPosition = new Point( (int)((float)relPosition.Y / CameraView.Size.Y * RenderHeight)
(int)((float)relPosition.X / CameraView.Size.X * RenderWidth), );
(int)((float)relPosition.Y / CameraView.Size.Y * RenderHeight) var scaleFactorX = (float)RenderWidth / CameraView.Size.X;
); var scaleFactorY = (float)RenderHeight / CameraView.Size.Y;
var scaleFactorX = (float)RenderWidth / CameraView.Size.X; var size = new Point(
var scaleFactorY = (float)RenderHeight / CameraView.Size.Y; (int)(drawSize.X * scaleFactorX),
var size = new Point( (int)(drawSize.Y * scaleFactorY)
(int)(drawSize.X * scaleFactorX), );
(int)(drawSize.Y * scaleFactorY)
); dest = new Rectangle(screenPosition, size);
rotationCenter = new Point(
dest = new Rectangle(screenPosition, size); (int)(sprite.RotationCenter.X * scaleFactorX),
rotationCenter = new Point( (int)(sprite.RotationCenter.Y * scaleFactorY)
(int)(sprite.RotationCenter.X * scaleFactorX), );
(int)(sprite.RotationCenter.Y * scaleFactorY) }
);
}
var destRect = dest.SdlRect; var destRect = dest.SdlRect;
var rotationCenterPoint = rotationCenter.SdlPoint; var rotationCenterPoint = rotationCenter.SdlPoint;
unsafe { unsafe {
var srcRectPtr = new IntPtr(&srcRect); var srcRectPtr = new IntPtr(&srcRect);
var destRectPtr = new IntPtr(&destRect); var destRectPtr = new IntPtr(&destRect);
var rotationCenterPtr = new IntPtr(&rotationCenterPoint); var rotationCenterPtr = new IntPtr(&rotationCenterPoint);
Render.RenderCopyEx( Render.RenderCopyEx(
renderer: _renderer, renderer: _renderer,
texture: sprite.Texture, texture: sprite.Texture,
srcRect: srcRectPtr, srcRect: srcRectPtr,
dstRect: destRectPtr, dstRect: destRectPtr,
angle: sprite.Rotation, angle: sprite.Rotation,
center: rotationCenterPtr, center: rotationCenterPtr,
flip: sprite.Flip flip: sprite.Flip
); );
}
} }
} }
@ -486,17 +495,13 @@ namespace DotSDL.Graphics {
/// <summary> /// <summary>
/// Displays the window and begins executing code that's associated with it. /// Displays the window and begins executing code that's associated with it.
/// </summary> /// </summary>
public void Start() { public void Start() => Start(0, 0);
Start(0, 0);
}
/// <summary> /// <summary>
/// Displays the window and begins executing code that's associated with it. /// Displays the window and begins executing code that's associated with it.
/// </summary> /// </summary>
/// <param name="updateRate">The desired number of milliseconds between frames and game logic updates. 0 causes the display and game to be continuously updated.</param> /// <param name="updateRate">The desired number of milliseconds between frames and game logic updates. 0 causes the display and game to be continuously updated.</param>
public void Start(uint updateRate) { public void Start(uint updateRate) => Start(updateRate, updateRate);
Start(updateRate, updateRate);
}
/// <summary> /// <summary>
/// Displays the window and begins executing code that's associated with it. /// Displays the window and begins executing code that's associated with it.

52
DotSDL/Graphics/Sprite.cs

@ -9,6 +9,8 @@ namespace DotSDL.Graphics {
public class Sprite : Canvas { public class Sprite : Canvas {
private readonly Point _effectiveSize = new Point(); private readonly Point _effectiveSize = new Point();
private Vector2<float> _scale; private Vector2<float> _scale;
private bool _collisionBoxSet;
private Rectangle _collisionBox;
/// <summary> /// <summary>
/// The position on the screen where the <see cref="Sprite"/> should be drawn. /// The position on the screen where the <see cref="Sprite"/> should be drawn.
@ -41,6 +43,9 @@ namespace DotSDL.Graphics {
_scale = value; _scale = value;
_effectiveSize.X = (int)(Width * _scale.X); _effectiveSize.X = (int)(Width * _scale.X);
_effectiveSize.Y = (int)(Height * _scale.Y); _effectiveSize.Y = (int)(Height * _scale.Y);
if(!_collisionBoxSet)
_collisionBox = new Rectangle(new Point(0, 0), _effectiveSize);
} }
} }
@ -84,15 +89,20 @@ namespace DotSDL.Graphics {
public CoordinateSystem CoordinateSystem { get; set; } = CoordinateSystem.WorldSpace; public CoordinateSystem CoordinateSystem { get; set; } = CoordinateSystem.WorldSpace;
/// <summary> /// <summary>
/// <c>true</c> if the sprite should be drawn to the screen, otherwise <c>false</c>. /// Gets or sets the size and position of the collision box for this <see cref="Sprite"/>.
/// </summary> /// </summary>
public bool Shown { get; set; } public Rectangle CollisionBox {
get => _collisionBox;
set {
_collisionBox = value;
_collisionBoxSet = true;
}
}
/// <summary> /// <summary>
/// The order in which the sprite is drawn. Lower numbered <see cref="Sprite"/> instances are drawn first /// Gets or sets whether or not collision calculations are performed on this <see cref="Sprite"/>.
/// and will appear on the bottom.
/// </summary> /// </summary>
public int ZOrder { get; set; } public bool HasCollision { get; set; }
/// <summary> /// <summary>
/// Initializes a new <see cref="Sprite"/>. /// Initializes a new <see cref="Sprite"/>.
@ -174,6 +184,35 @@ namespace DotSDL.Graphics {
(int)(Clipping.Size.Y * _scale.Y / 2) (int)(Clipping.Size.Y * _scale.Y / 2)
); );
/// <summary>
/// Determines whether or not a given point collides with this <see cref="Sprite"/>. By default
/// this will test the point against the sprite's <see cref="CollisionBox"/>, but this method
/// can be overridden.
/// </summary>
/// <param name="point">The <see cref="Point"/> to check.</param>
/// <returns><c>true</c> if this object's collision is enabled and this sprite's collision
/// routine determines that the given pixel collides with the sprite, otherwise <c>false</c>.</returns>
public virtual bool CheckCollision(Point point) {
if(!HasCollision) return false;
return point.X >= CollisionBox.Position.X + Position.X
&& point.X <= CollisionBox.Position.X + CollisionBox.Size.X + Position.X
&& point.Y >= CollisionBox.Position.Y + Position.Y
&& point.Y <= CollisionBox.Position.Y + CollisionBox.Size.Y + Position.Y;
}
/// <summary>
/// Determines whether or not a given point collides with this <see cref="Sprite"/>. By default
/// this will test the point against the sprite's <see cref="CollisionBox"/>. If you wish to
/// override this sprite's collision routine, override the <see cref="CheckCollision(DotSDL.Graphics.Point)"/>
/// method instead.
/// </summary>
/// <param name="x">The X coordinate of the point to check.</param>
/// <param name="y">The Y coordinate of the point to check.</param>
/// <returns><c>true</c> if this object's collision is enabled and this sprite's collision
/// routine determines that the given pixel collides with the sprite, otherwise <c>false</c>.</returns>
public bool CheckCollision(int x, int y) => CheckCollision(new Point(x, y));
/// <inheritdoc/> /// <inheritdoc/>
internal override void CreateTexture() { internal override void CreateTexture() {
CreateTexture(Render.TextureAccess.Static); CreateTexture(Render.TextureAccess.Static);
@ -184,7 +223,8 @@ namespace DotSDL.Graphics {
/// <see cref="Canvas.Pixels"/> array is changed after adding this sprite to the sprite list associated /// <see cref="Canvas.Pixels"/> array is changed after adding this sprite to the sprite list associated
/// with the application's <see cref="SdlWindow"/>. /// with the application's <see cref="SdlWindow"/>.
/// </summary> /// </summary>
/// <returns><c>true</c> if the texture was successfully updated, otherwise <c>false</c>. This will return <c>false</c> if this <see cref="Sprite"/> hasn't been added to the sprite list.</returns> /// <returns><c>true</c> if the texture was successfully updated, otherwise <c>false</c>. This will return
/// <c>false</c> if this <see cref="Sprite"/> hasn't been added to the sprite list.</returns>
public new bool UpdateTexture() { public new bool UpdateTexture() {
return base.UpdateTexture(); return base.UpdateTexture();
} }

Loading…
Cancel
Save