added resource loader
This commit is contained in:
parent
bd49258d30
commit
214eba6e71
|
@ -4,13 +4,15 @@ export default class Queue<T> implements Collection{
|
|||
private readonly MAX_ELEMENTS: number;
|
||||
private q: Array<T>;
|
||||
private head: number;
|
||||
private tail: number;
|
||||
private tail: number;
|
||||
private size: number;
|
||||
|
||||
constructor(maxElements: number = 100){
|
||||
this.MAX_ELEMENTS = maxElements;
|
||||
this.q = new Array(this.MAX_ELEMENTS);
|
||||
this.head = 0;
|
||||
this.tail = 0;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
enqueue(item: T): void{
|
||||
|
@ -18,6 +20,7 @@ export default class Queue<T> implements Collection{
|
|||
throw "Queue full - cannot add element"
|
||||
}
|
||||
|
||||
this.size += 1;
|
||||
this.q[this.tail] = item;
|
||||
this.tail = (this.tail + 1) % this.MAX_ELEMENTS;
|
||||
}
|
||||
|
@ -27,6 +30,8 @@ export default class Queue<T> implements Collection{
|
|||
throw "Queue empty - cannot remove element"
|
||||
}
|
||||
|
||||
|
||||
this.size -= 1;
|
||||
let item = this.q[this.head];
|
||||
this.head = (this.head + 1) % this.MAX_ELEMENTS;
|
||||
|
||||
|
@ -47,7 +52,13 @@ export default class Queue<T> implements Collection{
|
|||
return this.head !== this.tail;
|
||||
}
|
||||
|
||||
getSize(): number {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
// TODO: This should actually delete the items in the queue instead of leaving them here
|
||||
clear(): void {
|
||||
this.size = 0;
|
||||
this.head = this.tail;
|
||||
}
|
||||
|
||||
|
|
5
src/DataTypes/Tilesets/TileLayer.ts
Normal file
5
src/DataTypes/Tilesets/TileLayer.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default class TileLayer {
|
||||
public data: Array<number>;
|
||||
public collidable: boolean;
|
||||
public visible: boolean;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import Scene from "../Scene";
|
||||
import Viewport from "../../SceneGraph/Viewport";
|
||||
import Tilemap from "../../Nodes/Tilemap"
|
||||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||
import { TiledTilemapData } from "../../DataTypes/Tilesets/TiledData";
|
||||
import StringUtils from "../../Utils/StringUtils";
|
||||
import StaticBody from "../../Physics/StaticBody";
|
||||
import Vec2 from "../../DataTypes/Vec2";
|
||||
|
||||
export default class TilemapFactory {
|
||||
private scene: Scene;
|
||||
// TODO: get the resource manager OUT of here, it does not belong
|
||||
private resourceManager: ResourceManager;
|
||||
|
||||
constructor(scene: Scene){
|
||||
this.scene = scene;
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
}
|
||||
|
||||
add<T extends Tilemap>(constr: new (...a: any) => T, path: string, ...args: any): void {
|
||||
this.resourceManager.loadTilemap(path, (tilemapData: TiledTilemapData) => {
|
||||
// For each of the layers in the tilemap, create a tilemap
|
||||
for(let layer of tilemapData.layers){
|
||||
let tilemap = new constr(tilemapData, layer);
|
||||
tilemap.init(this.scene);
|
||||
|
||||
// Add to scene
|
||||
this.scene.addTilemap(tilemap);
|
||||
|
||||
if(tilemap.isCollidable()){
|
||||
// Register in physics as a tilemap
|
||||
this.scene.physics.addTilemap(tilemap);
|
||||
}
|
||||
|
||||
// Load images for the tilesets
|
||||
tilemap.getTilesets().forEach(tileset => {
|
||||
let imagePath = StringUtils.getPathFromFilePath(path) + tileset.getImageUrl();
|
||||
this.resourceManager.loadImage(imagePath, (path: string, image: HTMLImageElement) => {
|
||||
tileset.setImage(image);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import Stack from "../DataTypes/Stack";
|
||||
import Scene from "./Scene";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
|
||||
export default class GameState{
|
||||
private sceneStack: Stack<Scene>;
|
||||
private worldSize: Vec2;
|
||||
private viewport: Viewport;
|
||||
|
||||
constructor(viewport: Viewport){
|
||||
this.sceneStack = new Stack(10);
|
||||
this.worldSize = new Vec2(1600, 1000);
|
||||
this.viewport = viewport;
|
||||
this.viewport.setBounds(0, 0, 2560, 1280);
|
||||
}
|
||||
|
||||
createScene(): Scene{
|
||||
let scene = new Scene(this.viewport, this);
|
||||
this.addScene(scene);
|
||||
return scene;
|
||||
}
|
||||
|
||||
addScene(scene: Scene): void {
|
||||
this.sceneStack.push(scene);
|
||||
}
|
||||
|
||||
removeScene(startNewTopScene: boolean = true): void {
|
||||
this.sceneStack.pop();
|
||||
this.sceneStack.peek().setPaused(!startNewTopScene);
|
||||
}
|
||||
|
||||
changeScene(scene: Scene): void {
|
||||
this.sceneStack.clear();
|
||||
this.sceneStack.push(scene);
|
||||
}
|
||||
|
||||
update(deltaT: number): void {
|
||||
this.sceneStack.forEach((scene: Scene) => scene.update(deltaT));
|
||||
}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D): void {
|
||||
this.sceneStack.forEach((scene: Scene) => scene.render(ctx));
|
||||
}
|
||||
}
|
|
@ -2,10 +2,10 @@ import EventQueue from "../Events/EventQueue";
|
|||
import InputReceiver from "../Input/InputReceiver";
|
||||
import InputHandler from "../Input/InputHandler";
|
||||
import Recorder from "../Playback/Recorder";
|
||||
import GameState from "../GameState/GameState";
|
||||
import Debug from "../Debug/Debug";
|
||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
import SceneManager from "../Scene/SceneManager";
|
||||
|
||||
export default class GameLoop{
|
||||
// The amount of time to spend on a physics step
|
||||
|
@ -37,8 +37,8 @@ export default class GameLoop{
|
|||
private inputHandler: InputHandler;
|
||||
private inputReceiver: InputReceiver;
|
||||
private recorder: Recorder;
|
||||
private gameState: GameState;
|
||||
private resourceManager: ResourceManager;
|
||||
private sceneManager: SceneManager;
|
||||
|
||||
constructor(){
|
||||
this.maxFPS = 60;
|
||||
|
@ -66,8 +66,8 @@ export default class GameLoop{
|
|||
this.inputReceiver = InputReceiver.getInstance();
|
||||
this.inputReceiver.setViewport(this.viewport);
|
||||
this.recorder = new Recorder();
|
||||
this.gameState = new GameState(this.viewport);
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
this.sceneManager = new SceneManager(this.viewport);
|
||||
}
|
||||
|
||||
private initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
||||
|
@ -83,8 +83,8 @@ export default class GameLoop{
|
|||
this.simulationTimestep = Math.floor(1000/this.maxFPS);
|
||||
}
|
||||
|
||||
getGameState(): GameState {
|
||||
return this.gameState;
|
||||
getSceneManager(): SceneManager {
|
||||
return this.sceneManager;
|
||||
}
|
||||
|
||||
private updateFrameCount(timestep: number): void {
|
||||
|
@ -149,12 +149,12 @@ export default class GameLoop{
|
|||
this.eventQueue.update(deltaT);
|
||||
this.inputReceiver.update(deltaT);
|
||||
this.recorder.update(deltaT);
|
||||
this.gameState.update(deltaT);
|
||||
this.sceneManager.update(deltaT);
|
||||
}
|
||||
|
||||
render(): void {
|
||||
this.ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
||||
this.gameState.render(this.ctx);
|
||||
this.sceneManager.render(this.ctx);
|
||||
Debug.render(this.ctx);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import GameNode from "./GameNode";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Scene from "../GameState/Scene";
|
||||
import Layer from "../Scene/Layer";
|
||||
|
||||
export default abstract class CanvasNode extends GameNode{
|
||||
protected size: Vec2;
|
||||
|
|
|
@ -4,14 +4,14 @@ import Vec2 from "../DataTypes/Vec2";
|
|||
import Map from "../DataTypes/Map";
|
||||
import Receiver from "../Events/Receiver";
|
||||
import GameEvent from "../Events/GameEvent";
|
||||
import Scene from "../GameState/Scene";
|
||||
import Layer from "../Scene/Layer";
|
||||
|
||||
export default abstract class GameNode{
|
||||
private eventQueue: EventQueue;
|
||||
protected input: InputReceiver;
|
||||
protected position: Vec2;
|
||||
private receiver: Receiver;
|
||||
protected scene: Scene;
|
||||
protected scene: Layer;
|
||||
|
||||
constructor(){
|
||||
this.eventQueue = EventQueue.getInstance();
|
||||
|
@ -19,11 +19,11 @@ export default abstract class GameNode{
|
|||
this.position = new Vec2(0, 0);
|
||||
}
|
||||
|
||||
init(scene: Scene){
|
||||
init(scene: Layer){
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
getScene(): Scene {
|
||||
getScene(): Layer {
|
||||
return this.scene;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,38 +2,28 @@ import Vec2 from "../DataTypes/Vec2";
|
|||
import GameNode from "./GameNode";
|
||||
import Tileset from "../DataTypes/Tilesets/Tileset";
|
||||
import { TiledTilemapData, TiledLayerData } from "../DataTypes/Tilesets/TiledData"
|
||||
import TileLayer from "../DataTypes/Tilesets/TileLayer";
|
||||
|
||||
/**
|
||||
* Represents one layer of tiles
|
||||
*/
|
||||
export default abstract class Tilemap extends GameNode {
|
||||
protected data: number[];
|
||||
protected collisionData: number[];
|
||||
protected tilesets: Tileset[];
|
||||
protected tilesets: Array<Tileset>;
|
||||
protected worldSize: Vec2;
|
||||
protected tileSize: Vec2;
|
||||
protected visible: boolean;
|
||||
protected collidable: boolean;
|
||||
protected scale: Vec2;
|
||||
protected layers: Array<TileLayer>;
|
||||
|
||||
// TODO: Make this no longer be specific to Tiled
|
||||
constructor(tilemapData: TiledTilemapData, layerData: TiledLayerData) {
|
||||
constructor(tilemapData: TiledTilemapData) {
|
||||
super();
|
||||
this.tilesets = new Array<Tileset>();
|
||||
this.worldSize = new Vec2(0, 0);
|
||||
this.tileSize = new Vec2(0, 0);
|
||||
this.parseTilemapData(tilemapData, layerData);
|
||||
this.parseTilemapData(tilemapData);
|
||||
this.scale = new Vec2(4, 4);
|
||||
}
|
||||
|
||||
isCollidable(): boolean {
|
||||
return this.collidable;
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
getTilesets(): Tileset[] {
|
||||
return this.tilesets;
|
||||
}
|
||||
|
@ -56,24 +46,11 @@ export default abstract class Tilemap extends GameNode {
|
|||
|
||||
abstract getTileAt(worldCoords: Vec2): number;
|
||||
|
||||
isReady(): boolean {
|
||||
if(this.tilesets.length !== 0){
|
||||
for(let tileset of this.tilesets){
|
||||
if(!tileset.isReady()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract forEachTile(func: Function): void;
|
||||
|
||||
/**
|
||||
* Sets up the tileset using the data loaded from file
|
||||
*/
|
||||
// TODO: This shouldn't use tiled data specifically - it should be more general
|
||||
protected abstract parseTilemapData(tilemapData: TiledTilemapData, layerData: TiledLayerData): void;
|
||||
protected abstract parseTilemapData(tilemapData: TiledTilemapData): void;
|
||||
|
||||
abstract render(ctx: CanvasRenderingContext2D, origin: Vec2, viewportSize: Vec2): void;
|
||||
}
|
|
@ -2,27 +2,33 @@ import Tilemap from "../Tilemap";
|
|||
import Vec2 from "../../DataTypes/Vec2";
|
||||
import { TiledTilemapData, TiledLayerData } from "../../DataTypes/Tilesets/TiledData";
|
||||
import Tileset from "../../DataTypes/Tilesets/Tileset";
|
||||
import TileLayer from "../../DataTypes/Tilesets/TileLayer";
|
||||
|
||||
|
||||
export default class OrthogonalTilemap extends Tilemap {
|
||||
|
||||
protected parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void {
|
||||
protected parseTilemapData(tilemapData: TiledTilemapData): void {
|
||||
this.worldSize.set(tilemapData.width, tilemapData.height);
|
||||
this.tileSize.set(tilemapData.tilewidth, tilemapData.tileheight);
|
||||
this.data = layer.data;
|
||||
this.collisionData = this.data.map(tile => tile !== 0 ? 1 : 0);
|
||||
this.visible = layer.visible;
|
||||
this.collidable = false;
|
||||
if(layer.properties){
|
||||
for(let item of layer.properties){
|
||||
if(item.name === "Collidable"){
|
||||
this.collidable = item.value;
|
||||
for(let layerData of tilemapData.layers){
|
||||
let layer = new TileLayer();
|
||||
layer.data = layer.data;
|
||||
layer.visible = layer.visible;
|
||||
layer.collidable = false;
|
||||
if(layerData.properties){
|
||||
for(let item of layerData.properties){
|
||||
if(item.name === "Collidable"){
|
||||
layer.collidable = item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.layers.push(layer);
|
||||
}
|
||||
|
||||
tilemapData.tilesets.forEach(tilesetData => this.tilesets.push(new Tileset(tilesetData)));
|
||||
}
|
||||
|
||||
// TODO - Should this even work as it currently does? The layers make things more complicated
|
||||
getTileAt(worldCoords: Vec2): number {
|
||||
let localCoords = this.getColRowAt(worldCoords);
|
||||
if(localCoords.x < 0 || localCoords.x >= this.worldSize.x || localCoords.y < 0 || localCoords.y >= this.worldSize.y){
|
||||
|
@ -30,23 +36,39 @@ export default class OrthogonalTilemap extends Tilemap {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return this.data[localCoords.y * this.worldSize.x + localCoords.x];
|
||||
// Return the top nonzero tile
|
||||
let tile = 0;
|
||||
for(let layer of this.layers){
|
||||
if(layer.data[localCoords.y * this.worldSize.x + localCoords.x] !== 0){
|
||||
tile = layer.data[localCoords.y * this.worldSize.x + localCoords.x];
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
isTileCollidable(indexOrCol: number, row?: number): boolean {
|
||||
let index = 0;
|
||||
if(row){
|
||||
if(indexOrCol < 0 || indexOrCol >= this.worldSize.x || row < 0 || row >= this.worldSize.y){
|
||||
// There are no tiles in negative positions or out of bounds positions
|
||||
return false;
|
||||
}
|
||||
return this.collisionData[row * this.worldSize.x + indexOrCol] === 1 && this.collidable;
|
||||
index = row * this.worldSize.x + indexOrCol;
|
||||
} else {
|
||||
if(indexOrCol < 0 || indexOrCol >= this.collisionData.length){
|
||||
if(indexOrCol < 0 || indexOrCol >= this.layers[0].data.length){
|
||||
// Tiles that don't exist aren't collidable
|
||||
return false;
|
||||
}
|
||||
return this.collisionData[indexOrCol] === 1 && this.collidable;
|
||||
index = indexOrCol;
|
||||
}
|
||||
|
||||
for(let layer of this.layers){
|
||||
if(layer.data[index] !== 0 && layer.collidable){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Should this throw an error if someone tries to access an out of bounds value?
|
||||
|
@ -56,22 +78,20 @@ export default class OrthogonalTilemap extends Tilemap {
|
|||
return new Vec2(col, row);
|
||||
}
|
||||
|
||||
forEachTile(func: Function): void {
|
||||
for(let i = 0; i < this.data.length; i++){
|
||||
func(this.data[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
update(deltaT: number): void {}
|
||||
|
||||
// TODO: Don't render tiles that aren't on screen
|
||||
render(ctx: CanvasRenderingContext2D, origin: Vec2, viewportSize: Vec2) {
|
||||
for(let i = 0; i < this.data.length; i++){
|
||||
let tileIndex = this.data[i];
|
||||
for(let layer of this.layers){
|
||||
if(layer.visible){
|
||||
for(let i = 0; i < layer.data.length; i++){
|
||||
let tileIndex = layer.data[i];
|
||||
|
||||
for(let tileset of this.tilesets){
|
||||
if(tileset.hasTile(tileIndex)){
|
||||
tileset.renderTile(ctx, tileIndex, i, this.worldSize, origin, this.scale);
|
||||
for(let tileset of this.tilesets){
|
||||
if(tileset.hasTile(tileIndex)){
|
||||
tileset.renderTile(ctx, tileIndex, i, this.worldSize, origin, this.scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,37 @@
|
|||
import Map from "../DataTypes/Map";
|
||||
import Tilemap from "../Nodes/Tilemap";
|
||||
import Queue from "../DataTypes/Queue";
|
||||
import { TiledTilemapData } from "../DataTypes/Tilesets/TiledData";
|
||||
import StringUtils from "../Utils/StringUtils";
|
||||
|
||||
export default class ResourceManager {
|
||||
private static instance: ResourceManager;
|
||||
|
||||
private loading: boolean;
|
||||
|
||||
private constructor(){};
|
||||
private imagesLoaded: number;
|
||||
private imagesToLoad: number;
|
||||
private imageLoadingQueue: Queue<{key: string, path: string}>;
|
||||
private images: Map<HTMLImageElement>;
|
||||
|
||||
private tilemapsLoaded: number;
|
||||
private tilemapsToLoad: number;
|
||||
private tilemapLoadingQueue: Queue<{key: string, path: string}>;
|
||||
private tilemaps: Map<TiledTilemapData>;
|
||||
|
||||
private constructor(){
|
||||
this.loading = false;
|
||||
|
||||
this.imagesLoaded = 0;
|
||||
this.imagesToLoad = 0;
|
||||
this.imageLoadingQueue = new Queue();
|
||||
this.images = new Map();
|
||||
|
||||
this.tilemapsLoaded = 0;
|
||||
this.tilemapsToLoad = 0;
|
||||
this.tilemapLoadingQueue = new Queue();
|
||||
this.tilemaps = new Map();
|
||||
};
|
||||
|
||||
static getInstance(): ResourceManager {
|
||||
if(!this.instance){
|
||||
|
@ -11,11 +41,128 @@ export default class ResourceManager {
|
|||
return this.instance;
|
||||
}
|
||||
|
||||
public loadTilemap(pathToTilemapJSON: string, callback: Function): void {
|
||||
this.loadTextFile(pathToTilemapJSON, (fileText: string) => {
|
||||
let tilemapObject = JSON.parse(fileText);
|
||||
callback(tilemapObject);
|
||||
public image(key: string, path: string): void {
|
||||
this.imageLoadingQueue.enqueue({key: key, path: path});
|
||||
}
|
||||
|
||||
public spritesheet(key: string, path: string, frames: {hFrames: number, vFrames: number}): void {
|
||||
|
||||
}
|
||||
|
||||
public audio(key: string, path: string): void {
|
||||
|
||||
}
|
||||
|
||||
// This one is trickier than the others because we first have to load the json file, then we have to load the images
|
||||
public tilemap(key: string, path: string): void {
|
||||
// Add a function that loads the tilemap to the queue
|
||||
this.tilemapLoadingQueue.enqueue({key: key, path: path});
|
||||
|
||||
// this.tilemapLoadingQueue.enqueue((callback: Function) => {
|
||||
// this.loadTilemap(path, (tilemapData: TiledTilemapData) => {
|
||||
// // When the tilemap file loads, first construct the tilemap
|
||||
// // TODO: Ignore multiple layers for now, but this will have to be elegantly dealt with sometime in the future
|
||||
|
||||
// // Count the total number of images that need to be loaded
|
||||
|
||||
// let tilemap = new constr(tilemapData);
|
||||
// // For each of the tilesets in the tilemap, load the image
|
||||
// tilemap.getTilesets().forEach(tileset => {
|
||||
// let imagePath = StringUtils.getPathFromFilePath(path) + tileset.getImageUrl();
|
||||
// this.loadImage(imagePath, (image: HTMLImageElement) => {
|
||||
// tileset.setImage(image);
|
||||
// })
|
||||
// });
|
||||
|
||||
// this.tilemaps.add(key, tilemap);
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
loadResourcesFromQueue(callback: Function): void {
|
||||
this.loading = true;
|
||||
|
||||
// Load everything in the queues. Tilemaps have to come before images because they will add new images to the queue
|
||||
this.loadTilemapsFromQueue(() => {
|
||||
this.loadImagesFromQueue(() => {
|
||||
// Done loading
|
||||
this.loading = false;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private loadTilemapsFromQueue(onFinishLoading: Function){
|
||||
this.tilemapsToLoad = this.tilemapLoadingQueue.getSize();
|
||||
this.tilemapsLoaded = 0;
|
||||
|
||||
while(this.tilemapLoadingQueue.hasItems()){
|
||||
let tilemap = this.tilemapLoadingQueue.dequeue();
|
||||
this.loadTilemap(tilemap.key, tilemap.path, onFinishLoading);
|
||||
}
|
||||
}
|
||||
|
||||
private loadTilemap(key: string, pathToTilemapJSON: string, callbackIfLast: Function): void {
|
||||
this.loadTextFile(pathToTilemapJSON, (fileText: string) => {
|
||||
let tilemapObject = <TiledTilemapData>JSON.parse(fileText);
|
||||
|
||||
// We can parse the object later - it's much faster than loading
|
||||
this.tilemaps.add(key, tilemapObject);
|
||||
|
||||
// Grab the tileset images we need to load and add them to the imageloading queue
|
||||
for(let tileset of tilemapObject.tilesets){
|
||||
let key = tileset.image;
|
||||
let path = StringUtils.getPathFromFilePath(pathToTilemapJSON) + key;
|
||||
this.imageLoadingQueue.enqueue({key: key, path: path});
|
||||
}
|
||||
|
||||
// Finish loading
|
||||
this.finishLoadingTilemap(callbackIfLast);
|
||||
});
|
||||
}
|
||||
|
||||
private finishLoadingTilemap(callback: Function){
|
||||
this.tilemapsLoaded += 1;
|
||||
|
||||
if(this.tilemapsLoaded === this.tilemapsToLoad){
|
||||
// We're done loading tilemaps
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
private loadImagesFromQueue(onFinishLoading: Function): void {
|
||||
this.imagesToLoad = this.imageLoadingQueue.getSize();
|
||||
this.tilemapsLoaded = 0;
|
||||
|
||||
while(this.imageLoadingQueue.hasItems()){
|
||||
let image = this.imageLoadingQueue.dequeue();
|
||||
this.loadImage(image.key, image.path, onFinishLoading);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: When you switch to WebGL, make sure to make this private and make a "loadTexture" function
|
||||
public loadImage(key: string, path: string, callbackIfLast: Function): void {
|
||||
var image = new Image();
|
||||
|
||||
image.onload = () => {
|
||||
// Add to loaded images
|
||||
this.images.add(key, image);
|
||||
|
||||
// Finish image load
|
||||
this.finishLoadingImage(callbackIfLast);
|
||||
}
|
||||
|
||||
image.src = path;
|
||||
}
|
||||
|
||||
private finishLoadingImage(callback: Function): void {
|
||||
this.imagesLoaded += 1;
|
||||
|
||||
if(this.imagesLoaded === this.imagesToLoad ){
|
||||
// We're done loading tilemaps
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
private loadTextFile(textFilePath: string, callback: Function): void {
|
||||
|
@ -29,15 +176,4 @@ export default class ResourceManager {
|
|||
};
|
||||
xobj.send(null);
|
||||
}
|
||||
|
||||
// TODO: When you switch to WebGL, make sure to make this private and make a "loadTexture" function
|
||||
public loadImage(path: string, callback: Function): void {
|
||||
var image = new Image();
|
||||
|
||||
image.onload = function () {
|
||||
callback(path, image);
|
||||
}
|
||||
|
||||
image.src = path;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import Scene from "../Scene";
|
||||
import Layer from "../Layer";
|
||||
import Viewport from "../../SceneGraph/Viewport";
|
||||
import CanvasItem from "../../Nodes/CanvasNode"
|
||||
|
||||
export default class CanvasNodeFactory {
|
||||
private scene: Scene;
|
||||
private scene: Layer;
|
||||
|
||||
constructor(scene: Scene){
|
||||
constructor(scene: Layer){
|
||||
this.scene = scene;
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import Scene from "../Scene";
|
||||
import Layer from "../Layer";
|
||||
import Viewport from "../../SceneGraph/Viewport";
|
||||
import PhysicsNode from "../../Physics/PhysicsNode";
|
||||
import PhysicsManager from "../../Physics/PhysicsManager";
|
||||
import Tilemap from "../../Nodes/Tilemap";
|
||||
|
||||
export default class PhysicsNodeFactory {
|
||||
private scene: Scene;
|
||||
private scene: Layer;
|
||||
private physicsManager: PhysicsManager;
|
||||
|
||||
constructor(scene: Scene, physicsManager: PhysicsManager){
|
||||
constructor(scene: Layer, physicsManager: PhysicsManager){
|
||||
this.scene = scene;
|
||||
this.physicsManager = physicsManager;
|
||||
}
|
45
src/Scene/Factories/TilemapFactory.ts
Normal file
45
src/Scene/Factories/TilemapFactory.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import Layer from "../Layer";
|
||||
import Viewport from "../../SceneGraph/Viewport";
|
||||
import Tilemap from "../../Nodes/Tilemap";
|
||||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||
import { TiledTilemapData } from "../../DataTypes/Tilesets/TiledData";
|
||||
import StringUtils from "../../Utils/StringUtils";
|
||||
import StaticBody from "../../Physics/StaticBody";
|
||||
import Vec2 from "../../DataTypes/Vec2";
|
||||
|
||||
export default class TilemapFactory {
|
||||
private scene: Layer;
|
||||
// TODO: get the resource manager OUT of here, it does not belong
|
||||
private resourceManager: ResourceManager;
|
||||
|
||||
constructor(scene: Layer){
|
||||
this.scene = scene;
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
}
|
||||
|
||||
add<T extends Tilemap>(constr: new (...a: any) => T, path: string, ...args: any): void {
|
||||
// this.resourceManager.loadTilemap(path, (tilemapData: TiledTilemapData) => {
|
||||
// // For each of the layers in the tilemap, create a tilemap
|
||||
// for(let layer of tilemapData.layers){
|
||||
// let tilemap = new constr(tilemapData, layer);
|
||||
// tilemap.init(this.scene);
|
||||
|
||||
// // Add to scene
|
||||
// this.scene.addTilemap(tilemap);
|
||||
|
||||
// if(tilemap.isCollidable()){
|
||||
// // Register in physics as a tilemap
|
||||
// this.scene.physics.addTilemap(tilemap);
|
||||
// }
|
||||
|
||||
// // Load images for the tilesets
|
||||
// tilemap.getTilesets().forEach(tileset => {
|
||||
// let imagePath = StringUtils.getPathFromFilePath(path) + tileset.getImageUrl();
|
||||
// this.resourceManager.loadImage(imagePath, (path: string, image: HTMLImageElement) => {
|
||||
// tileset.setImage(image);
|
||||
// })
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
|
@ -4,15 +4,15 @@ import SceneGraph from "../SceneGraph/SceneGraph";
|
|||
import SceneGraphArray from "../SceneGraph/SceneGraphArray";
|
||||
import CanvasNode from "../Nodes/CanvasNode";
|
||||
import CanvasNodeFactory from "./Factories/CanvasNodeFactory";
|
||||
import GameState from "./GameState";
|
||||
import Scene from "./Scene";
|
||||
import Tilemap from "../Nodes/Tilemap";
|
||||
import TilemapFactory from "./Factories/TilemapFactory";
|
||||
import PhysicsManager from "../Physics/PhysicsManager";
|
||||
import PhysicsNodeFactory from "./Factories/PhysicsNodeFactory";
|
||||
import MathUtils from "../Utils/MathUtils";
|
||||
|
||||
export default class Scene {
|
||||
private gameState: GameState;
|
||||
export default class Layer {
|
||||
private gameState: Scene;
|
||||
private viewport: Viewport
|
||||
private parallax: Vec2;
|
||||
private sceneGraph: SceneGraph;
|
||||
|
@ -27,7 +27,7 @@ export default class Scene {
|
|||
public tilemap: TilemapFactory;
|
||||
public physics: PhysicsNodeFactory;
|
||||
|
||||
constructor(viewport: Viewport, gameState: GameState){
|
||||
constructor(viewport: Viewport, gameState: Scene){
|
||||
this.gameState = gameState;
|
||||
this.viewport = viewport;
|
||||
this.parallax = new Vec2(1, 1);
|
||||
|
@ -114,9 +114,7 @@ export default class Scene {
|
|||
|
||||
// Render tilemaps
|
||||
this.tilemaps.forEach(tilemap => {
|
||||
if(tilemap.isReady() && tilemap.isVisible()){
|
||||
tilemap.render(ctx, origin, size);
|
||||
}
|
||||
tilemap.render(ctx, origin, size);
|
||||
});
|
||||
|
||||
// Render visible set
|
41
src/Scene/Scene.ts
Normal file
41
src/Scene/Scene.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import Stack from "../DataTypes/Stack";
|
||||
import Layer from "./Layer";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
|
||||
export default class Scene{
|
||||
private layers: Stack<Layer>;
|
||||
private worldSize: Vec2;
|
||||
private viewport: Viewport;
|
||||
private running: boolean;
|
||||
|
||||
constructor(viewport: Viewport){
|
||||
this.layers = new Stack(10);
|
||||
this.worldSize = new Vec2(1600, 1000);
|
||||
this.viewport = viewport;
|
||||
this.viewport.setBounds(0, 0, 2560, 1280);
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
loadScene(): void {}
|
||||
|
||||
unloadScene(): void {}
|
||||
|
||||
setRunning(running: boolean): void {
|
||||
this.running = running;
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return this.isRunning();
|
||||
}
|
||||
|
||||
start(){}
|
||||
|
||||
update(deltaT: number): void {
|
||||
this.layers.forEach((scene: Layer) => scene.update(deltaT));
|
||||
}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D): void {
|
||||
this.layers.forEach((scene: Layer) => scene.render(ctx));
|
||||
}
|
||||
}
|
39
src/Scene/SceneManager.ts
Normal file
39
src/Scene/SceneManager.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import Scene from "./Scene";
|
||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
|
||||
export default class SceneManager{
|
||||
|
||||
private currentScene: Scene;
|
||||
private viewport: Viewport;
|
||||
private resourceManager: ResourceManager;
|
||||
|
||||
constructor(viewport: Viewport){
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
this.viewport = viewport;
|
||||
}
|
||||
|
||||
public addScene<T extends Scene>(constr: new (...args: any) => T){
|
||||
let scene = new constr(this.viewport);
|
||||
this.currentScene = scene;
|
||||
|
||||
// Enqueue all scene asset loads
|
||||
scene.loadScene();
|
||||
|
||||
// Load all assets
|
||||
this.resourceManager.loadResourcesFromQueue(() => {
|
||||
scene.start();
|
||||
scene.setRunning(true);
|
||||
})
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D){
|
||||
this.currentScene.render(ctx);
|
||||
}
|
||||
|
||||
public update(deltaT: number){
|
||||
if(this.currentScene.isRunning()){
|
||||
this.currentScene.update(deltaT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,15 +2,15 @@ import Viewport from "./Viewport";
|
|||
import CanvasNode from "../Nodes/CanvasNode";
|
||||
import Map from "../DataTypes/Map";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Scene from "../GameState/Scene";
|
||||
import Layer from "../Scene/Layer";
|
||||
|
||||
export default abstract class SceneGraph{
|
||||
protected viewport: Viewport;
|
||||
protected nodeMap: Map<CanvasNode>;
|
||||
protected idCounter: number;
|
||||
protected scene: Scene;
|
||||
protected scene: Layer;
|
||||
|
||||
constructor(viewport: Viewport, scene: Scene){
|
||||
constructor(viewport: Viewport, scene: Layer){
|
||||
this.viewport = viewport;
|
||||
this.scene = scene;
|
||||
this.nodeMap = new Map<CanvasNode>();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import SceneGraph from "./SceneGraph";
|
||||
import CanvasNode from "../Nodes/CanvasNode";
|
||||
import Viewport from "./Viewport";
|
||||
import Scene from "../GameState/Scene";
|
||||
import Layer from "../Scene/Layer";
|
||||
|
||||
export default class SceneGraphArray extends SceneGraph{
|
||||
private nodeList: Array<CanvasNode>;
|
||||
private turnOffViewportCulling_demoTool: boolean;
|
||||
|
||||
constructor(viewport: Viewport, scene: Scene){
|
||||
constructor(viewport: Viewport, scene: Layer){
|
||||
super(viewport, scene);
|
||||
|
||||
this.nodeList = new Array<CanvasNode>();
|
||||
|
|
124
src/main.ts
124
src/main.ts
|
@ -9,80 +9,80 @@ import OrthogonalTilemap from "./Nodes/Tilemaps/OrthogonalTilemap";
|
|||
function main(){
|
||||
// Create the game object
|
||||
let game = new GameLoop();
|
||||
let gameState = game.getGameState();
|
||||
let gameState = game.getSceneManager();
|
||||
|
||||
let backgroundScene = gameState.createScene();
|
||||
backgroundScene.setParallax(0.5, 0.8);
|
||||
backgroundScene.setAlpha(0.5);
|
||||
let mainScene = gameState.createScene();
|
||||
let uiLayer = gameState.createScene();
|
||||
uiLayer.setParallax(0, 0);
|
||||
let pauseMenu = gameState.createScene();
|
||||
pauseMenu.setParallax(0, 0);
|
||||
// let backgroundScene = gameState.createScene();
|
||||
// backgroundScene.setParallax(0.5, 0.8);
|
||||
// backgroundScene.setAlpha(0.5);
|
||||
// let mainScene = gameState.createScene();
|
||||
// let uiLayer = gameState.createScene();
|
||||
// uiLayer.setParallax(0, 0);
|
||||
// let pauseMenu = gameState.createScene();
|
||||
// pauseMenu.setParallax(0, 0);
|
||||
|
||||
// Initialize GameObjects
|
||||
let recordButton = uiLayer.canvasNode.add(Button);
|
||||
recordButton.setSize(100, 50);
|
||||
recordButton.setText("Record");
|
||||
recordButton.setPosition(400, 30);
|
||||
recordButton.onClickEventId = "record_button_press";
|
||||
// // Initialize GameObjects
|
||||
// let recordButton = uiLayer.canvasNode.add(Button);
|
||||
// recordButton.setSize(100, 50);
|
||||
// recordButton.setText("Record");
|
||||
// recordButton.setPosition(400, 30);
|
||||
// recordButton.onClickEventId = "record_button_press";
|
||||
|
||||
let stopButton = uiLayer.canvasNode.add(Button);
|
||||
stopButton.setSize(100, 50);
|
||||
stopButton.setText("Stop");
|
||||
stopButton.setPosition(550, 30);
|
||||
stopButton.onClickEventId = "stop_button_press";
|
||||
// let stopButton = uiLayer.canvasNode.add(Button);
|
||||
// stopButton.setSize(100, 50);
|
||||
// stopButton.setText("Stop");
|
||||
// stopButton.setPosition(550, 30);
|
||||
// stopButton.onClickEventId = "stop_button_press";
|
||||
|
||||
let playButton = uiLayer.canvasNode.add(Button);
|
||||
playButton.setSize(100, 50);
|
||||
playButton.setText("Play");
|
||||
playButton.setPosition(700, 30);
|
||||
playButton.onClickEventId = "play_button_press";
|
||||
// let playButton = uiLayer.canvasNode.add(Button);
|
||||
// playButton.setSize(100, 50);
|
||||
// playButton.setText("Play");
|
||||
// playButton.setPosition(700, 30);
|
||||
// playButton.onClickEventId = "play_button_press";
|
||||
|
||||
let cycleFramerateButton = uiLayer.canvasNode.add(Button);
|
||||
cycleFramerateButton.setSize(150, 50);
|
||||
cycleFramerateButton.setText("Cycle FPS");
|
||||
cycleFramerateButton.setPosition(5, 400);
|
||||
let i = 0;
|
||||
let fps = [15, 30, 60];
|
||||
cycleFramerateButton.onClick = () => {
|
||||
game.setMaxFPS(fps[i]);
|
||||
i = (i + 1) % 3;
|
||||
}
|
||||
// let cycleFramerateButton = uiLayer.canvasNode.add(Button);
|
||||
// cycleFramerateButton.setSize(150, 50);
|
||||
// cycleFramerateButton.setText("Cycle FPS");
|
||||
// cycleFramerateButton.setPosition(5, 400);
|
||||
// let i = 0;
|
||||
// let fps = [15, 30, 60];
|
||||
// cycleFramerateButton.onClick = () => {
|
||||
// game.setMaxFPS(fps[i]);
|
||||
// i = (i + 1) % 3;
|
||||
// }
|
||||
|
||||
let pauseButton = uiLayer.canvasNode.add(Button);
|
||||
pauseButton.setSize(100, 50);
|
||||
pauseButton.setText("Pause");
|
||||
pauseButton.setPosition(700, 400);
|
||||
pauseButton.onClick = () => {
|
||||
mainScene.setPaused(true);
|
||||
pauseMenu.enable();
|
||||
}
|
||||
// let pauseButton = uiLayer.canvasNode.add(Button);
|
||||
// pauseButton.setSize(100, 50);
|
||||
// pauseButton.setText("Pause");
|
||||
// pauseButton.setPosition(700, 400);
|
||||
// pauseButton.onClick = () => {
|
||||
// mainScene.setPaused(true);
|
||||
// pauseMenu.enable();
|
||||
// }
|
||||
|
||||
let modalBackground = pauseMenu.canvasNode.add(UIElement);
|
||||
modalBackground.setSize(400, 200);
|
||||
modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
|
||||
modalBackground.setPosition(200, 100);
|
||||
// let modalBackground = pauseMenu.canvasNode.add(UIElement);
|
||||
// modalBackground.setSize(400, 200);
|
||||
// modalBackground.setBackgroundColor(new Color(0, 0, 0, 0.4));
|
||||
// modalBackground.setPosition(200, 100);
|
||||
|
||||
let resumeButton = pauseMenu.canvasNode.add(Button);
|
||||
resumeButton.setSize(100, 50);
|
||||
resumeButton.setText("Resume");
|
||||
resumeButton.setPosition(400, 200);
|
||||
resumeButton.onClick = () => {
|
||||
mainScene.setPaused(false);
|
||||
pauseMenu.disable();
|
||||
}
|
||||
// let resumeButton = pauseMenu.canvasNode.add(Button);
|
||||
// resumeButton.setSize(100, 50);
|
||||
// resumeButton.setText("Resume");
|
||||
// resumeButton.setPosition(400, 200);
|
||||
// resumeButton.onClick = () => {
|
||||
// mainScene.setPaused(false);
|
||||
// pauseMenu.disable();
|
||||
// }
|
||||
|
||||
backgroundScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Background.json");
|
||||
mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Platformer.json");
|
||||
let player = mainScene.physics.add(Player, "platformer");
|
||||
// backgroundScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Background.json");
|
||||
// mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/Platformer.json");
|
||||
// let player = mainScene.physics.add(Player, "platformer");
|
||||
|
||||
// mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/TopDown.json");
|
||||
// let player = mainScene.physics.add(Player, "topdown");
|
||||
// // mainScene.tilemap.add(OrthogonalTilemap, "assets/tilemaps/TopDown.json");
|
||||
// // let player = mainScene.physics.add(Player, "topdown");
|
||||
|
||||
mainScene.getViewport().follow(player);
|
||||
// mainScene.getViewport().follow(player);
|
||||
|
||||
pauseMenu.disable();
|
||||
// pauseMenu.disable();
|
||||
|
||||
game.start();
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
"src/Events/GameEvent.ts",
|
||||
"src/Events/Receiver.ts",
|
||||
|
||||
"src/GameState/Factories/CanvasNodeFactory.ts",
|
||||
"src/GameState/Factories/PhysicsNodeFactory.ts",
|
||||
"src/GameState/Factories/TilemapFactory.ts",
|
||||
"src/GameState/GameState.ts",
|
||||
"src/GameState/Scene.ts",
|
||||
"src/Scene/Factories/CanvasNodeFactory.ts",
|
||||
"src/Scene/Factories/PhysicsNodeFactory.ts",
|
||||
"src/Scene/Factories/TilemapFactory.ts",
|
||||
"src/Scene/Scene.ts",
|
||||
"src/Scene/Layer.ts",
|
||||
|
||||
"src/Input/InputHandler.ts",
|
||||
"src/Input/InputReceiver.ts",
|
||||
|
|
Loading…
Reference in New Issue
Block a user