142 lines
4.7 KiB
Markdown
142 lines
4.7 KiB
Markdown
---
|
|
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 it 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<PlayerInputComponent>())
|
|
{
|
|
ref readonly var playerInputComponent = ref GetComponent<PlayerInputComponent>(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<PlayerInputComponent>** 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.
|