moving + started writing bounce section
parent
76153d714d
commit
5ad534b7cb
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
title: "Bouncing"
|
||||
date: 2019-05-23T18:38:51-07:00
|
||||
weight: 30
|
||||
---
|
||||
|
||||
Let's make the ball bounce off the sides of the game window.
|
||||
|
||||
I know what you're thinking. "Let's just read the dimensions of the game window. When the ball goes past them, we know it should bounce!"
|
||||
|
||||
**NO.**
|
||||
|
||||
We don't want the behavior of any object to be directly tied to some state outside of the game simulation. That's just asking for trouble!!
|
||||
|
||||
Let's make a BoundariesComponent instead. In **game/components/boundaries.ts**
|
||||
|
||||
```ts
|
||||
import { Component } from "encompass-ecs";
|
||||
|
||||
export class BoundariesComponent extends Component {
|
||||
public left: number;
|
||||
public top: number;
|
||||
public right: number;
|
||||
public bottom: number;
|
||||
}
|
||||
```
|
||||
|
||||
Now we have two options. We could put this on the ball or on a separate Entity. But let's think about it - the paddles need to respect the boundaries too, right? So let's make it a separate Entity.
|
||||
|
||||
{{% notice tip %}}
|
||||
It's perfectly valid to create Entities for the purpose of only holding one Component. It's good architecture a lot of the time!
|
||||
{{% /notice %}}
|
||||
|
||||
In **game/game.ts**:
|
||||
|
||||
```ts
|
||||
const boundaries_entity = world_builder.create_entity();
|
||||
const boundaries_component = boundaries_entity.add_component(BoundariesComponent);
|
||||
boundaries_component.left = 0;
|
||||
boundaries_component.top = 0;
|
||||
boundaries_component.right = 1280;
|
||||
boundaries_component.bottom = 720;
|
||||
```
|
||||
|
||||
Now how do our boundaries actually work? You might recall that our MotionEngine updates the positions of objects directly. But that means it could leave something out of the boundary. Remember that we can't have two Engines mutate the same Component type.
|
||||
|
||||
We could do boundary checking inside of MotionEngine. But we don't know that everything that moves will need to respect the boundary, so it seems bad to put that inside of MotionEngine.
|
||||
|
||||
I think what we should do is have two new Engines. The first will be an Engine that takes a Message that reads a position and checks if it is outside of the boundaries. Then we can make a new Engine that finalizes the new value of the PositionComponent.
|
||||
|
||||
```ts
|
||||
|
||||
```
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
title: "Moving"
|
||||
date: 2019-05-23T18:10:17-07:00
|
||||
weight: 20
|
||||
---
|
||||
|
||||
We already have MotionMessages and a MotionEngine. So it seems logical to re-use these structures for our ball.
|
||||
|
||||
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**:
|
||||
|
||||
```ts
|
||||
import { Component } from "encompass-ecs";
|
||||
|
||||
export class VelocityComponent extends Component {
|
||||
public x: number;
|
||||
public y: number;
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
In **game/engines/velocity.ts**:
|
||||
|
||||
```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";
|
||||
|
||||
@Emits(MotionMessage)
|
||||
export class VelocityEngine extends Detector {
|
||||
public component_types = [ PositionComponent, VelocityComponent ];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A Detector, like a Spawner, is an engine with one required property and method: *component_types* and *detect*.
|
||||
|
||||
When an Entity has all of the components specified in *component_types*, 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.
|
||||
|
||||
Let's add our new Engine to the WorldBuilder:
|
||||
|
||||
```ts
|
||||
world_builder.add_engine(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;
|
||||
```
|
||||
|
||||
Actually lets get rid of that magic value by adding velocity to the BallSpawnMessage.
|
||||
|
||||
**game/messages/ball_spawn.ts**
|
||||
|
||||
```ts
|
||||
import { Message } from "encompass-ecs";
|
||||
|
||||
export class BallSpawnMessage extends Message {
|
||||
public x: number;
|
||||
public y: number;
|
||||
public size: number;
|
||||
public x_velocity: number;
|
||||
public y_velocity: number;
|
||||
}
|
||||
```
|
||||
|
||||
**game/engines/spawners/ball.ts**
|
||||
|
||||
```ts
|
||||
const velocity_component = ball_entity.add_component(VelocityComponent);
|
||||
velocity_component.x = message.x_velocity;
|
||||
velocity_component.y = message.y_velocity;
|
||||
```
|
||||
|
||||
**game/game.ts**
|
||||
|
||||
```ts
|
||||
ball_spawn_message.x_velocity = 50;
|
||||
ball_spawn_message.y_velocity = -50;
|
||||
```
|
||||
|
||||
Let's run the game again.
|
||||
|
||||
<video width="640" height="360" autoplay="autoplay" muted="muted" loop="loop" style="display: block; margin: 0 auto; width: 640;">
|
||||
<source src="/images/moving_ball.webm" type="video/webm">
|
||||
</video>
|
||||
|
||||
Still pretty boring but we're getting somewhere.
|
Binary file not shown.
Loading…
Reference in New Issue