some renaming and rearranging

pull/1/head
Evan Hemsley 2019-05-29 11:31:39 -07:00
parent 1c3ce098d9
commit d0b7d6d35d
6 changed files with 347 additions and 20 deletions

View File

@ -34,7 +34,7 @@ In **game/engines/collision_check.ts**:
```ts ```ts
import { Emits, Engine, Entity, Reads } from "encompass-ecs"; import { Emits, Engine, Entity, Reads } from "encompass-ecs";
import { BoundariesComponent } from "game/components/boundaries"; import { BoundingBoxComponent } from "game/components/bounding_box";
import { CollisionTypesComponent } from "game/components/collision_types"; import { CollisionTypesComponent } from "game/components/collision_types";
import { PositionComponent } from "game/components/position"; import { PositionComponent } from "game/components/position";
import { CollisionMessage } from "game/messages/collision"; import { CollisionMessage } from "game/messages/collision";
@ -57,12 +57,12 @@ export class CollisionCheckEngine extends Engine {
for (const message of collision_check_messages.values()) { for (const message of collision_check_messages.values()) {
const entity = message.entity; const entity = message.entity;
const position = entity.get_component(PositionComponent); const position = entity.get_component(PositionComponent);
const boundaries = entity.get_component(BoundariesComponent); const bounding_box = entity.get_component(BoundingBoxComponent);
this.collision_world.update( this.collision_world.update(
message.entity, message.entity,
position.x - boundaries.width * 0.5, position.x - bounding_box.width * 0.5,
position.y - boundaries.height * 0.5 position.y - bounding_box.height * 0.5
); );
} }
@ -70,14 +70,14 @@ export class CollisionCheckEngine extends Engine {
for (const message of collision_check_messages.values()) { for (const message of collision_check_messages.values()) {
const entity = message.entity; const entity = message.entity;
const position = entity.get_component(PositionComponent); const position = entity.get_component(PositionComponent);
const boundaries = entity.get_component(BoundariesComponent); const bounding_box = entity.get_component(BoundingBoxComponent);
const x = position.x + message.x_delta; const x = position.x + message.x_delta;
const y = position.y + message.y_delta; const y = position.y + message.y_delta;
const [new_x, new_y, cols, len] = this.collision_world.check( const [new_x, new_y, cols, len] = this.collision_world.check(
entity, entity,
x - boundaries.width * 0.5, x - bounding_box.width * 0.5,
y - boundaries.height * 0.5, y - bounding_box.height * 0.5,
() => "touch" () => "touch"
); );

View File

@ -20,7 +20,7 @@ In **game/engines/collision/ball_wall.ts**:
```ts ```ts
import { Emits, Engine, Reads } from "encompass-ecs"; import { Emits, Engine, Reads } from "encompass-ecs";
import { BoundariesComponent } from "game/components/boundaries"; import { BoundingBoxComponent } from "game/components/bounding_box";
import { PositionComponent } from "game/components/position"; import { PositionComponent } from "game/components/position";
import { VelocityComponent } from "game/components/velocity"; import { VelocityComponent } from "game/components/velocity";
import { BallWallCollisionMessage } from "game/messages/collisions/ball_wall"; import { BallWallCollisionMessage } from "game/messages/collisions/ball_wall";
@ -34,15 +34,15 @@ export class BallWallCollisionEngine extends Engine {
for (const message of this.read_messages(BallWallCollisionMessage).values()) { for (const message of this.read_messages(BallWallCollisionMessage).values()) {
const ball_position = message.ball_entity.get_component(PositionComponent); const ball_position = message.ball_entity.get_component(PositionComponent);
const ball_velocity = message.ball_entity.get_component(VelocityComponent); const ball_velocity = message.ball_entity.get_component(VelocityComponent);
const ball_boundaries = message.ball_entity.get_component(BoundariesComponent); const ball_bounding_box = message.ball_entity.get_component(BoundingBoxComponent);
const velocity_message = this.emit_component_message(UpdateVelocityMessage, ball_velocity); const velocity_message = this.emit_component_message(UpdateVelocityMessage, ball_velocity);
velocity_message.x_delta = 2 * message.normal.x * Math.abs(ball_velocity.x); velocity_message.x_delta = 2 * message.normal.x * Math.abs(ball_velocity.x);
velocity_message.y_delta = 2 * message.normal.y * Math.abs(ball_velocity.y); velocity_message.y_delta = 2 * message.normal.y * Math.abs(ball_velocity.y);
// calculate bounce, remembering to re-transform coordinates to origin space // calculate bounce, remembering to re-transform coordinates to origin space
const y_distance = Math.abs(message.ball_new_y - (message.touch.y + ball_boundaries.height * 0.5)); const y_distance = Math.abs(message.ball_new_y - (message.touch.y + ball_bounding_box.height * 0.5));
const x_distance = Math.abs(message.ball_new_x - (message.touch.x + ball_boundaries.width * 0.5)); const x_distance = Math.abs(message.ball_new_x - (message.touch.x + ball_bounding_box.width * 0.5));
const position_message = this.emit_component_message(UpdatePositionMessage, ball_position); const position_message = this.emit_component_message(UpdatePositionMessage, ball_position);
position_message.x_delta = 2 * message.normal.x * x_distance; position_message.x_delta = 2 * message.normal.x * x_distance;

View File

@ -35,11 +35,11 @@ Here's the process we'll follow for our MotionEngine:
We associate MotionMessages with their PositionComponents. We consolidate them to get a total "x_delta" and a "y_delta". We create an UpdatePositionMessage containing these values. Next, we create CollisionCheckMessages containing the delta values if the PositionComponent's entity has a BoundingBoxComponent. We associate MotionMessages with their PositionComponents. We consolidate them to get a total "x_delta" and a "y_delta". We create an UpdatePositionMessage containing these values. Next, we create CollisionCheckMessages containing the delta values if the PositionComponent's entity has a BoundingBoxComponent.
Finally, we go over all BoundariesComponents that didn't have MotionMessages associated with them and create CollisionCheckMessages for those too. Otherwise things that didn't move wouldn't be collision checked, and that would not be correct. Finally, we go over all BoundingBoxComponents that didn't have MotionMessages associated with them and create CollisionCheckMessages for those too. Otherwise things that didn't move wouldn't be collision checked, and that would not be correct.
```ts ```ts
import { Emits, Engine, Reads } from "encompass-ecs"; import { Emits, Engine, Reads } from "encompass-ecs";
import { BoundariesComponent } from "game/components/boundaries"; import { BoundingBoxComponent } from "game/components/bounding_box";
import { PositionComponent } from "game/components/position"; import { PositionComponent } from "game/components/position";
import { CollisionCheckMessage } from "game/messages/collision_check"; import { CollisionCheckMessage } from "game/messages/collision_check";
import { MotionMessage } from "game/messages/component/motion"; import { MotionMessage } from "game/messages/component/motion";
@ -50,7 +50,7 @@ import { GCOptimizedList, GCOptimizedSet } from "tstl-gc-optimized-collections";
@Emits(UpdatePositionMessage, CollisionCheckMessage) @Emits(UpdatePositionMessage, CollisionCheckMessage)
export class MotionEngine extends Engine { export class MotionEngine extends Engine {
private component_to_message = new Map<PositionComponent, GCOptimizedList<MotionMessage>>(); private component_to_message = new Map<PositionComponent, GCOptimizedList<MotionMessage>>();
private boundaries_set = new GCOptimizedSet<BoundariesComponent>(); private bounding_box_set = new GCOptimizedSet<BoundingBoxComponent>();
public update(dt: number) { public update(dt: number) {
const motion_messages = this.read_messages(MotionMessage); const motion_messages = this.read_messages(MotionMessage);
@ -73,18 +73,18 @@ export class MotionEngine extends Engine {
update_position_message.x_delta = x_delta; update_position_message.x_delta = x_delta;
update_position_message.y_delta = y_delta; update_position_message.y_delta = y_delta;
if (entity.has_component(BoundariesComponent)) { if (entity.has_component(BoundingBoxComponent)) {
const collision_check_message = this.emit_message(CollisionCheckMessage); const collision_check_message = this.emit_message(CollisionCheckMessage);
collision_check_message.entity = entity; collision_check_message.entity = entity;
collision_check_message.x_delta = x_delta; collision_check_message.x_delta = x_delta;
collision_check_message.y_delta = y_delta; collision_check_message.y_delta = y_delta;
this.boundaries_set.add(entity.get_component(BoundariesComponent)); this.bounding_box_set.add(entity.get_component(BoundingBoxComponent));
} }
} }
for (const component of this.read_components(BoundariesComponent).values()) { for (const component of this.read_components(BoundingBoxComponent).values()) {
if (!this.boundaries_set.has(component)) { if (!this.bounding_box_set.has(component)) {
const collision_check_message = this.emit_message(CollisionCheckMessage); const collision_check_message = this.emit_message(CollisionCheckMessage);
collision_check_message.entity = this.get_entity(component.entity_id)!; collision_check_message.entity = this.get_entity(component.entity_id)!;
collision_check_message.x_delta = 0; collision_check_message.x_delta = 0;

View File

@ -1,7 +1,106 @@
--- ---
title: "Putting It All Together" title: "Putting It All Together"
date: 2019-05-28T21:22:44-07:00 date: 2019-05-28T21:22:44-07:00
weight: 900 weight: 1000
--- ---
Our Entities are getting a bit more complex now with the addition of BoundingBoxComponents and CollisionTypeComponents so why don't we go ahead and create Spawners for everything. Finally, we need to set up our initial game state with our spawn messages, and make sure we added and initialized all of our required Engines.
Our load method in **game/game.ts** should look something like this:
```ts
public load() {
this.canvas = love.graphics.newCanvas();
const collision_world = CollisionWorld.newWorld(32);
const world_builder = new WorldBuilder();
// ADD YOUR ENGINES HERE...
world_builder.add_engine(BallSpawner).initialize(collision_world);
world_builder.add_engine(GameBoundarySpawner).initialize(collision_world);
world_builder.add_engine(PaddleSpawner).initialize(collision_world);
world_builder.add_engine(InputEngine);
world_builder.add_engine(PaddleMovementEngine);
world_builder.add_engine(MotionEngine);
world_builder.add_engine(VelocityEngine);
world_builder.add_engine(CollisionCheckEngine).initialize(collision_world);
world_builder.add_engine(CollisionDispatchEngine);
world_builder.add_engine(BallWallCollisionEngine);
world_builder.add_engine(BallPaddleCollisionEngine);
world_builder.add_engine(UpdatePositionEngine);
world_builder.add_engine(UpdateVelocityEngine);
// ADD YOUR RENDERERS HERE...
world_builder.add_renderer(CanvasRenderer);
// ADD YOUR STARTING ENTITIES HERE...
const play_area_width = 1280;
const play_area_height = 720;
const boundary_width = 30;
const paddle_width = 20;
const paddle_height = 120;
const paddle_spacing = 40;
const paddle_speed = 400;
const ball_size = 16;
const paddle_spawn_message = world_builder.emit_message(PaddleSpawnMessage);
paddle_spawn_message.x = paddle_spacing;
paddle_spawn_message.y = play_area_height * 0.5;
paddle_spawn_message.width = paddle_width;
paddle_spawn_message.height = paddle_height;
paddle_spawn_message.move_speed = paddle_speed;
const ball_spawn_message = world_builder.emit_message(BallSpawnMessage);
ball_spawn_message.x = play_area_width * 0.5;
ball_spawn_message.y = play_area_height * 0.5;
ball_spawn_message.size = ball_size;
ball_spawn_message.x_velocity = 200;
ball_spawn_message.y_velocity = -400;
const top_wall_spawn_message = world_builder.emit_message(GameBoundarySpawnMessage);
top_wall_spawn_message.x = play_area_width * 0.5;
top_wall_spawn_message.y = -boundary_width * 0.5;
top_wall_spawn_message.width = play_area_width;
top_wall_spawn_message.height = boundary_width;
const right_wall_spawn_message = world_builder.emit_message(GameBoundarySpawnMessage);
right_wall_spawn_message.x = play_area_width + boundary_width * 0.5;
right_wall_spawn_message.y = play_area_height * 0.5;
right_wall_spawn_message.width = boundary_width;
right_wall_spawn_message.height = play_area_height;
const bottom_wall_spawn_message = world_builder.emit_message(GameBoundarySpawnMessage);
bottom_wall_spawn_message.x = play_area_width * 0.5;
bottom_wall_spawn_message.y = boundary_width * 0.5 + play_area_height;
bottom_wall_spawn_message.width = play_area_width;
bottom_wall_spawn_message.height = boundary_width;
const left_wall_spawn_message = world_builder.emit_message(GameBoundarySpawnMessage);
left_wall_spawn_message.x = -boundary_width * 0.5;
left_wall_spawn_message.y = play_area_height * 0.5;
left_wall_spawn_message.width = boundary_width;
left_wall_spawn_message.height = play_area_height;
this.world = world_builder.build();
}
```
Let's try it!
```sh
npm run love
```
<video width="640" height="360" autoplay="autoplay" muted="muted" loop="loop" style="display: block; margin: 0 auto; width: 640;">
<source src="/images/bouncing.webm" type="video/webm">
</video>
All our hard work paid off. Look at that! *chef kiss*

View File

@ -0,0 +1,228 @@
---
title: "Spawners"
date: 2019-05-29T11:05:16-07:00
weight: 900
---
Our Entities are getting a bit more complex now with the addition of BoundingBoxComponents and CollisionTypeComponents.
I think we should create Spawners for each of our game entities.
This will be pretty straightforward. Just decide which parameters we need to create our entities, and add the proper components with those parameters.
### Ball
In **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;
}
```
In **game/engines/spawners/ball.ts**:
```ts
import { Reads, Spawner } from "encompass-ecs";
import { BoundingBoxComponent } from "game/components/bounding_box";
import { CanvasComponent } from "game/components/canvas";
import { CollisionType, CollisionTypesComponent } from "game/components/collision_types";
import { PositionComponent } from "game/components/position";
import { VelocityComponent } from "game/components/velocity";
import { BallSpawnMessage } from "game/messages/ball_spawn";
import { World } from "lua-lib/bump";
@Reads(BallSpawnMessage)
export class BallSpawner extends Spawner {
public spawn_message_type = BallSpawnMessage;
public collision_world: World;
public initialize(collision_world: World) {
this.collision_world = collision_world;
}
public spawn(message: BallSpawnMessage) {
const ball_entity = this.create_entity();
const ball_position_component = ball_entity.add_component(PositionComponent);
ball_position_component.x = message.x;
ball_position_component.y = message.y;
const ball_canvas = love.graphics.newCanvas(message.size, message.size);
love.graphics.setCanvas(ball_canvas);
love.graphics.setBlendMode("alpha");
love.graphics.setColor(1, 1, 1, 1);
love.graphics.rectangle("fill", 0, 0, message.size, message.size);
love.graphics.setCanvas();
const ball_canvas_component = ball_entity.add_component(CanvasComponent);
ball_canvas_component.canvas = ball_canvas;
ball_canvas_component.x_scale = 1;
ball_canvas_component.y_scale = 1;
const velocity_component = ball_entity.add_component(VelocityComponent);
velocity_component.x = message.x_velocity;
velocity_component.y = message.y_velocity;
const boundaries_component = ball_entity.add_component(BoundingBoxComponent);
boundaries_component.width = message.size;
boundaries_component.height = message.size;
const collision_types_component = ball_entity.add_component(CollisionTypesComponent);
collision_types_component.collision_types = [ CollisionType.ball ];
this.collision_world.add(ball_entity, message.x, message.y, message.size, message.size);
}
}
```
### Game Boundary
In **game/messages/game_boundary_spawn.ts**:
```ts
import { Message } from "encompass-ecs";
export class GameBoundarySpawnMessage extends Message {
public x: number;
public y: number;
public width: number;
public height: number;
}
```
In **game/spawners/game_boundary.ts**:
```ts
import { Reads, Spawner } from "encompass-ecs";
import { BoundingBoxComponent } from "game/components/bounding_box";
import { CollisionType, CollisionTypesComponent } from "game/components/collision_types";
import { PositionComponent } from "game/components/position";
import { GameBoundarySpawnMessage } from "game/messages/game_boundary_spawn";
import { World } from "lua-lib/bump";
@Reads(GameBoundarySpawnMessage)
export class GameBoundarySpawner extends Spawner {
public spawn_message_type = GameBoundarySpawnMessage;
private collision_world: World;
public initialize(collision_world: World) {
this.collision_world = collision_world;
}
public spawn(message: GameBoundarySpawnMessage) {
const entity = this.create_entity();
const boundaries = entity.add_component(BoundingBoxComponent);
boundaries.width = message.width;
boundaries.height = message.height;
const position = entity.add_component(PositionComponent);
position.x = message.x;
position.y = message.y;
const collision_types_component = entity.add_component(CollisionTypesComponent);
collision_types_component.collision_types = [ CollisionType.wall ];
this.collision_world.add(
entity,
message.x - message.width * 0.5,
message.y - message.height * 0.5,
message.width,
message.height
);
}
}
```
### Paddle
In **game/messages/paddle_spawn.ts**:
```ts
import { Message } from "encompass-ecs";
export class PaddleSpawnMessage extends Message {
public x: number;
public y: number;
public width: number;
public height: number;
public move_speed: number;
}
```
In **game/spawners/paddle.ts**:
```ts
import { Reads, Spawner } from "encompass-ecs";
import { BoundingBoxComponent } from "game/components/bounding_box";
import { CanvasComponent } from "game/components/canvas";
import { CollisionType, CollisionTypesComponent } from "game/components/collision_types";
import { PaddleMoveSpeedComponent } from "game/components/paddle_move_speed";
import { PlayerOneComponent } from "game/components/player_one";
import { PositionComponent } from "game/components/position";
import { PaddleSpawnMessage } from "game/messages/paddle_spawn";
import { World } from "lua-lib/bump";
@Reads(PaddleSpawnMessage)
export class PaddleSpawner extends Spawner {
public spawn_message_type = PaddleSpawnMessage;
private collision_world: World;
public initialize(collision_world: World) {
this.collision_world = collision_world;
}
protected spawn(message: PaddleSpawnMessage) {
const paddle_entity = this.create_entity();
paddle_entity.add_component(PlayerOneComponent);
const width = message.width;
const height = message.height;
const paddle_canvas = love.graphics.newCanvas(width, height);
love.graphics.setCanvas(paddle_canvas);
love.graphics.setBlendMode("alpha");
love.graphics.setColor(1, 1, 1, 1);
love.graphics.rectangle("fill", 0, 0, width, height);
love.graphics.setCanvas();
const canvas_component = paddle_entity.add_component(CanvasComponent);
canvas_component.canvas = paddle_canvas;
canvas_component.x_scale = 1;
canvas_component.y_scale = 1;
const position_component = paddle_entity.add_component(PositionComponent);
position_component.x = message.x;
position_component.y = message.y;
const move_speed_component = paddle_entity.add_component(PaddleMoveSpeedComponent);
move_speed_component.y = message.move_speed;
const paddle_boundaries = paddle_entity.add_component(BoundingBoxComponent);
paddle_boundaries.width = width;
paddle_boundaries.height = height;
const collision_types_component = paddle_entity.add_component(CollisionTypesComponent);
collision_types_component.collision_types = [ CollisionType.paddle ];
this.collision_world.add(
paddle_entity,
message.x - width * 0.5,
message.y - height * 0.5,
width,
height
);
}
}
```

BIN
static/images/bouncing.webm Normal file

Binary file not shown.