diff --git a/content/concepts/component.md b/content/concepts/component.md index c2e5476..63265b2 100644 --- a/content/concepts/component.md +++ b/content/concepts/component.md @@ -6,13 +6,11 @@ weight: 5 A Component is a structure of related data. -To define a Component, declare a struct which implements the **IComponent** interface. - ```cs using Encompass; using System.Numerics; -public struct VelocityComponent : IComponent { +public struct VelocityComponent { public Vector2 Velocity { get; } diff --git a/content/concepts/message.md b/content/concepts/message.md index 1b2deac..fe03d5f 100644 --- a/content/concepts/message.md +++ b/content/concepts/message.md @@ -8,12 +8,12 @@ Similar to Components, Messages are collections of data. Messages are used to transmit data between Engines so they can manipulate the game state accordingly. -To define a message, declare a struct which implements the IMessage interface. +To define a message, declare a struct. ```cs using Encompass; -public struct MotionMessage : IMessage { +public struct MotionMessage { public Vector2 Motion { get; } } ``` diff --git a/content/concepts/renderer.md b/content/concepts/renderer.md index cc5a7ef..319160b 100644 --- a/content/concepts/renderer.md +++ b/content/concepts/renderer.md @@ -10,11 +10,7 @@ A Renderer is responsible for reading the game state and telling the game engine Remember: Encompass isn't a game engine and it doesn't have a rendering system. So Renderers aren't actually doing the rendering, it is just a way of structuring how we tell the game engine what to render. {{% /notice %}} -There are two kinds of renderers: GeneralRenderers and OrderedRenderers. - -A GeneralRenderer is a Renderer which reads the game state in order to draw elements to the screen. It also requires a layer, which represents the order in which the Draw method will execute in relation to other Renderers. - -If you were using FNA, a GeneralRenderer might look like this: +If you were using FNA, a Renderer might look like this: ```cs using System; @@ -25,12 +21,12 @@ using MyGame.Messages; namespace MyGame.Renderers { - public class GridRenderer : GeneralRenderer + public class GridRenderer : Renderer { private int gridSize; private PrimitiveDrawer primitiveDrawer; - public GridRenderer(PrimitiveDrawer primitiveDrawer) + public Renderer(PrimitiveDrawer primitiveDrawer) { this.primitiveDrawer = primitiveDrawer; this.gridSize = 16; @@ -58,51 +54,6 @@ namespace MyGame.Renderers } ``` -This GeneralRenderer will draw a rectangle at the position of the mouse on the screen if an `EditorModeComponent` exists in the world. +This Renderer will draw a rectangle at the position of the mouse on the screen if an `EditorModeComponent` exists in the world. -GeneralRenderers are great for things like a heads-up display, where we always want a group of particular elements to be drawn at a specific layer regardless of the specifics of the game state. - -An OrderedRenderer provides a structure for the common pattern of wanting to draw an individual Component at a specific layer. OrderedRenderers must specify a component that implements IDrawableComponent. - -If you were using FNA, an OrderedRenderer might look like this: - -```cs -using Encompass; -using Microsoft.Xna.Framework.Graphics; -using MyGame.Components; -using MyGame.Extensions; -using System; -using System.Numerics; - -namespace MyGame.Renderers -{ - public class Texture2DRenderer : OrderedRenderer - { - private SpriteBatch spriteBatch; - - public Texture2DRenderer(SpriteBatch spriteBatch) - { - this.spriteBatch = spriteBatch; - } - - public override void Render(Entity entity, Texture2DComponent textureComponent) - { - var transformComponent = GetComponent(entity); - - spriteBatch.Draw( - textureComponent.Texture, - transformComponent.Position, - null, - textureComponent.Color, - transformComponent.Rotation, - textureComponent.Origin, - transformComponent.Scale, - SpriteEffects.None, - 0 - ); - } - } -} -``` - -For 2D games, you will need to use layers to be specific about the order in which elements are drawn to the screen. For a 3D game you will probably end up delegating most of the rendering to some kind of scene/camera system. +It's your job to figure out how to interpret your game's data into rendering. Many games will only have one big Renderer that just renders everything. diff --git a/content/concepts/world_builder.md b/content/concepts/world_builder.md index 566e4bd..05885b3 100644 --- a/content/concepts/world_builder.md +++ b/content/concepts/world_builder.md @@ -27,7 +27,7 @@ public class MyGame : Game var worldBuilder = new WorldBuilder(); worldBuilder.AddEngine(new MotionEngine()); - worldBuilder.AddOrderedRenderer(new TextureRenderer()); + worldBuilder.AddRenderer(new TextureRenderer()); var entity = worldBuilder.CreateEntity(); diff --git a/content/pong/ball/bouncing/boundaries.md b/content/pong/ball/bouncing/boundaries.md index 6c2a40a..0304356 100644 --- a/content/pong/ball/bouncing/boundaries.md +++ b/content/pong/ball/bouncing/boundaries.md @@ -14,7 +14,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct BoundarySpawnMessage : IMessage + public struct BoundarySpawnMessage { public Position2D Position { get; } public int Width { get; } diff --git a/content/pong/ball/bouncing/collision_dispatch.md b/content/pong/ball/bouncing/collision_dispatch.md index 538be3d..ec2694c 100644 --- a/content/pong/ball/bouncing/collision_dispatch.md +++ b/content/pong/ball/bouncing/collision_dispatch.md @@ -15,7 +15,7 @@ using Encompass; namespace PongFE.Components { - public struct CanCauseBounceComponent : IComponent { } + public struct CanCauseBounceComponent { } } ``` @@ -26,7 +26,7 @@ using Encompass; namespace PongFE.Components { - public struct CanBeBouncedComponent : IComponent { } + public struct CanBeBouncedComponent { } } ``` @@ -38,7 +38,7 @@ using PongFE.Components; namespace PongFE.Messages { - public struct BounceMessage : IMessage, IHasEntity + public struct BounceMessage : IHasEntity { public Entity Entity { get; } public HitOrientation HitOrientation { get; } diff --git a/content/pong/ball/bouncing/collision_response.md b/content/pong/ball/bouncing/collision_response.md index e21544c..1cbab44 100644 --- a/content/pong/ball/bouncing/collision_response.md +++ b/content/pong/ball/bouncing/collision_response.md @@ -62,7 +62,7 @@ using Encompass; namespace PongFE.Messages { - public struct UpdateVelocityMessage : IMessage, IHasEntity + public struct UpdateVelocityMessage : IHasEntity { public Entity Entity { get; } public Vector2 Velocity { get; } diff --git a/content/pong/ball/bouncing/motion_engine.md b/content/pong/ball/bouncing/motion_engine.md index 3378852..8152415 100644 --- a/content/pong/ball/bouncing/motion_engine.md +++ b/content/pong/ball/bouncing/motion_engine.md @@ -20,7 +20,7 @@ using MoonTools.Bonk; namespace PongFE.Components { - public struct CollisionComponent : IComponent + public struct CollisionComponent { public Rectangle Rectangle { get; } @@ -72,7 +72,7 @@ namespace PongFE.Engines } ``` -First, at the very beginning of the processing, we insert all of our entities with a Position and Collision into the SpatialHash. +First, at the very beginning of the processing, we insert all of our entities with a Position and Collision into the SpatialHash. Next, in a separate block, let's consolidate our MotionMessages per Entity. @@ -146,7 +146,7 @@ Finally, let's implement our sweep test. } ``` -Here we use Bonk's **SweepTest** functionality. A sweep test moves a rectangle along a vector, checking for collisions along the movement of the sweep. First we sweep in a horizontal direction, and then in a vertical direction, returning the positions where collisions occurred. This means that objects won't awkwardly stop in place when they touch something. +Here we use Bonk's **SweepTest** functionality. A sweep test moves a rectangle along a vector, checking for collisions along the movement of the sweep. First we sweep in a horizontal direction, and then in a vertical direction, returning the positions where collisions occurred. This means that objects won't awkwardly stop in place when they touch something. {{% notice note %}} In the future you might want to change how the sweep tests resolve a position, and this would certainly be possible using this system. You could have different components like **SlideOnCollisionComponent** or **StickOnCollisionComponent**, for example, that would affect the returned sweep position. @@ -167,7 +167,7 @@ namespace PongFE.Messages Vertical } - public struct CollisionMessage : IMessage + public struct CollisionMessage { public Entity EntityA { get; } public Entity EntityB { get; } @@ -193,7 +193,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct UpdatePositionMessage : IMessage, IHasEntity + public struct UpdatePositionMessage : IHasEntity { public Entity Entity { get; } public Position2D Position { get; } diff --git a/content/pong/ball/moving.md b/content/pong/ball/moving.md index 5653e0b..cf72da6 100644 --- a/content/pong/ball/moving.md +++ b/content/pong/ball/moving.md @@ -18,7 +18,7 @@ using Encompass; namespace PongFE.Components { - public struct VelocityComponent : IComponent + public struct VelocityComponent { public Vector2 Velocity { get; } @@ -94,7 +94,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct BallSpawnMessage : IMessage + public struct BallSpawnMessage { public Position2D Position { get; } public Vector2 Velocity { get; } diff --git a/content/pong/ball/revisiting_spawners.md b/content/pong/ball/revisiting_spawners.md index 0b43b5d..846bce5 100644 --- a/content/pong/ball/revisiting_spawners.md +++ b/content/pong/ball/revisiting_spawners.md @@ -97,7 +97,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct PaddleSpawnMessage : IMessage + public struct PaddleSpawnMessage { public Position2D Position { get; } public PlayerIndex PlayerIndex { get; } @@ -189,17 +189,15 @@ using Microsoft.Xna.Framework.Graphics; namespace PongFE.Components { - public struct Texture2DComponent : IComponent, IDrawableComponent + public struct Texture2DComponent { public Texture2D Texture { get; } public Vector2 Scale { get; } - public int Layer { get; } - public Texture2DComponent(Texture2D texture, int layer, Vector2 scale) + public Texture2DComponent(Texture2D texture, Vector2 scale) { Texture = texture; Scale = scale; - Layer = layer; } } } @@ -208,22 +206,17 @@ namespace PongFE.Components And in **PongFE/Renderers/Texture2DRenderer.cs**: ```cs -public override void Render(Entity entity, in Texture2DComponent textureComponent) -{ - ref readonly var positionComponent = ref GetComponent(entity); - - _spriteBatch.Draw( - textureComponent.Texture, - positionComponent.Position.ToXNAVector(), - null, - Color.White, - 0, - Vector2.Zero, - textureComponent.Scale.ToXNAVector(), - SpriteEffects.None, - 0 - ); -} +_spriteBatch.Draw( + textureComponent.Texture, + positionComponent.Position.ToXNAVector(), + null, + Color.White, + 0, + Vector2.Zero, + textureComponent.Scale.ToXNAVector(), + SpriteEffects.None, + 0 +); ``` Now go back to the paddle spawner... @@ -287,7 +280,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct BallSpawnMessage : IMessage + public struct BallSpawnMessage { public Position2D Position { get; } public Vector2 Velocity { get; } diff --git a/content/pong/ball/spawning.md b/content/pong/ball/spawning.md index e7d80fb..0cc0d76 100644 --- a/content/pong/ball/spawning.md +++ b/content/pong/ball/spawning.md @@ -40,7 +40,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct BallSpawnMessage : IMessage + public struct BallSpawnMessage { public Position2D Position { get; } diff --git a/content/pong/draw_paddle/_index.md b/content/pong/draw_paddle/_index.md index 208411f..425ed9d 100644 --- a/content/pong/draw_paddle/_index.md +++ b/content/pong/draw_paddle/_index.md @@ -6,10 +6,6 @@ weight: 10 It's nice to see something on screen right away when we start making a game, so let's make that happen. -In a 2D game, Encompass needs to know which order that things should draw in. - -Encompass draws things back to front using integer layers. A negative value means farther in the back. A positive value means farther in the front. So an object on layer 10 will draw on top of an object on layer -10. - -We'll need two things to get a paddle drawing on screen: A *DrawComponent* and an *OrderedRenderer*. +We'll need two things to get a paddle drawing on screen: A *Component* and an *Renderer*. Let's start with the Component. diff --git a/content/pong/draw_paddle/initialize_world.md b/content/pong/draw_paddle/initialize_world.md index ae2266a..8e26d8d 100644 --- a/content/pong/draw_paddle/initialize_world.md +++ b/content/pong/draw_paddle/initialize_world.md @@ -86,7 +86,7 @@ Now we want to attach our Texture2DRenderer to the World. ```cs ... - WorldBuilder.AddOrderedRenderer(new Texture2DRenderer(SpriteBatch)); + WorldBuilder.AddRenderer(new Texture2DRenderer(SpriteBatch)); var paddle = WorldBuilder.CreateEntity(); ... @@ -103,9 +103,7 @@ Our game's Draw method should look like this: { GraphicsDevice.Clear(Color.CornflowerBlue); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); World.Draw(); - SpriteBatch.End(); base.Draw(gameTime); } @@ -161,7 +159,7 @@ namespace PongFE SpriteBatch.End(); GraphicsDevice.SetRenderTarget(null); - WorldBuilder.AddOrderedRenderer(new Texture2DRenderer(SpriteBatch)); + WorldBuilder.AddRenderer(new Texture2DRenderer(SpriteBatch)); var paddle = WorldBuilder.CreateEntity(); WorldBuilder.SetComponent(paddle, new PositionComponent(new MoonTools.Structs.Position2D(5, 5))); @@ -186,9 +184,7 @@ namespace PongFE { GraphicsDevice.Clear(Color.CornflowerBlue); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); World.Draw(); - SpriteBatch.End(); base.Draw(gameTime); } diff --git a/content/pong/draw_paddle/position_component.md b/content/pong/draw_paddle/position_component.md index 5f9e181..073531c 100644 --- a/content/pong/draw_paddle/position_component.md +++ b/content/pong/draw_paddle/position_component.md @@ -48,7 +48,7 @@ using MoonTools.Structs; namespace PongFE.Components { - public struct PositionComponent : IComponent + public struct PositionComponent { public Position2D Position { get; } diff --git a/content/pong/draw_paddle/texture_component.md b/content/pong/draw_paddle/texture_component.md index 17d956e..499835d 100644 --- a/content/pong/draw_paddle/texture_component.md +++ b/content/pong/draw_paddle/texture_component.md @@ -6,25 +6,22 @@ weight: 5 In a 2D game using FNA, the main way you will be drawing elements to the screen is via Texture2D. -Let's set up a Texture2DComponent. To create a new Component type, we define a struct that implements the IComponent interface. +Let's set up a Texture2DComponent. To create a new Component type, we define a struct. Create a file: **PongFE/Components/Texture2DComponent.cs** ```cs -using Encompass; using Microsoft.Xna.Framework.Graphics; namespace PongFE.Components { - public struct Texture2DComponent : IComponent, IDrawableComponent + public struct Texture2DComponent { public Texture2D Texture { get; } - public int Layer { get; } - public Texture2DComponent(Texture2D texture, int layer) + public Texture2DComponent(Texture2D texture) { Texture = texture; - Layer = layer; } } } @@ -32,7 +29,7 @@ namespace PongFE.Components There's a lot of new information here, so let's break this down a bit. -*using* means that we are using functionality provided to us by another project. In this case, Encompass provides us with the IComponent and IDrawableComponent interfaces, and FNA provides us with the Texture2D class through the Microsoft.Xna.Framework.Graphics namespace. +*using* means that we are using functionality provided to us by another project. In this case, FNA provides us with the Texture2D class through the Microsoft.Xna.Framework.Graphics namespace. *namespace* is used for organization and to prevent naming collisions. Let's say that we have a Texture2DComponent here, but another library that we include also defines something called Texture2DComponent. Now we have an ambiguity! Using namespaces avoids this problem. @@ -42,8 +39,6 @@ Why is the namespace Microsoft.Xna.Framework instead of just FNA? Remember that *public* means you would like Texture2DComponent to be accessible in other files and projects. Most of your classes and structs will be public, though there are cases where you might want them to be *private* or *internal*, for example a utility class that you don't want to expose externally. -What is IDrawableComponent? An IDrawableComponent is an interface that lets Encompass know that the Component includes a *Layer* property, which is used for rendering. Any time you want a component to be drawn in a specific order before or after other components, you will need to declare that your component implements IDrawableComponent. - Finally, that method is called a *constructor*. When we create an instance of Texture2DComponent, we will assign it a Texture2D that has stuff drawn on it and tell it which layer to use. We'll get to all that in a minute. That's it for our component. We need one more bit of information before we can write our Renderer. diff --git a/content/pong/draw_paddle/texture_renderer.md b/content/pong/draw_paddle/texture_renderer.md index 4a9fcda..a6123cf 100644 --- a/content/pong/draw_paddle/texture_renderer.md +++ b/content/pong/draw_paddle/texture_renderer.md @@ -16,9 +16,9 @@ using PongFE.Components; namespace PongFE.Renderers { - public class Texture2DRenderer : OrderedRenderer + public class Texture2DRenderer : Renderer { - public override void Render(Entity entity, in Texture2DComponent textureComponent) + public override void Render() { } @@ -30,8 +30,6 @@ Before we go any further, let's talk about some of the new concepts introduced h Notice that this is a *class* instead of a struct, like we have done before. There are many reasons why you might want to use classes vs structs in C# in general, but for our purposes, you can use the following rule of thumb: if the object contains data, it should be a struct. If it contains logic, it should be a class. Since Renderers draw things (logic), they are classes. -An *OrderedRenderer* is defined by the Component type it tracks and a *Render* method. It uses the *Layer* property of the specified Component to draw things in the correct order. Each time *World.Draw* is called, the OrderedRenderer will run its *Render* method for each Entity that contains a Component of its specified tracking type. - The most efficient way for us to draw Texture2Ds is to use FNA's SpriteBatch system. If you actually care about how SpriteBatch works and why we need to structure our draws using it, read the Note section below. Otherwise feel free to just skip ahead. {{% notice note %}} @@ -52,7 +50,7 @@ using PongFE.Components; namespace PongFE.Renderers { - public class Texture2DRenderer : OrderedRenderer + public class Texture2DRenderer : Renderer { private readonly SpriteBatch _spriteBatch; @@ -61,21 +59,28 @@ namespace PongFE.Renderers _spriteBatch = spriteBatch; } - public override void Render(Entity entity, in Texture2DComponent textureComponent) + public override void Render() { - ref readonly var positionComponent = ref GetComponent(entity); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); - _spriteBatch.Draw( - textureComponent.Texture, - positionComponent.Position, - null, - Color.White, - 0, - Vector2.Zero, - Vector2.One, - SpriteEffects.None, - 0 - ); + foreach (ref readonly var entity in ReadEntities()) + { + ref readonly var positionComponent = ref GetComponent(entity); + + _spriteBatch.Draw( + textureComponent.Texture, + positionComponent.Position, + null, + Color.White, + 0, + Vector2.Zero, + Vector2.One, + SpriteEffects.None, + 0 + ); + } + + SpriteBatch.End(); } } } @@ -83,9 +88,11 @@ namespace PongFE.Renderers First of all, when we construct the Texture2DRenderer, we will pass in a SpriteBatch instance. Simple enough. -Our *Render* method will run exactly once for each Texture2DComponent that lives in our world. It also gives us a reference to the Entity that each specific Texture2DComponent is attached to. The *in* keyword means that the textureComponent is accessed by reference, but cannot be modified. We shouldn't ever be changing the data of a component inside a Renderer, because that is not the Renderer's job. So the method requires us to use the *in* keyword here. +Our *Render* method will run exactly once when the World draws. -Next, we want to retrieve our position data. We can do this with the Renderer's `GetComponent` method. +First, we begin the SpriteBatch and tell it to draw in *Deferred* mode, which waits as long as possible before pushing data to the GPU. + +Next, we want to iterate over all our entities that have texture components and retrieve their position data. We can do this with the Renderer's `GetComponent` method. `GetComponent(entity)` means that we retrieve the PositionComponent that is attached to the given entity. Simple! The brackets mean that the method is what is called a *generic method*. That means we have to tell the method which type it needs to return, which in this case is PositionComponent. @@ -154,6 +161,6 @@ The seventh argument is a scaling value, which multiples the sprite's dimensions The eighth argument is a `SpriteEffects` argument, which can be used to flip the sprite horizontally or vertically. This argument is basically useless and you will pretty much never need to pass anything except `SpriteEffects.None` here, because passing in negative scaling values can handle sprite flipping. This is one of those examples of the XNA design being a bit weird and crusty in certain places. -The ninth and final argument is a `layerDepth` integer. This is only used when the SpriteBatch uses an internal sorting technique. This is much less efficient than letting Encompass do the sorting, so we will ignore this value and just pass 0 for everything. +The ninth and final argument is a `layerDepth` integer. This is only used when the SpriteBatch uses an internal sorting technique. This is much less efficient than drawing things in order yourself, so we will ignore this value and just pass 0 for everything. That's it! Now we need to set up our World with its starting configuration so our Encompass elements can work in concert. diff --git a/content/pong/move_paddle/decoupling.md b/content/pong/move_paddle/decoupling.md index 81a4b15..260d022 100644 --- a/content/pong/move_paddle/decoupling.md +++ b/content/pong/move_paddle/decoupling.md @@ -35,7 +35,7 @@ namespace PongFE.Messages Down } - public struct PaddleMoveMessage : IMessage, IHasEntity + public struct PaddleMoveMessage : IHasEntity { public Entity Entity { get; } public PaddleMoveDirection PaddleMoveDirection { get; } diff --git a/content/pong/move_paddle/input_handling.md b/content/pong/move_paddle/input_handling.md index c94803e..e85a424 100644 --- a/content/pong/move_paddle/input_handling.md +++ b/content/pong/move_paddle/input_handling.md @@ -55,7 +55,7 @@ namespace PongFE.Components Two } - public struct PlayerInputComponent : IComponent + public struct PlayerInputComponent { public PlayerIndex PlayerIndex { get; } diff --git a/content/pong/move_paddle/magic_values.md b/content/pong/move_paddle/magic_values.md index 035e156..159c255 100644 --- a/content/pong/move_paddle/magic_values.md +++ b/content/pong/move_paddle/magic_values.md @@ -37,7 +37,7 @@ using Encompass; namespace PongFE.Components { - public struct PaddleMoveSpeedComponent : IComponent + public struct PaddleMoveSpeedComponent { public float Speed { get; } diff --git a/content/pong/move_paddle/motion_engine.md b/content/pong/move_paddle/motion_engine.md index b8ea8ae..acef2f3 100644 --- a/content/pong/move_paddle/motion_engine.md +++ b/content/pong/move_paddle/motion_engine.md @@ -39,7 +39,7 @@ using Encompass; namespace PongFE.Messages { - public struct MotionMessage : IMessage, IHasEntity + public struct MotionMessage : IHasEntity { public Entity Entity { get; } public Vector2 Movement { get; } @@ -53,7 +53,7 @@ namespace PongFE.Messages } ``` -Similar to a component, a message is a struct which implements the *IMessage* interface. Also, motion is something that refers to a specific object, right? So we want our message to have a reference to an entity. We can declare the *IHasEntity* interface on our message, which allows Encompass to perform certain lookup optimizations. We'll talk about that in a second. +Similar to a component, a message is just a struct. Also, motion is something that refers to a specific object, right? So we want our message to have a reference to an entity. We can declare the *IHasEntity* interface on our message, which allows Encompass to perform certain lookup optimizations. We'll talk about that in a second. {{% notice warning %}} Don't **ever** have a Message that refers to another Message. That is very bad. diff --git a/content/pong/opponent/_index.md b/content/pong/opponent/_index.md index a751f78..ff14d1f 100644 --- a/content/pong/opponent/_index.md +++ b/content/pong/opponent/_index.md @@ -32,7 +32,7 @@ using PongFE.Enums; namespace PongFE.Messages { - public struct PaddleSpawnMessage : IMessage + public struct PaddleSpawnMessage { public Position2D Position { get; } public PlayerIndex PlayerIndex { get; } @@ -68,7 +68,7 @@ using PongFE.Enums; namespace PongFE.Components { - public struct ComputerControlComponent : IComponent + public struct ComputerControlComponent { public PlayerIndex PlayerIndex { get; } @@ -104,7 +104,7 @@ using Encompass; namespace PongFE.Components { - public struct CanBeTrackedComponent : IComponent { } + public struct CanBeTrackedComponent { } } ``` diff --git a/content/pong/polish/paddle_bounce.md b/content/pong/polish/paddle_bounce.md index 98e0ae0..b3fb1b9 100644 --- a/content/pong/polish/paddle_bounce.md +++ b/content/pong/polish/paddle_bounce.md @@ -19,7 +19,7 @@ using Encompass; namespace PongFE.Components { - public struct CanCauseAngledBounceComponent : IComponent { } + public struct CanCauseAngledBounceComponent { } } ``` @@ -31,7 +31,7 @@ using PongFE.Enums; namespace PongFE.Messages { - public struct AngledBounceMessage : IMessage + public struct AngledBounceMessage { public Entity Bounced { get; } public Entity Bouncer { get; } @@ -79,7 +79,7 @@ using Encompass; namespace PongFE.Components { - public struct ScaleComponent : IComponent + public struct ScaleComponent { public int Width { get; } public int Height { get; } @@ -220,7 +220,7 @@ using Encompass; namespace PongFE.Components { - public struct SpawnBallAfterDestroyComponent : IComponent + public struct SpawnBallAfterDestroyComponent { public float Speed { get; } public float Seconds { get; } diff --git a/content/pong/polish/title.md b/content/pong/polish/title.md index 8172dab..9ba4310 100644 --- a/content/pong/polish/title.md +++ b/content/pong/polish/title.md @@ -28,7 +28,7 @@ using PongFE.Enums; namespace PongFE.Components { - public struct GameStateComponent : IComponent + public struct GameStateComponent { public GameState GameState { get; } @@ -158,7 +158,7 @@ namespace PongFE.Engines Notice how we took most of the *StartGame* stuff from the *PongFEGame.LoadContent* method. Make sure to take those message sends out of that method. -Now we need to draw the title screen. There's many different approaches. Generic UI text rendering elements would probably be a good idea. But I'm lazy, so let's just set up a GeneralRenderer. +Now we need to draw the title screen. There's many different approaches. Generic UI text rendering elements would probably be a good idea. But I'm lazy, so let's just set up a very basic renderer. In **PongFE/Renderers/TitleRenderer.cs**: @@ -172,7 +172,7 @@ using SpriteFontPlus; namespace PongFE.Renderers { - public class TitleRenderer : GeneralRenderer + public class TitleRenderer : Renderer { private SpriteBatch SpriteBatch { get; } private DynamicSpriteFont TitleFont { get; } @@ -248,7 +248,7 @@ Let's set everything up in **PongFEGame**. ... - WorldBuilder.AddGeneralRenderer(new TitleRenderer(SpriteBatch, ScoreFont, InstructionFont), 0); + WorldBuilder.AddRenderer(new TitleRenderer(SpriteBatch, ScoreFont, InstructionFont)); ... ``` diff --git a/content/pong/polish/win_condition.md b/content/pong/polish/win_condition.md index 66cadc1..27f1f3f 100644 --- a/content/pong/polish/win_condition.md +++ b/content/pong/polish/win_condition.md @@ -18,7 +18,7 @@ using SpriteFontPlus; namespace PongFE.Components { - public struct UITextComponent : IComponent + public struct UITextComponent { public DynamicSpriteFont Font { get; } public string Text { get; } @@ -41,7 +41,7 @@ using SpriteFontPlus; namespace PongFE.Messages { - public struct UITextSpawnMessage : IMessage + public struct UITextSpawnMessage { public Position2D Position { get; } public string Text { get; } @@ -93,7 +93,7 @@ using SpriteFontPlus; namespace PongFE.Renderers { - public class UITextRenderer : GeneralRenderer + public class UITextRenderer : Renderer { private SpriteBatch SpriteBatch { get; } @@ -104,6 +104,8 @@ namespace PongFE.Renderers public override void Render() { + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Nonpremultiplied); + foreach (ref readonly var entity in ReadEntities()) { ref readonly var uiTextComponent = ref GetComponent(entity); @@ -116,6 +118,8 @@ namespace PongFE.Renderers Color.White ); } + + SpriteBatch.End(); } } } @@ -133,7 +137,7 @@ using PongFE.Enums; namespace PongFE.Messages { - public struct GameWinMessage : IMessage + public struct GameWinMessage { public PlayerIndex PlayerIndex { get; } @@ -153,7 +157,7 @@ using PongFE.Enums; namespace PongFE.Messages { - public struct ChangeGameStateMessage : IMessage + public struct ChangeGameStateMessage { public GameState GameState { get; } @@ -244,7 +248,7 @@ using Encompass; namespace PongFE.Components { - public struct BallParametersComponent : IComponent + public struct BallParametersComponent { public int Speed { get; } public double Delay { get; } @@ -547,7 +551,7 @@ WorldBuilder.AddEngine(new UITextSpawner()); ... -WorldBuilder.AddGeneralRenderer(new UITextRenderer(SpriteBatch), 0); +WorldBuilder.AddRenderer(new UITextRenderer(SpriteBatch)); ``` That's it... our re-implementation of Pong is complete! If you followed along to this point, give yourself a pat on the back. diff --git a/content/pong/scoring/center_line.md b/content/pong/scoring/center_line.md index 6316e8f..72cdc27 100644 --- a/content/pong/scoring/center_line.md +++ b/content/pong/scoring/center_line.md @@ -6,7 +6,7 @@ weight: 50 Now we need to draw the center line. -This will be a fairly basic GeneralRenderer - it doesn't need to react to anything. +This will be a fairly basic renderer - it doesn't need to react to anything. In **PongFE/Renderers/CenterLineRenderer.cs**: @@ -19,7 +19,7 @@ using PongFE.Components; namespace PongFE.Renderers { - public class CenterLineRenderer : GeneralRenderer + public class CenterLineRenderer : Renderer { public SpriteBatch SpriteBatch { get; } public Texture2D WhitePixel { get; } @@ -85,7 +85,7 @@ The main magic to understand here is the matrix transformation - the gist of it Add our CenterLineRenderer to the WorldBuilder... ```ts -WorldBuilder.AddRenderer(new CenterLineRendereR()); +WorldBuilder.AddRenderer(new CenterLineRenderer()); ``` ![center dashed line](/images/center_line.png) diff --git a/content/pong/scoring/display_scaling.md b/content/pong/scoring/display_scaling.md index eafa7a9..d861bf7 100644 --- a/content/pong/scoring/display_scaling.md +++ b/content/pong/scoring/display_scaling.md @@ -27,7 +27,7 @@ using Encompass; namespace PongFE.Components { - public struct PlayAreaComponent : IComponent + public struct PlayAreaComponent { public int Width { get; } public int Height { get; } diff --git a/content/pong/scoring/drawing_score.md b/content/pong/scoring/drawing_score.md index 2e3111f..9d57e54 100644 --- a/content/pong/scoring/drawing_score.md +++ b/content/pong/scoring/drawing_score.md @@ -4,9 +4,7 @@ date: 2019-06-04T17:21:52-07:00 weight: 40 --- -Remember Renderers? Haven't thought about those in a while! - -All we need to draw new elements to the screen are Renderers. Since displaying the score is a UI element, I think it would be best to do this with a GeneralRenderer. +Remember Renderers? Haven't thought about those in a while! All we need to draw new elements to the screen are Renderers. But first, we're gonna need a font. I liked [this font](https://www.dafont.com/squared-display.font). But you can pick any font you like. It's your world and you can do whatever you like in it. @@ -54,7 +52,7 @@ using PongFE.Enums; namespace PongFE.Messages { - public struct GoalBoundarySpawnMessage : IMessage + public struct GoalBoundarySpawnMessage { public PlayerIndex PlayerIndex { get; } public Position2D Position { get; } @@ -137,7 +135,7 @@ using SpriteFontPlus; namespace PongFE.Renderers { - public class ScoreRenderer : GeneralRenderer + public class ScoreRenderer : Renderer { public SpriteBatch SpriteBatch { get; } public DynamicSpriteFont Font { get; } @@ -215,7 +213,7 @@ Let's add our ScoreRenderer to the WorldBuilder. First we need to set up the fon ... - WorldBuilder.AddGeneralRenderer(new ScoreRenderer(SpriteBatch, ScoreFont), 0); + WorldBuilder.AddRenderer(new ScoreRenderer(SpriteBatch, ScoreFont)); ... ``` diff --git a/content/pong/scoring/goal_collision.md b/content/pong/scoring/goal_collision.md index 33eb6d5..67cb5e7 100644 --- a/content/pong/scoring/goal_collision.md +++ b/content/pong/scoring/goal_collision.md @@ -13,7 +13,7 @@ using Encompass; namespace PongFE.Components { - public struct CanDestroyComponent : IComponent { } + public struct CanDestroyComponent { } } ``` @@ -24,7 +24,7 @@ using Encompass; namespace PongFE.Components { - public struct CanBeDestroyedComponent : IComponent { } + public struct CanBeDestroyedComponent { } } ``` @@ -35,7 +35,7 @@ using Encompass; namespace PongFE.Components { - public struct SpawnBallAfterDestroyComponent : IComponent + public struct SpawnBallAfterDestroyComponent { public float Seconds { get; } @@ -54,7 +54,7 @@ using Encompass; namespace PongFE.Messages { - public struct DestroyMessage : IMessage + public struct DestroyMessage { public Entity Entity { get; } @@ -145,7 +145,7 @@ using MoonTools.Structs; namespace PongFE.Messages { - public struct GoalBoundarySpawnMessage : IMessage + public struct GoalBoundarySpawnMessage { public Position2D Position { get; } public int Width { get; } diff --git a/content/pong/scoring/tracking_score.md b/content/pong/scoring/tracking_score.md index 7e31a3e..9132154 100644 --- a/content/pong/scoring/tracking_score.md +++ b/content/pong/scoring/tracking_score.md @@ -17,7 +17,7 @@ using Encompass; namespace PongFE.Components { - public struct ScoreComponent : IComponent + public struct ScoreComponent { public int Score { get; } @@ -46,7 +46,7 @@ using Encompass; namespace PongFE.Components { - public struct IncreaseScoreAfterDestroyComponent : IComponent { } + public struct IncreaseScoreAfterDestroyComponent { } } ``` @@ -59,7 +59,7 @@ using Encompass; namespace PongFE.Messages { - public struct DestroyMessage : IMessage + public struct DestroyMessage { public Entity Entity { get; } public Entity DestroyedBy { get; } @@ -97,7 +97,7 @@ using Encompass; namespace PongFE.Messages { - public struct ScoreMessage : IMessage + public struct ScoreMessage { public Entity Entity { get; } @@ -220,7 +220,7 @@ using PongFE.Enums; namespace PongFE.Components { - public struct PlayerComponent : IComponent + public struct PlayerComponent { public PlayerIndex PlayerIndex { get; }