--- title: "Input Handling" date: 2019-05-23T13:38:42-07:00 weight: 10 --- In Pong, the paddles move when the player moves the joystick on their controller up or down. We currently have a MotionEngine that reads MotionMessages and moves the PositionComponents they reference. So... it makes sense that we could have an InputEngine that sends MotionMessages, yeah? Create a file: **PongFE/Engines/InputEngine.cs** ```cs using Encompass; using Microsoft.Xna.Framework.Input; using PongFE.Messages; namespace PongFE.Engines { public class InputEngine : Engine { public override void Update(double dt) { var keyboardState = Keyboard.GetState(); if (keyboardState.IsKeyDown(Keys.Down)) { SendMessage(new MotionMessage( } } } } ``` *record scratch* Uh oh. **SendMessage** emits a message, as the name suggests, and we can use it to send out a MotionMessage. But our new MotionMessage needs a reference to our paddle entity. At this point, you might be tempted to attach our paddle entity as a property on InputEngine. This would be a *terrible mistake*. We *absolutely never* want to have our game state directly attached to the state of an Engine. What we want instead is to have a component that the InputEngine can use to look up the appropriate entity. Essentially, we use a Component to designate that an Entity is a certain kind of object that we want to be able to reference. Create a file: **PongFE/Components/PlayerInputComponent.cs** ```cs using Encompass; namespace PongFE.Components { public enum PlayerIndex { One, Two } public struct PlayerInputComponent : IComponent { public PlayerIndex PlayerIndex { get; } public PlayerInputComponent(PlayerIndex playerIndex) { PlayerIndex = playerIndex; } } } ``` Why an *enum* instead of just an integer or something? When we write programs it is very easy to shoot ourselves in the foot. What if someone accidentally typed -1 in as a value or something? Enums structure our data to make it harder for us to make silly mistakes like this. Let's add this component to our paddle entity. In **PongFEGame.cs**: ```cs ... var paddle = WorldBuilder.CreateEntity(); WorldBuilder.SetComponent(paddle, new PlayerInputComponent(PongFE.Components.PlayerIndex.One)); WorldBuilder.SetComponent(paddle, new PositionComponent(new MoonTools.Structs.Position2D(5, 5))); WorldBuilder.SetComponent(paddle, new Texture2DComponent(PaddleTexture, 0)); ... ``` Now we can go back to our InputEngine. ```cs using Encompass; using Microsoft.Xna.Framework.Input; using PongFE.Components; using PongFE.Messages; namespace PongFE.Engines { [Reads(typeof(PlayerInputComponent))] [Sends(typeof(MotionMessage))] public class InputEngine : Engine { public override void Update(double dt) { var keyboardState = Keyboard.GetState(); foreach (ref readonly var playerInputEntity in ReadEntities()) { ref readonly var playerInputComponent = ref GetComponent(playerInputEntity); if (playerInputComponent.PlayerIndex == PlayerIndex.One) { if (keyboardState.IsKeyDown(Keys.Down)) { SendMessage(new MotionMessage(playerInputEntity, new System.Numerics.Vector2(0, 10))); } } } } } } ``` Engines have total freedom to read anything in the game state that they desire. This gives Engines a lot of flexibility to do what they need to do. In this case, **ReadEntities** lets us get a reference to each entity that has a PlayerInputComponent attached to it. From there, we can get the specific PlayerInputComponent for that Entity, check which PlayerIndex it contains, and then send a message to its entity if the Down key is pressed. Also, remember when we had to declare **Reads** and **Receives** and **Writes** on our MotionEngine? Well, similarly, we have to declare **Sends** when our engine emits a certain kind of Message. Otherwise Encompass will get mad at us and crash the game for our own safety. Let's add our PlayerInputEngine to the WorldBuilder. In **PongFEGame.cs**: ```cs ... WorldBuilder.AddEngine(new InputEngine()); WorldBuilder.AddEngine(new MotionEngine()); WorldBuilder.AddOrderedRenderer(new Texture2DRenderer(SpriteBatch)); ... ``` It doesn't matter which order they go in, because remember, Encompass figures it out automatically. I just prefer this order for some reason. Once we have a lot of Engines it stops mattering pretty quickly anyway.