added audio and sprite loading
This commit is contained in:
parent
2093d8e4ab
commit
9e86192bb0
|
@ -6,6 +6,7 @@ import Debug from "../Debug/Debug";
|
|||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
import SceneManager from "../Scene/SceneManager";
|
||||
import AudioManager from "../Sound/AudioManager";
|
||||
|
||||
export default class GameLoop{
|
||||
// The amount of time to spend on a physics step
|
||||
|
@ -39,6 +40,7 @@ export default class GameLoop{
|
|||
private recorder: Recorder;
|
||||
private resourceManager: ResourceManager;
|
||||
private sceneManager: SceneManager;
|
||||
private audioManager: AudioManager;
|
||||
|
||||
constructor(){
|
||||
this.maxFPS = 60;
|
||||
|
@ -68,6 +70,7 @@ export default class GameLoop{
|
|||
this.recorder = new Recorder();
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
this.sceneManager = new SceneManager(this.viewport, this);
|
||||
this.audioManager = AudioManager.getInstance();
|
||||
}
|
||||
|
||||
private initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
||||
|
@ -150,6 +153,7 @@ export default class GameLoop{
|
|||
this.inputReceiver.update(deltaT);
|
||||
this.recorder.update(deltaT);
|
||||
this.sceneManager.update(deltaT);
|
||||
this.resourceManager.update(deltaT);
|
||||
}
|
||||
|
||||
render(): void {
|
||||
|
|
|
@ -13,6 +13,23 @@ export default class MainScene extends Scene {
|
|||
loadScene(){
|
||||
this.load.tilemap("platformer", "assets/tilemaps/Platformer.json");
|
||||
this.load.tilemap("background", "assets/tilemaps/Background.json");
|
||||
this.load.image("player", "assets/sprites/player.png");
|
||||
this.load.audio("player_jump", "assets/sounds/jump-3.wav");
|
||||
this.load.audio("level_music", "assets/sounds/level.wav");
|
||||
|
||||
let loadingScreen = this.addLayer();
|
||||
let box = this.add.graphic(Rect, loadingScreen, new Vec2(200, 300), new Vec2(400, 60));
|
||||
box.setColor(new Color(0, 0, 0));
|
||||
let bar = this.add.graphic(Rect, loadingScreen, new Vec2(205, 305), new Vec2(0, 50));
|
||||
bar.setColor(new Color(0, 200, 200));
|
||||
|
||||
this.load.onLoadProgress = (percentProgress: number) => {
|
||||
bar.setSize(295 * percentProgress, bar.getSize().y);
|
||||
}
|
||||
|
||||
this.load.onLoadComplete = () => {
|
||||
loadingScreen.disable();
|
||||
}
|
||||
}
|
||||
|
||||
startScene(){
|
||||
|
@ -22,6 +39,9 @@ export default class MainScene extends Scene {
|
|||
backgroundTilemap.getLayer().setParallax(0.5, 0.8);
|
||||
backgroundTilemap.getLayer().setAlpha(0.5);
|
||||
|
||||
// Add the music and start playing it on a loop
|
||||
this.add.audio("level_music").play(true);
|
||||
|
||||
// Add the tilemap
|
||||
this.add.tilemap("platformer", OrthogonalTilemap);
|
||||
|
||||
|
@ -30,10 +50,12 @@ export default class MainScene extends Scene {
|
|||
|
||||
// Add a player
|
||||
let player = this.add.physics(Player, mainLayer, "platformer");
|
||||
let playerSprite = this.add.graphic(Rect, mainLayer, new Vec2(0, 0), new Vec2(50, 50));
|
||||
playerSprite.setColor(new Color(255, 0, 0));
|
||||
let playerSprite = this.add.sprite("player", mainLayer)
|
||||
player.setSprite(playerSprite);
|
||||
|
||||
// TODO - Should sound playing be handled with events?
|
||||
let playerJumpSound = this.add.audio("player_jump");
|
||||
player.jumpSound = playerJumpSound;
|
||||
|
||||
this.viewport.follow(player);
|
||||
|
||||
|
|
34
src/Nodes/Sprites/Sprite.ts
Normal file
34
src/Nodes/Sprites/Sprite.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import CanvasNode from "../CanvasNode";
|
||||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||
import Vec2 from "../../DataTypes/Vec2";
|
||||
|
||||
export default class Sprite extends CanvasNode {
|
||||
private imageId: string;
|
||||
private scale: Vec2;
|
||||
|
||||
constructor(imageId: string){
|
||||
super();
|
||||
this.imageId = imageId;
|
||||
let image = ResourceManager.getInstance().getImage(this.imageId);
|
||||
this.size = new Vec2(image.width, image.height);
|
||||
this.scale = new Vec2(1, 1);
|
||||
}
|
||||
|
||||
getScale(): Vec2 {
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
setScale(scale: Vec2): void {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
update(deltaT: number): void {}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D): void {
|
||||
let image = ResourceManager.getInstance().getImage(this.imageId);
|
||||
let origin = this.getViewportOriginWithParallax();
|
||||
ctx.drawImage(image,
|
||||
0, 0, this.size.x, this.size.y,
|
||||
this.position.x - origin.x, this.position.y - origin.y, this.size.x * this.scale.x, this.size.y * this.scale.y);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import Vec2 from "./DataTypes/Vec2";
|
|||
import Debug from "./Debug/Debug";
|
||||
import AABB from "./Physics/Colliders/AABB";
|
||||
import CanvasNode from "./Nodes/CanvasNode";
|
||||
import Audio from "./Sound/Audio";
|
||||
|
||||
export default class Player extends PhysicsNode {
|
||||
velocity: Vec2;
|
||||
|
@ -11,6 +12,7 @@ export default class Player extends PhysicsNode {
|
|||
size: Vec2;
|
||||
gravity: number = 7000;
|
||||
type: string;
|
||||
jumpSound: Audio;
|
||||
|
||||
constructor(type: string){
|
||||
super();
|
||||
|
@ -81,6 +83,10 @@ export default class Player extends PhysicsNode {
|
|||
let vel = new Vec2(0, this.velocity.y);
|
||||
|
||||
if(this.grounded){
|
||||
if(dir.y === -1){
|
||||
// Jumping
|
||||
this.jumpSound.play();
|
||||
}
|
||||
vel.y = dir.y*1800;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,16 @@ import Tilemap from "../Nodes/Tilemap";
|
|||
import Queue from "../DataTypes/Queue";
|
||||
import { TiledTilemapData } from "../DataTypes/Tilesets/TiledData";
|
||||
import StringUtils from "../Utils/StringUtils";
|
||||
import AudioManager from "../Sound/AudioManager";
|
||||
|
||||
export default class ResourceManager {
|
||||
private static instance: ResourceManager;
|
||||
|
||||
private loading: boolean;
|
||||
private justLoaded: boolean;
|
||||
|
||||
public onLoadProgress: Function;
|
||||
public onLoadComplete: Function;
|
||||
|
||||
private imagesLoaded: number;
|
||||
private imagesToLoad: number;
|
||||
|
@ -19,8 +24,17 @@ export default class ResourceManager {
|
|||
private tilemapLoadingQueue: Queue<{key: string, path: string}>;
|
||||
private tilemaps: Map<TiledTilemapData>;
|
||||
|
||||
private audioLoaded: number;
|
||||
private audioToLoad: number;
|
||||
private audioLoadingQueue: Queue<{key: string, path: string}>;
|
||||
private audioBuffers: Map<AudioBuffer>;
|
||||
|
||||
// The number of different types of things to load
|
||||
private typesToLoad: number;
|
||||
|
||||
private constructor(){
|
||||
this.loading = false;
|
||||
this.justLoaded = false;
|
||||
|
||||
this.imagesLoaded = 0;
|
||||
this.imagesToLoad = 0;
|
||||
|
@ -31,6 +45,11 @@ export default class ResourceManager {
|
|||
this.tilemapsToLoad = 0;
|
||||
this.tilemapLoadingQueue = new Queue();
|
||||
this.tilemaps = new Map();
|
||||
|
||||
this.audioLoaded = 0;
|
||||
this.audioToLoad = 0;
|
||||
this.audioLoadingQueue = new Queue();
|
||||
this.audioBuffers = new Map();
|
||||
};
|
||||
|
||||
static getInstance(): ResourceManager {
|
||||
|
@ -45,7 +64,7 @@ export default class ResourceManager {
|
|||
this.imageLoadingQueue.enqueue({key: key, path: path});
|
||||
}
|
||||
|
||||
public getImage(key: string){
|
||||
public getImage(key: string): HTMLImageElement{
|
||||
return this.images.get(key);
|
||||
}
|
||||
|
||||
|
@ -54,12 +73,14 @@ export default class ResourceManager {
|
|||
}
|
||||
|
||||
public audio(key: string, path: string): void {
|
||||
|
||||
this.audioLoadingQueue.enqueue({key: key, path: path});
|
||||
}
|
||||
|
||||
public getAudio(key: string): AudioBuffer {
|
||||
return this.audioBuffers.get(key);
|
||||
}
|
||||
|
||||
// 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});
|
||||
}
|
||||
|
||||
|
@ -67,17 +88,23 @@ export default class ResourceManager {
|
|||
return this.tilemaps.get(key);
|
||||
}
|
||||
|
||||
// TODO - Should everything be loaded in order, one file at a time?
|
||||
loadResourcesFromQueue(callback: Function): void {
|
||||
this.typesToLoad = 3;
|
||||
|
||||
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(() => {
|
||||
this.loadAudioFromQueue(() => {
|
||||
// Done loading
|
||||
this.loading = false;
|
||||
this.justLoaded = true;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -121,7 +148,7 @@ export default class ResourceManager {
|
|||
|
||||
private loadImagesFromQueue(onFinishLoading: Function): void {
|
||||
this.imagesToLoad = this.imageLoadingQueue.getSize();
|
||||
this.tilemapsLoaded = 0;
|
||||
this.imagesLoaded = 0;
|
||||
|
||||
while(this.imageLoadingQueue.hasItems()){
|
||||
let image = this.imageLoadingQueue.dequeue();
|
||||
|
@ -148,7 +175,47 @@ export default class ResourceManager {
|
|||
this.imagesLoaded += 1;
|
||||
|
||||
if(this.imagesLoaded === this.imagesToLoad ){
|
||||
// We're done loading tilemaps
|
||||
// We're done loading images
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
private loadAudioFromQueue(onFinishLoading: Function){
|
||||
this.audioToLoad = this.audioLoadingQueue.getSize();
|
||||
this.audioLoaded = 0;
|
||||
|
||||
while(this.audioLoadingQueue.hasItems()){
|
||||
let audio = this.audioLoadingQueue.dequeue();
|
||||
this.loadAudio(audio.key, audio.path, onFinishLoading);
|
||||
}
|
||||
}
|
||||
|
||||
private loadAudio(key: string, path: string, callbackIfLast: Function): void {
|
||||
let audioCtx = AudioManager.getInstance().getAudioContext();
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
request.open('GET', path, true);
|
||||
request.responseType = 'arraybuffer';
|
||||
|
||||
request.onload = () => {
|
||||
audioCtx.decodeAudioData(request.response, (buffer) => {
|
||||
// Add to list of audio buffers
|
||||
this.audioBuffers.add(key, buffer);
|
||||
|
||||
// Finish loading sound
|
||||
this.finishLoadingAudio(callbackIfLast);
|
||||
}, (error) =>{
|
||||
throw "Error loading sound";
|
||||
});
|
||||
}
|
||||
request.send();
|
||||
}
|
||||
|
||||
private finishLoadingAudio(callback: Function): void {
|
||||
this.audioLoaded += 1;
|
||||
|
||||
if(this.audioLoaded === this.audioToLoad){
|
||||
// We're done loading audio
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
@ -164,4 +231,20 @@ export default class ResourceManager {
|
|||
};
|
||||
xobj.send(null);
|
||||
}
|
||||
|
||||
private getLoadPercent(): number {
|
||||
return (this.tilemapsLoaded/this.tilemapsToLoad
|
||||
+ this.imagesLoaded/this.imagesToLoad
|
||||
+ this.audioLoaded/this.audioToLoad)
|
||||
/ this.typesToLoad;
|
||||
}
|
||||
|
||||
public update(deltaT: number): void {
|
||||
if(this.loading){
|
||||
this.onLoadProgress(this.getLoadPercent());
|
||||
} else if(this.justLoaded){
|
||||
this.justLoaded = false;
|
||||
this.onLoadComplete();
|
||||
}
|
||||
}
|
||||
}
|
21
src/Scene/Factories/AudioFactory.ts
Normal file
21
src/Scene/Factories/AudioFactory.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||
import AudioManager from "../../Sound/AudioManager";
|
||||
import Scene from "../Scene";
|
||||
import Audio from "../../Sound/Audio";
|
||||
|
||||
export default class AudioFactory {
|
||||
private scene: Scene;
|
||||
private resourceManager: ResourceManager;
|
||||
private audioManager: AudioManager;
|
||||
|
||||
init(scene: Scene){
|
||||
this.scene = scene;
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
this.audioManager = AudioManager.getInstance();
|
||||
}
|
||||
|
||||
addAudio = (key: string, ...args: any): Audio => {
|
||||
let audio = new Audio(key);
|
||||
return audio;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import Scene from "../Scene";
|
||||
import CanvasItem from "../../Nodes/CanvasNode"
|
||||
import SceneGraph from "../../SceneGraph/SceneGraph";
|
||||
import UIElement from "../../Nodes/UIElement";
|
||||
import Layer from "../Layer";
|
||||
import Graphic from "../../Nodes/Graphic";
|
||||
import Sprite from "../../Nodes/Sprites/Sprite";
|
||||
|
||||
export default class CanvasNodeFactory {
|
||||
private scene: Scene;
|
||||
|
@ -27,8 +27,8 @@ export default class CanvasNodeFactory {
|
|||
return instance;
|
||||
}
|
||||
|
||||
addSprite = <T extends CanvasItem>(constr: new (...a: any) => T, layer: Layer, ...args: any): T => {
|
||||
let instance = new constr(...args);
|
||||
addSprite = (imageId: string, layer: Layer, ...args: any): Sprite => {
|
||||
let instance = new Sprite(imageId);
|
||||
|
||||
// Add instance to scene
|
||||
instance.setScene(this.scene);
|
||||
|
|
|
@ -2,20 +2,23 @@ import Scene from "../Scene";
|
|||
import PhysicsNodeFactory from "./PhysicsNodeFactory";
|
||||
import CanvasNodeFactory from "./CanvasNodeFactory";
|
||||
import TilemapFactory from "./TilemapFactory";
|
||||
import AudioFactory from "./AudioFactory";
|
||||
import PhysicsManager from "../../Physics/PhysicsManager";
|
||||
import SceneGraph from "../../SceneGraph/SceneGraph";
|
||||
import Tilemap from "../../Nodes/Tilemap";
|
||||
|
||||
export default class FactoryManager {
|
||||
|
||||
private canvasNodeFactory: CanvasNodeFactory = new CanvasNodeFactory();;
|
||||
private physicsNodeFactory: PhysicsNodeFactory = new PhysicsNodeFactory();;
|
||||
private tilemapFactory: TilemapFactory = new TilemapFactory();;
|
||||
private canvasNodeFactory: CanvasNodeFactory = new CanvasNodeFactory();
|
||||
private physicsNodeFactory: PhysicsNodeFactory = new PhysicsNodeFactory();
|
||||
private tilemapFactory: TilemapFactory = new TilemapFactory();
|
||||
private audioFactory: AudioFactory = new AudioFactory();
|
||||
|
||||
constructor(scene: Scene, sceneGraph: SceneGraph, physicsManager: PhysicsManager, tilemaps: Array<Tilemap>){
|
||||
this.canvasNodeFactory.init(scene, sceneGraph);
|
||||
this.physicsNodeFactory.init(scene, physicsManager);
|
||||
this.tilemapFactory.init(scene, tilemaps, physicsManager);
|
||||
this.audioFactory.init(scene);
|
||||
}
|
||||
|
||||
uiElement = this.canvasNodeFactory.addUIElement;
|
||||
|
@ -23,4 +26,5 @@ export default class FactoryManager {
|
|||
graphic = this.canvasNodeFactory.addGraphic;
|
||||
physics = this.physicsNodeFactory.add;
|
||||
tilemap = this.tilemapFactory.add;
|
||||
audio = this.audioFactory.addAudio;
|
||||
}
|
26
src/Sound/Audio.ts
Normal file
26
src/Sound/Audio.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import AudioManager from "./AudioManager";
|
||||
|
||||
export default class Audio {
|
||||
private key: string;
|
||||
private sound: AudioBufferSourceNode;
|
||||
|
||||
constructor(key: string){
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
play(loop?: boolean){
|
||||
this.sound = AudioManager.getInstance().createSound(this.key);
|
||||
|
||||
if(loop){
|
||||
this.sound.loop = true;
|
||||
}
|
||||
|
||||
this.sound.start();
|
||||
}
|
||||
|
||||
stop(){
|
||||
if(this.sound){
|
||||
this.sound.stop();
|
||||
}
|
||||
}
|
||||
}
|
50
src/Sound/AudioManager.ts
Normal file
50
src/Sound/AudioManager.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
|
||||
export default class AudioManager {
|
||||
private static instance: AudioManager;
|
||||
|
||||
private audioCtx: AudioContext;
|
||||
|
||||
private constructor(){
|
||||
this.initAudio();
|
||||
}
|
||||
|
||||
public static getInstance(): AudioManager {
|
||||
if(!this.instance){
|
||||
this.instance = new AudioManager();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
private initAudio(): void {
|
||||
try {
|
||||
window.AudioContext = window.AudioContext;// || window.webkitAudioContext;
|
||||
this.audioCtx = new AudioContext();
|
||||
console.log('Web Audio API successfully loaded');
|
||||
} catch(e) {
|
||||
console.log('Web Audio API is not supported in this browser');
|
||||
}
|
||||
}
|
||||
|
||||
public getAudioContext(): AudioContext {
|
||||
return this.audioCtx;
|
||||
}
|
||||
|
||||
createSound(key: string): AudioBufferSourceNode {
|
||||
// Get audio buffer
|
||||
let buffer = ResourceManager.getInstance().getAudio(key);
|
||||
|
||||
// creates a sound source
|
||||
var source = this.audioCtx.createBufferSource();
|
||||
|
||||
// tell the source which sound to play
|
||||
source.buffer = buffer;
|
||||
|
||||
// connect the source to the context's destination
|
||||
// i.e. the speakers
|
||||
source.connect(this.audioCtx.destination);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
"files": [
|
||||
"src/main.ts",
|
||||
"src/Player.ts",
|
||||
"src/PlayerSprite.ts",
|
||||
|
||||
"src/DataTypes/Tilesets/TiledData.ts",
|
||||
"src/DataTypes/Tilesets/Tileset.ts",
|
||||
|
@ -33,9 +34,7 @@
|
|||
"src/Nodes/UIElements/Button.ts",
|
||||
"src/Nodes/UIElements/Label.ts",
|
||||
"src/Nodes/CanvasNode.ts",
|
||||
"src/ColoredCircle.ts",
|
||||
"src/Nodes/GameNode.ts",
|
||||
"src/PlayerSprite.ts",
|
||||
"src/Nodes/Tilemap.ts",
|
||||
"src/Nodes/UIElement.ts",
|
||||
|
||||
|
@ -52,6 +51,8 @@
|
|||
"src/SceneGraph/SceneGraphArray.ts",
|
||||
"src/SceneGraph/Viewport.ts",
|
||||
|
||||
"src/Sound/AudioManager.ts",
|
||||
|
||||
"src/Utils/Color.ts",
|
||||
"src/Utils/MathUtils.ts",
|
||||
"src/Utils/RandUtils.ts",
|
||||
|
|
Loading…
Reference in New Issue
Block a user