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. 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 ```cs
import { Component } from "encompass-ecs"; using System.Numerics;
using Encompass;
export class VelocityComponent extends Component { namespace PongFE.Components
public x: number; {
public y: number; 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. 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. 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 ```cs
import { Detector, Emits, Entity } from "encompass-ecs"; using Encompass;
import { PositionComponent } from "game/components/position"; using PongFE.Components;
import { VelocityComponent } from "game/components/velocity"; using PongFE.Messages;
import { MotionMessage } from "game/messages/component/motion";
@Emits(MotionMessage) namespace PongFE.Engines
@Detects(PositionComponent, VelocityComponent) {
export class VelocityEngine extends Detector { [Sends(typeof(MotionMessage))]
protected detect(entity: Entity) { [QueryWith(typeof(PositionComponent), typeof(VelocityComponent))]
const position_component = entity.get_component(PositionComponent); public class VelocityEngine : Engine
const velocity_component = entity.get_component(VelocityComponent); {
public override void Update(double dt)
const motion_message = this.emit_component_message(MotionMessage, position_component); {
motion_message.x = velocity_component.x; foreach (var entity in TrackedEntities)
motion_message.y = velocity_component.y; {
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. {{% 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.
So, our VelocityEngine will track everything with a PositionComponent and VelocityComponent and create a MotionMessage every frame. {{% /notice %}}
Let's add our new Engine to the WorldBuilder: Let's add our new Engine to the WorldBuilder:
```ts ```cs
world_builder.add_engine(VelocityEngine); WorldBuilder.AddEngine(new VelocityEngine());
``` ```
And add our new VelocityComponent in the BallSpawner. And add our new VelocityComponent in the BallSpawner.
```ts ```cs
const velocity_component = ball_entity.add_component(VelocityComponent); AddComponent(ball, new VelocityComponent(new System.Numerics.Vector2(50, -50)));
velocity_component.x = 50;
velocity_component.y = -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 ```cs
import { Message } from "encompass-ecs"; using System.Numerics;
using Encompass;
using MoonTools.Structs;
export class BallSpawnMessage extends Message { namespace PongFE.Messages
public x: number; {
public y: number; public struct BallSpawnMessage : IMessage
public size: number; {
public x_velocity: number; public Position2D Position { get; }
public y_velocity: number; 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 ```cs
const velocity_component = ball_entity.add_component(VelocityComponent); using Encompass;
velocity_component.x = message.x_velocity; using Microsoft.Xna.Framework.Graphics;
velocity_component.y = message.y_velocity; 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 ```cs
ball_spawn_message.x_velocity = 50; WorldBuilder.SendMessage(
ball_spawn_message.y_velocity = -50; new BallSpawnMessage(
new MoonTools.Structs.Position2D(640, 360),
new System.Numerics.Vector2(50, -50)
)
);
``` ```
Let's run the game again. Let's run the game again.