@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
242 lines (175 loc) • 7.12 kB
JavaScript
import Rectangle from "../../core/geom/2d/Rectangle.js";
import Vector2 from "../../core/geom/Vector2.js";
import { EntityObserver } from "../../engine/ecs/EntityObserver.js";
import Terrain from "../../engine/ecs/terrain/ecs/Terrain.js";
import { obtainTerrain } from "../../engine/ecs/terrain/util/obtainTerrain.js";
import { Transform } from "../../engine/ecs/transform/Transform.js";
import { Camera } from "../../engine/graphics/ecs/camera/Camera.js";
import TopDownCameraController from "../../engine/graphics/ecs/camera/topdown/TopDownCameraController.js";
import { PointerDevice } from "../../engine/input/devices/PointerDevice.js";
import domify from "../DOM.js";
import EmptyView from "../elements/EmptyView.js";
import View from "../View.js";
import { MinimapCameraView } from "./dom/MinimapCameraView.js";
import { MinimapFogOfWar } from "./gl/MinimapFogOfWar.js";
import { MinimapMarkersGL } from "./gl/MinimapMarkersGL.js";
import { MinimapTerrainGL } from "./gl/MinimapTerrainGL.js";
import { MinimapWorldGL } from "./gl/MinimapWorldGL.js";
class MinimapView extends View {
/**
*
* @constructor
* @param {EntityComponentDataset} entityDataset
* @param {AssetManager} assetManager
*/
constructor(entityDataset, assetManager) {
super(entityDataset, assetManager);
this.el = domify('div').addClass('ui-minimap-view').el;
/**
*
* @type {EntityComponentDataset}
*/
this.entityDataset = entityDataset;
const vWorld = new EmptyView({ classList: ['world'] });
this.addChild(vWorld);
/**
*
* @type {Rectangle}
*/
const world = this.world = new Rectangle(0, 0, 0, 0);
const worldScale = new Vector2(1, 1);
this.worldGL = new MinimapWorldGL({ canvasSize: this.size, worldBounds: world });
/**
*
* @param {MinimapWorldGL} worldGL
*/
function setupWorld(worldGL) {
worldGL.addLayer(new MinimapMarkersGL(entityDataset, assetManager));
worldGL.addLayer(new MinimapFogOfWar(entityDataset, world));
}
const lTerrain = new MinimapTerrainGL({ assetManager });
this.worldGL.addLayer(lTerrain);
setupWorld(this.worldGL);
this.vMarkers = new EmptyView({ classList: ["ui-minimap-marker-view"] });
vWorld.addChild(this.vMarkers);
this.__worldView = vWorld;
this.__terrainView = lTerrain;
/**
*
* @type {MinimapCameraView[]}
*/
const cameraViews = this.__cameraViews = [];
const self = this;
function cameraAdded(camera, transform, entity) {
const cameraView = new MinimapCameraView({ camera, transform, entity, world, worldScale });
const terrain = obtainTerrain(entityDataset);
cameraView.terrain.set(terrain);
self.__cameraViews.push(cameraView);
self.__worldView.addChild(cameraView);
}
function cameraRemoved(camera, transform, entity) {
const cameraView = self.__cameraViews.find(v => v.entity === entity);
if (cameraView === undefined) {
//do nothing
console.warn('Camera view not found', camera, transform, entity);
return;
}
const i = self.__cameraViews.indexOf(cameraView);
self.__cameraViews.splice(i, 1);
self.__worldView.removeChild(cameraView);
}
this.cameraObserver = new EntityObserver([Camera, Transform], cameraAdded, cameraRemoved);
function updateWorldScale() {
let worldSizeX = world.size.x;
let worldSizeY = world.size.y;
let scaleX;
if (worldSizeX === 0) {
scaleX = 0;
} else {
scaleX = self.size.x / worldSizeX;
}
let scaleY;
if (worldSizeY === 0) {
scaleY = 0;
} else {
scaleY = self.size.y / worldSizeY;
}
worldScale.set(scaleX, scaleY);
}
world.size.onChanged.add(updateWorldScale);
this.size.onChanged.add(function (x, y) {
updateWorldScale();
});
const pointer = this.pointer = new PointerDevice(vWorld.el);
function focus(x, y) {
const worldX = (x / worldScale.x) + world.position.x;
const worldZ = (y / worldScale.y) + world.position.y;
//find active camera
const v = cameraViews.find(function (v) {
/**
* @type {Camera}
*/
const camera = v.camera;
return camera.active.getValue();
});
if (v === undefined) {
console.warn(`No camera present. Focusing attempt failed, (x=${x}, y=${y})`);
} else {
/**
*
* @type {TopDownCameraController}
*/
const controller = entityDataset.getComponent(v.entity, TopDownCameraController);
if (controller !== null) {
controller.target.set(worldX, controller.target.y, worldZ);
}
}
}
pointer.on.down.add(function (position, event) {
focus(event.layerX, event.layerY);
});
pointer.on.drag.add(function (position, origin, lastDragPosition, event) {
focus(event.layerX, event.layerY);
});
}
clear() {
for (let i = 0, l = this.__cameraViews.length; i < l; i++) {
const v = this.__cameraViews[i];
this.__worldView.addChild(v);
}
}
link() {
super.link();
const entityDataset = this.entityDataset;
let self = this;
/**
*
* @param {Terrain} t
*/
function visitTerrain(t) {
self.__terrainView.terrain.set(t);
self.__cameraViews.forEach(c => c.terrain.set(t));
}
entityDataset.traverseComponents(Terrain, visitTerrain);
entityDataset.addObserver(this.cameraObserver, true);
try {
this.worldGL.startup();
} catch (e) {
console.error('Startup of marker manager failed', e);
}
const dom = domify(this.vMarkers.el);
dom.clear();
dom.append(this.worldGL.domElement);
this.pointer.start();
}
unlink() {
super.unlink();
this.pointer.stop();
this.worldGL.shutdown();
//clear out all camera views
this.entityDataset.removeObserver(this.cameraObserver, true);
//remove all exiting markers
this.clear();
}
}
export default MinimapView;