added resource culling and resource saving in the ResourceManager
This commit is contained in:
		
							parent
							
								
									460d0e3643
								
							
						
					
					
						commit
						b085612908
					
				|  | @ -1,6 +1,5 @@ | |||
| import AI from "../DataTypes/Interfaces/AI"; | ||||
| import StateMachine from "../DataTypes/State/StateMachine"; | ||||
| import GameEvent from "../Events/GameEvent"; | ||||
| import GameNode from "../Nodes/GameNode"; | ||||
| 
 | ||||
| /** | ||||
|  | @ -17,6 +16,7 @@ export default class StateMachineAI extends StateMachine implements AI { | |||
| 	destroy(){ | ||||
| 		// Get rid of our reference to the owner
 | ||||
| 		delete this.owner; | ||||
| 		this.receiver.destroy(); | ||||
| 	} | ||||
| 
 | ||||
| 	// @implemented
 | ||||
|  |  | |||
|  | @ -13,6 +13,9 @@ export default interface Physical { | |||
|     /** Represents whether the object is moving or not. */ | ||||
|     moving: boolean; | ||||
| 
 | ||||
|     /** Represent whether the object is frozen from moving or not. */ | ||||
|     frozen: boolean; | ||||
| 
 | ||||
|     /** Represents whether the object is on the ground or not. */ | ||||
|     onGround: boolean; | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ export default class Queue<T> implements Collection { | |||
|      */ | ||||
|     enqueue(item: T): void{ | ||||
|         if((this.tail + 1) % this.MAX_ELEMENTS === this.head){ | ||||
|             throw "Queue full - cannot add element" | ||||
|             throw new Error("Queue full - cannot add element"); | ||||
|         } | ||||
| 
 | ||||
|         this.size += 1; | ||||
|  | @ -51,7 +51,7 @@ export default class Queue<T> implements Collection { | |||
|      */ | ||||
|     dequeue(): T { | ||||
|         if(this.head === this.tail){ | ||||
|             throw "Queue empty - cannot remove element" | ||||
|             throw new Error("Queue empty - cannot remove element"); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import Collection from "./Collection"; | |||
|  */ | ||||
| export default class Stack<T> implements Collection { | ||||
|     /** The maximum number of elements in the Stack */ | ||||
|     private readonly MAX_ELEMENTS: number; | ||||
|     private MAX_ELEMENTS: number; | ||||
|      | ||||
|     /** The internal representation of the stack */ | ||||
|     private stack: Array<T>; | ||||
|  |  | |||
|  | @ -81,7 +81,7 @@ export default class EventQueue { | |||
|     unsubscribe(receiver: Receiver, ...events: Array<string>): void { | ||||
|         this.receivers.forEach(eventName => { | ||||
|             // If keys were provided, only continue if this key is one of them
 | ||||
|             if(events !== undefined && events.indexOf(eventName) === -1) return; | ||||
|             if(events.length > 0 && events.indexOf(eventName) === -1) return; | ||||
| 
 | ||||
|             // Find the index of our receiver for this key
 | ||||
|             let index = this.receivers.get(eventName).indexOf(receiver); | ||||
|  |  | |||
|  | @ -36,7 +36,12 @@ export default class Receiver { | |||
| 	 * @param event The event to receive | ||||
| 	 */ | ||||
| 	receive(event: GameEvent): void { | ||||
| 		try{ | ||||
| 		this.q.enqueue(event); | ||||
| 		} catch(e){ | ||||
| 			console.warn("Receiver overflow for event " + event.toString()); | ||||
| 			throw e; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ export default abstract class GameNode implements Positioned, Unique, Updateable | |||
| 	/*---------- PHYSICAL ----------*/ | ||||
| 	hasPhysics: boolean = false; | ||||
| 	moving: boolean = false; | ||||
| 	frozen: boolean = false; | ||||
| 	onGround: boolean = false; | ||||
| 	onWall: boolean = false; | ||||
| 	onCeiling: boolean = false; | ||||
|  | @ -151,11 +152,13 @@ export default abstract class GameNode implements Positioned, Unique, Updateable | |||
|      * @param velocity The velocity with which to move the object. | ||||
|      */ | ||||
| 	move(velocity: Vec2): void { | ||||
| 		if(this.frozen) return; | ||||
| 		this.moving = true; | ||||
| 		this._velocity = velocity; | ||||
| 	}; | ||||
| 
 | ||||
| 	moveOnPath(speed: number, path: NavigationPath): void { | ||||
| 		if(this.frozen) return; | ||||
| 		this.path = path; | ||||
| 		let dir = path.getMoveDirection(this); | ||||
| 		this.moving = true; | ||||
|  | @ -230,6 +233,9 @@ export default abstract class GameNode implements Positioned, Unique, Updateable | |||
| 
 | ||||
| 	/** Removes this object from the physics system */ | ||||
|     removePhysics(): void { | ||||
| 		// Remove this from the physics manager
 | ||||
| 		this.scene.getPhysicsManager().deregisterObject(this); | ||||
| 
 | ||||
| 		// Nullify all physics fields
 | ||||
| 		this.hasPhysics = false; | ||||
| 		this.moving = false; | ||||
|  | @ -250,9 +256,16 @@ export default abstract class GameNode implements Positioned, Unique, Updateable | |||
| 		this.collisionShape = null; | ||||
| 		this.colliderOffset = Vec2.ZERO; | ||||
| 		this.sweptRect = null; | ||||
| 	} | ||||
| 
 | ||||
| 		// Remove this from the physics manager
 | ||||
| 		this.scene.getPhysicsManager().deregisterObject(this); | ||||
| 	/** Disables physics movement for this node */ | ||||
| 	freeze(): void { | ||||
| 		this.frozen = true; | ||||
| 	} | ||||
| 
 | ||||
| 	/** Reenables physics movement for this node */ | ||||
| 	unfreeze(): void { | ||||
| 		this.frozen = false; | ||||
| 	} | ||||
| 
 | ||||
|     /** Prevents this object from participating in all collisions and triggers. It can still move. */ | ||||
|  |  | |||
|  | @ -90,7 +90,6 @@ export default abstract class UIElement extends CanvasNode { | |||
| 				} | ||||
| 				if(this.onClickEventId !== null){ | ||||
| 					let data = {}; | ||||
| 					console.log("Click event: " + this.onClickEventId) | ||||
| 					this.emitter.fireEvent(this.onClickEventId, data); | ||||
| 				} | ||||
| 			} | ||||
|  |  | |||
|  | @ -72,7 +72,6 @@ export default class BasicPhysicsManager extends PhysicsManager { | |||
| 	 * @param options A record of options | ||||
| 	 */ | ||||
| 	protected parseOptions(options: Record<string, any>): void { | ||||
| 		console.log("Parsing physics options: ", options); | ||||
| 		if(options.groupNames !== undefined && options.collisions !== undefined){ | ||||
| 			for(let i = 0; i < options.groupNames.length; i++){ | ||||
| 				let group = options.groupNames[i]; | ||||
|  | @ -108,6 +107,7 @@ export default class BasicPhysicsManager extends PhysicsManager { | |||
| 
 | ||||
| 	// @override
 | ||||
| 	deregisterObject(node: Physical): void { | ||||
| 		console.log("Deregistering physics object"); | ||||
| 		if(node.isStatic){ | ||||
| 			// Remove the node from the static list
 | ||||
| 			const index = this.staticNodes.indexOf(node); | ||||
|  |  | |||
|  | @ -33,6 +33,10 @@ export default abstract class PhysicsManager implements Updateable { | |||
| 		this.groupNames = new Array(); | ||||
| 	} | ||||
| 
 | ||||
| 	destroy(): void { | ||||
| 		this.receiver.destroy(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Registers a gamenode with this physics manager | ||||
| 	 * @param object The object to register | ||||
|  |  | |||
|  | @ -81,7 +81,6 @@ export default class ResourceManager { | |||
|     private loadonly_gl_ShaderProgramsToLoad: number; | ||||
|     private loadonly_gl_ShaderLoadingQueue: Queue<KeyPath_Shader>; | ||||
| 
 | ||||
|     private gl_DefaultShaderPrograms: Map<WebGLProgramType>; | ||||
|     private gl_ShaderPrograms: Map<WebGLProgramType>; | ||||
| 
 | ||||
|     private gl_Textures: Map<number>; | ||||
|  | @ -90,6 +89,13 @@ export default class ResourceManager { | |||
| 
 | ||||
|     private gl: WebGLRenderingContext; | ||||
| 
 | ||||
|     /* ########## UNLOADING AND EXCLUSION LIST ########## */ | ||||
|     /** A list of resources that will be unloaded at the end of the current scene */ | ||||
|     private resourcesToUnload: Array<ResourceReference>; | ||||
| 
 | ||||
|     /** A list of resources to keep until further notice */ | ||||
|     private resourcesToKeep: Array<ResourceReference>; | ||||
| 
 | ||||
|     private constructor(){ | ||||
|         this.loading = false; | ||||
|         this.justLoaded = false; | ||||
|  | @ -123,14 +129,17 @@ export default class ResourceManager { | |||
|         this.loadonly_gl_ShaderProgramsToLoad = 0; | ||||
|         this.loadonly_gl_ShaderLoadingQueue = new Queue(); | ||||
| 
 | ||||
|         this.gl_DefaultShaderPrograms = new Map(); | ||||
|         this.gl_ShaderPrograms = new Map(); | ||||
| 
 | ||||
|         this.gl_Textures = new Map(); | ||||
|         this.gl_NextTextureID = 0; | ||||
|         this.gl_Buffers = new Map(); | ||||
| 
 | ||||
|         this.resourcesToUnload = new Array(); | ||||
|         this.resourcesToKeep = new Array(); | ||||
|     }; | ||||
| 
 | ||||
|     /* ######################################## SINGLETON ########################################*/ | ||||
|     /** | ||||
|      * Returns the current instance of this class or a new instance if none exist | ||||
|      * @returns The resource manager | ||||
|  | @ -143,6 +152,7 @@ export default class ResourceManager { | |||
|         return this.instance; | ||||
|     } | ||||
| 
 | ||||
|     /* ######################################## PUBLIC FUNCTION ########################################*/ | ||||
|     /** | ||||
|      * Activates or deactivates the use of WebGL | ||||
|      * @param flag True if WebGL should be used, false otherwise | ||||
|  | @ -165,6 +175,14 @@ export default class ResourceManager { | |||
|         this.loadonly_imageLoadingQueue.enqueue({key: key, path: path}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|     public keepImage(key: string): void { | ||||
|         this.keepResource(key, ResourceType.IMAGE); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves a loaded image | ||||
|      * @param key The key of the loaded image | ||||
|  | @ -187,6 +205,14 @@ export default class ResourceManager { | |||
|         this.loadonly_spritesheetLoadingQueue.enqueue({key: key, path: path}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|     public keepSpritesheet(key: string): void { | ||||
|         this.keepResource(key, ResourceType.SPRITESHEET); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves a loaded spritesheet | ||||
|      * @param key The key of the spritesheet to load | ||||
|  | @ -205,6 +231,14 @@ export default class ResourceManager { | |||
|         this.loadonly_audioLoadingQueue.enqueue({key: key, path: path}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|      public keepAudio(key: string): void { | ||||
|         this.keepResource(key, ResourceType.AUDIO); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves a loaded audio file | ||||
|      * @param key The key of the audio file to load | ||||
|  | @ -223,6 +257,14 @@ export default class ResourceManager { | |||
|         this.loadonly_tilemapLoadingQueue.enqueue({key: key, path: path}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|      public keepTilemap(key: string): void { | ||||
|         this.keepResource(key, ResourceType.TILEMAP); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retreives a loaded tilemap | ||||
|      * @param key The key of the loaded tilemap | ||||
|  | @ -241,6 +283,14 @@ export default class ResourceManager { | |||
|         this.loadonly_jsonLoadingQueue.enqueue({key: key, path: path}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|      public keepObject(key: string): void { | ||||
|         this.keepResource(key, ResourceType.JSON); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retreives a loaded object | ||||
|      * @param key The key of the loaded object | ||||
|  | @ -250,6 +300,7 @@ export default class ResourceManager { | |||
|         return this.jsonObjects.get(key); | ||||
|     } | ||||
| 
 | ||||
|     /* ######################################## LOAD FUNCTION ########################################*/ | ||||
|     /** | ||||
|      * Loads all resources currently in the queue | ||||
|      * @param callback The function to cal when the resources are finished loading | ||||
|  | @ -293,6 +344,21 @@ export default class ResourceManager { | |||
|         callback(); | ||||
|     } | ||||
| 
 | ||||
|     /* ######################################## UNLOAD FUNCTION ########################################*/ | ||||
|      | ||||
|     private keepResource(key: string, type: ResourceType): void { | ||||
|         console.log("Keep resource..."); | ||||
|         for(let i = 0; i < this.resourcesToUnload.length; i++){ | ||||
|             let resource = this.resourcesToUnload[i]; | ||||
|             if(resource.key === key && resource.resourceType === type){ | ||||
|                 console.log("Found resource " + key + " of type " + type + ". Keeping."); | ||||
|                 let resourceToMove = this.resourcesToUnload.splice(i, 1); | ||||
|                 this.resourcesToKeep.push(...resourceToMove); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Deletes references to all resources in the resource manager | ||||
|      */ | ||||
|  | @ -300,29 +366,46 @@ export default class ResourceManager { | |||
|         this.loading = false; | ||||
|         this.justLoaded = false; | ||||
| 
 | ||||
|         this.loadonly_imagesLoaded = 0; | ||||
|         this.loadonly_imagesToLoad = 0; | ||||
|         this.images.clear(); | ||||
| 
 | ||||
|         this.loadonly_spritesheetsLoaded = 0; | ||||
|         this.loadonly_spritesheetsToLoad = 0; | ||||
|         this.spritesheets.clear(); | ||||
| 
 | ||||
|         this.loadonly_tilemapsLoaded = 0; | ||||
|         this.loadonly_tilemapsToLoad = 0; | ||||
|         this.tilemaps.clear(); | ||||
| 
 | ||||
|         this.loadonly_audioLoaded = 0; | ||||
|         this.loadonly_audioToLoad = 0; | ||||
|         this.audioBuffers.clear(); | ||||
| 
 | ||||
|         // WebGL
 | ||||
|         // Delete all programs through webGL
 | ||||
|         this.gl_ShaderPrograms.forEach(key => this.gl_ShaderPrograms.get(key).delete(this.gl)); | ||||
|         this.gl_ShaderPrograms.clear(); | ||||
|         this.gl_Textures.clear(); | ||||
|         for(let resource of this.resourcesToUnload){ | ||||
|             // Unload the resource
 | ||||
|             this.unloadResource(resource); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private unloadResource(resource: ResourceReference): void { | ||||
|         // Delete the resource itself
 | ||||
|         switch(resource.resourceType){ | ||||
|             case ResourceType.IMAGE: | ||||
|                 this.images.delete(resource.key); | ||||
|                 if(this.gl_WebGLActive){ | ||||
|                     this.gl_Textures.delete(resource.key); | ||||
|                 } | ||||
|                 break; | ||||
|             case ResourceType.TILEMAP: | ||||
|                 this.tilemaps.delete(resource.key); | ||||
|                 break; | ||||
|             case ResourceType.SPRITESHEET: | ||||
|                 this.spritesheets.delete(resource.key); | ||||
|                 break; | ||||
|             case ResourceType.AUDIO: | ||||
|                 this.audioBuffers.delete(resource.key); | ||||
|                 break; | ||||
|             case ResourceType.JSON: | ||||
|                 this.jsonObjects.delete(resource.key); | ||||
|                 break; | ||||
|             case ResourceType.SHADER: | ||||
|                 this.gl_ShaderPrograms.get(resource.key).delete(this.gl); | ||||
|                 this.gl_ShaderPrograms.delete(resource.key); | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         // Delete any dependencies
 | ||||
|         for(let dependency of resource.dependencies){ | ||||
|             this.unloadResource(dependency); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* ######################################## WORK FUNCTIONS ########################################*/ | ||||
|     /** | ||||
|      * Loads all tilemaps currently in the tilemap loading queue | ||||
|      * @param onFinishLoading The function to call when loading is complete | ||||
|  | @ -355,22 +438,32 @@ export default class ResourceManager { | |||
|              | ||||
|             // We can parse the object later - it's much faster than loading
 | ||||
|             this.tilemaps.add(key, tilemapObject); | ||||
|             let resource = new ResourceReference(key, ResourceType.TILEMAP); | ||||
| 
 | ||||
|             // Grab the tileset images we need to load and add them to the imageloading queue
 | ||||
|             for(let tileset of tilemapObject.tilesets){ | ||||
|                 if(tileset.image){ | ||||
|                     let key = tileset.image; | ||||
|                     let path = StringUtils.getPathFromFilePath(pathToTilemapJSON) + key; | ||||
|                     this.loadonly_imageLoadingQueue.enqueue({key: key, path: path}); | ||||
|                     this.loadonly_imageLoadingQueue.enqueue({key: key, path: path, isDependency: true}); | ||||
| 
 | ||||
|                     // Add this image as a dependency to the tilemap
 | ||||
|                     resource.addDependency(new ResourceReference(key, ResourceType.IMAGE)); | ||||
|                 } else if(tileset.tiles){ | ||||
|                     for(let tile of tileset.tiles){ | ||||
|                         let key = tile.image; | ||||
|                         let path = StringUtils.getPathFromFilePath(pathToTilemapJSON) + key; | ||||
|                         this.loadonly_imageLoadingQueue.enqueue({key: key, path: path}); | ||||
|                         this.loadonly_imageLoadingQueue.enqueue({key: key, path: path, isDependency: true}); | ||||
| 
 | ||||
|                         // Add this image as a dependency to the tilemap
 | ||||
|                         resource.addDependency(new ResourceReference(key, ResourceType.IMAGE)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // Add the resource reference to the list of resource to unload
 | ||||
|             this.resourcesToUnload.push(resource); | ||||
| 
 | ||||
|             // Finish loading
 | ||||
|             this.finishLoadingTilemap(callbackIfLast); | ||||
|         }); | ||||
|  | @ -422,9 +515,14 @@ export default class ResourceManager { | |||
|             // We can parse the object later - it's much faster than loading
 | ||||
|             this.spritesheets.add(key, spritesheet); | ||||
| 
 | ||||
|             let resource = new ResourceReference(key, ResourceType.SPRITESHEET); | ||||
| 
 | ||||
|             // Grab the image we need to load and add it to the imageloading queue
 | ||||
|             let path = StringUtils.getPathFromFilePath(pathToSpritesheetJSON) + spritesheet.spriteSheetImage; | ||||
|             this.loadonly_imageLoadingQueue.enqueue({key: spritesheet.name, path: path}); | ||||
|             this.loadonly_imageLoadingQueue.enqueue({key: spritesheet.name, path: path, isDependency: true}); | ||||
| 
 | ||||
|             resource.addDependency(new ResourceReference(spritesheet.name, ResourceType.IMAGE)); | ||||
|             this.resourcesToUnload.push(resource); | ||||
| 
 | ||||
|             // Finish loading
 | ||||
|             this.finishLoadingSpritesheet(callbackIfLast); | ||||
|  | @ -460,7 +558,7 @@ export default class ResourceManager { | |||
| 
 | ||||
|         while(this.loadonly_imageLoadingQueue.hasItems()){ | ||||
|             let image = this.loadonly_imageLoadingQueue.dequeue(); | ||||
|             this.loadImage(image.key, image.path, onFinishLoading); | ||||
|             this.loadImage(image.key, image.path, image.isDependency, onFinishLoading); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -470,13 +568,18 @@ export default class ResourceManager { | |||
|      * @param path The path to the image to load | ||||
|      * @param callbackIfLast The function to call if this is the last image | ||||
|      */ | ||||
|     public loadImage(key: string, path: string, callbackIfLast: Function): void { | ||||
|     public loadImage(key: string, path: string, isDependency: boolean, callbackIfLast: Function): void { | ||||
|         var image = new Image(); | ||||
| 
 | ||||
|         image.onload = () => { | ||||
|             // Add to loaded images
 | ||||
|             this.images.add(key, image); | ||||
| 
 | ||||
|             // If not a dependency, push it to the unload list. Otherwise it's managed by something else
 | ||||
|             if(!isDependency){ | ||||
|                 this.resourcesToUnload.push(new ResourceReference(key, ResourceType.IMAGE)); | ||||
|             } | ||||
| 
 | ||||
|             // If WebGL is active, create a texture
 | ||||
|             if(this.gl_WebGLActive){ | ||||
|                 this.createWebGLTexture(key, image); | ||||
|  | @ -539,6 +642,7 @@ export default class ResourceManager { | |||
|             audioCtx.decodeAudioData(request.response, (buffer) => { | ||||
|                 // Add to list of audio buffers
 | ||||
|                 this.audioBuffers.add(key, buffer); | ||||
|                 this.resourcesToUnload.push(new ResourceReference(key, ResourceType.AUDIO)); | ||||
| 
 | ||||
|                 // Finish loading sound
 | ||||
|                 this.finishLoadingAudio(callbackIfLast); | ||||
|  | @ -592,6 +696,9 @@ export default class ResourceManager { | |||
|         this.loadTextFile(path, (fileText: string) => { | ||||
|             let obj = JSON.parse(fileText); | ||||
|             this.jsonObjects.add(key, obj); | ||||
| 
 | ||||
|             this.resourcesToUnload.push(new ResourceReference(key, ResourceType.JSON)); | ||||
| 
 | ||||
|             this.finishLoadingObject(callbackIfLast); | ||||
|         }); | ||||
|     } | ||||
|  | @ -706,6 +813,14 @@ export default class ResourceManager { | |||
|         this.loadonly_gl_ShaderLoadingQueue.enqueue(paths); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Tells the resource manager to keep this resource | ||||
|      * @param key The key of the resource | ||||
|      */ | ||||
|      public keepShader(key: string): void { | ||||
|         this.keepResource(key, ResourceType.IMAGE); | ||||
|     } | ||||
| 
 | ||||
|     private gl_LoadShadersFromQueue(onFinishLoading: Function): void { | ||||
|         this.loadonly_gl_ShaderProgramsToLoad = this.loadonly_gl_ShaderLoadingQueue.getSize(); | ||||
|         this.loadonly_gl_ShaderProgramsLoaded = 0; | ||||
|  | @ -741,6 +856,8 @@ export default class ResourceManager { | |||
|                 // Add to our map
 | ||||
|                 this.gl_ShaderPrograms.add(key, programWrapper); | ||||
| 
 | ||||
|                 this.resourcesToUnload.push(new ResourceReference(key, ResourceType.SHADER)); | ||||
| 
 | ||||
|                 // Finish loading
 | ||||
|                 this.gl_FinishLoadingShader(callbackIfLast); | ||||
|             }); | ||||
|  | @ -769,7 +886,7 @@ export default class ResourceManager { | |||
|         const program = this.gl.createProgram(); | ||||
|         if(!program) { | ||||
|             // Error creating
 | ||||
|             console.log("Failed to create program"); | ||||
|             console.warn("Failed to create program"); | ||||
|             return null; | ||||
|         } | ||||
|      | ||||
|  | @ -782,7 +899,7 @@ export default class ResourceManager { | |||
|         if(!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)){ | ||||
|             // Error linking
 | ||||
|             const error = this.gl.getProgramInfoLog(program); | ||||
|             console.log("Failed to link program: " + error); | ||||
|             console.warn("Failed to link program: " + error); | ||||
|      | ||||
|             // Clean up
 | ||||
|             this.gl.deleteProgram(program); | ||||
|  | @ -810,7 +927,7 @@ export default class ResourceManager { | |||
|      | ||||
|         // If we couldn't create the shader, error
 | ||||
|         if(shader === null){ | ||||
|             console.log("Unable to create shader"); | ||||
|             console.warn("Unable to create shader"); | ||||
|             return null; | ||||
|         } | ||||
|      | ||||
|  | @ -822,7 +939,7 @@ export default class ResourceManager { | |||
|         if(!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)){ | ||||
|             // Not compiled - error
 | ||||
|             const error = this.gl.getShaderInfoLog(shader); | ||||
|             console.log("Failed to compile shader: " + error); | ||||
|             console.warn("Failed to compile shader: " + error); | ||||
|      | ||||
|             // Clean up
 | ||||
|             this.gl.deleteShader(shader); | ||||
|  | @ -871,9 +988,44 @@ export default class ResourceManager { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A class representing a reference to a resource. | ||||
|  * This is used for the exemption list to assure assets and their dependencies don't get | ||||
|  * destroyed if they are still needed. | ||||
|  */ | ||||
| class ResourceReference { | ||||
|     key: string; | ||||
|     resourceType: ResourceType; | ||||
|     dependencies: Array<ResourceReference>; | ||||
| 
 | ||||
|     constructor(key: string, resourceType: ResourceType){ | ||||
|         this.key = key; | ||||
|         this.resourceType = resourceType; | ||||
|         this. dependencies = new Array(); | ||||
|     } | ||||
| 
 | ||||
|     addDependency(resource: ResourceReference): void { | ||||
|         this.dependencies.push(resource); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| enum ResourceType { | ||||
|     IMAGE = "IMAGE", | ||||
|     TILEMAP = "TILEMAP", | ||||
|     SPRITESHEET = "SPRITESHEET", | ||||
|     AUDIO = "AUDIO", | ||||
|     JSON = "JSON", | ||||
|     SHADER = "SHADER" | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A pair representing a key and the path of the resource to load | ||||
|  */ | ||||
| class KeyPathPair { | ||||
|     key: string; | ||||
|     path: string; | ||||
|     isDependency?: boolean = false; | ||||
| } | ||||
| 
 | ||||
| class KeyPath_Shader { | ||||
|  |  | |||
|  | @ -90,7 +90,6 @@ export default class TilemapFactory { | |||
|             } | ||||
| 
 | ||||
|             if(isParallaxLayer){ | ||||
|                 console.log("Adding parallax layer: " + layer.name) | ||||
|                 sceneLayer = this.scene.addParallaxLayer(layer.name, new Vec2(1, 1), depth); | ||||
|             } else { | ||||
|                 sceneLayer = this.scene.addLayer(layer.name, depth); | ||||
|  |  | |||
|  | @ -81,9 +81,12 @@ export default class Scene implements Updateable { | |||
|     /** 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 */ | ||||
|     /** An interface that allows the loading of different files for use in the scene. An alias for resourceManager */ | ||||
|     public load: ResourceManager; | ||||
| 
 | ||||
|     /** An interface that allows the loading and unloading of different files for use in the scene */ | ||||
|     public resourceManager: ResourceManager; | ||||
| 
 | ||||
|     /** The configuration options for this scene */ | ||||
|     public sceneOptions: SceneOptions; | ||||
| 
 | ||||
|  | @ -120,7 +123,8 @@ export default class Scene implements Updateable { | |||
| 
 | ||||
|         this.add = new FactoryManager(this, this.tilemaps); | ||||
| 
 | ||||
|         this.load = ResourceManager.getInstance(); | ||||
|         this.load = ResourceManager.getInstance() | ||||
|         this.resourceManager = this.load; | ||||
| 
 | ||||
|         // Get the timer manager and clear any existing timers
 | ||||
|         TimerManager.getInstance().clearTimers(); | ||||
|  | @ -132,9 +136,6 @@ export default class Scene implements Updateable { | |||
|     /** 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 {} | ||||
| 
 | ||||
|  | @ -144,6 +145,9 @@ export default class Scene implements Updateable { | |||
|      */ | ||||
|     updateScene(deltaT: number): void {} | ||||
| 
 | ||||
|     /** A lifecycle method that gets called on scene destruction. Specify which files you no longer need for garbage collection. */ | ||||
|     unloadScene(): void {} | ||||
| 
 | ||||
|     update(deltaT: number): void { | ||||
|         this.updateScene(deltaT); | ||||
| 
 | ||||
|  | @ -232,6 +236,12 @@ export default class Scene implements Updateable { | |||
|             node.destroy(); | ||||
|         } | ||||
| 
 | ||||
|         for(let tilemap of this.tilemaps){ | ||||
|             tilemap.destroy(); | ||||
|         } | ||||
| 
 | ||||
|         this.receiver.destroy(); | ||||
| 
 | ||||
|         delete this.sceneGraph; | ||||
|         delete this.physicsManager; | ||||
|         delete this.navManager; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import Scene from "./Scene"; | |||
| import ResourceManager from "../ResourceManager/ResourceManager"; | ||||
| import Viewport from "../SceneGraph/Viewport"; | ||||
| import RenderingManager from "../Rendering/RenderingManager"; | ||||
| import MemoryUtils from "../Utils/MemoryUtils"; | ||||
| 
 | ||||
| /** | ||||
|  * The SceneManager acts as an interface to create Scenes, and handles the lifecycle methods of Scenes. | ||||
|  | @ -25,6 +26,7 @@ export default class SceneManager { | |||
| 
 | ||||
| 	/** For consistency, only change scenes at the beginning of the update cycle */ | ||||
| 	protected pendingScene: Scene; | ||||
| 	protected pendingSceneInit: Record<string, any>; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Creates a new SceneManager | ||||
|  | @ -37,6 +39,7 @@ export default class SceneManager { | |||
| 		this.viewport = viewport; | ||||
| 		this.renderingManager = renderingManager; | ||||
| 		this.idCounter = 0; | ||||
| 		this.pendingScene = null; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
|  | @ -46,31 +49,47 @@ export default class SceneManager { | |||
| 	 * @param init An object to pass to the init function of the new scene | ||||
| 	 */ | ||||
| 	public changeToScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void { | ||||
| 		console.log("Creating the new scene - change is pending until next update"); | ||||
| 		this.pendingScene = new constr(this.viewport, this, this.renderingManager, options); | ||||
| 		this.pendingSceneInit = init; | ||||
| 	} | ||||
| 
 | ||||
| 	protected doSceneChange(){ | ||||
| 		console.log("Performing scene change"); | ||||
| 		this.viewport.setCenter(this.viewport.getHalfSize().x, this.viewport.getHalfSize().y); | ||||
| 		 | ||||
| 		let scene = new constr(this.viewport, this, this.renderingManager, options); | ||||
| 		 | ||||
| 		if(this.currentScene){ | ||||
| 			console.log("Destroying Old Scene"); | ||||
| 			console.log("Unloading old scene") | ||||
| 			this.currentScene.unloadScene(); | ||||
| 
 | ||||
| 			console.log("Destroying old scene"); | ||||
| 			this.currentScene.destroy(); | ||||
| 		} | ||||
| 
 | ||||
| 		this.currentScene = scene; | ||||
| 		console.log("Unloading old resources..."); | ||||
| 		this.resourceManager.unloadAllResources(); | ||||
| 
 | ||||
| 		scene.initScene(init); | ||||
| 		// Make the pending scene the current one
 | ||||
| 		this.currentScene = this.pendingScene; | ||||
| 
 | ||||
| 		// Make the pending scene null
 | ||||
| 		this.pendingScene = null; | ||||
| 
 | ||||
| 		// Init the scene
 | ||||
| 		this.currentScene.initScene(this.pendingSceneInit); | ||||
| 
 | ||||
| 		// Enqueue all scene asset loads
 | ||||
| 		scene.loadScene(); | ||||
| 		this.currentScene.loadScene(); | ||||
| 
 | ||||
| 		// Load all assets
 | ||||
| 		console.log("Starting Scene Load"); | ||||
| 		this.resourceManager.loadResourcesFromQueue(() => { | ||||
| 			console.log("Starting Scene"); | ||||
| 			scene.startScene(); | ||||
| 			scene.setRunning(true); | ||||
| 			this.currentScene.startScene(); | ||||
| 			this.currentScene.setRunning(true); | ||||
| 		}); | ||||
| 
 | ||||
| 		this.renderingManager.setScene(scene); | ||||
| 		this.renderingManager.setScene(this.currentScene); | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
|  | @ -85,15 +104,21 @@ export default class SceneManager { | |||
| 	 * Renders the current Scene | ||||
| 	 */ | ||||
| 	public render(): void { | ||||
| 		if(this.currentScene){ | ||||
| 			this.currentScene.render(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Updates the current Scene | ||||
| 	 * @param deltaT The timestep of the Scene | ||||
| 	 */ | ||||
| 	public update(deltaT: number){ | ||||
| 		if(this.currentScene.isRunning()){ | ||||
| 		if(this.pendingScene !== null){ | ||||
| 			this.doSceneChange(); | ||||
| 		} | ||||
| 
 | ||||
| 		if(this.currentScene && this.currentScene.isRunning()){ | ||||
| 			this.currentScene.update(deltaT); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -155,7 +155,7 @@ export default class Viewport { | |||
|      * @param zoom The zoom level | ||||
|      */ | ||||
|     setZoomLevel(zoom: number): void { | ||||
|         this.view.halfSize.scale(1/zoom); | ||||
|         this.view.halfSize.copy(this.canvasSize.scaled(1/zoom/2)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ export default class AudioManager { | |||
|             this.audioCtx = new AudioContext();  | ||||
|             console.log('Web Audio API successfully loaded'); | ||||
|         } catch(e) { | ||||
|             console.log('Web Audio API is not supported in this browser');  | ||||
|             console.warn('Web Audio API is not supported in this browser');  | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										46
									
								
								src/Wolfie2D/Utils/MemoryUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/Wolfie2D/Utils/MemoryUtils.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| import Stack from "../DataTypes/Stack"; | ||||
| 
 | ||||
| export default class MemoryUtils { | ||||
|     /** | ||||
|      * Returns an approximate size in bytes of any object | ||||
|      * @param obj The object to get the size of | ||||
|      * @returns An approximation of the number of bytes in the object provided | ||||
|      */ | ||||
|     static approxSizeOf(obj: any): number { | ||||
|         let objectList = new Array<object>(); | ||||
|         let stack = new Stack<any>(10000); | ||||
|         stack.push(obj); | ||||
|         let bytes = 0; | ||||
| 
 | ||||
|         while(!stack.isEmpty()){ | ||||
|             let item = stack.pop(); | ||||
| 
 | ||||
|             // We aren't interested in counting window and document
 | ||||
|             if(item === window || item === document) continue; | ||||
| 
 | ||||
|             if((typeof item) === "boolean"){ | ||||
|                 bytes += 4; | ||||
|             } else if((typeof item) === "number"){ | ||||
|                 bytes += 8; | ||||
|             } else if((typeof item) === "string"){ | ||||
|                 bytes += item.length; | ||||
|             } else if((typeof item) === "object" && objectList.indexOf(item) === -1){ | ||||
|                 // We haven't seen this object before - log it
 | ||||
|                 objectList.push(item); | ||||
| 
 | ||||
|                 // Get the number of bytes in all of its fields
 | ||||
|                 for(let field in item){ | ||||
|                     try { | ||||
|                         stack.push(item[field]); | ||||
|                     } catch(e){ | ||||
|                         console.log("Pushing item: ", + item[field]); | ||||
|                         console.warn(`Ran out of room counting memory (Stack has size ${stack.size()}) - returning current number of bytes`); | ||||
|                         return bytes; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return bytes; | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user