encompass-cs-docs/content/pong/scoring/goal_collision.md

227 lines
5.5 KiB
Markdown
Raw Normal View History

2019-06-03 20:11:53 +00:00
---
title: "Goal Collision"
date: 2019-06-03T12:39:11-07:00
weight: 10
---
2020-07-15 22:37:38 +00:00
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
{
2021-05-01 19:07:59 +00:00
public struct CanDestroyComponent { }
2019-06-03 20:11:53 +00:00
}
```
2020-07-15 22:37:38 +00:00
Our Receiver: **PongFE/Components/CanBeDestroyedComponent.cs**:
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
```cs
using Encompass;
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
namespace PongFE.Components
{
2021-05-01 19:07:59 +00:00
public struct CanBeDestroyedComponent { }
2019-06-03 20:11:53 +00:00
}
```
2020-07-15 22:37:38 +00:00
Our Response: **PongFE/Components/SpawnBallAfterDestroyComponent.cs**:
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
```cs
using Encompass;
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
namespace PongFE.Components
{
2021-05-01 19:07:59 +00:00
public struct SpawnBallAfterDestroyComponent
2020-07-15 22:37:38 +00:00
{
public float Seconds { get; }
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
public SpawnBallAfterDestroyComponent(float seconds)
{
Seconds = seconds;
}
}
}
2019-06-03 20:11:53 +00:00
```
2020-07-15 22:37:38 +00:00
Now let's create **PongFE/Messages/DestroyMessage.cs**:
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
```cs
using Encompass;
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
namespace PongFE.Messages
{
2021-05-01 19:07:59 +00:00
public struct DestroyMessage
2020-07-15 22:37:38 +00:00
{
public Entity Entity { get; }
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
public DestroyMessage(Entity entity)
{
Entity = entity;
}
2019-06-03 20:11:53 +00:00
}
2020-07-15 22:37:38 +00:00
}
```
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
In **CollisionEngine.cs**:
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
```cs
...
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
CheckBounce(message.EntityA, message.EntityB, message.HitOrientation);
CheckBounce(message.EntityB, message.EntityA, message.HitOrientation);
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
CheckDestroy(message.EntityA, message.EntityB);
CheckDestroy(message.EntityB, message.EntityA);
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
...
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
private void CheckDestroy(Entity a, Entity b)
{
if (HasComponent<CanDestroyComponent>(a))
{
if (HasComponent<CanBeDestroyedComponent>(b))
{
SendMessage(new DestroyMessage(b));
}
}
}
2019-06-03 20:11:53 +00:00
```
2020-07-15 22:37:38 +00:00
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);
}
}
}
}
```
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
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.
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
We'll need a way to create our goal entities now.
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
Create a file, **PongFE/Messages/GoalBoundarySpawnMessage.cs**:
```cs
using Encompass;
using MoonTools.Structs;
namespace PongFE.Messages
{
2021-05-01 19:07:59 +00:00
public struct GoalBoundarySpawnMessage
2020-07-15 22:37:38 +00:00
{
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;
}
2019-06-03 20:11:53 +00:00
}
2020-07-15 22:37:38 +00:00
}
```
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
And a file, **PongFE/Engines/Spawners/GoalBoundarySpawner.cs**:
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
```cs
using Encompass;
using PongFE.Components;
using PongFE.Messages;
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
namespace PongFE.Spawners
{
public class GoalBoundarySpawner : Spawner<GoalBoundarySpawnMessage>
{
2020-07-22 01:28:40 +00:00
protected override void Spawn(in GoalBoundarySpawnMessage message)
2020-07-15 22:37:38 +00:00
{
var entity = CreateEntity();
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
AddComponent(entity, new PositionComponent(message.Position));
AddComponent(entity, new CollisionComponent(new MoonTools.Bonk.Rectangle(0, 0, message.Width, message.Height)));
AddComponent(entity, new CanDestroyComponent());
}
2019-06-03 20:11:53 +00:00
}
}
```
2020-07-15 22:37:38 +00:00
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
)
);
```
2019-06-03 20:11:53 +00:00
2020-07-15 22:37:38 +00:00
Now when the ball hits the left or right side of the play area, it will destroy and respawn. Great!