feat: tileset geometry
This commit is contained in:
parent
176d55d78d
commit
2eb2c58054
|
@ -5,6 +5,12 @@ import {ShapeList, PolygonShape, RectangleShape} from '@avocado/physics';
|
|||
|
||||
import {AbstractBody} from '../abstract/body';
|
||||
|
||||
// Trick matter into not needing decomp for our convex polygons.
|
||||
const g = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : undefined;
|
||||
if (g) {
|
||||
g.decomp = true;
|
||||
}
|
||||
|
||||
export class Body extends AbstractBody {
|
||||
|
||||
static collisionCategory(group) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {Container, Sprite} from '@avocado/graphics';
|
||||
import {Vector} from '@avocado/math';
|
||||
import {BodyView} from '@avocado/physics';
|
||||
|
||||
import {Layer} from './layer';
|
||||
import {TilesRenderer} from './tiles-renderer';
|
||||
|
@ -11,20 +12,21 @@ export class LayerView extends Container {
|
|||
this.layer = layer;
|
||||
this.entityContainer = new Container();
|
||||
this.layerContainer = new Container();
|
||||
this.tileGeometryContainer = new Container();
|
||||
this._renderer = renderer;
|
||||
this.tileGeometryViews = [];
|
||||
// Handle entity add/remove.
|
||||
layer.on('entityAdded', this.onLayerEntityAdded, this);
|
||||
layer.on('entityRemoved', this.onLayerEntityRemoved, this);
|
||||
for (const entity of layer.entityList) {
|
||||
this.onLayerEntityAdded(entity);
|
||||
}
|
||||
// Watch tile data change.
|
||||
layer.on('tileDataChanged', this.onLayerTileDataChanged, this);
|
||||
// Watch tileset change.
|
||||
layer.on('tileGeometryChanged', this.onLayerTileGeometryChanged, this);
|
||||
layer.on('tilesetChanged', this.onLayerTilesetChanged, this);
|
||||
// Add to graphics.
|
||||
this.addChild(this.layerContainer);
|
||||
this.addChild(this.entityContainer);
|
||||
this.addChild(this.tileGeometryContainer);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -33,6 +35,14 @@ export class LayerView extends Container {
|
|||
layer.off('entityRemoved', this.onLayerEntityRemoved);
|
||||
layer.off('tileDataChanged', this.onLayerTileDataChanged);
|
||||
layer.off('tilesetChanged', this.onLayerTilesetChanged);
|
||||
this.destroyTileGeometryBodies();
|
||||
}
|
||||
|
||||
destroyTileGeometryBodies() {
|
||||
for (const view of this.tileGeometryViews) {
|
||||
view.destroy();
|
||||
}
|
||||
this.tileGeometryViews = [];
|
||||
}
|
||||
|
||||
onLayerEntityAdded(entity) {
|
||||
|
@ -51,6 +61,15 @@ export class LayerView extends Container {
|
|||
this.render();
|
||||
}
|
||||
|
||||
onLayerTileGeometryChanged() {
|
||||
this.destroyTileGeometryBodies();
|
||||
for (const body of this.layer.tileGeometry) {
|
||||
const bodyView = new BodyView(body);
|
||||
this.tileGeometryViews.push(bodyView);
|
||||
this.tileGeometryContainer.addChild(bodyView);
|
||||
}
|
||||
}
|
||||
|
||||
onLayerTilesetChanged() {
|
||||
this.render();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as I from 'immutable';
|
|||
|
||||
import {compose} from '@avocado/core';
|
||||
import {create as createEntity, EntityList} from '@avocado/entity';
|
||||
import {Vector} from '@avocado/math';
|
||||
import {EventEmitter, Property} from '@avocado/mixins';
|
||||
import {Synchronized} from '@avocado/state';
|
||||
|
||||
|
@ -16,6 +17,9 @@ const decorate = compose(
|
|||
Property('tileset', {
|
||||
track: true,
|
||||
}),
|
||||
Property('world', {
|
||||
track: true,
|
||||
}),
|
||||
);
|
||||
|
||||
export class Layer extends decorate(Synchronized) {
|
||||
|
@ -23,20 +27,49 @@ export class Layer extends decorate(Synchronized) {
|
|||
constructor() {
|
||||
super();
|
||||
this.entityList = new EntityList();
|
||||
this.tileGeometry = [];
|
||||
this.tiles = new Tiles();
|
||||
// Listeners.
|
||||
this.entityList.on('entityAdded', this.onEntityAddedToLayer, this);
|
||||
this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer, this);
|
||||
this.tiles.on('dataChanged', this.onTileDataChanged, this);
|
||||
// Watch tileset URI change.
|
||||
this.on('tilesetChanged', this.onTilesetChanged, this);
|
||||
this.on('tilesetUriChanged', this.onTilesetUriChanged, this);
|
||||
this.onTilesetUriChanged();
|
||||
this.on('worldChanged', this.onWorldChanged, this);
|
||||
}
|
||||
|
||||
addEntity(entity) {
|
||||
this.entityList.addEntity(entity);
|
||||
}
|
||||
|
||||
addTileGeometry() {
|
||||
const tileset = this.tileset;
|
||||
if (!tileset) {
|
||||
return false;
|
||||
}
|
||||
const world = this.world;
|
||||
if (!world) {
|
||||
return false;
|
||||
}
|
||||
this.tiles.forEachTile((tile, x, y, i) => {
|
||||
const shape = this.tileset.geometry(tile);
|
||||
if (!shape) {
|
||||
return;
|
||||
}
|
||||
const body = world.createBody(shape);
|
||||
const halfTileSize = Vector.scale(tileset.tileSize, 0.5);
|
||||
body.position = Vector.add(
|
||||
halfTileSize,
|
||||
Vector.mul([x, y], tileset.tileSize),
|
||||
);
|
||||
body.static = true;
|
||||
world.addBody(body);
|
||||
this.tileGeometry.push(body);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.entityList.destroy();
|
||||
this.entityList.off('entityAdded', this.onEntityAddedToLayer);
|
||||
|
@ -85,6 +118,20 @@ export class Layer extends decorate(Synchronized) {
|
|||
this.emit('tileDataChanged');
|
||||
}
|
||||
|
||||
onTilesetChanged(oldTileset) {
|
||||
let didChange = false;
|
||||
if (oldTileset) {
|
||||
this.removeTileGeometry();
|
||||
didChange = true;
|
||||
}
|
||||
if (this.addTileGeometry()) {
|
||||
didChange = true;
|
||||
}
|
||||
if (didChange) {
|
||||
this.emit('tileGeometryChanged');
|
||||
}
|
||||
}
|
||||
|
||||
onTilesetUriChanged() {
|
||||
if (!this.tilesetUri) {
|
||||
return;
|
||||
|
@ -94,10 +141,28 @@ export class Layer extends decorate(Synchronized) {
|
|||
});
|
||||
}
|
||||
|
||||
onWorldChanged(oldWorld) {
|
||||
let didChange = false;
|
||||
if (oldWorld) {
|
||||
this.removeTileGeometry();
|
||||
didChange = true;
|
||||
}
|
||||
if (this.addTileGeometry()) {
|
||||
didChange = true;
|
||||
}
|
||||
if (didChange) {
|
||||
this.emit('tileGeometryChanged');
|
||||
}
|
||||
}
|
||||
|
||||
removeEntity(entity) {
|
||||
this.entityList.removeEntity(entity);
|
||||
}
|
||||
|
||||
removeTileGeometry() {
|
||||
// ... tag geometry in world for removal?
|
||||
}
|
||||
|
||||
setTileAt(x, y, tile) {
|
||||
this.tiles.setTileAt(x, y, tile);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ export class Room extends decorate(Synchronized) {
|
|||
// Listeners.
|
||||
this.layers.on('entityAdded', this.onEntityAddedToRoom, this);
|
||||
this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this);
|
||||
this.layers.on('layerAdded', this.onLayerAdded, this);
|
||||
this.on('sizeChanged', this.onSizeChanged, this);
|
||||
this.on('worldChanged', this.onWorldChanged, this);
|
||||
}
|
||||
|
@ -74,6 +75,10 @@ export class Room extends decorate(Synchronized) {
|
|||
this.emit('entityRemoved', entity);
|
||||
}
|
||||
|
||||
onLayerAdded(layer, index) {
|
||||
layer.world = this.world;
|
||||
}
|
||||
|
||||
onSizeChanged() {
|
||||
this.updateBounds();
|
||||
}
|
||||
|
@ -81,6 +86,7 @@ export class Room extends decorate(Synchronized) {
|
|||
onWorldChanged() {
|
||||
const world = this.world;
|
||||
for (const {layer} of this.layers) {
|
||||
layer.world = world;
|
||||
for (const entity of layer.entityList) {
|
||||
if (entity.is('physical')) {
|
||||
entity.world = world;
|
||||
|
|
|
@ -19,6 +19,31 @@ export class Tiles extends decorate(Synchronized) {
|
|||
this.data = I.List();
|
||||
}
|
||||
|
||||
forEachTile(fn) {
|
||||
let [x, y] = [0, 0];
|
||||
let [width, height] = this.size;
|
||||
let i = 0;
|
||||
for (let k = 0; k < height; ++k) {
|
||||
for (let j = 0; j < width; ++j) {
|
||||
fn(this.data.get(i), x, y, i);
|
||||
++i;
|
||||
++x;
|
||||
}
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
if (json.size) {
|
||||
this.size = json.size;
|
||||
}
|
||||
if (json.data) {
|
||||
this.data = I.fromJS(json.data);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
patchStateStep(key, step) {
|
||||
if ('data' === key) {
|
||||
const oldData = this.data;
|
||||
|
@ -34,16 +59,6 @@ export class Tiles extends decorate(Synchronized) {
|
|||
super.patchStateStep(key, step);
|
||||
}
|
||||
|
||||
fromJSON(json) {
|
||||
if (json.size) {
|
||||
this.size = json.size;
|
||||
}
|
||||
if (json.data) {
|
||||
this.data = I.fromJS(json.data);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
get rectangle() {
|
||||
return Rectangle.compose([0, 0], this.size);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {compose} from '@avocado/core';
|
||||
import {hasGraphics, Image} from '@avocado/graphics';
|
||||
import {Rectangle, Vector} from '@avocado/math';
|
||||
import {shapeFromJSON} from '@avocado/physics';
|
||||
import {Resource} from '@avocado/resource';
|
||||
|
||||
const decorate = compose(
|
||||
|
@ -13,6 +14,7 @@ export class Tileset extends decorate(Resource) {
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
this._geometry = {};
|
||||
this._image = undefined;
|
||||
this.subimages = [];
|
||||
}
|
||||
|
@ -26,6 +28,12 @@ export class Tileset extends decorate(Resource) {
|
|||
|
||||
fromJSON(json) {
|
||||
let promise;
|
||||
if (json.geometry) {
|
||||
for (const i in json.geometry) {
|
||||
const shapeJSON = json.geometry[i];
|
||||
this._geometry[i] = shapeFromJSON(shapeJSON);
|
||||
}
|
||||
}
|
||||
if (json.tileSize) {
|
||||
this.tileSize = json.tileSize;
|
||||
}
|
||||
|
@ -39,6 +47,10 @@ export class Tileset extends decorate(Resource) {
|
|||
});
|
||||
}
|
||||
|
||||
geometry(index) {
|
||||
return this._geometry[index];
|
||||
}
|
||||
|
||||
get image() {
|
||||
return this._image;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user