encompass-cs-docs/content/pong/ball/spawning.md

154 lines
5.1 KiB
Markdown

---
title: "Spawning"
date: 2019-05-23T18:09:07-07:00
weight: 10
---
In **PongFEGame.cs**...
```cs
protected override void LoadContent()
{
...
BallTexture = new RenderTarget2D(GraphicsDevice, 16, 16);
GraphicsDevice.SetRenderTarget(BallTexture);
SpriteBatch.Begin();
SpriteBatch.Draw(WhitePixel, new Rectangle(0, 0, 16, 16), Color.White);
SpriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
...
var ball = WorldBuilder.CreateEntity();
WorldBuilder.SetComponent(ball, new PositionComponent(new MoonTools.Structs.Position2D(640, 360)));
WorldBuilder.SetComponent(ball, new Texture2DComponent(BallTexture, 0));
...
}
```
OK. We're both thinking it. Why is all this crap going straight in **PongFEGame.cs**? And there's magic values everywhere! You are absolutely right. Encompass actually has a built-in abstraction Engine for creating new Entities called a Spawner for just this purpose. Let's create one.
First off, we need a spawn message. There's nothing particularly special about a spawn message, just create one that has the parameters you think you'll need to spawn a particular sort of entity.
Create a file: **PongFE/Messages/BallSpawnMessage.cs**
```cs
using Encompass;
using MoonTools.Structs;
namespace PongFE.Messages
{
public struct BallSpawnMessage : IMessage
{
public Position2D Position { get; }
public BallSpawnMessage(Position2D position)
{
Position = position;
}
}
}
```
Let's create a new folder: **PongFEGame/Engines/Spawners**
And a new file: **PongFE/Engines/Spawners/Ball.cs**
```cs
using Encompass;
using Microsoft.Xna.Framework.Graphics;
using PongFE.Components;
using PongFE.Messages;
namespace PongFE.Spawners
{
[Writes(typeof(PositionComponent))]
[Writes(typeof(Texture2DComponent))]
public class BallSpawner : Spawner<BallSpawnMessage>
{
private Texture2D BallTexture { get; }
public BallSpawner(Texture2D ballTexture)
{
BallTexture = ballTexture;
}
protected override void Spawn(BallSpawnMessage message)
{
var ball = CreateEntity();
SetComponent(ball, new PositionComponent(new MoonTools.Structs.Position2D(640, 360)));
SetComponent(ball, new Texture2DComponent(BallTexture, 0));
}
}
}
```
Spawners aren't very complicated. They have one required method: *Spawn*. Any time a message of the given type is sent, Spawn will run once for that message. When we create the BallSpawner, we give it a BallTexture so it can attach the texture to the ball entities it creates.
Now let's actually send out the BallSpawnMessage.
In **PongFEGame.ts**, get rid of the ball entity code and do:
```cs
...
WorldBuilder.AddEngine(new BallSpawner(BallTexture));
...
WorldBuilder.SendMessage(new BallSpawnMessage(new MoonTools.Structs.Position2D(640, 360)));
...
```
Now, if we try to run the game...
```sh
[ERROR] FATAL UNHANDLED EXCEPTION: Encompass.Exceptions.EngineWriteConflictException: Multiple Engines write the same Component without declaring priority:
PositionComponent written by: MotionEngine, BallSpawner
To resolve the conflict, add priority arguments to the Writes declarations or use a DefaultWritePriority attribute.
```
Oh no!!! It's yelling at us! That's ok though. Encompass lets us know exactly what the problem is: we have two different Engines that both try to write PositionComponent. Think about why this is bad... if two engines write the same component to the same entity on the same frame, only one of those values is gonna get through, right? That could surprise us and make the game harder to understand. I hate surprises.
The Writes attribute allows you to specify a priority value, to allow one engine's component writes to take priority over another's. In this case, however, it is unnecessary, because we have access to a special variant of **SetComponent**, called **AddComponent**.
**AddComponent** is only valid when an entity has been created in the same context as it was called. This makes it ideal for spawning new entities. It means you don't have to worry about write priority, because the entity must have been newly created.
Let's revise the BallSpawner to use AddComponent.
```cs
using Encompass;
using Microsoft.Xna.Framework.Graphics;
using PongFE.Components;
using PongFE.Messages;
namespace PongFE.Spawners
{
public class BallSpawner : Spawner<BallSpawnMessage>
{
private Texture2D BallTexture { get; }
public BallSpawner(Texture2D ballTexture)
{
BallTexture = ballTexture;
}
protected override void Spawn(BallSpawnMessage message)
{
var ball = CreateEntity();
AddComponent(ball, new PositionComponent(new MoonTools.Structs.Position2D(640, 360)));
AddComponent(ball, new Texture2DComponent(BallTexture, 0));
}
}
}
```
And run the game...
![boring ball](/images/paddle_with_ball.png)
Well, it draws! but it's a bit boring without any movement. Let's make it move around.