diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..2fe98d26 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: craftworkgames diff --git a/Source/Demos/Features/Demos/CollisionDemo.cs b/Source/Demos/Features/Demos/CollisionDemo.cs index 77977625..dcc448b9 100644 --- a/Source/Demos/Features/Demos/CollisionDemo.cs +++ b/Source/Demos/Features/Demos/CollisionDemo.cs @@ -36,21 +36,21 @@ namespace Features.Demos _blankTexture = new Texture2D(GraphicsDevice, 1, 1); _blankTexture.SetData(new[] { Color.WhiteSmoke }); - var demoBall1 = new DemoBall(new Sprite(_spikeyBallTexture)) + var spikyBallLeft = new DemoBall(new Sprite(_spikeyBallTexture)) { Position = new Vector2(200, 240), Velocity = new Vector2(0, -120) }; - _actors.Add(demoBall1); + _actors.Add(spikyBallLeft); - var demoBall2 = new DemoBall(new Sprite(_spikeyBallTexture)) + var spikeyBallRight = new DemoBall(new Sprite(_spikeyBallTexture)) { Position = new Vector2(600, 240), Velocity = new Vector2(0, 120) }; - _actors.Add(demoBall2); + _actors.Add(spikeyBallRight); - var controllableBall = new DemoBall(new Sprite(_spikeyBallTexture)) + var controllableBall = new ControllableBall(new Sprite(_spikeyBallTexture)) { Position = new Vector2(400, 240), Velocity = new Vector2(0, 0) @@ -58,26 +58,26 @@ namespace Features.Demos _actors.Add(controllableBall); _controllableBall = controllableBall; - var wall1 = new DemoWall(new Sprite(_blankTexture)) + var topWall = new DemoWall(new Sprite(_blankTexture)) { Bounds = new RectangleF(0, 0, 800, 20), Position = new Vector2(0, 0) }; - _actors.Add(wall1); + _actors.Add(topWall); - var wall2 = new DemoWall(new Sprite(_blankTexture)) + var bottomWall = new DemoWall(new Sprite(_blankTexture)) { Position = new Vector2(0, 460), Bounds = new RectangleF(0, 0, 800, 20) }; - _actors.Add(wall2); + _actors.Add(bottomWall); - var centerWall = new DemoWall(new Sprite(_blankTexture)) + var spikeyBallCenter = new StationaryBall(new Sprite(_spikeyBallTexture)) { - Bounds = new RectangleF(0, 0, 50, 50), - Position = new Vector2(400, 200) + Position = new Vector2(400, 240), + Velocity = Vector2.Zero, }; - _actors.Add(centerWall); + _actors.Add(spikeyBallCenter); foreach (var actor in _actors) { @@ -126,13 +126,6 @@ namespace Features.Demos foreach (var actor in _actors) { actor.Draw(_spriteBatch); - if (actor.Bounds is RectangleF rect) - { - _spriteBatch.Draw(_blankTexture, rect.ToRectangle(), Color.WhiteSmoke); - } else if (actor.Bounds is CircleF circle) - { - _spriteBatch.Draw(_blankTexture, circle.Position, Color.WhiteSmoke); - } } _spriteBatch.End(); @@ -153,9 +146,10 @@ namespace Features.Demos Bounds = sprite.GetBoundingRectangle(new Transform2()); } - - public Vector2 Offset { get; set; } - + /// + /// Gets or sets the actor's position and updates teh actor's bounds + /// position. + /// public Vector2 Position { get { return _position; } @@ -166,8 +160,20 @@ namespace Features.Demos } } + /// + /// Gets or sets the actor's collision bounds. + /// public IShapeF Bounds { get; set; } + /// + /// Gets or sets how far the actor's collision bounds are offset from + /// the actor's position. + /// + public Vector2 Offset { get; set; } + + /// + /// Gets or sets the actor's velocity. + /// public Vector2 Velocity { get; set; } public virtual void OnCollision(CollisionEventArgs collisionInfo) @@ -201,14 +207,12 @@ namespace Features.Demos { public DemoBall(Sprite sprite) : base(sprite) { - Offset = new Vector2(75, 75); Bounds = new CircleF(Position + Offset, 60); } public override void Update(GameTime gameTime) { base.Update(gameTime); - Bounds.Position = Position; } public override void OnCollision(CollisionEventArgs collisionInfo) @@ -220,5 +224,40 @@ namespace Features.Demos } } + /// + /// Ball that doesn't move when collided with. + /// + class StationaryBall : DemoBall + { + public StationaryBall(Sprite sprite) : base(sprite) + { + } + + public override void OnCollision(CollisionEventArgs collisionInfo) + { + } + } + + /// + /// Player controlled ball. + /// + class ControllableBall : DemoBall + { + public ControllableBall(Sprite sprite) : base(sprite) + { + Bounds = new CircleF(Position + Offset, 60); + } + + public override void Update(GameTime gameTime) + { + base.Update(gameTime); + } + + public override void OnCollision(CollisionEventArgs collisionInfo) + { + Position -= collisionInfo.PenetrationVector; + } + } + #endregion } \ No newline at end of file diff --git a/Source/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs b/Source/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs index c8e009d3..7d4b9b2f 100644 --- a/Source/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs +++ b/Source/MonoGame.Extended.Collisions/QuadTree/CollisionComponent.cs @@ -36,20 +36,13 @@ namespace MonoGame.Extended.Collisions /// public override void Update(GameTime gameTime) { - // Update bounding box locations. - foreach (var value in _targetDataDictionary.Values) - { - _collisionTree.Remove(value); - value.Bounds = value.Target.Bounds; - _collisionTree.Insert(value); - } - // Detect collisions foreach (var value in _targetDataDictionary.Values) { + _collisionTree.Remove(value); + var target = value.Target; - var collisions =_collisionTree.Query(target.Bounds) - .Where(data => data.Target != target); + var collisions =_collisionTree.Query(target.Bounds); // Generate list of collision Infos foreach (var other in collisions) @@ -61,7 +54,9 @@ namespace MonoGame.Extended.Collisions }; target.OnCollision(collisionInfo); + value.Bounds = value.Target.Bounds; } + _collisionTree.Insert(value); } } @@ -156,7 +151,10 @@ namespace MonoGame.Extended.Collisions private static Vector2 PenetrationVector(CircleF circ1, CircleF circ2) { - Debug.Assert(circ1.Intersects(circ2)); + if (!circ1.Intersects(circ2)) + { + return Vector2.Zero; + } var displacement = Point2.Displacement(circ1.Center, circ2.Center); diff --git a/Source/MonoGame.Extended.Entities/World.cs b/Source/MonoGame.Extended.Entities/World.cs index a4f34d47..022d4b8c 100644 --- a/Source/MonoGame.Extended.Entities/World.cs +++ b/Source/MonoGame.Extended.Entities/World.cs @@ -26,6 +26,9 @@ namespace MonoGame.Extended.Entities foreach (var drawSystem in _drawSystems) drawSystem.Dispose(); + _updateSystems.Clear(); + _drawSystems.Clear(); + base.Dispose(); } diff --git a/Source/MonoGame.Extended.Tiled/TiledMap.cs b/Source/MonoGame.Extended.Tiled/TiledMap.cs index e2328b7c..d9b1d14b 100644 --- a/Source/MonoGame.Extended.Tiled/TiledMap.cs +++ b/Source/MonoGame.Extended.Tiled/TiledMap.cs @@ -69,6 +69,10 @@ namespace MonoGame.Extended.Tiled private void AddLayer(TiledMapLayer layer, bool root) { if (root) _layers.Add(layer); + + if (_layersByName.ContainsKey(layer.Name)) + throw new ArgumentException($"The TiledMap '{Name}' contains two or more layers named '{layer.Name}'. Please ensure all layers have unique names."); + _layersByName.Add(layer.Name, layer); switch(layer) diff --git a/Source/MonoGame.Extended/Collections/Bag.cs b/Source/MonoGame.Extended/Collections/Bag.cs index cfa59689..292c1d19 100644 --- a/Source/MonoGame.Extended/Collections/Bag.cs +++ b/Source/MonoGame.Extended/Collections/Bag.cs @@ -85,11 +85,11 @@ namespace MonoGame.Extended.Collections if(Count == 0) return; - Count = 0; - // non-primitive types are cleared so the garbage collector can release them if (!_isPrimitive) Array.Clear(_items, 0, Count); + + Count = 0; } public bool Contains(T element) @@ -191,4 +191,4 @@ namespace MonoGame.Extended.Collections } } } -} \ No newline at end of file +} diff --git a/Source/MonoGame.Extended/Math/ShapeExtensions.cs b/Source/MonoGame.Extended/Math/ShapeExtensions.cs index 51ea932e..30a0b59b 100644 --- a/Source/MonoGame.Extended/Math/ShapeExtensions.cs +++ b/Source/MonoGame.Extended/Math/ShapeExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net.NetworkInformation; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.Shapes; @@ -276,6 +277,21 @@ namespace MonoGame.Extended DrawPolygon(spriteBatch, new Vector2(x, y), CreateCircle(radius, sides), color, thickness); } + /// + /// Draw an ellipse. + /// + /// The destination drawing surface + /// Center of the ellipse + /// Radius of the ellipse + /// The number of sides to generate. + /// The color of the ellipse. + /// The thickness of the line around the ellipse. + public static void DrawEllipse(this SpriteBatch spriteBatch, Vector2 center, Vector2 radius, int sides, + Color color, float thickness = 1f) + { + DrawPolygon(spriteBatch, center, CreateEllipse(radius.X, radius.Y, sides), color, thickness); + } + private static Vector2[] CreateCircle(double radius, int sides) { const double max = 2.0*Math.PI; @@ -291,5 +307,20 @@ namespace MonoGame.Extended return points; } + + private static Vector2[] CreateEllipse(float rx, float ry, int sides) + { + var vertices = new Vector2[sides]; + + var t = 0.0; + var dt = 2.0 * Math.PI / sides; + for (var i = 0; i < sides; i++, t += dt) + { + var x = (float)(rx * Math.Cos(t)); + var y = (float)(ry * Math.Sin(t)); + vertices[i] = new Vector2(x, y); + } + return vertices; + } } } \ No newline at end of file