diff --git a/PongFE/Components/BallParametersComponent.cs b/PongFE/Components/BallParametersComponent.cs new file mode 100644 index 0000000..15ae648 --- /dev/null +++ b/PongFE/Components/BallParametersComponent.cs @@ -0,0 +1,16 @@ +using Encompass; + +namespace PongFE.Components +{ + public struct BallParametersComponent : IComponent + { + public int Speed { get; } + public double Delay { get; } + + public BallParametersComponent(int speed, double delay) + { + Speed = speed; + Delay = delay; + } + } +} diff --git a/PongFE/Components/SpawnBallAfterDestroyComponent.cs b/PongFE/Components/SpawnBallAfterDestroyComponent.cs deleted file mode 100644 index 912596e..0000000 --- a/PongFE/Components/SpawnBallAfterDestroyComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Encompass; - -namespace PongFE.Components -{ - public struct SpawnBallAfterDestroyComponent : IComponent - { - public float Speed { get; } - public float Seconds { get; } - - public SpawnBallAfterDestroyComponent(float speed, float seconds) - { - Speed = speed; - Seconds = seconds; - } - } -} diff --git a/PongFE/Components/UITextComponent.cs b/PongFE/Components/UITextComponent.cs new file mode 100644 index 0000000..613fd8e --- /dev/null +++ b/PongFE/Components/UITextComponent.cs @@ -0,0 +1,17 @@ +using Encompass; +using SpriteFontPlus; + +namespace PongFE.Components +{ + public struct UITextComponent : IComponent + { + public DynamicSpriteFont Font { get; } + public string Text { get; } + + public UITextComponent(DynamicSpriteFont font, string text) + { + Font = font; + Text = text; + } + } +} diff --git a/PongFE/Engines/DestroyEngine.cs b/PongFE/Engines/DestroyEngine.cs index 64e0697..2ecd382 100644 --- a/PongFE/Engines/DestroyEngine.cs +++ b/PongFE/Engines/DestroyEngine.cs @@ -5,35 +5,16 @@ using PongFE.Messages; namespace PongFE.Engines { [Reads( - typeof(SpawnBallAfterDestroyComponent), typeof(IncreaseScoreAfterDestroyComponent) )] [Receives(typeof(DestroyMessage))] - [Sends( - typeof(BallSpawnMessage), - typeof(ScoreMessage) - )] + [Sends(typeof(ScoreMessage))] public class DestroyEngine : Engine { public override void Update(double dt) { foreach (ref readonly var message in ReadMessages()) { - if (HasComponent(message.Entity)) - { - ref readonly var respawnComponent = ref GetComponent(message.Entity); - - SendMessage( - new BallSpawnMessage( - new MoonTools.Structs.Position2D(640, 360), - respawnComponent.Speed, - 16, - 16 - ), - respawnComponent.Seconds - ); - } - if (HasComponent(message.Entity)) { SendMessage(new ScoreMessage(message.DestroyedBy)); diff --git a/PongFE/Engines/GameStateEngine.cs b/PongFE/Engines/GameStateEngine.cs index 9d9baa4..c310389 100644 --- a/PongFE/Engines/GameStateEngine.cs +++ b/PongFE/Engines/GameStateEngine.cs @@ -1,22 +1,39 @@ using Encompass; using Microsoft.Xna.Framework.Input; +using MoonTools.Structs; using PongFE.Components; using PongFE.Enums; using PongFE.Messages; +using SpriteFontPlus; namespace PongFE.Engines { - [Reads(typeof(GameStateComponent), typeof(PlayAreaComponent))] + [Reads( + typeof(PositionComponent), + typeof(GameStateComponent), + typeof(PlayAreaComponent), + typeof(UITextComponent) + )] [Receives(typeof(ChangeGameStateMessage))] [Sends( typeof(BallSpawnMessage), typeof(PaddleSpawnMessage), typeof(BoundarySpawnMessage), - typeof(GoalBoundarySpawnMessage) + typeof(GoalBoundarySpawnMessage), + typeof(UITextSpawnMessage) )] [Writes(typeof(GameStateComponent))] public class GameStateEngine : Engine { + private DynamicSpriteFont TitleFont { get; } + private DynamicSpriteFont InstructionFont { get; } + + public GameStateEngine(DynamicSpriteFont titleFont, DynamicSpriteFont instructionFont) + { + TitleFont = titleFont; + InstructionFont = instructionFont; + } + public override void Update(double dt) { ref readonly var gameStateEntity = ref ReadEntity(); @@ -42,15 +59,12 @@ namespace PongFE.Engines return; } - if (gameStateComponent.GameState == GameState.Game) + if (changeGameStateMessage.GameState == GameState.Title) { - if (changeGameStateMessage.GameState == GameState.Title) - { - EndGame(); - StartTitle(); + EndGame(); + StartTitle(); - SetComponent(gameStateEntity, new GameStateComponent(GameState.Title)); - } + SetComponent(gameStateEntity, new GameStateComponent(GameState.Title)); } } } @@ -137,12 +151,36 @@ namespace PongFE.Engines private void StartTitle() { + ref readonly var playAreaComponent = ref ReadComponent(); + var titleDimensions = TitleFont.MeasureString("PongFE"); + var titlePosition = new Position2D( + (playAreaComponent.Width - titleDimensions.X) / 2, + (playAreaComponent.Height - titleDimensions.Y) / 4 + ); + + SendMessage(new UITextSpawnMessage( + titlePosition, + TitleFont, + "PongFE" + )); + + var instructionDimensions = InstructionFont.MeasureString("Press Enter to begin"); + var instructionPosition = new Position2D( + (playAreaComponent.Width - instructionDimensions.X) / 2, + playAreaComponent.Height * 2 / 3 + ); + + SendMessage(new UITextSpawnMessage( + instructionPosition, + InstructionFont, + "Press Enter to play" + )); } private void EndTitle() { - + DestroyAllWith(); } } } diff --git a/PongFE/Engines/GameWinEngine.cs b/PongFE/Engines/GameWinEngine.cs new file mode 100644 index 0000000..f4d9878 --- /dev/null +++ b/PongFE/Engines/GameWinEngine.cs @@ -0,0 +1,55 @@ +using Encompass; +using PongFE.Components; +using PongFE.Enums; +using PongFE.Messages; +using SpriteFontPlus; + +namespace PongFE.Engines +{ + [Reads(typeof(PlayAreaComponent))] + [Receives(typeof(GameWinMessage))] + [Sends(typeof(UITextSpawnMessage), typeof(ChangeGameStateMessage))] + public class GameWinEngine : Engine + { + public DynamicSpriteFont Font { get; } + private readonly string _playerOneWinText = "Player 1 Wins!"; + private readonly string _playerTwoWinText = "Player 2 Wins!"; + + public GameWinEngine(DynamicSpriteFont font) + { + Font = font; + } + + public override void Update(double dt) + { + if (SomeMessage()) + { + ref readonly var gameWinMessage = ref ReadMessage(); + ref readonly var playAreaComponent = ref ReadComponent(); + + string winText; + if (gameWinMessage.PlayerIndex == PlayerIndex.One) + { + winText = _playerOneWinText; + } + else + { + winText = _playerTwoWinText; + } + + var textDimensions = Font.MeasureString(winText); + + SendMessage(new UITextSpawnMessage( + new MoonTools.Structs.Position2D( + (playAreaComponent.Width - textDimensions.X) / 2, + playAreaComponent.Height / 4 + ), + Font, + winText + )); + + SendMessage(new ChangeGameStateMessage(GameState.Title), 2); + } + } + } +} diff --git a/PongFE/Engines/ScoreEngine.cs b/PongFE/Engines/ScoreEngine.cs index 8e51019..6e3b63c 100644 --- a/PongFE/Engines/ScoreEngine.cs +++ b/PongFE/Engines/ScoreEngine.cs @@ -4,8 +4,13 @@ using PongFE.Messages; namespace PongFE.Engines { - [Reads(typeof(ScoreComponent))] + [Reads( + typeof(ScoreComponent), + typeof(PlayerComponent), + typeof(BallParametersComponent) + )] [Receives(typeof(ScoreMessage))] + [Sends(typeof(GameWinMessage), typeof(BallSpawnMessage))] [Writes(typeof(ScoreComponent))] public class ScoreEngine : Engine { @@ -17,6 +22,26 @@ namespace PongFE.Engines { ref readonly var scoreComponent = ref GetComponent(scoreMessage.Entity); SetComponent(scoreMessage.Entity, new ScoreComponent(scoreComponent.Score + 1)); + + if (scoreComponent.Score + 1 >= 2) + { + ref readonly var playerComponent = ref GetComponent(scoreMessage.Entity); + SendMessage(new GameWinMessage(playerComponent.PlayerIndex)); + } + else + { + ref readonly var ballParametersComponent = ref ReadComponent(); + + SendMessage( + new BallSpawnMessage( + new MoonTools.Structs.Position2D(640, (int)MathHelper.RandomFloat(20, 700)), + ballParametersComponent.Speed, + 16, + 16 + ), + ballParametersComponent.Delay + ); + } } } } diff --git a/PongFE/Engines/Spawners/BallSpawner.cs b/PongFE/Engines/Spawners/BallSpawner.cs index a52ff5b..9269e39 100644 --- a/PongFE/Engines/Spawners/BallSpawner.cs +++ b/PongFE/Engines/Spawners/BallSpawner.cs @@ -32,7 +32,6 @@ namespace PongFE.Spawners AddComponent(ball, new BounceResponseComponent()); AddComponent(ball, new CanBeTrackedComponent()); AddComponent(ball, new CanBeDestroyedComponent()); - AddComponent(ball, new SpawnBallAfterDestroyComponent(message.Speed, 0.5f)); AddComponent(ball, new IncreaseScoreAfterDestroyComponent()); } } diff --git a/PongFE/Engines/Spawners/UITextSpawner.cs b/PongFE/Engines/Spawners/UITextSpawner.cs new file mode 100644 index 0000000..a0f0472 --- /dev/null +++ b/PongFE/Engines/Spawners/UITextSpawner.cs @@ -0,0 +1,17 @@ +using Encompass; +using PongFE.Components; +using PongFE.Messages; + +namespace PongFE.Spawners +{ + public class UITextSpawner : Spawner + { + protected override void Spawn(UITextSpawnMessage message) + { + var entity = CreateEntity(); + + AddComponent(entity, new PositionComponent(message.Position)); + AddComponent(entity, new UITextComponent(message.Font, message.Text)); + } + } +} diff --git a/PongFE/Enums/Enums.cs b/PongFE/Enums/Enums.cs index 26e0523..3ec53f6 100644 --- a/PongFE/Enums/Enums.cs +++ b/PongFE/Enums/Enums.cs @@ -26,6 +26,7 @@ namespace PongFE.Enums public enum GameState { + Init, Title, Game } diff --git a/PongFE/Messages/GameWinMessage.cs b/PongFE/Messages/GameWinMessage.cs new file mode 100644 index 0000000..ba75fda --- /dev/null +++ b/PongFE/Messages/GameWinMessage.cs @@ -0,0 +1,15 @@ +using Encompass; +using PongFE.Enums; + +namespace PongFE.Messages +{ + public struct GameWinMessage : IMessage + { + public PlayerIndex PlayerIndex { get; } + + public GameWinMessage(PlayerIndex playerIndex) + { + PlayerIndex = playerIndex; + } + } +} diff --git a/PongFE/Messages/UITextSpawnMessage.cs b/PongFE/Messages/UITextSpawnMessage.cs new file mode 100644 index 0000000..7beb8df --- /dev/null +++ b/PongFE/Messages/UITextSpawnMessage.cs @@ -0,0 +1,20 @@ +using Encompass; +using MoonTools.Structs; +using SpriteFontPlus; + +namespace PongFE.Messages +{ + public struct UITextSpawnMessage : IMessage + { + public Position2D Position { get; } + public string Text { get; } + public DynamicSpriteFont Font { get; } + + public UITextSpawnMessage(Position2D position, DynamicSpriteFont font, string text) + { + Position = position; + Font = font; + Text = text; + } + } +} diff --git a/PongFE/PongFEGame.cs b/PongFE/PongFEGame.cs index 5f74205..e50711f 100644 --- a/PongFE/PongFEGame.cs +++ b/PongFE/PongFEGame.cs @@ -69,7 +69,7 @@ namespace PongFE 48 ); - WorldBuilder.AddEngine(new GameStateEngine()); + WorldBuilder.AddEngine(new GameStateEngine(ScoreFont, InstructionFont)); WorldBuilder.AddEngine(new InputEngine()); WorldBuilder.AddEngine(new PaddleMovementEngine()); WorldBuilder.AddEngine(new VelocityEngine()); @@ -82,23 +82,29 @@ namespace PongFE WorldBuilder.AddEngine(new UpdatePositionEngine()); WorldBuilder.AddEngine(new UpdateVelocityEngine()); WorldBuilder.AddEngine(new ComputerControlEngine()); + WorldBuilder.AddEngine(new GameWinEngine(ScoreFont)); WorldBuilder.AddEngine(new BallSpawner(WhitePixel)); WorldBuilder.AddEngine(new BoundarySpawner()); WorldBuilder.AddEngine(new GoalBoundarySpawner()); WorldBuilder.AddEngine(new PaddleSpawner(WhitePixel)); + WorldBuilder.AddEngine(new UITextSpawner()); WorldBuilder.AddOrderedRenderer(new Texture2DRenderer(SpriteBatch)); WorldBuilder.AddGeneralRenderer(new CenterLineRenderer(SpriteBatch, WhitePixel), 0); WorldBuilder.AddGeneralRenderer(new ScoreRenderer(SpriteBatch, ScoreFont), 0); - WorldBuilder.AddGeneralRenderer(new TitleRenderer(SpriteBatch, ScoreFont, InstructionFont), 0); + WorldBuilder.AddGeneralRenderer(new UITextRenderer(SpriteBatch), 0); var playAreaEntity = WorldBuilder.CreateEntity(); WorldBuilder.SetComponent(playAreaEntity, new PlayAreaComponent(PLAY_AREA_WIDTH, PLAY_AREA_HEIGHT)); var gameStateEntity = WorldBuilder.CreateEntity(); - WorldBuilder.SetComponent(gameStateEntity, new GameStateComponent(GameState.Title)); + WorldBuilder.SetComponent(gameStateEntity, new GameStateComponent(GameState.Init)); + var ballParametersEntity = WorldBuilder.CreateEntity(); + WorldBuilder.SetComponent(ballParametersEntity, new BallParametersComponent(500, 0.5)); + + WorldBuilder.SendMessage(new ChangeGameStateMessage(GameState.Title)); World = WorldBuilder.Build(); } diff --git a/PongFE/Renderers/TitleRenderer.cs b/PongFE/Renderers/TitleRenderer.cs deleted file mode 100644 index 56ceb95..0000000 --- a/PongFE/Renderers/TitleRenderer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Encompass; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using PongFE.Components; -using PongFE.Enums; -using SpriteFontPlus; - -namespace PongFE.Renderers -{ - public class TitleRenderer : GeneralRenderer - { - private SpriteBatch SpriteBatch { get; } - private DynamicSpriteFont TitleFont { get; } - private DynamicSpriteFont InstructionFont { get; } - - public TitleRenderer(SpriteBatch spriteBatch, DynamicSpriteFont titleFont, DynamicSpriteFont instructionFont) - { - SpriteBatch = spriteBatch; - TitleFont = titleFont; - InstructionFont = instructionFont; - } - - public override void Render() - { - ref readonly var gameStateComponent = ref ReadComponent(); - ref readonly var playAreaComponent = ref ReadComponent(); - - if (gameStateComponent.GameState == GameState.Title) - { - var titleDimensions = TitleFont.MeasureString("PongFE"); - var titlePosition = new Vector2( - (playAreaComponent.Width - titleDimensions.X) / 2, - (playAreaComponent.Height - titleDimensions.Y) / 4 - ); - - var instructionDimensions = InstructionFont.MeasureString("Press Enter to begin"); - var instructionPosition = new Vector2( - (playAreaComponent.Width - instructionDimensions.X) / 2, - playAreaComponent.Height * 2 / 3 - ); - - SpriteBatch.DrawString(TitleFont, "PongFE", titlePosition, Color.White); - SpriteBatch.DrawString(InstructionFont, "Press Enter to begin", instructionPosition, Color.White); - } - } - } -} diff --git a/PongFE/Renderers/UITextRenderer.cs b/PongFE/Renderers/UITextRenderer.cs new file mode 100644 index 0000000..bd14877 --- /dev/null +++ b/PongFE/Renderers/UITextRenderer.cs @@ -0,0 +1,35 @@ +using Encompass; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using PongFE.Components; +using PongFE.Extensions; +using SpriteFontPlus; + +namespace PongFE.Renderers +{ + public class UITextRenderer : GeneralRenderer + { + private SpriteBatch SpriteBatch { get; } + + public UITextRenderer(SpriteBatch spriteBatch) + { + SpriteBatch = spriteBatch; + } + + public override void Render() + { + foreach (ref readonly var entity in ReadEntities()) + { + ref readonly var uiTextComponent = ref GetComponent(entity); + ref readonly var positionComponent = ref GetComponent(entity); + + SpriteBatch.DrawString( + uiTextComponent.Font, + uiTextComponent.Text, + positionComponent.Position.ToXNAVector(), + Color.White + ); + } + } + } +} diff --git a/encompass-cs b/encompass-cs index 59014c7..b4a5a4c 160000 --- a/encompass-cs +++ b/encompass-cs @@ -1 +1 @@ -Subproject commit 59014c7a9e7c3c90b902c8025d907a33a0ffb75c +Subproject commit b4a5a4c66adb1fe268954acdfb6839bd21189b1d