--- title: "Drawing the Score" 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. 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. Place the font of your heart's desire into the directory **PongFE/Content/Fonts**. Now we need to get our font in the game. There are a few different font rendering tools available. SpriteFontPlus has worked pretty well for me so far. In your terminal, do the following commands: ```sh git submodule add https://github.com/rds1983/SpriteFontPlus.git git submodule update --init --recursive ``` In **PongFE.Framework.csproj**: ```xml ``` And in **PongFE.Core.csproj**: ```xml ``` Now we can use SpriteFontPlus. First, we need to be able to track which player's score is which. Why don't we add our new PlayerComponent to GoalBoundarySpawner? In **GoalBoundarySpawnMessage.cs**: ```cs using Encompass; using MoonTools.Structs; using PongFE.Enums; namespace PongFE.Messages { public struct GoalBoundarySpawnMessage { public PlayerIndex PlayerIndex { get; } public Position2D Position { get; } public int Width { get; } public int Height { get; } public GoalBoundarySpawnMessage(PlayerIndex playerIndex, Position2D position, int width, int height) { PlayerIndex = playerIndex; Position = position; Width = width; Height = height; } } } ``` **GoalBoundarySpawner.cs**: ```cs using Encompass; using PongFE.Components; using PongFE.Messages; namespace PongFE.Spawners { public class GoalBoundarySpawner : Spawner { protected override void Spawn(in GoalBoundarySpawnMessage message) { var entity = CreateEntity(); AddComponent(entity, new PositionComponent(message.Position)); AddComponent(entity, new CollisionComponent(new MoonTools.Bonk.Rectangle(0, 0, message.Width, message.Height))); AddComponent(entity, new CanDestroyComponent()); AddComponent(entity, new ScoreComponent(0)); AddComponent(entity, new PlayerComponent(message.PlayerIndex)); } } } ``` Now we can adjust the spawn messages in **PongFEGame.cs**: ```cs // right boundary WorldBuilder.SendMessage( new GoalBoundarySpawnMessage( Enums.PlayerIndex.One, new MoonTools.Structs.Position2D(1280, 0), 6, 720 ) ); // left boundary WorldBuilder.SendMessage( new GoalBoundarySpawnMessage( Enums.PlayerIndex.Two, new MoonTools.Structs.Position2D(-6, 0), 6, 720 ) ); ``` Scoring on the right goal increases the score of player one, and scoring on the left goal increases the score of player two. Now let's write our ScoreRenderer. In **PongFE/Renderers/ScoreRenderer.cs**: ```cs using Encompass; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using PongFE.Components; using PongFE.Enums; using SpriteFontPlus; namespace PongFE.Renderers { public class ScoreRenderer : Renderer { public SpriteBatch SpriteBatch { get; } public DynamicSpriteFont Font { get; } public int SpacingFromCenter { get; } = 240; public int SpacingFromTop { get; } = 20; public ScoreRenderer(SpriteBatch spriteBatch, DynamicSpriteFont font) { SpriteBatch = spriteBatch; Font = font; } public override void Render() { int? playerOneScore = null; int? playerTwoScore = null; foreach (ref readonly var entity in ReadEntities()) { ref readonly var scoreComponent = ref GetComponent(entity); ref readonly var playerComponent = ref GetComponent(entity); if (playerComponent.PlayerIndex == Enums.PlayerIndex.One) { playerOneScore = scoreComponent.Score; } else if (playerComponent.PlayerIndex == Enums.PlayerIndex.Two) { playerTwoScore = scoreComponent.Score; } } if (playerOneScore.HasValue) { SpriteBatch.DrawString( Font, playerOneScore.Value.ToString(), new Vector2(640 - SpacingFromCenter, SpacingFromTop), Color.White ); } if (playerTwoScore.HasValue) { SpriteBatch.DrawString( Font, playerTwoScore.Value.ToString(), new Vector2(640 + SpacingFromCenter - (Font.Size / 2), SpacingFromTop), Color.White ); } } } } ``` What's the question mark on those variables? This creates a *Nullable* value. That means the variable can either contain an int, or be _null_. We don't necessarily have a guarantee that the score values will exist in the world, so we provide a way for the renderer to fail gracefully if the search for the score value fails. Basically, we find each entity with a score component and figure out which player the score belongs to. Then we draw the score component's value to the screen as a string. Let's add our ScoreRenderer to the WorldBuilder. First we need to set up the font. ```cs ... DynamicSpriteFont ScoreFont { get; set; } ... protected override void LoadContent() { ... ScoreFont = DynamicSpriteFont.FromTtf(File.ReadAllBytes(@"Content/Fonts/SquaredDisplay.ttf"), 128); ... WorldBuilder.AddRenderer(new ScoreRenderer(SpriteBatch, ScoreFont)); ... ``` Now you should be able to see the game score. This is starting to look and feel like a more complete game now. Hang on a sec though - what's that magic value *640* in our ScoreRenderer? So far we've been assuming throughout our code that our UI and our play area are locked to 1280x720. What if that isn't true?