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 Updateable from "../DataTypes/Interfaces/Updateable"; 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"; import SceneOptions from "./SceneOptions"; import RenderingManager from "../Rendering/RenderingManager"; import Debug from "../Debug/Debug"; /** * Scenes are the main container in the game engine. * Your main scene is the current level or menu of the game, and will contain all of the GameNodes needed. * Scenes provide an easy way to load assets, add assets to the game world, and unload assets, * and have lifecycle methods exposed for these functions. */ export default class Scene implements Updateable { /** 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; /** The renderingManager of the scene */ protected renderingManager: RenderingManager; /** 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; /** The configuration options for this scene */ public sceneOptions: SceneOptions; /** * Creates a new Scene. To add a new Scene in your game, use addScene() in @reference[SceneManager] * @param viewport The viewport of the game * @param sceneManager The SceneManager that owns this Scene * @param renderingManager The RenderingManager that will handle this Scene's rendering * @param game The instance of the GameLoop * @param options The options for Scene initialization */ constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, game: GameLoop, options: Record){ this.sceneOptions = SceneOptions.parse(options); 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.sceneOptions.physics); this.navManager = new NavigationManager(); this.aiManager = new AIManager(); this.renderingManager = renderingManager; 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 The time this frame represents */ 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); } /** * Collects renderable sets and coordinates with the RenderingManager to draw the Scene */ render(): void { // Get the visible set of nodes 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); } } }); // Send the visible set, tilemaps, and uiLayers to the renderer this.renderingManager.render(visibleSet, this.tilemaps, this.uiLayers); let nodes = this.sceneGraph.getAllNodes(); this.tilemaps.forEach(tilemap => tilemap.visible && tilemap.debugRender()); Debug.setNodes(nodes); } /** * Sets the scene as running or not * @param running True if the Scene should be running, false if not */ setRunning(running: boolean): void { this.running = running; } /** * Returns whether or not the Scene is running * @returns True if the scene is running, false otherwise */ 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 * @returns The newly created Layer */ addLayer(name: string, depth?: number): Layer { if(this.layers.has(name) || this.parallaxLayers.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 * @returns The newly created ParallaxLayer */ addParallaxLayer(name: string, parallax: Vec2, depth?: number): ParallaxLayer { if(this.layers.has(name) || this.parallaxLayers.has(name) || this.uiLayers.has(name)){ throw `Layer with name ${name} already exists`; } let layer = new ParallaxLayer(this, name, parallax); this.parallaxLayers.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 * @returns The newly created UILayer */ addUILayer(name: string): UILayer { if(this.layers.has(name) || this.parallaxLayers.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. * This can be a Layer or any of its subclasses * @param name The name of the layer * @returns The Layer found with that name */ 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 The name of the layer * @returns True if this layer is a ParallaxLayer */ isParallaxLayer(name: string): boolean { return this.parallaxLayers.has(name); } /** * Returns true if this layer is a UILayer * @param name The name of the layer * @returns True if this layer is ParallaxLayer */ 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). * This value is affected by the parallax level of the @reference[Layer] the node is on. * @param node The node to check the viewport with respect to * @returns A Vec2 containing the translation of viewport with respect to this 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 * @returns The zoom level of the viewport */ getViewScale(): number { return this.viewport.getZoomLevel(); } /** * Returns the Viewport associated with this scene * @returns The current Viewport */ getViewport(): Viewport { return this.viewport; } /** * Gets the world size of this Scene * @returns The world size in a Vec2 */ getWorldSize(): Vec2 { return this.worldSize; } /** * Gets the SceneGraph associated with this Scene * @returns The SceneGraph */ getSceneGraph(): SceneGraph { return this.sceneGraph; } /** * Gets the PhysicsManager associated with this Scene * @returns The PhysicsManager */ getPhysicsManager(): PhysicsManager { return this.physicsManager; } /** * Gets the NavigationManager associated with this Scene * @returns The NavigationManager */ getNavigationManager(): NavigationManager { return this.navManager; } /** * Gets the AIManager associated with this Scene * @returns The AIManager */ getAIManager(): AIManager { return this.aiManager; } /** * Generates an ID for a GameNode * @returns The new ID */ generateId(): number { return this.sceneManager.generateId(); } /** * Retrieves a Tilemap in this Scene * @param name The name of the Tilemap * @returns The Tilemap, if one this name exists, otherwise null */ getTilemap(name: string): Tilemap { for(let tilemap of this .tilemaps){ if(tilemap.name === name){ return tilemap; } } return null; } }