playable.js
Version:
A lightweight HTML5 game engine.
162 lines (146 loc) • 4.85 kB
text/typescript
import {Request} from './Request';
import {Media} from '../media/Media';
import {Sound} from '../media/Sound';
import {Texture} from '../media/Texture';
import {Stage} from '../display/Stage';
import {Event} from '../event/Event';
import {EventEmitter} from '../event/EventEmitter';
export class ResourceManager extends EventEmitter {
public static readonly TYPE_TEXT: string = 'text';
public static readonly TYPE_JSON: string = 'json';
public static readonly TYPE_BINARY: string = 'binary';
public static readonly TYPE_TEXTURE: string = 'texture';
public static readonly TYPE_SOUND: string = 'sound';
public threads: number;
public timeout: number;
public retryTimes: number;
protected $errorCount: number = 0;
protected $loadedCount: number = 0;
protected $loadingCount: number = 0;
protected $list: Array<ResourceInfo>;
protected readonly $total: number;
protected readonly $stage: Stage;
protected readonly $resources: Object;
public constructor(stage: Stage, list: Array<ResourceInfo>, options?: ResourceManagerOption) {
super();
this.$stage = stage;
this.threads = options && options.threads || 2;
this.timeout = options && options.timeout || 10000;
this.retryTimes = options && options.retryTimes || 3;
this.$list = list.concat();
this.$total = list.length;
this.$resources = {};
this.$checkPendingTasks();
}
public get total(): number {
return this.$total;
}
public get errorCount(): number {
return this.$errorCount;
}
public get loadedCount(): number {
return this.$loadedCount;
}
protected $checkPendingTasks(): void {
if (this.$loadingCount < this.threads && this.$list.length > 0) {
++this.$loadingCount;
this.$load(this.$list.shift(), 1);
}
}
protected $load(info: ResourceInfo, attempts: number): void {
let timer;
let resource;
let name = info.name;
let type = info.type;
let url = info.url;
let total = this.$total;
let stage = this.$stage;
let ticker = stage.ticker;
let resources = this.$resources;
let retryTimes = this.retryTimes;
let successCallback = () => {
let errorCount = this.$errorCount;
let loadedCount = ++this.$loadedCount;
--this.$loadingCount;
ticker.clearTimeout(timer);
if (resource instanceof Request) {
resources[name] = resource.response;
} else if (resource instanceof Media) {
resources[name] = resource;
}
resource.off(Event.LOAD, successCallback);
resource.off(Event.ERROR, errorCallback);
let event = Event.create(Event.PROGRESS, (loadedCount + errorCount) / total);
this.emit(event);
event.release();
if (loadedCount + errorCount === total) {
this.emit(Event.COMPLETE);
} else {
this.$checkPendingTasks();
}
};
let errorCallback = () => {
if (attempts < retryTimes) {
this.$load(info, attempts + 1);
} else {
--this.$loadingCount;
let loadedCount = this.$loadedCount;
let errorCount = ++this.$errorCount;
if (resource instanceof Request) {
resources[name] = resource.response;
} else if (resource instanceof Media) {
resources[name] = resource;
}
this.emit(Event.PROGRESS, (loadedCount + errorCount) / total);
if (loadedCount + errorCount === total) {
this.emit(Event.COMPLETE);
} else {
this.$checkPendingTasks();
}
}
ticker.clearTimeout(timer);
resource.off(Event.LOAD, successCallback);
resource.off(Event.ERROR, errorCallback);
};
if (type === ResourceManager.TYPE_TEXT) {
resource = new Request(url, {responseType: 'text'});
resource.on(Event.LOAD, successCallback);
resource.on(Event.ERROR, errorCallback);
} else if (type === ResourceManager.TYPE_JSON) {
resource = new Request(url, {responseType: 'json'});
resource.on(Event.LOAD, successCallback);
resource.on(Event.ERROR, errorCallback);
} else if (type === ResourceManager.TYPE_BINARY) {
resource = new Request(url, {responseType: 'arraybuffer'});
resource.on(Event.LOAD, successCallback);
resource.on(Event.ERROR, errorCallback);
} else if (type === ResourceManager.TYPE_TEXTURE) {
resource = new Texture(stage, url);
resource.on(Event.LOAD, successCallback);
resource.on(Event.ERROR, errorCallback);
} else if (type === ResourceManager.TYPE_SOUND) {
resource = new Sound(stage, url);
resource.on(Event.LOAD, successCallback);
resource.on(Event.ERROR, errorCallback);
} else {
throw new Error('Unsupported resource type: ' + type);
}
timer = ticker.setTimeout(errorCallback, this.timeout);
}
public has(name: string): boolean {
return !!this.$resources[name];
}
public get<Resource>(name: string): Resource {
return this.$resources[name];
}
}
export interface ResourceInfo {
name: string,
type: string,
url: string
}
export interface ResourceManagerOption {
threads?: number,
timeout?: number,
retryTimes?: number
}