--- title: "Goal Collision" date: 2019-06-03T12:39:11-07:00 weight: 10 --- A goal destroys a ball, and then spawns a ball. So let's set up a new actor-receiver-response set of components to model this behavior. Our Actor: **PongFE/Components/CanDestroyComponent.cs**: ```cs using Encompass; namespace PongFE.Components { public struct CanDestroyComponent { } } ``` Our Receiver: **PongFE/Components/CanBeDestroyedComponent.cs**: ```cs using Encompass; namespace PongFE.Components { public struct CanBeDestroyedComponent { } } ``` Our Response: **PongFE/Components/SpawnBallAfterDestroyComponent.cs**: ```cs using Encompass; namespace PongFE.Components { public struct SpawnBallAfterDestroyComponent { public float Seconds { get; } public SpawnBallAfterDestroyComponent(float seconds) { Seconds = seconds; } } } ``` Now let's create **PongFE/Messages/DestroyMessage.cs**: ```cs using Encompass; namespace PongFE.Messages { public struct DestroyMessage { public Entity Entity { get; } public DestroyMessage(Entity entity) { Entity = entity; } } } ``` In **CollisionEngine.cs**: ```cs ... CheckBounce(message.EntityA, message.EntityB, message.HitOrientation); CheckBounce(message.EntityB, message.EntityA, message.HitOrientation); CheckDestroy(message.EntityA, message.EntityB); CheckDestroy(message.EntityB, message.EntityA); ... private void CheckDestroy(Entity a, Entity b) { if (HasComponent(a)) { if (HasComponent(b)) { SendMessage(new DestroyMessage(b)); } } } ``` Don't forget to add **typeof(DestroyMessage)** to CollisionEngine's Sends. Now let's create **PongFE/Engines/DestroyEngine.cs**: ```cs using Encompass; using PongFE.Components; using PongFE.Messages; namespace PongFE.Engines { [Reads(typeof(SpawnBallAfterDestroyComponent))] [Receives(typeof(DestroyMessage))] [Sends(typeof(BallSpawnMessage))] public class DestroyEngine : Engine { public override void Update(double dt) { foreach (ref readonly var message in ReadMessages()) { if (HasComponent(message.Entity)) { ref readonly var respawnComponent = ref GetComponent(message.Entity); SendMessage( new BallSpawnMessage( new MoonTools.Structs.Position2D(640, 360), new System.Numerics.Vector2(-200, 100), 16, 16 ), respawnComponent.Seconds ); } Destroy(message.Entity); } } } } ``` Notice that **SendMessage** has an extra argument here. What's that about? If you pass a numeric value along with **SendMessage**, the message will be delayed, and send after the given number of seconds has elapsed. This can be very convenient for producing timing-based behaviors. Be warned however, that there is no way to cancel a delayed message send. So if you need to be able to cancel a timed action, it's probably better to set up a component/engine structure that keeps track of the time. We'll need a way to create our goal entities now. Create a file, **PongFE/Messages/GoalBoundarySpawnMessage.cs**: ```cs using Encompass; using MoonTools.Structs; namespace PongFE.Messages { public struct GoalBoundarySpawnMessage { public Position2D Position { get; } public int Width { get; } public int Height { get; } public GoalBoundarySpawnMessage(Position2D position, int width, int height) { Position = position; Width = width; Height = height; } } } ``` And a file, **PongFE/Engines/Spawners/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()); } } } ``` Let's add some components to **BallSpawner.cs**: ```cs AddComponent(ball, new CanBeDestroyedComponent()); AddComponent(ball, new SpawnBallAfterDestroyComponent(0.5f)); ``` Finally let's add our new engines and spawn messages to **PongFEGame.cs**: ```cs ... WorldBuilder.AddEngine(new DestroyEngine()); ... WorldBuilder.AddEngine(new GoalBoundarySpawner()); ... // right boundary WorldBuilder.SendMessage( new GoalBoundarySpawnMessage( new MoonTools.Structs.Position2D(1280, 0), 6, 720 ) ); // left boundary WorldBuilder.SendMessage( new GoalBoundarySpawnMessage( new MoonTools.Structs.Position2D(-6, 0), 6, 720 ) ); ``` Now when the ball hits the left or right side of the play area, it will destroy and respawn. Great!