moving
continuous-integration/drone/push Build is failing Details

main
Evan Hemsley 2020-07-12 15:49:08 -07:00
parent ecd1024d71
commit d3622b34f0
1 changed files with 101 additions and 52 deletions

View File

@ -10,94 +10,143 @@ What is actually going to be sending out the MotionMessages?
What is the main characteristic of the ball in Pong? That's right - it is continuously moving. In other words, it has velocity.
Let's make a VelocityComponent. In **game/components/velocity.ts**:
Let's make a VelocityComponent. In **PongFE/Components/VelocityComponent.cs**:
```ts
import { Component } from "encompass-ecs";
```cs
using System.Numerics;
using Encompass;
export class VelocityComponent extends Component {
public x: number;
public y: number;
namespace PongFE.Components
{
public struct VelocityComponent : IComponent
{
public Vector2 Velocity { get; }
public VelocityComponent(Vector2 velocity)
{
Velocity = velocity;
}
}
}
```
Now, the XNA API actually provides us with a Vector2 struct. Why am I using System.Numerics here? The answer is that it is more up-to-date and optimized than the XNA specification. As long as we can convert the values when we need to hand the Vector2 off to something, it won't be an issue.
Let's also create a VelocityEngine.
What does our VelocityEngine actually do? Basically, if something has both a PositionComponent and VelocityComponent, we want the PositionComponent to update based on the VelocityComponent every frame.
It turns out Encompass provides a structure for this pattern, called a Detector. Let's use it now.
It turns out Encompass provides a structure for this pattern. Let's use it now.
In **game/engines/velocity.ts**:
Create **PongFE/Engines/VelocityEngine.cs**:
```ts
import { Detector, Emits, Entity } from "encompass-ecs";
import { PositionComponent } from "game/components/position";
import { VelocityComponent } from "game/components/velocity";
import { MotionMessage } from "game/messages/component/motion";
```cs
using Encompass;
using PongFE.Components;
using PongFE.Messages;
@Emits(MotionMessage)
@Detects(PositionComponent, VelocityComponent)
export class VelocityEngine extends Detector {
protected detect(entity: Entity) {
const position_component = entity.get_component(PositionComponent);
const velocity_component = entity.get_component(VelocityComponent);
const motion_message = this.emit_component_message(MotionMessage, position_component);
motion_message.x = velocity_component.x;
motion_message.y = velocity_component.y;
namespace PongFE.Engines
{
[Sends(typeof(MotionMessage))]
[QueryWith(typeof(PositionComponent), typeof(VelocityComponent))]
public class VelocityEngine : Engine
{
public override void Update(double dt)
{
foreach (var entity in TrackedEntities)
{
ref readonly var velocityComponent = ref GetComponent<VelocityComponent>(entity);
SendMessage(new MotionMessage(entity, velocityComponent.Velocity * (float)dt));
}
}
}
}
```
A Detector, like a Spawner, is an engine with one required method: *detect*.
**QueryWith** is a class attribute that allows us to specify a set of components that will cause the Engine to track an entity. In this case, our QueryWith attribute will cause entities that have both a PositionComponent and a VelocityComponent to be tracked. QueryWith also implicitly creates Reads for the relevant Components.
When an Entity has all of the components specified in **@Detects**, it begins to track the Entity. Each frame, it calls its *detect* method on that Entity.
So, our VelocityEngine will track everything with a PositionComponent and VelocityComponent and create a MotionMessage every frame.
{{% notice note }}
There is also a **QueryWithout** that will exclude entities from tracking if they have the specified component(s). This can come in handy if you, say, want to temporarily pause motion or something.
{{% /notice %}}
Let's add our new Engine to the WorldBuilder:
```ts
world_builder.add_engine(VelocityEngine);
```cs
WorldBuilder.AddEngine(new VelocityEngine());
```
And add our new VelocityComponent in the BallSpawner.
```ts
const velocity_component = ball_entity.add_component(VelocityComponent);
velocity_component.x = 50;
velocity_component.y = -50;
```cs
AddComponent(ball, new VelocityComponent(new System.Numerics.Vector2(50, -50)));
```
Actually lets get rid of that magic value by adding velocity to the BallSpawnMessage.
Actually... let's get rid of that magic value by adding velocity to the BallSpawnMessage.
**game/messages/ball_spawn.ts**
**PongFE/Messages/BallSpawnMessage.cs**
```ts
import { Message } from "encompass-ecs";
```cs
using System.Numerics;
using Encompass;
using MoonTools.Structs;
export class BallSpawnMessage extends Message {
public x: number;
public y: number;
public size: number;
public x_velocity: number;
public y_velocity: number;
namespace PongFE.Messages
{
public struct BallSpawnMessage : IMessage
{
public Position2D Position { get; }
public Vector2 Velocity { get; }
public BallSpawnMessage(Position2D position, Vector2 velocity)
{
Position = position;
Velocity = velocity;
}
}
}
```
**game/engines/spawners/ball.ts**
**PongFE/Engines/Spawners/BallSpawner.cs**
```ts
const velocity_component = ball_entity.add_component(VelocityComponent);
velocity_component.x = message.x_velocity;
velocity_component.y = message.y_velocity;
```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(message.Position));
AddComponent(ball, new VelocityComponent(message.Velocity));
AddComponent(ball, new Texture2DComponent(BallTexture, 0));
}
}
}
```
**game/game.ts**
**PongFEGame.cs**
```ts
ball_spawn_message.x_velocity = 50;
ball_spawn_message.y_velocity = -50;
```cs
WorldBuilder.SendMessage(
new BallSpawnMessage(
new MoonTools.Structs.Position2D(640, 360),
new System.Numerics.Vector2(50, -50)
)
);
```
Let's run the game again.