227 lines
5.6 KiB
Markdown
227 lines
5.6 KiB
Markdown
---
|
|
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 : IComponent { }
|
|
}
|
|
```
|
|
|
|
Our Receiver: **PongFE/Components/CanBeDestroyedComponent.cs**:
|
|
|
|
```cs
|
|
using Encompass;
|
|
|
|
namespace PongFE.Components
|
|
{
|
|
public struct CanBeDestroyedComponent : IComponent { }
|
|
}
|
|
```
|
|
|
|
Our Response: **PongFE/Components/SpawnBallAfterDestroyComponent.cs**:
|
|
|
|
```cs
|
|
using Encompass;
|
|
|
|
namespace PongFE.Components
|
|
{
|
|
public struct SpawnBallAfterDestroyComponent : IComponent
|
|
{
|
|
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 : IMessage
|
|
{
|
|
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<CanDestroyComponent>(a))
|
|
{
|
|
if (HasComponent<CanBeDestroyedComponent>(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<DestroyMessage>())
|
|
{
|
|
if (HasComponent<SpawnBallAfterDestroyComponent>(message.Entity))
|
|
{
|
|
ref readonly var respawnComponent = ref GetComponent<SpawnBallAfterDestroyComponent>(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 : IMessage
|
|
{
|
|
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<GoalBoundarySpawnMessage>
|
|
{
|
|
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!
|