moving
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
ecd1024d71
commit
d3622b34f0
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue