DHO4JCJIELKX4R42XXAMAHVQTTE6OWULLP2QF4BXO3UWC5UTMSMAC
WTINQMZSNRT5MSAXLMMZVM6OU6TMD4AP4OEUYAXZUFNY5IWFLZSQC
2ZSVFRN4O7L6ARZNYSEIVECQTTSNPTHVMJU7TQDMEIYHOV36OG2QC
4CBMJ7C2376HBVEBEHGJGR3YZJSCHDVAD4Q6AEGBAZZFLLKD52SAC
TQ57VE45BHV7MOZ6GKTYZEAMAOTXLPQ3ROCWJ2FUCITQWOYVMUIAC
ZGS4FTTFBXTF2SHYTPAJJBWEUVWVYXPSJVEFI5NYJWTW273B4NHAC
WO2ALETBVNH7N3NXJ6JKWCQ2YIZN6LBO7WEXPHAT3EQQJEUCJITQC
3EAR4KTTJ26IRF5QUGEDRMJVBR4X3VWSQ6OBIJCLDH3A4ZZ4O3ZAC
ZHOSSPNKGFIKSFPDXCGLMSYMMX2J433VU2BUUWBKUH7TOLQUBSPQC
// public async getAsync(): Promise<T> {
// if (this._value !== undefined || this._wasConstructed === true) {
// return Promise.resolve(this._value!);
// } else {
// return new Promise<T>((resolve, reject) => {
// this._value = this._factory();
// this._wasConstructed = true;
// resolve(this._value);
// });
// }
// }
// root container
public stage!: Pixi.Container;
// Contains HUD, and other entities that don't move when game camera moves
public fixedCameraStage!: Pixi.Container;
// Contains game entities that move when game camera pans/zooms. Highly encouraged to have further subdivions.
public actionStage!: Pixi.Container;
// Contains a few entities that doesn't move when game camera moves, but located behind action stage entities, e.g. static backgrounds
public backdropStage!: Pixi.Container;
export type BaseApplicationProps = DeepReadonly<{
gameState: GameState,
windowSize: Vector2,
}>
constructor(config?: Partial<Config>) {
assertOnlyCalledOnce("Application constructor");
this.config = Object.assign({}, defaultConfig, config);
constructor(args: Partial<Config> = {}, props: BaseApplicationProps) {
assertOnlyCalledOnce("Base application constructor");
this.config = Object.assign({}, defaultConfig, args);
const appSize = BaseApplication.appSizeFromWindowSize(new Vector2(
this.config.originalWindowWidth, this.config.originalWindowHeight
));
this.originalAppWidth = appSize.x;
this.originalAppHeight = appSize.y;
this.state.appSize = BaseApplication.appSizeFromWindowSize(props.windowSize);
this.originalAppWidth = this.state.appSize.x;
this.originalAppHeight = this.state.appSize.y;
this.stage = this.app.stage;
this.stage.sortableChildren = true;
this.fixedCameraStage = new Pixi.Sprite();
this.fixedCameraStage.zIndex = 1;
this.fixedCameraStage.sortableChildren = true;
this.stage.addChild(this.fixedCameraStage);
this.actionStage = new Pixi.Sprite();
this.actionStage.zIndex = 0;
this.actionStage.sortableChildren = true;
this.stage.addChild(this.actionStage);
this.backdropStage = new Pixi.Sprite();
this.backdropStage.zIndex = -1;
this.backdropStage.sortableChildren = true;
this.stage.addChild(this.backdropStage);
this.keyboard = new KeyboardState();
this.app.ticker.add(() => {
this.keyboard.update();
})
this.fpsTracker = new FpsTracker();
this.app.ticker.add((delta) => {
// delta should be approximately equal to 1
this.fpsTracker.tick(delta);
})
let pointNodeTexture = generatePointNodeTexture(this.app.renderer);
public rerender(props: {
gameState: DeepReadonly<GameState>,
pixiComponentState: PixiComponentState,
}) {
updateState(delta: number, props: BaseApplicationProps) {
this.state.appSize = BaseApplication.appSizeFromWindowSize(props.windowSize);
}
render(delta: number, props: BaseApplicationProps) {
this.app.renderer.resize(this.state.appSize.x, this.state.appSize.y);
}
baseGameLoop(delta: number) {
* this.queuedEvents // used for onEvent listeners, to delay action until the next tick
* this.queuedEvents // used for onEvent listeners, to delay action until the next tick; perhaps this should interact with the global event queue?
* globalEventQueue.push(eventAction: (delta, prevProps, prevState, updaters) => void)
and then in the root component, every tick:
globalEventQueue.map((eventAction) => eventAction(
* update(delta, props, updaters) { // edits this.state and sends parent state updates upwards. typically looks like:
* updateState(delta, props) { // component is given new props which should be stored; also edits this.state . typically looks like:
* TL;DR the flow is:
* constructor, initialize state based on const args and initial props
* immediately after the constructor, first render again with initial props
* onX => enqueues event actions onto global event queue; event actions can modify state and parent state
* ticker tick goes off
* in root component, global event queue fires off callbacks, editing state everywhere
* shouldUpdateState fires recursively, conditioning:
* updateState fires recursively, causing state updates to flow down as props to children // should prevState/prevProps be kept here and in the previous method??
* shouldRender fires recursively, conditioning:
* render fires recursively, redrawing everything based on props & state. if new children are constructed, they get constructed, rendered, and then the parent continues its own rendering.
* componentDidMount for newly created components. actions here are put onto global event queue and delayed until next tick.
* componentDidUpdate fires recursively, all actions here are put onto global event queue and delayed until next tick
* componentDidUnmount ??
import * as Pixi from "pixi.js";
import { KeyboardState } from "../lib/pixi/keyboard";
import { FpsTracker } from "../lib/util/fpsTracker";
import { registerDraggable } from "../lib/pixi/DraggableHelper";
import createBunnyExample from "./BunnyExample";
import { Chunk, RenderedChunk } from "./Chunk";
import { Vector2 } from "../lib/util/geometry/vector2";
import { ZLevel } from "./ZLevel";
import { RenderedZLevel } from "./RenderedZLevel";
import { HashMap } from "../lib/util/data_structures/hash";
import { GameState, PointNodeRef } from "../data/GameState";
import { generatePointNodeTexture } from "./textures/PointNodeTexture";
import { Reticle } from "./Reticle";
import { ZLevelGenFactory } from "../dataFactory/WorldGenStateFactory";
import { assertOnlyCalledOnce, DeepReadonly, Lazy } from "../lib/util/misc";
type RootApplicationState = {}
type RootApplicationProps = {}
export class RootApplication {
state!: RootApplicationState;
props!: DeepReadonly<RootApplicationProps>;
/* children */
// Contains HUD, and other entities that don't move when game camera moves
public fixedCameraStage!: Pixi.Container;
// Contains game entities that move when game camera pans/zooms. Highly encouraged to have further subdivions.
public actionStage!: Pixi.Container;
// Contains a few entities that doesn't move when game camera moves, but located behind action stage entities, e.g. static backgrounds
public backdropStage!: Pixi.Container;
public keyboard!: KeyboardState;
public fpsTracker!: FpsTracker;
public static appSizeFromWindowSize(window: Vector2): Vector2 {
return new Vector2({
x: Math.min(1280, window.x),
y: Math.min(720, window.y),
});
}
/**
* Need to provide config to set up the pixi canvas
*/
constructor(config?: Partial<Config>) {
assertOnlyCalledOnce("Base application constructor");
this.config = Object.assign({}, defaultConfig, config);
const appSize = BaseApplication.appSizeFromWindowSize(new Vector2(
this.config.originalWindowWidth, this.config.originalWindowHeight
));
this.originalAppWidth = appSize.x;
this.originalAppHeight = appSize.y;
this.app = new Pixi.Application({
width: this.originalAppWidth, // both are ignored - see resize() below
height: this.originalAppHeight,
antialias: true, // both about the same FPS, i get around 30 fps on 1600 x 900
transparent: true, // true -> better fps?? https://github.com/pixijs/pixi.js/issues/5580
resolution: window.devicePixelRatio || 1, // lower -> more FPS but uglier
// resolution: 0.5,
// resolution: 2,
autoDensity: true,
powerPreference: "low-power", // the only valid one for webgl
backgroundColor: 0xffffff, // immaterial - we recommend setting color in backdrop graphics
});
this.stage = this.app.stage;
this.stage.sortableChildren = true;
// this.fixedCameraStage = new Pixi.Sprite();
// this.fixedCameraStage.zIndex = 1;
// this.fixedCameraStage.sortableChildren = true;
// this.stage.addChild(this.fixedCameraStage);
// this.actionStage = new Pixi.Sprite();
// this.actionStage.zIndex = 0;
// this.actionStage.sortableChildren = true;
// this.stage.addChild(this.actionStage);
// this.backdropStage = new Pixi.Sprite();
// this.backdropStage.zIndex = -1;
// this.backdropStage.sortableChildren = true;
// this.stage.addChild(this.backdropStage);
// this.keyboard = new KeyboardState();
// this.app.ticker.add(() => {
// this.keyboard.update();
// })
// this.fpsTracker = new FpsTracker();
// this.app.ticker.add((delta) => {
// // delta should be approximately equal to 1
// this.fpsTracker.tick(delta);
// })
let pointNodeTexture = new Lazy(() => generatePointNodeTexture(this.app.renderer));
// test
// createBunnyExample({ parent: this.actionStage, ticker: this.app.ticker, x: this.app.screen.width / 2, y: this.app.screen.height / 2 });
this.app.ticker.add((delta) => this.baseGameLoop(delta));
}
/**
* Please only call once!!
* Usage: const container = useRef<HTMLDivElement>(null); useEffect(() => { application.register(container.current!); }, []);
*/
public register(curr: HTMLDivElement) {
curr.appendChild(this.app.view);
}
// render(props: {
// gameState: DeepReadonly<GameState>,
// pixiComponentState: PixiComponentState,
// }) { }
baseGameLoop(delta: number) {
}
}