refactor: generation
This commit is contained in:
parent
f68e5e9616
commit
ed264d8f0b
|
@ -1,11 +1,10 @@
|
|||
export default function floodwalk2D(data, position, w, h) {
|
||||
export default function floodwalk2D(b, data, [x, y, w, h]) {
|
||||
const n = data.length;
|
||||
const [x, y] = position;
|
||||
let i = x + w * y;
|
||||
if (i < 0 || i >= n) {
|
||||
return [];
|
||||
}
|
||||
const b = data[i];
|
||||
// const b = data[i];
|
||||
const points = [];
|
||||
const seen = [];
|
||||
seen[-1] = true;
|
||||
|
@ -14,7 +13,7 @@ export default function floodwalk2D(data, position, w, h) {
|
|||
while (todo.length > 0) {
|
||||
i = todo.pop();
|
||||
const v = data[i];
|
||||
if (b !== v) {
|
||||
if (!b.has(v)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
|
39
packages/math/src/generator.js
Normal file
39
packages/math/src/generator.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
export default class Generator {
|
||||
|
||||
constructor({
|
||||
calculate,
|
||||
children = [],
|
||||
covers,
|
||||
size: [w, h],
|
||||
}) {
|
||||
this.calculate = calculate;
|
||||
this.children = children;
|
||||
this.covers = covers;
|
||||
this.size = [w, h];
|
||||
this.matrix = new Array(w * h).fill(0);
|
||||
}
|
||||
|
||||
compute(i, x, y) {
|
||||
if (!this.covers([x, y])) {
|
||||
return;
|
||||
}
|
||||
this.matrix[i] = this.calculate([x, y]);
|
||||
if (!this.children) {
|
||||
return;
|
||||
}
|
||||
for (let j = 0; j < this.children.length; j++) {
|
||||
this.children[j].compute(i, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
generate = () => {
|
||||
const [w, h] = this.size;
|
||||
let i = 0;
|
||||
for (let y = 0; y < h; ++y) {
|
||||
for (let x = 0; x < w; ++x) {
|
||||
this.compute(i++, x, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ import * as Rectangle from './rectangle';
|
|||
import types from './types';
|
||||
import * as Vector from './vector';
|
||||
|
||||
export {default as Generator} from './generator';
|
||||
export * from './math';
|
||||
export {default as floodwalk2D} from './floodwalk';
|
||||
export {default as QuadTree} from './quadtree';
|
||||
|
|
|
@ -21,35 +21,31 @@ export default (Layer) => class PhysicsLayer extends Layer {
|
|||
if (!tileset) {
|
||||
return;
|
||||
}
|
||||
const indices = Object.keys(this.#impassable).map((i) => parseInt(i, 10));
|
||||
for (let i = 0; i < indices.length; i++) {
|
||||
const hulls = this.tiles.indexHulls(indices[i]);
|
||||
if (0 === hulls.length) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
for (let j = 0; j < hulls.length; ++j) {
|
||||
const scaled = [];
|
||||
for (let k = 0; k < hulls[j].length; ++k) {
|
||||
scaled.push(Vector.mul(hulls[j][k], tileset.tileSize));
|
||||
}
|
||||
const [vertices, position] = Vertice.localize(scaled);
|
||||
const shape = new PolygonShape({position, vertices});
|
||||
const body = this.#world.createBody(shape);
|
||||
body.static = true;
|
||||
this.#tileBodies.push(body);
|
||||
this.#world.addBody(body);
|
||||
const hulls = this.tiles.indexHulls(this.#impassable);
|
||||
if (0 === hulls.length) {
|
||||
return;
|
||||
}
|
||||
for (let j = 0; j < hulls.length; ++j) {
|
||||
const scaled = [];
|
||||
for (let k = 0; k < hulls[j].length; ++k) {
|
||||
scaled.push(Vector.mul(hulls[j][k], tileset.tileSize));
|
||||
}
|
||||
const [vertices, position] = Vertice.localize(scaled);
|
||||
const shape = new PolygonShape({position, vertices});
|
||||
const body = this.#world.createBody(shape);
|
||||
body.static = true;
|
||||
this.#tileBodies.push(body);
|
||||
this.#world.addBody(body);
|
||||
}
|
||||
}
|
||||
|
||||
async load(json = {}) {
|
||||
await super.load(json);
|
||||
const {impassable = []} = json;
|
||||
this.#impassable = impassable.reduce((r, index) => ({...r, [index]: true}), {});
|
||||
this.#impassable = new Set(impassable);
|
||||
}
|
||||
|
||||
onTileDataChanged() {
|
||||
onTilesUpdate() {
|
||||
this.removeTileBodies();
|
||||
this.addTileBodies();
|
||||
}
|
||||
|
@ -63,6 +59,14 @@ export default (Layer) => class PhysicsLayer extends Layer {
|
|||
}
|
||||
}
|
||||
|
||||
setTiles(tiles) {
|
||||
if (this.tiles) {
|
||||
this.tiles.off('update', this.onTilesUpdate);
|
||||
}
|
||||
super.setTiles(tiles);
|
||||
this.tiles.on('update', this.onTilesUpdate, this);
|
||||
}
|
||||
|
||||
set world(world) {
|
||||
this.removeTileBodies();
|
||||
this.#world = world;
|
||||
|
|
|
@ -11,6 +11,7 @@ export default class LayerView extends Container {
|
|||
super();
|
||||
this.entityListView = new EntityListView(layer.entityList);
|
||||
this.tilesView = new TilesView(layer.tiles, layer.tileset, renderer);
|
||||
this.lastExtent = [0, 0, 0, 0];
|
||||
this.layer = layer;
|
||||
this.addChild(this.tilesView);
|
||||
this.addChild(this.entityListView);
|
||||
|
@ -27,17 +28,18 @@ export default class LayerView extends Container {
|
|||
}
|
||||
|
||||
renderChunksForExtent(extent) {
|
||||
this.lastExtent = extent;
|
||||
this.tilesView.renderChunksForExtent(extent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if (module.hot) {
|
||||
// module.hot.accept('./tiles-renderer', () => {
|
||||
// const it = views.values();
|
||||
// for (let value = it.next(); value.done !== true; value = it.next()) {
|
||||
// const {value: view} = value;
|
||||
// view.render();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
if (module.hot) {
|
||||
module.hot.accept('./tiles-view', () => {
|
||||
const it = views.values();
|
||||
for (let value = it.next(); value.done !== true; value = it.next()) {
|
||||
const {value: view} = value;
|
||||
view.renderChunksForExtent(view.lastExtent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -126,12 +126,13 @@ export default (latus) => {
|
|||
this.startSynchronizing(tiles);
|
||||
}
|
||||
|
||||
stampAt(position, tiles, w, h) {
|
||||
this.tiles.stampAt(position, tiles, w, h);
|
||||
stampAt([x, y, w, h], tiles) {
|
||||
this.tiles.stampAt([x, y, w, h], tiles);
|
||||
}
|
||||
|
||||
tick(elapsed) {
|
||||
this.entityList.tick(elapsed);
|
||||
this.tiles.tick();
|
||||
}
|
||||
|
||||
tileAt(position) {
|
||||
|
|
|
@ -19,6 +19,8 @@ export default (latus) => {
|
|||
);
|
||||
return class Tiles extends decorate(Class) {
|
||||
|
||||
#hasUpdates = false;
|
||||
|
||||
#packets = [];
|
||||
|
||||
#updates = [];
|
||||
|
@ -44,7 +46,7 @@ export default (latus) => {
|
|||
this.#updates = [];
|
||||
}
|
||||
|
||||
indexHulls(index) {
|
||||
indexHulls(indices) {
|
||||
const hulls = [];
|
||||
const seen = [];
|
||||
const n = this.data.length;
|
||||
|
@ -52,9 +54,9 @@ export default (latus) => {
|
|||
let x = 0;
|
||||
let y = 0;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
if (index === this.data[i]) {
|
||||
if (indices.has(this.data[i])) {
|
||||
if (!seen[i]) {
|
||||
const points = floodwalk2D(this.data, [x, y], w, h);
|
||||
const points = floodwalk2D(indices, this.data, [x, y, w, h]);
|
||||
if (points.length > 0) {
|
||||
const body = [];
|
||||
for (let j = 0; j < points.length; ++j) {
|
||||
|
@ -64,28 +66,28 @@ export default (latus) => {
|
|||
const xx = k % w;
|
||||
const yy = Math.floor(k / w);
|
||||
const u = k - w;
|
||||
if ((u < 0 || yy < 1) || this.data[u] !== index) {
|
||||
if ((u < 0 || yy < 1) || !indices.has(this.data[u])) {
|
||||
body.push([xx, yy]);
|
||||
body.push([xx + 1, yy]);
|
||||
}
|
||||
const r = k + 1;
|
||||
if ((r < 0 || xx + 1 >= w) || this.data[r] !== index) {
|
||||
if ((r < 0 || xx + 1 >= w) || !indices.has(this.data[r])) {
|
||||
body.push([xx + 1, yy]);
|
||||
body.push([xx + 1, yy + 1]);
|
||||
}
|
||||
const d = k + w;
|
||||
if ((d < 0 || yy + 1 >= h) || this.data[d] !== index) {
|
||||
if ((d < 0 || yy + 1 >= h) || !indices.has(this.data[d])) {
|
||||
body.push([xx + 1, yy + 1]);
|
||||
body.push([xx, yy + 1]);
|
||||
}
|
||||
const l = k - 1;
|
||||
if ((l < 0 || xx < 1) || this.data[l] !== index) {
|
||||
if ((l < 0 || xx < 1) || !indices.has(this.data[l])) {
|
||||
body.push([xx, yy + 1]);
|
||||
body.push([xx, yy]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const hull = Vertice.removeCollinear(Vertice.smooth(body));
|
||||
const hull = Vertice.removeCollinear(Vertice.ortho(Vertice.unique(body)));
|
||||
if (hull.length > 0) {
|
||||
hulls.push(hull);
|
||||
}
|
||||
|
@ -135,6 +137,7 @@ export default (latus) => {
|
|||
this.data[index] = tile;
|
||||
if ('client' !== process.env.SIDE) {
|
||||
this.#updates.push([[x, y], [1, 1], [tile]]);
|
||||
this.#hasUpdates = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,9 +198,17 @@ export default (latus) => {
|
|||
}
|
||||
if (isDirty && 'client' !== process.env.SIDE) {
|
||||
this.#updates.push([[x, y], [w, h], tiles]);
|
||||
this.#hasUpdates = true;
|
||||
}
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (this.#hasUpdates) {
|
||||
this.emit('update');
|
||||
}
|
||||
this.#hasUpdates = false;
|
||||
}
|
||||
|
||||
tileAt([x, y]) {
|
||||
const [w, h] = this.size;
|
||||
return x < 0 || x >= w || y < 0 || y >= h ? undefined : this.data[y * w + x];
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import {
|
||||
Canvas,
|
||||
Container,
|
||||
// Image,
|
||||
Image,
|
||||
Sprite,
|
||||
} from '@avocado/graphics';
|
||||
import {Vector} from '@avocado/math';
|
||||
import {
|
||||
noise,
|
||||
Vector,
|
||||
Vertice,
|
||||
} from '@avocado/math';
|
||||
|
||||
export default class TilesView extends Container {
|
||||
|
||||
|
@ -12,11 +16,20 @@ export default class TilesView extends Container {
|
|||
|
||||
constructor(tiles, tileset, renderer) {
|
||||
super();
|
||||
this.wrapper = new Container();
|
||||
this.addChild(this.wrapper);
|
||||
this.tiles = tiles;
|
||||
this.tiles.on('update', this.onTilesUpdate, this);
|
||||
this.tileset = tileset;
|
||||
this.renderer = renderer;
|
||||
this.rendered = [];
|
||||
// this.hulls = this.tiles.indexHulls(0);
|
||||
// const mask = this.renderMask(Vector.mul(this.tiles.size, this.tileset.tileSize));
|
||||
// if (mask) {
|
||||
// this.addChild(mask);
|
||||
// this.wrapper.mask = mask;
|
||||
// mask.anchor = [0, 0];
|
||||
// }
|
||||
}
|
||||
|
||||
chunksForExtent([x, y, w, h]) {
|
||||
|
@ -104,9 +117,12 @@ export default class TilesView extends Container {
|
|||
if (cx >= fw || cy >= fh) {
|
||||
return;
|
||||
}
|
||||
const sw = cx + cw > fw ? (cx + cw) - fw : cw;
|
||||
const sh = cy + ch > fh ? (cy + ch) - fh : ch;
|
||||
const slice = this.tiles.slice([cx, cy, sw, sh]);
|
||||
const slice = this.tiles.slice([
|
||||
cx,
|
||||
cy,
|
||||
cx + cw > fw ? (cx + cw) - fw : cw,
|
||||
cy + ch > fh ? (cy + ch) - fh : ch,
|
||||
]);
|
||||
if (!slice) {
|
||||
return;
|
||||
}
|
||||
|
@ -147,7 +163,7 @@ export default class TilesView extends Container {
|
|||
sprite.anchor = [0, 0];
|
||||
sprite.position = [cux * cw * tw, cuy * ch * th];
|
||||
this.rendered[this.constructor.hashChunk([cux, cuy])] = sprite;
|
||||
this.addChild(sprite);
|
||||
this.wrapper.addChild(sprite);
|
||||
container.destroy();
|
||||
// if (mask) {
|
||||
// mask.destroy();
|
||||
|
@ -166,73 +182,72 @@ export default class TilesView extends Container {
|
|||
}
|
||||
}
|
||||
|
||||
// renderMask(rectangle) {
|
||||
// const hulls = this.tiles.indexHulls(0);
|
||||
// if (!hulls.length > 0) {
|
||||
// return undefined;
|
||||
// }
|
||||
// const size = Rectangle.size(rectangle);
|
||||
// const {tileSize} = this.tileset;
|
||||
// const canvasSize = Vector.mul(size, tileSize);
|
||||
// const canvas = window.document.createElement('canvas');
|
||||
// [canvas.width, canvas.height] = canvasSize;
|
||||
// const ctx = canvas.getContext('2d');
|
||||
// ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
// ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
// ctx.fillStyle = 'rgba(0, 0, 0, 1)';
|
||||
// for (let i = 0; i < hulls.length; ++i) {
|
||||
// const scaled = [];
|
||||
// for (let j = 0; j < hulls[i].length; j++) {
|
||||
// scaled.push(Vector.scale(hulls[i][j], 16));
|
||||
// }
|
||||
// scaled.push(Vector.scale(hulls[i][0], 16));
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(scaled[0][0], scaled[0][1]);
|
||||
// for (let j = 0; j < scaled.length - 1; j++) {
|
||||
// const p0 = scaled[j + 0];
|
||||
// const p1 = scaled[j + 1];
|
||||
// const points = Vertice.bresenham(p0, p1);
|
||||
// const isReversed = (
|
||||
// (p0[0] > p1[0] && points[0][0] < points[points.length - 1][0])
|
||||
// || (p0[1] > p1[1] && points[0][1] < points[points.length - 1][1])
|
||||
// );
|
||||
// const u = Vector.scale(
|
||||
// Vector.fromRadians((Math.PI * 0.5) + Vector.toRadians(Vector.sub(p1, p0))),
|
||||
// 2,
|
||||
// );
|
||||
// for (
|
||||
// let k = (isReversed ? points.length - 1 : 0);
|
||||
// (isReversed ? k >= 0 : k < points.length);
|
||||
// k += (isReversed ? -1 : 1)
|
||||
// ) {
|
||||
// const [x, y] = points[k];
|
||||
// const shift = Vector.add(
|
||||
// u,
|
||||
// Vector.scale(
|
||||
// Vector.mul(
|
||||
// [
|
||||
// noise(Vector.scale([x, y], 10)),
|
||||
// noise(Vector.scale([y, x], 10)),
|
||||
// ],
|
||||
// [
|
||||
// noise(Vector.scale([x, y], 20)),
|
||||
// noise(Vector.scale([y, x], 20)),
|
||||
// ],
|
||||
// ),
|
||||
// 5,
|
||||
// ),
|
||||
// );
|
||||
// const [vx, vy] = Vector.add(
|
||||
// points[k],
|
||||
// shift,
|
||||
// );
|
||||
// ctx.lineTo(vx, vy);
|
||||
// }
|
||||
// }
|
||||
// ctx.closePath();
|
||||
// ctx.fill();
|
||||
// }
|
||||
// return new Sprite(Image.from(canvas));
|
||||
// }
|
||||
renderMask(size) {
|
||||
const {hulls} = this;
|
||||
if (!hulls.length > 0) {
|
||||
return undefined;
|
||||
}
|
||||
const {tileSize} = this.tileset;
|
||||
const canvasSize = Vector.mul(size, tileSize);
|
||||
const canvas = window.document.createElement('canvas');
|
||||
[canvas.width, canvas.height] = canvasSize;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
|
||||
for (let i = 0; i < hulls.length; ++i) {
|
||||
const scaled = [];
|
||||
for (let j = 0; j < hulls[i].length; j++) {
|
||||
scaled.push(Vector.scale(hulls[i][j], 16));
|
||||
}
|
||||
scaled.push(Vector.scale(hulls[i][0], 16));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(scaled[0][0], scaled[0][1]);
|
||||
for (let j = 0; j < scaled.length - 1; j++) {
|
||||
const p0 = scaled[j + 0];
|
||||
const p1 = scaled[j + 1];
|
||||
const points = Vertice.bresenham(p0, p1);
|
||||
const isReversed = (
|
||||
(p0[0] > p1[0] && points[0][0] < points[points.length - 1][0])
|
||||
|| (p0[1] > p1[1] && points[0][1] < points[points.length - 1][1])
|
||||
);
|
||||
const u = Vector.scale(
|
||||
Vector.fromRadians((Math.PI * 0.5) + Vector.toRadians(Vector.sub(p1, p0))),
|
||||
2,
|
||||
);
|
||||
for (
|
||||
let k = (isReversed ? points.length - 1 : 0);
|
||||
(isReversed ? k >= 0 : k < points.length);
|
||||
k += (isReversed ? -1 : 1)
|
||||
) {
|
||||
const [x, y] = points[k];
|
||||
const shift = Vector.add(
|
||||
u,
|
||||
Vector.scale(
|
||||
Vector.mul(
|
||||
[
|
||||
noise(Vector.scale([x, y], 10)),
|
||||
noise(Vector.scale([y, x], 10)),
|
||||
],
|
||||
[
|
||||
noise(Vector.scale([x, y], 20)),
|
||||
noise(Vector.scale([y, x], 20)),
|
||||
],
|
||||
),
|
||||
5,
|
||||
),
|
||||
);
|
||||
const [vx, vy] = Vector.add(
|
||||
points[k],
|
||||
shift,
|
||||
);
|
||||
ctx.lineTo(vx, vy);
|
||||
}
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
return new Sprite(Image.from(canvas));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ describe('Tiles', () => {
|
|||
size: [8, 4],
|
||||
},
|
||||
);
|
||||
expect(tiles.indexHulls(1)).to.deep.equal(hulls);
|
||||
expect(tiles.indexHulls(new Set([1]))).to.deep.equal(hulls);
|
||||
});
|
||||
it("can stamp at", async () => {
|
||||
const {Tiles} = latus.get('%resources');
|
||||
|
@ -138,6 +138,6 @@ describe('Tiles', () => {
|
|||
size: [8, 4],
|
||||
},
|
||||
);
|
||||
expect(tiles.indexHulls(1)).to.deep.equal(hulls);
|
||||
expect(tiles.indexHulls(new Set([1]))).to.deep.equal(hulls);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user