import Layer from "./Layer"; import Viewport from "../SceneGraph/Viewport"; import Vec2 from "../DataTypes/Vec2"; import SceneGraph from "../SceneGraph/SceneGraph"; import PhysicsManager from "../Physics/PhysicsManager"; import BasicPhysicsManager from "../Physics/BasicPhysicsManager"; import SceneGraphArray from "../SceneGraph/SceneGraphArray"; import FactoryManager from "./Factories/FactoryManager"; import Tilemap from "../Nodes/Tilemap"; import ResourceManager from "../ResourceManager/ResourceManager"; import GameLoop from "../Loop/GameLoop"; import SceneManager from "./SceneManager"; import Receiver from "../Events/Receiver"; import Emitter from "../Events/Emitter"; import { Renderable, Updateable } from "../DataTypes/Interfaces/Descriptors"; import NavigationManager from "../Pathfinding/NavigationManager"; import AIManager from "../AI/AIManager"; import Map from "../DataTypes/Map"; import ParallaxLayer from "./Layers/ParallaxLayer"; import UILayer from "./Layers/UILayer"; import CanvasNode from "../Nodes/CanvasNode"; import GameNode from "../Nodes/GameNode"; export default class Scene implements Updateable, Renderable { /** The size of the game world. */ protected worldSize: Vec2; /** The viewport. */ protected viewport: Viewport; /** A flag that represents whether this scene is running or not. */ protected running: boolean; /** The overall game loop. */ protected game: GameLoop; /** The manager of this scene. */ protected sceneManager: SceneManager; /** The receiver for this scene. */ protected receiver: Receiver; /** The emitter for this scene. */ protected emitter: Emitter; /** This list of tilemaps in this scene. */ protected tilemaps: Array; /** A map from layer names to the layers themselves */ protected layers: Map; /** A map from parallax layer names to the parallax layers themselves */ protected parallaxLayers: Map; /** A map from uiLayer names to the uiLayers themselves */ protected uiLayers: Map; /** The scene graph of the Scene*/ protected sceneGraph: SceneGraph; /** The physics manager of the Scene */ protected physicsManager: PhysicsManager; /** The navigation manager of the Scene */ protected navManager: NavigationManager; /** The AI manager of the Scene */ protected aiManager: AIManager; /** An interface that allows the adding of different nodes to the scene */ public add: FactoryManager; /** An interface that allows the loading of different files for use in the scene */ public load: ResourceManager; constructor(viewport: Viewport, sceneManager: SceneManager, game: GameLoop){ this.worldSize = new Vec2(500, 500); this.viewport = viewport; this.viewport.setBounds(0, 0, 2560, 1280); this.running = false; this.game = game; this.sceneManager = sceneManager; this.receiver = new Receiver(); this.emitter = new Emitter(); this.tilemaps = new Array(); this.sceneGraph = new SceneGraphArray(this.viewport, this); this.layers = new Map(); this.uiLayers = new Map(); this.parallaxLayers = new Map(); this.physicsManager = new BasicPhysicsManager(this.game.gameOptions.physics); this.navManager = new NavigationManager(); this.aiManager = new AIManager(); this.add = new FactoryManager(this, this.tilemaps); this.load = ResourceManager.getInstance(); } /** A lifecycle method that gets called when a new scene is created. Load all files you wish to access in the scene here. */ loadScene(): void {} /** A lifecycle method that gets called on scene destruction. Specify which files you no longer need for garbage collection. */ unloadScene(): void {} /** A lifecycle method called strictly after loadScene(). Create any game objects you wish to use in the scene here. */ startScene(): void {} /** * A lifecycle method called every frame of the game. This is where you can dynamically do things like add in new enemies * @param delta */ updateScene(deltaT: number): void {} update(deltaT: number): void { this.updateScene(deltaT); // Do all AI updates this.aiManager.update(deltaT); // Update all physics objects this.physicsManager.update(deltaT); // Update all canvas objects this.sceneGraph.update(deltaT); // Update all tilemaps this.tilemaps.forEach(tilemap => { if(!tilemap.getLayer().isPaused()){ tilemap.update(deltaT); } }); // Update viewport this.viewport.update(deltaT); } render(ctx: CanvasRenderingContext2D): void { // For webGL, pass a visible set to the renderer // We need to keep track of the order of things. let visibleSet = this.sceneGraph.getVisibleSet(); // Add parallax layer items to the visible set (we're rendering them all for now) this.parallaxLayers.forEach(key => { let pLayer = this.parallaxLayers.get(key); for(let node of pLayer.getItems()){ if(node instanceof CanvasNode){ visibleSet.push(node); } } }); // Sort by depth, then by visible set by y-value visibleSet.sort((a, b) => { if(a.getLayer().getDepth() === b.getLayer().getDepth()){ return (a.boundary.bottom) - (b.boundary.bottom); } else { return a.getLayer().getDepth() - b.getLayer().getDepth(); } }); // Render scene graph for demo this.sceneGraph.render(ctx); // Render tilemaps this.tilemaps.forEach(tilemap => { tilemap.render(ctx); }); // Render visible set visibleSet.forEach(node => node.visible ? node.render(ctx) : ""); // Debug render the physicsManager this.physicsManager.debug_render(ctx); // Render the uiLayers this.uiLayers.forEach(key => this.uiLayers.get(key).getItems().forEach(node => (node).render(ctx))); } setRunning(running: boolean): void { this.running = running; } isRunning(): boolean { return this.running; } /** * Adds a new layer to the scene and returns it * @param name The name of the new layer * @param depth The depth of the layer */ addLayer(name: string, depth?: number): Layer { if(this.layers.has(name) || this.uiLayers.has(name)){ throw `Layer with name ${name} already exists`; } let layer = new Layer(this, name); this.layers.add(name, layer); if(depth){ layer.setDepth(depth); } return layer; } /** * Adds a new parallax layer to this scene and returns it * @param name The name of the parallax layer * @param parallax The parallax level * @param depth The depth of the layer */ addParallaxLayer(name: string, parallax: Vec2, depth?: number): ParallaxLayer { if(this.layers.has(name) || this.uiLayers.has(name)){ throw `Layer with name ${name} already exists`; } let layer = new ParallaxLayer(this, name, parallax); this.layers.add(name, layer); if(depth){ layer.setDepth(depth); } return layer; } /** * Adds a new UILayer to the scene * @param name The name of the new UIlayer */ addUILayer(name: string): UILayer { if(this.layers.has(name) || this.uiLayers.has(name)){ throw `Layer with name ${name} already exists`; } let layer = new UILayer(this, name); this.uiLayers.add(name, layer); return layer; } /** * Gets a layer from the scene by name if it exists * @param name The name of the layer */ getLayer(name: string): Layer { if(this.layers.has(name)){ return this.layers.get(name); } else if(this.parallaxLayers.has(name)){ return this.parallaxLayers.get(name); } else if(this.uiLayers.has(name)){ return this.uiLayers.get(name); } else { throw `Requested layer ${name} does not exist.`; } } /** * Returns true if this layer is a ParallaxLayer * @param name */ isParallaxLayer(name: string): boolean { return this.parallaxLayers.has(name); } /** * Returns true if this layer is a UILayer * @param name */ isUILayer(name: string): boolean { return this.uiLayers.has(name); } /** * Returns the translation of this node with respect to camera space (due to the viewport moving); * @param node */ getViewTranslation(node: GameNode): Vec2 { let layer = node.getLayer(); if(layer instanceof ParallaxLayer || layer instanceof UILayer){ return this.viewport.getOrigin().mult(layer.parallax); } else { return this.viewport.getOrigin(); } } /** Returns the scale level of the view */ getViewScale(): number { return this.viewport.getZoomLevel(); } /** Returns the viewport associated with this scene */ getViewport(): Viewport { return this.viewport; } getWorldSize(): Vec2 { return this.worldSize; } getSceneGraph(): SceneGraph { return this.sceneGraph; } getPhysicsManager(): PhysicsManager { return this.physicsManager; } getNavigationManager(): NavigationManager { return this.navManager; } getAIManager(): AIManager { return this.aiManager; } generateId(): number { return this.sceneManager.generateId(); } }