diff --git a/Engine b/Engine index c03d74d..6a104d8 160000 --- a/Engine +++ b/Engine @@ -1 +1 @@ -Subproject commit c03d74dbe0949d75411cd368c8b07dbafd871a20 +Subproject commit 6a104d8abd013334ed2787c4b4c6fa6481e4c6a4 diff --git a/Game/.vscode/settings.json b/Game/.vscode/settings.json new file mode 100644 index 0000000..f39a2ca --- /dev/null +++ b/Game/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "cSpell.words": [ + "AABB", + "DAABB", + "Syntriax" + ] +} diff --git a/Game/Behaviours/CircleBehaviour.cs b/Game/Behaviours/CircleBehaviour.cs new file mode 100644 index 0000000..6e107a7 --- /dev/null +++ b/Game/Behaviours/CircleBehaviour.cs @@ -0,0 +1,23 @@ +using Microsoft.Xna.Framework; + +using Apos.Shapes; + +using Syntriax.Engine.Physics2D.Primitives; + +namespace Pong.Behaviours; + +public class CircleBehaviour : Syntriax.Engine.Physics2D.Collider2DCircleBehaviour, IDisplayableShape +{ + public CircleBehaviour(Circle circle) { this.CircleLocal = circle; } + public CircleBehaviour(Circle circle, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; } + public CircleBehaviour(Circle circle, Color color) { this.CircleLocal = circle; Color = color; } + public CircleBehaviour(Circle circle, Color color, float Thickness) { this.CircleLocal = circle; this.Thickness = Thickness; Color = color; } + + public Color Color { get; set; } = Color.White; + public float Thickness { get; set; } = .5f; + + public void Draw(ShapeBatch shapeBatch) + { + shapeBatch.BorderCircle(CircleWorld.Center.ToDisplayVector2(), CircleWorld.Radius, Color); + } +} diff --git a/Game/Behaviours/IDisplayableShape.cs b/Game/Behaviours/IDisplayableShape.cs new file mode 100644 index 0000000..0949f13 --- /dev/null +++ b/Game/Behaviours/IDisplayableShape.cs @@ -0,0 +1,8 @@ +using Apos.Shapes; + +namespace Pong.Behaviours; + +public interface IDisplayableShape +{ + void Draw(ShapeBatch shapeBatch); +} diff --git a/Game/Behaviours/KeyboardInputsBehaviour.cs b/Game/Behaviours/KeyboardInputsBehaviour.cs new file mode 100644 index 0000000..46bf534 --- /dev/null +++ b/Game/Behaviours/KeyboardInputsBehaviour.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; + +using Microsoft.Xna.Framework.Input; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Input; + +namespace Pong.Behaviours; + +public class KeyboardInputsBehaviour : BehaviourOverride, IButtonInputs +{ + private readonly Dictionary, Keys>> OnPressed = new(256); + private readonly Dictionary, Keys>> OnReleased = new(256); + + private int cachePressedCurrentlyCount = 0; + private readonly Keys[] cachePressedCurrently = new Keys[256]; + + private int cachePressedPreviouslyCount = 0; + private readonly Keys[] cachePressedPreviously = new Keys[256]; + + public void RegisterOnPress(Keys key, Action, Keys> callback) + { + if (OnPressed.TryGetValue(key, out var action)) + { + action += callback; + return; + } + + OnPressed.Add(key, callback); + } + + public void UnregisterOnPress(Keys key, Action, Keys> callback) + { + if (OnPressed.TryGetValue(key, out var action)) + action -= callback; + } + + public void RegisterOnRelease(Keys key, Action, Keys> callback) + { + if (OnReleased.TryGetValue(key, out var action)) + { + action += callback; + return; + } + + OnReleased.Add(key, callback); + } + + public void UnregisterOnRelease(Keys key, Action, Keys> callback) + { + if (OnReleased.TryGetValue(key, out var action)) + action -= callback; + } + + protected override void OnUpdate() + { + KeyboardState keyboardState = Keyboard.GetState(); + keyboardState.GetPressedKeys(cachePressedCurrently); + cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount(); + + for (int i = 0; i < cachePressedCurrentlyCount; i++) + { + Keys currentlyPressedKey = cachePressedCurrently[i]; + + if (!OnPressed.TryGetValue(currentlyPressedKey, out var action)) + continue; + + if (WasPressed(currentlyPressedKey)) + continue; + + action.Invoke(this, currentlyPressedKey); + } + + for (int i = 0; i < cachePressedPreviouslyCount; i++) + { + Keys previouslyPressedKey = cachePressedPreviously[i]; + + if (!OnReleased.TryGetValue(previouslyPressedKey, out var action)) + continue; + + if (IsPressed(previouslyPressedKey)) + continue; + + action.Invoke(this, previouslyPressedKey); + } + + Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount); + cachePressedPreviouslyCount = cachePressedCurrentlyCount; + } + + public bool IsPressed(Keys key) + { + for (int i = 0; i < cachePressedCurrentlyCount; i++) + if (cachePressedCurrently[i] == key) + return true; + return false; + } + + public bool WasPressed(Keys key) + { + for (int i = 0; i < cachePressedPreviouslyCount; i++) + if (cachePressedPreviously[i] == key) + return true; + return false; + } +} diff --git a/Game/Behaviours/MonoGameCameraBehaviour.cs b/Game/Behaviours/MonoGameCameraBehaviour.cs new file mode 100644 index 0000000..9cb088d --- /dev/null +++ b/Game/Behaviours/MonoGameCameraBehaviour.cs @@ -0,0 +1,106 @@ +using System; + +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; + +namespace Pong.Behaviours; + +public class MonoGameCameraBehaviour : BehaviourOverride, ICamera +{ + public Action? OnPositionChanged { get; set; } = null; + public Action? OnMatrixTransformChanged { get; set; } = null; + public Action? OnViewportChanged { get; set; } = null; + public Action? OnRotationChanged { get; set; } = null; + public Action? OnZoomChanged { get; set; } = null; + + private Matrix _matrixTransform = Matrix.Identity; + + private Viewport _viewport = default; + + private float _zoom = 1f; + + public Matrix MatrixTransform + { + get => _matrixTransform; + set + { + if (_matrixTransform == value) + return; + + _matrixTransform = value; + OnMatrixTransformChanged?.Invoke(this); + } + } + + public Vector2D Position + { + get => Transform.Position; + set => Transform.Position = value; + } + + public Viewport Viewport + { + get => _viewport; + set + { + if (_viewport.Equals(value)) + return; + + _viewport = value; + OnViewportChanged?.Invoke(this); + } + } + + public float Zoom + { + get => _zoom; + set + { + float newValue = value >= .1f ? value : .1f; + + if (_zoom == newValue) + return; + + _zoom = newValue; + OnZoomChanged?.Invoke(this); + } + } + + public float Rotation + { + get => Transform.Rotation; + set => Transform.Rotation = value; + } + public Action? OnTransformAssigned { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + ITransform IAssignableTransform.Transform => throw new NotImplementedException(); + + public void Update() + { + MatrixTransform = + Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) * + Matrix.CreateRotationZ(Rotation) * + Matrix.CreateScale(Zoom) * + Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f)); + } + + protected override void OnInitialize() + { + Transform.OnRotationChanged += OnTransformRotationChanged; + Transform.OnPositionChanged += OnTransformPositionChanged; + } + + protected override void OnFinalize() + { + Transform.OnRotationChanged -= OnTransformRotationChanged; + Transform.OnPositionChanged -= OnTransformPositionChanged; + } + + private void OnTransformRotationChanged(ITransform _) => OnRotationChanged?.Invoke(this); + private void OnTransformPositionChanged(ITransform _) => OnPositionChanged?.Invoke(this); + + public bool Assign(ITransform transform) => GameObject.Assign(transform); +} diff --git a/Game/Behaviours/MovementBallBehaviour.cs b/Game/Behaviours/MovementBallBehaviour.cs index b7cd436..063c79e 100644 --- a/Game/Behaviours/MovementBallBehaviour.cs +++ b/Game/Behaviours/MovementBallBehaviour.cs @@ -3,43 +3,48 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Syntriax.Engine.Core; using Syntriax.Engine.Input; +using Syntriax.Engine.Physics2D.Abstract; namespace Pong.Behaviours; - -public class MovementBallBehaviour(Vector2 StartDirection, PlayAreaBehaviour PlayAreaBehaviour, float Speed) : BehaviourOverride +public class MovementBallBehaviour(Vector2D StartDirection, float Speed) : BehaviourOverride { - public Vector2 StartDirection { get; private set; } = StartDirection; - public PlayAreaBehaviour PlayAreaBehaviour { get; } = PlayAreaBehaviour; + public Vector2D StartDirection { get; private set; } = Vector2D.Normalize(StartDirection); public float Speed { get; set; } = Speed; - protected override void OnInitialize() => StartDirection.Normalize(); - - protected override void OnUpdate(GameTime time) + protected override void OnFirstActiveFrame() { - GameObject.Transform.Position += StartDirection * (time.ElapsedGameTime.Nanoseconds * .001f) * Speed; + if (!BehaviourController.TryGetBehaviour(out IRigidBody2D? rigidBody)) + throw new Exception($"Where's my {nameof(IRigidBody2D)}????"); - float absY = MathF.Abs(GameObject.Transform.Position.Y); - float differenceY = absY - PlayAreaBehaviour.PlayArea.Y * 0.5f; - if (differenceY > 0f) - { - if (GameObject.Transform.Position.Y > 0f) - GameObject.Transform.Position -= Vector2.UnitY * differenceY * 2f; - else - GameObject.Transform.Position += Vector2.UnitY * differenceY * 2f; - - StartDirection = new(StartDirection.X, -StartDirection.Y); - } - - float absX = MathF.Abs(GameObject.Transform.Position.X); - float differenceX = absX - PlayAreaBehaviour.PlayArea.X * 0.5f; - if (differenceX > 0f) - { - if (GameObject.Transform.Position.X > 0f) - GameObject.Transform.Position -= Vector2.UnitX * differenceX * 2f; - else - GameObject.Transform.Position += Vector2.UnitX * differenceX * 2f; - - StartDirection = new(-StartDirection.X, StartDirection.Y); - } + rigidBody.Velocity = StartDirection * Speed; } + + // protected override void OnUpdate(GameTime time) + // { + // GameObject.Transform.Position += StartDirection * (time.ElapsedGameTime.Nanoseconds * .001f) * Speed; + + // float absY = MathF.Abs(GameObject.Transform.Position.Y); + // float differenceY = absY - PlayAreaBehaviour.PlayArea.Y * 0.5f; + // if (differenceY > 0f) + // { + // if (GameObject.Transform.Position.Y > 0f) + // GameObject.Transform.Position -= Vector2.UnitY * differenceY * 2f; + // else + // GameObject.Transform.Position += Vector2.UnitY * differenceY * 2f; + + // StartDirection = new(StartDirection.X, -StartDirection.Y); + // } + + // float absX = MathF.Abs(GameObject.Transform.Position.X); + // float differenceX = absX - PlayAreaBehaviour.PlayArea.X * 0.5f; + // if (differenceX > 0f) + // { + // if (GameObject.Transform.Position.X > 0f) + // GameObject.Transform.Position -= Vector2.UnitX * differenceX * 2f; + // else + // GameObject.Transform.Position += Vector2.UnitX * differenceX * 2f; + + // StartDirection = new(-StartDirection.X, StartDirection.Y); + // } + // } } diff --git a/Game/Behaviours/MovementBoxBehaviour.cs b/Game/Behaviours/MovementBoxBehaviour.cs index a2ebebc..fd9468a 100644 --- a/Game/Behaviours/MovementBoxBehaviour.cs +++ b/Game/Behaviours/MovementBoxBehaviour.cs @@ -17,25 +17,25 @@ public class MovementBoxBehaviour(Keys Up, Keys Down, float High, float Low, flo private bool isUpPressed = false; private bool isDownPressed = false; - private IKeyboardInputs inputs = null!; + private IButtonInputs inputs = null!; - protected override void OnUpdate(GameTime time) + protected override void OnUpdate() { if (isUpPressed && isDownPressed) return; if (isUpPressed) - GameObject.Transform.Position = GameObject.Transform.Position + Vector2.UnitY * (float)time.ElapsedGameTime.TotalSeconds * Speed; + GameObject.Transform.Position = GameObject.Transform.Position + Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed; else if (isDownPressed) - GameObject.Transform.Position = GameObject.Transform.Position + -Vector2.UnitY * (float)time.ElapsedGameTime.TotalSeconds * Speed; + GameObject.Transform.Position = GameObject.Transform.Position + -Vector2D.Up * (float)Time.Elapsed.TotalSeconds * Speed; - GameObject.Transform.Position = new Vector2(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low)); + GameObject.Transform.Position = new Vector2D(GameObject.Transform.Position.X, MathF.Max(MathF.Min(GameObject.Transform.Position.Y, High), Low)); } - protected override void OnFirstActiveFrame(GameTime time) + protected override void OnFirstActiveFrame() { - if (!BehaviourController.TryGetBehaviour(out var behaviourResult)) - throw new Exception($"{nameof(IKeyboardInputs)} is missing on ${GameObject.Name}."); + if (!BehaviourController.TryGetBehaviour>(out var behaviourResult)) + throw new Exception($"{nameof(IButtonInputs)} is missing on ${GameObject.Name}."); inputs = behaviourResult; @@ -55,8 +55,8 @@ public class MovementBoxBehaviour(Keys Up, Keys Down, float High, float Low, flo inputs.UnregisterOnRelease(Down, OnDownReleased); } - private void OnUpPressed(IKeyboardInputs inputs, Keys keys) => isUpPressed = true; - private void OnUpReleased(IKeyboardInputs inputs, Keys keys) => isUpPressed = false; - private void OnDownPressed(IKeyboardInputs inputs, Keys keys) => isDownPressed = true; - private void OnDownReleased(IKeyboardInputs inputs, Keys keys) => isDownPressed = false; + private void OnUpPressed(IButtonInputs inputs, Keys keys) => isUpPressed = true; + private void OnUpReleased(IButtonInputs inputs, Keys keys) => isUpPressed = false; + private void OnDownPressed(IButtonInputs inputs, Keys keys) => isDownPressed = true; + private void OnDownReleased(IButtonInputs inputs, Keys keys) => isDownPressed = false; } diff --git a/Game/Behaviours/MovementMouseBehaviour.cs b/Game/Behaviours/MovementMouseBehaviour.cs new file mode 100644 index 0000000..afdb67e --- /dev/null +++ b/Game/Behaviours/MovementMouseBehaviour.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.Xna.Framework.Input; +using Syntriax.Engine.Core; + +namespace Pong.Behaviours; + +public class MovementMouseBehaviour : BehaviourOverride +{ + protected override void OnUpdate() + { + GameObject.Transform.Position = Mouse.GetState().Position.ToVector2D().ApplyDisplayScale(); + } +} diff --git a/Game/Behaviours/RotatableBehaviour.cs b/Game/Behaviours/RotatableBehaviour.cs new file mode 100644 index 0000000..09185c4 --- /dev/null +++ b/Game/Behaviours/RotatableBehaviour.cs @@ -0,0 +1,25 @@ +using Syntriax.Engine.Core; + +namespace Pong.Behaviours; + +public class RotatableBehaviour : BehaviourOverride +{ + private KeyboardInputsBehaviour? inputs = null; + + protected override void OnFirstActiveFrame() + { + if (!BehaviourController.TryGetBehaviour(out inputs)) + inputs = BehaviourController.AddBehaviour(); + } + + protected override void OnUpdate() + { + if (inputs is null) + return; + + if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad4)) + Transform.Rotation += Time.Elapsed.Nanoseconds * 0.0025f; + if (inputs.IsPressed(Microsoft.Xna.Framework.Input.Keys.NumPad6)) + Transform.Rotation -= Time.Elapsed.Nanoseconds * 0.0025f; + } +} diff --git a/Game/Behaviours/ShapeAABBBehaviour.cs b/Game/Behaviours/ShapeAABBBehaviour.cs new file mode 100644 index 0000000..635ef75 --- /dev/null +++ b/Game/Behaviours/ShapeAABBBehaviour.cs @@ -0,0 +1,47 @@ +using Microsoft.Xna.Framework; + +using Apos.Shapes; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Input; +using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Pong.Behaviours; + +public class ShapeAABBBehaviour : BehaviourOverride, IDisplayableShape +{ + private IShapeCollider2D? shapeCollider = null; + + public ShapeAABBBehaviour() { } + public ShapeAABBBehaviour(float Thickness) { this.Thickness = Thickness; } + public ShapeAABBBehaviour(Color color) { Color = color; } + public ShapeAABBBehaviour(Color color, float Thickness) { this.Thickness = Thickness; Color = color; } + + public Color Color { get; set; } = Color.White; + public float Thickness { get; set; } = .5f; + public bool display = true; + + protected override void OnFirstActiveFrame() + { + BehaviourController.TryGetBehaviour(out shapeCollider); + + if (BehaviourController.TryGetBehaviour(out IButtonInputs? keys)) + keys.RegisterOnPress(Microsoft.Xna.Framework.Input.Keys.D, (_1, _2) => display = !display); + } + + public void Draw(ShapeBatch shapeBatch) + { + if (!display) + return; + + if (shapeCollider is null) + return; + + AABB aabb = AABB.FromVectors(shapeCollider.ShapeWorld); + + shapeBatch.BorderCircle(aabb.Center.ToDisplayVector2(), 7.5f, Color.Beige); + + shapeBatch.DrawRectangle(aabb.Center.ApplyDisplayScale().Subtract(aabb.SizeHalf).ToVector2(), aabb.Size.ToVector2(), Color.Transparent, Color.Blue); + } +} diff --git a/Game/Behaviours/ShapeBehaviour.cs b/Game/Behaviours/ShapeBehaviour.cs new file mode 100644 index 0000000..c27ad33 --- /dev/null +++ b/Game/Behaviours/ShapeBehaviour.cs @@ -0,0 +1,29 @@ +using Microsoft.Xna.Framework; + +using Apos.Shapes; + +using Syntriax.Engine.Physics2D.Primitives; + +namespace Pong.Behaviours; + +public class ShapeBehaviour : Syntriax.Engine.Physics2D.Collider2DShapeBehaviour, IDisplayableShape +{ + public ShapeBehaviour(Shape Shape) { this.ShapeLocal = Shape; } + public ShapeBehaviour(Shape Shape, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; } + public ShapeBehaviour(Shape Shape, Color color) { this.ShapeLocal = Shape; Color = color; } + public ShapeBehaviour(Shape Shape, Color color, float Thickness) { this.ShapeLocal = Shape; this.Thickness = Thickness; Color = color; } + + public Color Color { get; set; } = Color.White; + public float Thickness { get; set; } = .5f; + + public void Draw(ShapeBatch shapeBatch) + { + int count = ShapeWorld.Vertices.Count; + + shapeBatch.BorderCircle(Transform.Position.ToDisplayVector2(), 5f, Color.DarkRed); + + for (int i = 0; i < count - 1; i++) + shapeBatch.DrawLine(ShapeWorld[i].ToDisplayVector2(), ShapeWorld[i + 1].ToDisplayVector2(), Thickness, Color, Color); + shapeBatch.DrawLine(ShapeWorld[0].ToDisplayVector2(), ShapeWorld[^1].ToDisplayVector2(), Thickness, Color, Color); + } +} diff --git a/Game/EngineConverter.cs b/Game/EngineConverter.cs new file mode 100644 index 0000000..fe904cc --- /dev/null +++ b/Game/EngineConverter.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using Microsoft.Xna.Framework; +using Syntriax.Engine.Core; + +namespace Pong; + +public static class EngineConverter +{ + public readonly static Vector2D screenScale = Vector2D.Down + Vector2D.Right; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EngineTime ToEngineTime(this GameTime gameTime) => new(gameTime.TotalGameTime, gameTime.ElapsedGameTime); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2D ApplyDisplayScale(this Vector2D vector) => vector.Scale(screenScale); +} diff --git a/Game/Game.csproj b/Game/Game.csproj index 9b23577..f15750f 100644 --- a/Game/Game.csproj +++ b/Game/Game.csproj @@ -20,13 +20,14 @@ + - + diff --git a/Game/Game.sln b/Game/Game.sln deleted file mode 100644 index 58640a0..0000000 --- a/Game/Game.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game.csproj", "{42644486-9F9E-4242-B6C4-AF31BBFA31D2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "..\Engine\Engine.Core\Engine.Core.csproj", "{EF1FE4A2-40DF-4967-8003-CF6D98010D02}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42644486-9F9E-4242-B6C4-AF31BBFA31D2}.Release|Any CPU.Build.0 = Release|Any CPU - {EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF1FE4A2-40DF-4967-8003-CF6D98010D02}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {AB3225EE-1621-439F-8F83-DF4515922FEF} - EndGlobalSection -EndGlobal diff --git a/Game/Game1.cs b/Game/Game1.cs index 8278dc5..9515341 100644 --- a/Game/Game1.cs +++ b/Game/Game1.cs @@ -2,26 +2,37 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; + using Pong.Behaviours; +using Apos.Shapes; + using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; -using Syntriax.Engine.Graphics.TwoDimensional; -using Syntriax.Engine.Input; +using Syntriax.Engine.Physics2D; +using Syntriax.Engine.Physics2D.Primitives; namespace Pong; public class Game1 : Game { private GraphicsDeviceManager _graphics = null!; + private PhysicsEngine2D engine; private SpriteBatch _spriteBatch = null!; - private GameManager gameManager = null!; + private ShapeBatch _shapeBatch = null!; + public static GameManager gameManager = null!; + public static Sprite spriteBox = null!; + private MonoGameCameraBehaviour cameraBehaviour = null!; + public Game1() { + engine = new PhysicsEngine2D(); + _graphics = new GraphicsDeviceManager(this) { PreferredBackBufferWidth = 1024, - PreferredBackBufferHeight = 576 + PreferredBackBufferHeight = 576, + GraphicsProfile = GraphicsProfile.HiDef }; Content.RootDirectory = "Content"; @@ -41,44 +52,85 @@ public class Game1 : Game protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); + _shapeBatch = new ShapeBatch(GraphicsDevice, Content); - Sprite spriteBox = new Sprite() { Texture2D = Content.Load("Sprites/Pixel") }; - Sprite spriteBall = new Sprite() { Texture2D = Content.Load("Sprites/Circle") }; + // spriteBox = new Sprite() { Texture2D = Content.Load("Sprites/Pixel") }; + // Sprite spriteBall = new Sprite() { Texture2D = Content.Load("Sprites/Circle") }; IGameObject gameObjectCamera = gameManager.InstantiateGameObject(); gameObjectCamera.Name = "Camera"; - gameObjectCamera.Transform.Position = Vector2.Zero; + gameObjectCamera.Transform.Position = Vector2D.Zero; - gameManager.Camera = gameObjectCamera.BehaviourController.AddBehaviour(); - gameManager.Camera.Viewport = GraphicsDevice.Viewport; + cameraBehaviour = gameObjectCamera.BehaviourController.AddBehaviour(); + cameraBehaviour.Viewport = GraphicsDevice.Viewport; + gameManager.Camera = cameraBehaviour; - IGameObject gameObjectPlayArea = gameManager.InstantiateGameObject(); - PlayAreaBehaviour playAreaBehaviour = gameObjectPlayArea.BehaviourController.AddBehaviour(); - playAreaBehaviour.PlayArea = new Vector2(_graphics.PreferredBackBufferWidth, _graphics.PreferredBackBufferHeight); - IGameObject gameObjectBall = gameManager.InstantiateGameObject(); - gameObjectBall.Name = "Ball"; - gameObjectBall.Transform.Position = Vector2.Zero; - gameObjectBall.Transform.Scale = new Vector2(1f / 51.2f, 1f / 51.2f); - gameObjectBall.BehaviourController.AddBehaviour(new Vector2(.1f, .1f), playAreaBehaviour, 100f); - gameObjectBall.BehaviourController.AddBehaviour().Assign(spriteBall); + // IGameObject gameObjectCircle = gameManager.InstantiateGameObject(); + // gameObjectCircle.Name = "Circle"; + // gameObjectCircle.Transform.Position = new Vector2D(0f, -50f); + // gameObjectCircle.Transform.Scale = new Vector2D(25f, 25f); + // gameObjectCircle.BehaviourController.AddBehaviour(); + // gameObjectCircle.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); + // gameObjectCircle.BehaviourController.AddBehaviour(new Circle(Vector2D.Zero, 1f)); + // engine.AddRigidBody(gameObjectCircle.BehaviourController.AddBehaviour()); - IGameObject gameObjectLeft = gameManager.InstantiateGameObject(); - gameObjectLeft.Name = "Left"; - gameObjectLeft.Transform.Position = new Vector2(-452, 0f); - gameObjectLeft.Transform.Scale = new Vector2(10f, 40f); - gameObjectLeft.BehaviourController.AddBehaviour(); - gameObjectLeft.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); - gameObjectLeft.BehaviourController.AddBehaviour().Assign(spriteBox); + IGameObject gameObjectCircle2 = gameManager.InstantiateGameObject(); + gameObjectCircle2.Name = "Circle2"; + gameObjectCircle2.Transform.Position = new Vector2D(5f, 50f); + gameObjectCircle2.Transform.Scale = new Vector2D(25f, 25f); + gameObjectCircle2.BehaviourController.AddBehaviour(); + gameObjectCircle2.BehaviourController.AddBehaviour(new Circle(Vector2D.Zero, 1f)); + engine.AddRigidBody(gameObjectCircle2.BehaviourController.AddBehaviour()); - IGameObject gameObjectRight = gameManager.InstantiateGameObject(); - gameObjectRight.Name = "Right"; - gameObjectRight.Transform.Position = new Vector2(452, 0f); - gameObjectRight.Transform.Scale = new Vector2(10f, 40f); - gameObjectRight.BehaviourController.AddBehaviour(); - gameObjectRight.BehaviourController.AddBehaviour(Keys.Up, Keys.Down, 268f, -268f, 400f); - gameObjectRight.BehaviourController.AddBehaviour().Assign(spriteBox); - // TODO: use this.Content to load your game content here + IGameObject gameObjectDiamond = gameManager.InstantiateGameObject(); + gameObjectDiamond.Name = "Diamond"; + gameObjectDiamond.Transform.Position = new Vector2D(-150f, -150f); + gameObjectDiamond.Transform.Scale = new Vector2D(50f, 50f); + gameObjectDiamond.BehaviourController.AddBehaviour(); + gameObjectDiamond.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); + gameObjectDiamond.BehaviourController.AddBehaviour(); + gameObjectDiamond.BehaviourController.AddBehaviour(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, -Vector2D.One, Vector2D.Left])); + gameObjectDiamond.BehaviourController.AddBehaviour(); + engine.AddRigidBody(gameObjectDiamond.BehaviourController.AddBehaviour()); + + IGameObject gameObjectBox = gameManager.InstantiateGameObject(); + gameObjectBox.Name = "Box"; + gameObjectBox.Transform.Position = new Vector2D(150f, -150f); + gameObjectBox.Transform.Scale = new Vector2D(100f, 100f); + gameObjectBox.BehaviourController.AddBehaviour(); + gameObjectBox.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); + gameObjectBox.BehaviourController.AddBehaviour(); + gameObjectBox.BehaviourController.AddBehaviour(Shape.Pentagon); + gameObjectBox.BehaviourController.AddBehaviour(); + engine.AddRigidBody(gameObjectBox.BehaviourController.AddBehaviour()); + + for (int i = 3; i < 10; i++) + { + IGameObject Test = gameManager.InstantiateGameObject(); + Test.Name = i.ToString(); + Test.Transform.Position = new Vector2D((i - 6) * 150, 0f); + Test.Transform.Scale = new Vector2D(75f, 75f); + Test.BehaviourController.AddBehaviour(); + Test.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); + Test.BehaviourController.AddBehaviour(); + Test.BehaviourController.AddBehaviour(Shape.CreateNgon(i)); + Test.BehaviourController.AddBehaviour(); + RigidBody2D rigidBody = Test.BehaviourController.AddBehaviour(); + rigidBody.AngularVelocity = 90f; + engine.AddRigidBody(rigidBody); + } + + + // IGameObject gameObjectShape = gameManager.InstantiateGameObject(); + // gameObjectShape.Name = "Shape"; + // gameObjectShape.Transform.Position = new Vector2D(250f, 0f); + // gameObjectShape.Transform.Scale = new Vector2D(100f, 100f); + // gameObjectShape.BehaviourController.AddBehaviour(); + // gameObjectShape.BehaviourController.AddBehaviour(Keys.W, Keys.S, 268f, -268f, 400f); + // gameObjectShape.BehaviourController.AddBehaviour(); + // gameObjectShape.BehaviourController.AddBehaviour(new Shape([Vector2D.Up, Vector2D.One, Vector2D.Right, Vector2D.Down, Vector2D.Zero, Vector2D.Left])); + // gameObjectShape.BehaviourController.AddBehaviour(); } protected override void Update(GameTime gameTime) @@ -97,36 +149,96 @@ public class Game1 : Game _graphics.IsFullScreen = true; _graphics.ApplyChanges(); - float previousScreenSize = MathF.Sqrt(MathF.Pow(gameManager.Camera.Viewport.Width, 2f) + MathF.Pow(gameManager.Camera.Viewport.Height, 2f)); + float previousScreenSize = MathF.Sqrt(MathF.Pow(cameraBehaviour.Viewport.Width, 2f) + MathF.Pow(cameraBehaviour.Viewport.Height, 2f)); float currentScreenSize = MathF.Sqrt(MathF.Pow(GraphicsDevice.Viewport.Width, 2f) + MathF.Pow(GraphicsDevice.Viewport.Height, 2f)); - gameManager.Camera.Zoom /= previousScreenSize / currentScreenSize; - gameManager.Camera.Viewport = GraphicsDevice.Viewport; + cameraBehaviour.Zoom /= previousScreenSize / currentScreenSize; + cameraBehaviour.Viewport = GraphicsDevice.Viewport; } if (Keyboard.GetState().IsKeyDown(Keys.U)) - gameManager.Camera.Zoom += gameTime.ElapsedGameTime.Nanoseconds * 0.00025f; + cameraBehaviour.Zoom += gameTime.ElapsedGameTime.Nanoseconds * 0.00025f; if (Keyboard.GetState().IsKeyDown(Keys.J)) - gameManager.Camera.Zoom -= gameTime.ElapsedGameTime.Nanoseconds * 0.00025f; + cameraBehaviour.Zoom -= gameTime.ElapsedGameTime.Nanoseconds * 0.00025f; if (Keyboard.GetState().IsKeyDown(Keys.Q)) - gameManager.Camera.Rotation += gameTime.ElapsedGameTime.Nanoseconds * 0.000025f; + cameraBehaviour.BehaviourController.GameObject.Transform.Rotation += gameTime.ElapsedGameTime.Nanoseconds * 0.000025f; if (Keyboard.GetState().IsKeyDown(Keys.E)) - gameManager.Camera.Rotation -= gameTime.ElapsedGameTime.Nanoseconds * 0.000025f; + cameraBehaviour.BehaviourController.GameObject.Transform.Rotation -= gameTime.ElapsedGameTime.Nanoseconds * 0.000025f; - // TODO: Add your update logic here - gameManager.Update(gameTime); + if (Keyboard.GetState().IsKeyDown(Keys.N)) + { + seconds = 70f; + while (physicsTimer + 0.01f < seconds) + { + physicsTimer += 0.01f; + engine.Step(.01f); + } + } + if (Keyboard.GetState().IsKeyDown(Keys.M)) + { + seconds = 0f; + while (physicsTimer - 0.01f > seconds) + { + physicsTimer -= 0.01f; + engine.Step(-.01f); + } + } + if (Keyboard.GetState().IsKeyDown(Keys.Space)) + { + seconds += gameTime.ElapsedGameTime.Milliseconds * .005f; + while (physicsTimer + 0.01f < seconds) + { + Console.WriteLine($"Physics Timer: {physicsTimer}"); + physicsTimer += 0.01f; + engine.Step(.01f); + } + } + if (Keyboard.GetState().IsKeyDown(Keys.B)) + { + seconds -= gameTime.ElapsedGameTime.Milliseconds * .005f; + while (physicsTimer - 0.01f > seconds) + { + Console.WriteLine($"Physics Timer: {physicsTimer}"); + physicsTimer -= 0.01f; + engine.Step(-.01f); + } + } + + while (physicsTimer + 0.01f < gameTime.TotalGameTime.TotalMilliseconds * .001f)//seconds) + { + // Console.WriteLine($"Physics Timer: {physicsTimer}"); + physicsTimer += 0.01f; + engine.Step(.01f); + } + gameManager.Update(gameTime.ToEngineTime()); base.Update(gameTime); } + static float physicsTimer = 0f; + static float seconds = 0f; protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(new Color() { R = 32, G = 32, B = 32 }); + // gameManager.Camera.Position = gameObjectBall.Transform.Position; + // Console.WriteLine($"Pos: {gameManager.Camera.Position}"); + // TODO: Add your drawing code here - gameManager.PreDraw(gameTime); + gameManager.PreDraw(); gameManager.Camera.Update(); - gameManager.Draw(_spriteBatch); + + _spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cameraBehaviour.MatrixTransform); + foreach (IGameObject gameObject in gameManager) + foreach (var displayable in gameObject.BehaviourController.GetBehaviours()) + displayable.Draw(_spriteBatch); + _spriteBatch.End(); + + _shapeBatch.Begin(cameraBehaviour.MatrixTransform); + foreach (IGameObject gameObject in gameManager) + foreach (var displayableShape in gameObject.BehaviourController.GetBehaviours()) + displayableShape.Draw(_shapeBatch); + _shapeBatch.End(); base.Draw(gameTime); } diff --git a/Game/Graphics/Abstract/Assignable/IAssignableSprite.cs b/Game/Graphics/Abstract/Assignable/IAssignableSprite.cs new file mode 100644 index 0000000..8f05c68 --- /dev/null +++ b/Game/Graphics/Abstract/Assignable/IAssignableSprite.cs @@ -0,0 +1,26 @@ +using System; + +namespace Syntriax.Engine.Core.Abstract; + +/// +/// Indicates the object is an with an assignable field. +/// +public interface IAssignableSprite : IAssignable +{ + /// + /// Callback triggered when the value has has been assigned a new value. + /// + Action? OnSpriteAssigned { get; set; } + + /// + ISprite Sprite { get; } + + /// + /// Assign a value to the field of this object + /// + /// New to assign. + /// + /// , if the value given assigned successfully assigned, if not. + /// + bool Assign(ISprite sprite); +} diff --git a/Game/Graphics/Abstract/ISprite.cs b/Game/Graphics/Abstract/ISprite.cs new file mode 100644 index 0000000..feb19e8 --- /dev/null +++ b/Game/Graphics/Abstract/ISprite.cs @@ -0,0 +1,13 @@ +using System; + +using Microsoft.Xna.Framework.Graphics; + +namespace Syntriax.Engine.Core.Abstract; + +// TODO Probably gonna have to rethink this +public interface ISprite +{ + Action? OnTextureChanged { get; set; } + + Texture2D Texture2D { get; set; } +} diff --git a/Game/Graphics/Sprite.cs b/Game/Graphics/Sprite.cs new file mode 100644 index 0000000..5adcdd3 --- /dev/null +++ b/Game/Graphics/Sprite.cs @@ -0,0 +1,27 @@ +using System; + +using Microsoft.Xna.Framework.Graphics; + +using Syntriax.Engine.Core.Abstract; + +namespace Syntriax.Engine.Core; + +public class Sprite : ISprite +{ + public Action? OnTextureChanged { get; set; } + + private Texture2D _texture = null!; + + public Texture2D Texture2D + { + get => _texture; + set + { + if (_texture == value) + return; + + _texture = value; + OnTextureChanged?.Invoke(this); + } + } +} diff --git a/Game/Graphics/TwoDimensional/Abstract/IDisplayable.cs b/Game/Graphics/TwoDimensional/Abstract/IDisplayable.cs new file mode 100644 index 0000000..d27a812 --- /dev/null +++ b/Game/Graphics/TwoDimensional/Abstract/IDisplayable.cs @@ -0,0 +1,8 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Syntriax.Engine.Core.Abstract; + +public interface IDisplayable +{ + public void Draw(SpriteBatch spriteBatch); +} diff --git a/Game/Graphics/TwoDimensional/Abstract/IDisplayableSprite.cs b/Game/Graphics/TwoDimensional/Abstract/IDisplayableSprite.cs new file mode 100644 index 0000000..83b8215 --- /dev/null +++ b/Game/Graphics/TwoDimensional/Abstract/IDisplayableSprite.cs @@ -0,0 +1,20 @@ +using System; + +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +using Syntriax.Engine.Core.Abstract; + +namespace Syntriax.Engine.Graphics.TwoDimensional.Abstract; +public interface IDisplayableSprite : IDisplayable, IAssignableSprite +{ + Action? OnSpriteEffectsChanged { get; set; } + Action? OnOriginChanged { get; set; } + Action? OnColorChanged { get; set; } + Action? OnDepthChanged { get; set; } + + SpriteEffects SpriteEffects { get; set; } + Vector2 Origin { get; set; } + Color Color { get; set; } + float Depth { get; set; } +} diff --git a/Game/Graphics/TwoDimensional/DisplayableSpriteBehaviour.cs b/Game/Graphics/TwoDimensional/DisplayableSpriteBehaviour.cs new file mode 100644 index 0000000..05b2d0d --- /dev/null +++ b/Game/Graphics/TwoDimensional/DisplayableSpriteBehaviour.cs @@ -0,0 +1,117 @@ +using System; + +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Graphics.TwoDimensional.Abstract; + +namespace Syntriax.Engine.Graphics.TwoDimensional; + +public class DisplayableSpriteBehaviour : Behaviour, IDisplayableSprite, IAssignableSprite +{ + public Action? OnSpriteAssigned { get; set; } = null; + public Action? OnSpriteEffectsChanged { get; set; } = null; + public Action? OnOriginChanged { get; set; } = null; + public Action? OnColorChanged { get; set; } = null; + public Action? OnDepthChanged { get; set; } = null; + + private ISprite _sprite = null!; + private Color _color = Color.White; + private float _depth = 0f; + private SpriteEffects _spriteEffects = SpriteEffects.None; + private Vector2 _origin = Vector2.One * .5f; + + + public ISprite Sprite => _sprite; + + public SpriteEffects SpriteEffects + { + get => _spriteEffects; + set + { + if (_spriteEffects == value) + return; + + _spriteEffects = value; + OnSpriteEffectsChanged?.Invoke(this); + } + } + + public Vector2 Origin + { + get => _origin; + set + { + if (_origin == value) + return; + + _origin = value; + OnOriginChanged?.Invoke(this); + } + } + + public Color Color + { + get => _color; + set + { + if (_color == value) + return; + + _color = value; + OnColorChanged?.Invoke(this); + } + } + + public float Depth + { + get => _depth; + set + { + if (_depth == value) + return; + + _depth = value; + OnDepthChanged?.Invoke(this); + } + } + + public void Draw(SpriteBatch spriteBatch) + { + if (!BehaviourController.GameObject.StateEnable.Enabled || !StateEnable.Enabled) + return; + + ITransform transform = BehaviourController.GameObject.Transform; + Vector2D position = transform.Position; + Vector2D scale = transform.Scale; + + Rectangle rectangle = new((int)position.X, -(int)position.Y, (int)(Sprite.Texture2D.Width * scale.X), (int)(Sprite.Texture2D.Height * scale.Y)); + + spriteBatch.Draw(Sprite.Texture2D, rectangle, null, Color, transform.Rotation, new Vector2(Sprite.Texture2D.Width, Sprite.Texture2D.Height) * Origin, SpriteEffects, Depth); + } + + public bool Assign(ISprite sprite) + { + _sprite = sprite; + OnSpriteAssigned?.Invoke(this); + return true; + } + + public DisplayableSpriteBehaviour() => OnUnassigned += OnUnassign; + public DisplayableSpriteBehaviour( + Color? color = null, + float? depth = null, + SpriteEffects? spriteEffects = null, + Vector2? origin = null) + { + OnUnassigned += OnUnassign; + + _color = color ?? _color; + _depth = depth ?? _depth; + _spriteEffects = spriteEffects ?? _spriteEffects; + _origin = origin ?? _origin; + } + private void OnUnassign(IAssignable assignable) => _sprite = null!; +} diff --git a/Pong.sln b/Pong.sln index d951ade..f5561fd 100644 --- a/Pong.sln +++ b/Pong.sln @@ -11,7 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine\Engine.Input\Engine.Input.csproj", "{7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Graphics", "Engine\Engine.Graphics\Engine.Graphics.csproj", "{7371D9AF-6F4D-4F05-8458-197C4EE66B01}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine\Engine.Physics2D\Engine.Physics2D.csproj", "{0D97F83C-B043-48B1-B155-7354C4E84FC0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,14 +34,14 @@ Global {7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EED4EC3-79D5-4C6C-A54D-1B396213C0E4}.Release|Any CPU.Build.0 = Release|Any CPU - {7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7371D9AF-6F4D-4F05-8458-197C4EE66B01}.Release|Any CPU.Build.0 = Release|Any CPU + {0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {990CA10C-1EBB-4395-A43A-456B7029D8C9} = {F7F62670-237A-4C93-A30E-CE661C6FC401} {7EED4EC3-79D5-4C6C-A54D-1B396213C0E4} = {F7F62670-237A-4C93-A30E-CE661C6FC401} - {7371D9AF-6F4D-4F05-8458-197C4EE66B01} = {F7F62670-237A-4C93-A30E-CE661C6FC401} + {0D97F83C-B043-48B1-B155-7354C4E84FC0} = {F7F62670-237A-4C93-A30E-CE661C6FC401} EndGlobalSection EndGlobal