diff --git a/Phaser/Game.ts b/Phaser/Game.ts index c996087f..c2e373ac 100644 --- a/Phaser/Game.ts +++ b/Phaser/Game.ts @@ -14,6 +14,7 @@ /// /// /// +/// /// /// /// @@ -298,12 +299,12 @@ module Phaser { this.stage = new Stage(this, parent, width, height); this.world = new World(this, width, height); this.add = new GameObjectFactory(this); - this.sound = new SoundManager(this); this.cache = new Cache(this); this.load = new Loader(this, this.loadComplete); this.time = new Time(this); this.tweens = new TweenManager(this); this.input = new Input(this); + this.sound = new SoundManager(this); this.rnd = new RandomDataGenerator([(Date.now() * Math.random()).toString()]); this.physics = new Physics.Manager(this); @@ -391,6 +392,7 @@ module Phaser { this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); if (this.onPausedCallback !== null) { @@ -407,6 +409,7 @@ module Phaser { this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); this.physics.update(); this.world.update(); @@ -636,6 +639,7 @@ module Phaser { if (value == true && this._paused == false) { this._paused = true; + this.sound.pauseAll(); this._raf.callback = this.pausedLoop; } else if (value == false && this._paused == true) @@ -643,6 +647,7 @@ module Phaser { this._paused = false; //this.time.time = window.performance.now ? (performance.now() + performance.timing.navigationStart) : Date.now(); this.input.reset(); + this.sound.resumeAll(); if (this.isRunning == false) { diff --git a/Phaser/Stage.ts b/Phaser/Stage.ts index d147a03a..e3b251ae 100644 --- a/Phaser/Stage.ts +++ b/Phaser/Stage.ts @@ -61,6 +61,8 @@ module Phaser { document.addEventListener('visibilitychange', (event) => this.visibilityChange(event), false); document.addEventListener('webkitvisibilitychange', (event) => this.visibilityChange(event), false); + document.addEventListener('pagehide', (event) => this.visibilityChange(event), false); + document.addEventListener('pageshow', (event) => this.visibilityChange(event), false); window.onblur = (event) => this.visibilityChange(event); window.onfocus = (event) => this.visibilityChange(event); @@ -211,24 +213,27 @@ module Phaser { */ private visibilityChange(event) { - if (this.disablePauseScreen) + if (event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) { - return; - } - - if (event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) - { - if (this._game.paused == false) + if (this._game.paused == false && this.disablePauseScreen == false) { this.pauseGame(); } + else + { + this._game.paused = true; + } } else { - if (this._game.paused == true) + if (this._game.paused == true && this.disablePauseScreen == false) { this.resumeGame(); } + else + { + this._game.paused = false; + } } } diff --git a/Phaser/Statics.ts b/Phaser/Statics.ts index 8eb0656c..af1e0e3e 100644 --- a/Phaser/Statics.ts +++ b/Phaser/Statics.ts @@ -30,6 +30,10 @@ module Phaser { static BODY_KINETIC: number = 2; static BODY_DYNAMIC: number = 3; + static OUT_OF_BOUNDS_KILL: number = 0; + static OUT_OF_BOUNDS_DESTROY: number = 1; + static OUT_OF_BOUNDS_PERSIST: number = 2; + /** * Flag used to allow GameObjects to collide on their left side * @type {number} diff --git a/Phaser/components/sprite/Events.ts b/Phaser/components/sprite/Events.ts index d1619f65..7a66c3ef 100644 --- a/Phaser/components/sprite/Events.ts +++ b/Phaser/components/sprite/Events.ts @@ -23,6 +23,7 @@ module Phaser.Components.Sprite { this.onRemovedFromGroup = new Phaser.Signal; this.onKilled = new Phaser.Signal; this.onRevived = new Phaser.Signal; + this.onOutOfBounds = new Phaser.Signal; } @@ -101,6 +102,9 @@ module Phaser.Components.Sprite { */ public onAnimationLoop: Phaser.Signal; + /** + * Dispatched by the Sprite when it first leaves the world bounds + */ public onOutOfBounds: Phaser.Signal; } diff --git a/Phaser/gameobjects/Button.ts b/Phaser/gameobjects/Button.ts index c435935a..98c35fcf 100644 --- a/Phaser/gameobjects/Button.ts +++ b/Phaser/gameobjects/Button.ts @@ -19,9 +19,14 @@ module Phaser { * Create a new Button object. * * @param game {Phaser.Game} Current game instance. - * @param [x] {number} the initial x position of the button. - * @param [y] {number} the initial y position of the button. - * @param [key] {string} Key of the graphic you want to load for this button. + * @param [x] {number} X position of the button. + * @param [y] {number} Y position of the button. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this button. + * @param [callback] {function} The function to call when this button is pressed + * @param [callbackContext] {object} The context in which the callback will be called (usually 'this') + * @param [overFrame] {string|number} This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name. + * @param [outFrame] {string|number} This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name. + * @param [downFrame] {string|number} This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name. */ constructor(game: Game, x?: number = 0, y?: number = 0, key?: string = null, callback? = null, callbackContext? = null, overFrame? = null, outFrame? = null, downFrame? = null) { diff --git a/Phaser/gameobjects/GameObjectFactory.ts b/Phaser/gameobjects/GameObjectFactory.ts index e6251ada..78e981a3 100644 --- a/Phaser/gameobjects/GameObjectFactory.ts +++ b/Phaser/gameobjects/GameObjectFactory.ts @@ -94,6 +94,10 @@ module Phaser { return this._world.group.add(new Sprite(this._game, x, y, key, frame, bodyType)); } + public audio(key: string, volume?: number = 1, loop?: bool = false) { + return this._game.sound.add(key, volume, loop); + } + /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -182,13 +186,14 @@ module Phaser { } /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param obj Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - public tween(obj): Tween { - return this._game.tweens.create(obj); + public tween(obj, localReference?:bool = false): Tween { + return this._game.tweens.create(obj, localReference); } /** @@ -202,6 +207,17 @@ module Phaser { return this._world.group.add(sprite); } + /** + * Add an existing Button to the current world. + * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. + * + * @param button The Button to add to the Game World + * @return {Phaser.Button} The Button object + */ + public existingButton(button: Button): Button { + return this._world.group.add(button); + } + /** * Add an existing GeomSprite to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. diff --git a/Phaser/gameobjects/Sprite.ts b/Phaser/gameobjects/Sprite.ts index f8e2f012..aa1ed7eb 100644 --- a/Phaser/gameobjects/Sprite.ts +++ b/Phaser/gameobjects/Sprite.ts @@ -81,6 +81,9 @@ module Phaser { this.transform.setCache(); + this.outOfBounds = false; + this.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST; + // Handy proxies this.scale = this.transform.scale; this.alpha = this.texture.alpha; @@ -128,6 +131,17 @@ module Phaser { */ public alive: bool; + /** + * Is the Sprite out of the world bounds or not? + */ + public outOfBounds: bool; + + /** + * The action to be taken when the sprite is fully out of the world bounds + * Defaults to Phaser.Types.OUT_OF_BOUNDS_KILL + */ + public outOfBoundsAction: number; + /** * Sprite physics body. */ @@ -171,6 +185,11 @@ module Phaser { */ public cameraView: Phaser.Rectangle; + /** + * A local tween variable. Used by the TweenManager when setting a tween on this sprite or a property of it. + */ + public tween: Phaser.Tween; + /** * A boolean representing if the Sprite has been modified in any way via a scale, rotate, flip or skew. */ @@ -314,44 +333,34 @@ module Phaser { } /** - * Automatically called after update() by the game loop. + * Automatically called after update() by the game loop for all 'alive' objects. */ public postUpdate() { this.animations.update(); - /* - if (this.worldBounds != null) + if (Phaser.RectangleUtils.intersects(this.worldView, this.game.world.bounds)) { - if (this.outOfBoundsAction == GameObject.OUT_OF_BOUNDS_KILL) + this.outOfBounds = false; + } + else + { + if (this.outOfBounds == false) { - if (this.x < this.worldBounds.x || this.x > this.worldBounds.right || this.y < this.worldBounds.y || this.y > this.worldBounds.bottom) - { - this.kill(); - } + this.events.onOutOfBounds.dispatch(this); } - else - { - if (this.x < this.worldBounds.x) - { - this.x = this.worldBounds.x; - } - else if (this.x > this.worldBounds.right) - { - this.x = this.worldBounds.right; - } - if (this.y < this.worldBounds.y) - { - this.y = this.worldBounds.y; - } - else if (this.y > this.worldBounds.bottom) - { - this.y = this.worldBounds.bottom; - } + this.outOfBounds = true; + + if (this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_KILL) + { + this.kill(); + } + else if (this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_DESTROY) + { + this.destroy(); } } - */ if (this.modified == true && this.transform.scale.equals(1) && this.transform.skew.equals(0) && this.transform.rotation == 0 && this.transform.rotationOffset == 0 && this.texture.flippedX == false && this.texture.flippedY == false) { diff --git a/Phaser/input/Input.ts b/Phaser/input/Input.ts index 50596489..b427341c 100644 --- a/Phaser/input/Input.ts +++ b/Phaser/input/Input.ts @@ -275,7 +275,7 @@ module Phaser { * @property recordPointerHistory * @type {Boolean} **/ - public recordPointerHistory: bool = true; + public recordPointerHistory: bool = false; /** * The rate in milliseconds at which the Pointer objects should update their tracking history @@ -286,7 +286,7 @@ module Phaser { /** * The total number of entries that can be recorded into the Pointer objects tracking history. - * The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. + * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. * @property recordLimit * @type {Number} */ diff --git a/Phaser/input/Pointer.ts b/Phaser/input/Pointer.ts index 288346e4..53add492 100644 --- a/Phaser/input/Pointer.ts +++ b/Phaser/input/Pointer.ts @@ -33,6 +33,10 @@ module Phaser { } + private _highestRenderOrderID: number; + private _highestRenderObject: number; + private _highestInputPriorityID: number; + /** * Local private reference to game. * @property game @@ -239,6 +243,13 @@ module Phaser { **/ public totalTouches: number = 0; + /** + * The number of miliseconds since the last click + * @property msSinceLastClick + * @type {Number} + **/ + public msSinceLastClick: number = Number.MAX_VALUE; + /** * How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1. * @property duration @@ -306,7 +317,12 @@ module Phaser { this.withinGame = true; this.isDown = true; this.isUp = false; + + // Work out how long it has been since the last click + this.msSinceLastClick = this.game.time.now - this.timeDown; + this.timeDown = this.game.time.now; + this._holdSent = false; // This sets the x/y and other local values @@ -371,10 +387,6 @@ module Phaser { } - private _highestRenderOrderID: number; - private _highestRenderObject: number; - private _highestInputPriorityID: number; - /** * Called when the Pointer is moved on the touchscreen * @method move diff --git a/Phaser/input/Touch.ts b/Phaser/input/Touch.ts index 8d46b706..34d2e71c 100644 --- a/Phaser/input/Touch.ts +++ b/Phaser/input/Touch.ts @@ -79,9 +79,7 @@ module Phaser { * @param {Any} event **/ private consumeTouchMove(event) { - event.preventDefault(); - } /** diff --git a/Phaser/loader/Cache.ts b/Phaser/loader/Cache.ts index 7e82516f..c4f15c8a 100644 --- a/Phaser/loader/Cache.ts +++ b/Phaser/loader/Cache.ts @@ -122,22 +122,68 @@ module Phaser { * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ - public addSound(key: string, url: string, data) { + public addSound(key: string, url: string, data, webAudio: bool = true, audioTag: bool = false) { - this._sounds[key] = { url: url, data: data, decoded: false }; + console.log('Cache addSound: ' + key + ' url: ' + url, webAudio, audioTag); + + var locked: bool = this._game.sound.touchLocked; + var decoded: bool = false; + + if (audioTag) { + decoded = true; + } + + this._sounds[key] = { url: url, data: data, locked: locked, isDecoding: false, decoded: decoded, webAudio: webAudio, audioTag: audioTag }; + + } + + public reloadSound(key: string) { + + console.log('reloadSound', key); + + if (this._sounds[key]) + { + this._sounds[key].data.src = this._sounds[key].url; + this._sounds[key].data.addEventListener('canplaythrough', () => this.reloadSoundComplete(key), false); + this._sounds[key].data.load(); + } + + } + + public onSoundUnlock: Phaser.Signal = new Phaser.Signal; + + public reloadSoundComplete(key: string) { + + console.log('reloadSoundComplete', key); + + if (this._sounds[key]) + { + this._sounds[key].locked = false; + this.onSoundUnlock.dispatch(key); + } + + } + + public updateSound(key: string, property: string, value) { + + if (this._sounds[key]) + { + this._sounds[key][property] = value; + } } /** * Add a new decoded sound. * @param key {string} Asset key for the sound. - * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ public decodedSound(key: string, data) { + console.log('decoded sound', key); this._sounds[key].data = data; this._sounds[key].decoded = true; + this._sounds[key].isDecoding = false; } @@ -201,12 +247,28 @@ module Phaser { } + /** + * Get sound by key. + * @param key Asset key of the sound you want. + * @return {object} The sound you want. + */ + public getSound(key: string) { + + if (this._sounds[key]) + { + return this._sounds[key]; + } + + return null; + + } + /** * Get sound data by key. * @param key Asset key of the sound you want. * @return {object} The sound data you want. */ - public getSound(key: string) { + public getSoundData(key: string) { if (this._sounds[key]) { @@ -231,6 +293,22 @@ module Phaser { } + /** + * Check whether an asset is decoded sound. + * @param key Asset key of the sound you want. + * @return {object} The sound data you want. + */ + public isSoundReady(key: string): bool { + + if (this._sounds[key] && this._sounds[key].decoded == true && this._sounds[key].locked == false) + { + return true; + } + + return false; + + } + /** * Check whether an asset is sprite sheet. * @param key Asset key of the sprite sheet you want. diff --git a/Phaser/loader/Loader.ts b/Phaser/loader/Loader.ts index 0218c5a8..ffd736ec 100644 --- a/Phaser/loader/Loader.ts +++ b/Phaser/loader/Loader.ts @@ -218,14 +218,15 @@ module Phaser { /** * Add a new audio file loading request. * @param key {string} Unique asset key of the audio file. - * @param url {string} URL of audio file. + * @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ] + * @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process. */ - public audio(key: string, url: string) { + public audio(key: string, urls: string[], autoDecode: bool = true) { if (this.checkKeyExists(key) === false) { this._queueSize++; - this._fileList[key] = { type: 'audio', key: key, url: url, data: null, buffer: null, error: false, loaded: false }; + this._fileList[key] = { type: 'audio', key: key, url: urls, data: null, buffer: null, error: false, loaded: false, autoDecode: autoDecode }; this._keys.push(key); } @@ -327,11 +328,48 @@ module Phaser { break; case 'audio': - this._xhr.open("GET", file.url, true); - this._xhr.responseType = "arraybuffer"; - this._xhr.onload = () => this.fileComplete(file.key); - this._xhr.onerror = () => this.fileError(file.key); - this._xhr.send(); + + file.url = this.getAudioURL(file.url); + console.log('Loader audio'); + console.log(file.url); + + if (file.url !== null) + { + // WebAudio or Audio Tag? + if (this._game.sound.usingWebAudio) + { + this._xhr.open("GET", file.url, true); + this._xhr.responseType = "arraybuffer"; + this._xhr.onload = () => this.fileComplete(file.key); + this._xhr.onerror = () => this.fileError(file.key); + this._xhr.send(); + } + else if (this._game.sound.usingAudioTag) + { + if (this._game.sound.touchLocked) + { + // If audio is locked we can't do this yet, so need to queue this load request somehow. Bum. + console.log('Audio is touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.preload = 'auto'; + file.data.src = file.url; + this.fileComplete(file.key); + } + else + { + console.log('Audio not touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.onerror = () => this.fileError(file.key); + file.data.preload = 'auto'; + file.data.src = file.url; + file.data.addEventListener('canplaythrough', () => this.fileComplete(file.key), false); + file.data.load(); + } + } + } + break; case 'text': @@ -345,6 +383,28 @@ module Phaser { } + private getAudioURL(urls): string { + + var extension: string; + + for (var i = 0; i < urls.length; i++) + { + extension = urls[i].toLowerCase(); + extension = extension.substr((Math.max(0, extension.lastIndexOf(".")) || Infinity) + 1); + + if (this._game.device.canPlayAudio(extension)) + { + console.log('getAudioURL', urls[i]); + console.log(urls[i]); + return urls[i]; + } + + } + + return null; + + } + /** * Error occured when load a file. * @param key {string} Key of the error loading file. @@ -406,8 +466,32 @@ module Phaser { break; case 'audio': - file.data = this._xhr.response; - this._game.cache.addSound(file.key, file.url, file.data); + + if (this._game.sound.usingWebAudio) + { + file.data = this._xhr.response; + + this._game.cache.addSound(file.key, file.url, file.data, true, false); + + if (file.autoDecode) + { + this._game.cache.updateSound(key, 'isDecoding', true); + + var that = this; + var key = file.key; + + this._game.sound.context.decodeAudioData(file.data, function (buffer) { + if (buffer) + { + that._game.cache.decodedSound(key, buffer); + } + }); + } + } + else + { + this._game.cache.addSound(file.key, file.url, file.data, false, true); + } break; case 'text': diff --git a/Phaser/physics/Body.ts b/Phaser/physics/Body.ts index c4758a03..e3468674 100644 --- a/Phaser/physics/Body.ts +++ b/Phaser/physics/Body.ts @@ -340,6 +340,16 @@ module Phaser.Physics { } + private _newPosition: Phaser.Vec2 = new Phaser.Vec2; + + public setPosition(x: number, y: number) { + + this._newPosition.setTo(this.game.physics.pixelsToMeters(x), this.game.physics.pixelsToMeters(y)); + + this.setTransform(this._newPosition, this.angle); + + } + public setTransform(pos:Phaser.Vec2, angle:number) { // inject the transform into this.position diff --git a/Phaser/sound/Sound.ts b/Phaser/sound/Sound.ts index 83670f07..d060129f 100644 --- a/Phaser/sound/Sound.ts +++ b/Phaser/sound/Sound.ts @@ -13,100 +13,376 @@ module Phaser { /** * Sound constructor - * @param context {object} The AudioContext instance. - * @param gainNode {object} Gain node instance. - * @param data {object} Sound data. * @param [volume] {number} volume of this sound when playing. * @param [loop] {boolean} loop this sound when playing? (Default to false) */ - constructor(context, gainNode, data, volume?: number = 1, loop?: bool = false) { + constructor(game: Phaser.Game, key: string, volume?: number = 1, loop?: bool = false) { - this._context = context; - this._gainNode = gainNode; - this._buffer = data; - this._volume = volume; - this.loop = loop; + this.game = game; - // Local volume control - if (this._context !== null) + this.usingWebAudio = this.game.sound.usingWebAudio; + this.usingAudioTag = this.game.sound.usingAudioTag; + this.key = key; + + if (this.usingWebAudio) { - this._localGainNode = this._context.createGainNode(); - this._localGainNode.connect(this._gainNode); - this._localGainNode.gain.value = this._volume; - } + this.context = this.game.sound.context; + this.masterGainNode = this.game.sound.masterGain; - if (this._buffer === null) - { - this.isDecoding = true; + if (typeof this.context.createGain === 'undefined') + { + this.gainNode = this.context.createGainNode(); + } + else + { + this.gainNode = this.context.createGain(); + } + + this.gainNode.gain.value = volume * this.game.sound.volume; + this.gainNode.connect(this.masterGainNode); } else { - this.play(); + if (this.game.cache.getSound(key).locked == false) + { + this._sound = this.game.cache.getSoundData(key); + this.totalDuration = this._sound.duration; + } + else + { + this.game.cache.onSoundUnlock.add(this.soundHasUnlocked, this); + } + } + + this._volume = volume; + this.loop = loop; + this.markers = {}; + + this.onDecoded = new Phaser.Signal; + this.onPlay = new Phaser.Signal; + this.onPause = new Phaser.Signal; + this.onResume = new Phaser.Signal; + this.onLoop = new Phaser.Signal; + this.onStop = new Phaser.Signal; + this.onMute = new Phaser.Signal; + + } + + private soundHasUnlocked(key:string) { + + if (key == this.key) + { + this._sound = this.game.cache.getSoundData(this.key); + this.totalDuration = this._sound.duration; + console.log('sound has unlocked', this._sound); } } /** - * Local private reference to AudioContext. + * Local reference to the current Phaser.Game. */ - private _context; + public game: Game; + + /** + * Reference to AudioContext instance. + */ + public context = null; + /** * Reference to gain node of SoundManager. */ - private _gainNode; + public masterGainNode; + /** * GainNode of this sound. */ - private _localGainNode; + public gainNode; + /** - * Decoded data buffer. + * Decoded data buffer / Audio tag. */ private _buffer; + /** * Volume of this sound. */ private _volume: number; + /** * The real sound object (buffer source). */ private _sound; + private _muteVolume: number; + private _muted: bool = false; + private _tempPosition: number; + private _tempVolume: number; + private _tempLoop: bool; + private _tempMarker: string; + + public usingWebAudio: bool = false; + public usingAudioTag: bool = false; + + public name: string = ''; + + autoplay: bool = false; + totalDuration: number = 0; + startTime: number = 0; + currentTime: number = 0; + duration: number = 0; + stopTime: number = 0; + position: number; + paused: bool = false; loop: bool = false; - duration: number; isPlaying: bool = false; - isDecoding: bool = false; + key: string; + markers; + currentMarker: string = ''; - public setDecodedBuffer(data) { + // events + public onDecoded: Phaser.Signal; + public onPlay: Phaser.Signal; + public onPause: Phaser.Signal; + public onResume: Phaser.Signal; + public onLoop: Phaser.Signal; + public onStop: Phaser.Signal; + public onMute: Phaser.Signal; - this._buffer = data; - this.isDecoding = false; - //this.play(); + public pendingPlayback: bool = false; + + public get isDecoding(): bool { + return this.game.cache.getSound(this.key).isDecoding; + } + + public get isDecoded(): bool { + return this.game.cache.isSoundDecoded(this.key); + } + + public addMarker(name: string, start: number, stop: number, volume: number = 1, loop: bool = false) { + this.markers[name] = { name: name, start: start, stop: stop, volume: volume, duration: stop - start, loop: loop }; + } + + public removeMarker(name: string) { + delete this.markers[name]; + } + + public update() { + + if (this.pendingPlayback && this.game.cache.isSoundReady(this.key)) + { + console.log('pending over'); + this.pendingPlayback = false; + this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop); + } + + if (this.isPlaying) + { + this.currentTime = this.game.time.now - this.startTime; + + if (this.currentTime >= this.duration) + { + if (this.usingWebAudio) + { + if (this.loop) + { + this.onLoop.dispatch(this); + this.currentTime = 0; + this.startTime = this.game.time.now; + } + else + { + this.stop(); + } + } + else + { + if (this.loop) + { + this.onLoop.dispatch(this); + this.play(this.currentMarker, 0, this.volume, true, true); + } + else + { + this.stop(); + } + } + } + + } } /** - * Play this sound. + * Play this sound, or a marked section of it. + * @param marker {string} Assets key of the sound you want to play. + * @param [volume] {number} volume of the sound you want to play. + * @param [loop] {boolean} loop when it finished playing? (Default to false) + * @return {Sound} The playing sound object. */ - public play() { + public play(marker: string = '', position?: number = 0, volume?: number = 1, loop?: bool = false, forceRestart: bool = false) { - if (this._buffer === null || this.isDecoding === true) + if (this.isPlaying == true && forceRestart == false) { + // Use Restart instead return; } - this._sound = this._context.createBufferSource(); - this._sound.buffer = this._buffer; - this._sound.connect(this._localGainNode); + this.currentMarker = marker; - if (this.loop) + if (marker !== '' && this.markers[marker]) { - this._sound.loop = true; + this.position = this.markers[marker].start; + this.volume = this.markers[marker].volume; + this.loop = this.markers[marker].loop; + this.duration = this.markers[marker].duration * 1000; + } + else + { + this.position = position; + this.volume = volume; + this.loop = loop; + this.duration = 0; } - this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + this._tempMarker = marker; + this._tempPosition = position; + this._tempVolume = volume; + this._tempLoop = loop; - this.duration = this._sound.buffer.duration; - this.isPlaying = true; + if (this.usingWebAudio) + { + // Does the sound need decoding? + if (this.game.cache.isSoundDecoded(this.key)) + { + // Do we need to do this every time we play? How about just if the buffer is empty? + this._buffer = this.game.cache.getSoundData(this.key); + this._sound = this.context.createBufferSource(); + this._sound.buffer = this._buffer; + this._sound.connect(this.gainNode); + this.totalDuration = this._sound.buffer.duration; + + if (this.duration == 0) + { + this.duration = this.totalDuration * 1000; + } + + if (this.loop) + { + this._sound.loop = true; + } + + // Useful to cache this somewhere perhaps? + if (typeof this._sound.start === 'undefined') + { + this._sound.noteGrainOn(0, this.position, this.duration / 1000); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } + else + { + this._sound.start(0, this.position, this.duration / 1000); + } + + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } + else + { + this.pendingPlayback = true; + + if (this.game.cache.getSound(this.key).isDecoding == false) + { + this.game.sound.decode(this.key, this); + } + } + } + else + { + console.log('Sound play Audio'); + + if (this.game.cache.getSound(this.key).locked) + { + console.log('tried playing locked sound, pending set, reload started'); + this.game.cache.reloadSound(this.key); + this.pendingPlayback = true; + } + else + { + console.log('sound not locked, state?', this._sound.readyState); + if (this._sound && this._sound.readyState == 4) + { + if (this.duration == 0) + { + this.duration = this.totalDuration * 1000; + } + + console.log('playing', this._sound); + this._sound.currentTime = this.position; + this._sound.muted = this._muted; + this._sound.volume = this._volume; + this._sound.play(); + + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } + else + { + this.pendingPlayback = true; + } + } + + } + + } + + public restart(marker: string = '', position?: number = 0, volume?: number = 1, loop?: bool = false) { + this.play(marker, position, volume, loop, true); + } + + public pause() { + + if (this.isPlaying && this._sound) + { + this.stop(); + this.isPlaying = false; + this.paused = true; + this.onPause.dispatch(this); + } + + } + + public resume() { + + //if (this.isPlaying == false && this._sound) + if (this.paused && this._sound) + { + if (this.usingWebAudio) + { + if (typeof this._sound.start === 'undefined') + { + this._sound.noteGrainOn(0, this.position, this.duration); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } + else + { + this._sound.start(0, this.position, this.duration); + } + } + else + { + this._sound.play(); + } + + this.isPlaying = true; + this.paused = false; + this.onResume.dispatch(this); + } } @@ -115,37 +391,87 @@ module Phaser { */ public stop() { - if (this.isPlaying === true) + if (this.isPlaying && this._sound) { - this.isPlaying = false; + if (this.usingWebAudio) + { + if (typeof this._sound.stop === 'undefined') + { + this._sound.noteOff(0); + } + else + { + this._sound.stop(0); + } + } + else if (this.usingAudioTag) + { + this._sound.pause(); + this._sound.currentTime = 0; + } + + this.isPlaying = false; + this.currentMarker = ''; + this.onStop.dispatch(this); - this._sound.noteOff(0); } } /** - * Mute the sound. + * Mute sounds. */ - public mute() { - - this._localGainNode.gain.value = 0; - + public get mute(): bool { + return this._muted; } - /** - * Enable the sound. - */ - public unmute() { + public set mute(value: bool) { - this._localGainNode.gain.value = this._volume; + if (value) + { + this._muted = true; + + if (this.usingWebAudio) + { + this._muteVolume = this.gainNode.gain.value; + this.gainNode.gain.value = 0; + } + else if (this.usingAudioTag && this._sound) + { + this._muteVolume = this._sound.volume; + this._sound.volume = 0; + } + } + else + { + this._muted = false; + + if (this.usingWebAudio) + { + this.gainNode.gain.value = this._muteVolume; + } + else if (this.usingAudioTag && this._sound) + { + this._sound.volume = this._muteVolume; + } + } + + this.onMute.dispatch(this); } public set volume(value: number) { this._volume = value; - this._localGainNode.gain.value = this._volume; + + if (this.usingWebAudio) + { + this.gainNode.gain.value = value; + } + else if (this.usingAudioTag && this._sound) + { + this._sound.volume = value; + } } @@ -153,6 +479,30 @@ module Phaser { return this._volume; } + /** + * Renders the Pointer.circle object onto the stage in green if down or red if up. + * @method renderDebug + */ + public renderDebug(x: number, y: number) { + + this.game.stage.context.fillStyle = 'rgb(255,255,255)'; + this.game.stage.context.font = '16px Courier'; + this.game.stage.context.fillText('Sound: ' + this.key + ' Locked: ' + this.game.sound.touchLocked + ' Pending Playback: ' + this.pendingPlayback, x, y); + this.game.stage.context.fillText('Decoded: ' + this.isDecoded + ' Decoding: ' + this.isDecoding, x, y + 20); + this.game.stage.context.fillText('Total Duration: ' + this.totalDuration + ' Playing: ' + this.isPlaying, x, y + 40); + this.game.stage.context.fillText('Time: ' + this.currentTime, x, y + 60); + this.game.stage.context.fillText('Volume: ' + this.volume + ' Muted: ' + this.mute, x, y + 80); + this.game.stage.context.fillText('WebAudio: ' + this.usingWebAudio + ' Audio: ' + this.usingAudioTag, x, y + 100); + + if (this.currentMarker !== '') + { + this.game.stage.context.fillText('Marker: ' + this.currentMarker + ' Duration: ' + this.duration, x, y + 120); + this.game.stage.context.fillText('Start: ' + this.markers[this.currentMarker].start + ' Stop: ' + this.markers[this.currentMarker].stop, x, y + 140); + this.game.stage.context.fillText('Position: ' + this.position, x, y + 160); + } + + } + } } \ No newline at end of file diff --git a/Phaser/sound/SoundManager.ts b/Phaser/sound/SoundManager.ts index 9e39e33c..c39a0dfa 100644 --- a/Phaser/sound/SoundManager.ts +++ b/Phaser/sound/SoundManager.ts @@ -17,48 +17,110 @@ module Phaser { */ constructor(game: Game) { - this._game = game; + this.game = game; - if (window['PhaserGlobal'] && window['PhaserGlobal'].disableAudio == true) + this._volume = 1; + this._muted = false; + this._sounds = []; + + if (this.game.device.iOS && this.game.device.webAudio == false) { - return; + this.channels = 1; } - if (game.device.webaudio == true) + if (this.game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock)) { - if (!!window['AudioContext']) + console.log('iOS Touch Locked'); + this.game.input.touch.callbackContext = this; + this.game.input.touch.touchStartCallback = this.unlock; + this.game.input.mouse.callbackContext = this; + this.game.input.mouse.mouseDownCallback = this.unlock; + this.touchLocked = true; + } + else + { + // What about iOS5? + this.touchLocked = false; + } + + if (window['PhaserGlobal']) + { + // Check to see if all audio playback is disabled (i.e. handled by a 3rd party class) + if (window['PhaserGlobal'].disableAudio == true) { - this._context = new window['AudioContext'](); - } - else if (!!window['webkitAudioContext']) - { - this._context = new window['webkitAudioContext'](); + this.usingWebAudio = false; + this.noAudio = true; + return; } - if (this._context !== null) + // Check if the Web Audio API is disabled (for testing Audio Tag playback during development) + if (window['PhaserGlobal'].disableWebAudio == true) { - this._gainNode = this._context.createGainNode(); - this._gainNode.connect(this._context.destination); - this._volume = 1; + this.usingWebAudio = false; + this.usingAudioTag = true; + this.noAudio = false; + return; } } + + this.usingWebAudio = true; + this.noAudio = false; + + if (!!window['AudioContext']) + { + this.context = new window['AudioContext'](); + } + else if (!!window['webkitAudioContext']) + { + this.context = new window['webkitAudioContext'](); + } + else if (!!window['Audio']) + { + this.usingWebAudio = false; + this.usingAudioTag = true; + } + else + { + this.usingWebAudio = false; + this.noAudio = true; + } + + if (this.context !== null) + { + if (typeof this.context.createGain === 'undefined') + { + this.masterGain = this.context.createGainNode(); + } + else + { + this.masterGain = this.context.createGain(); + } + + this.masterGain.gain.value = 1; + this.masterGain.connect(this.context.destination); + } + } + public usingWebAudio: bool = false; + public usingAudioTag: bool = false; + public noAudio: bool = false; + /** - * Local private reference to game. + * Local reference to the current Phaser.Game. */ - private _game: Game; + public game: Game; /** * Reference to AudioContext instance. */ - private _context = null; + public context = null; /** - * Gain node created from audio context. + * The Master Gain node through which all sounds */ - private _gainNode; + public masterGain; /** * Volume of sounds. @@ -66,100 +128,247 @@ module Phaser { */ private _volume: number; - /** - * Mute sounds. - */ - public mute() { + private _sounds: Phaser.Sound[]; - this._gainNode.gain.value = 0; + private _muteVolume: number; + private _muted: bool = false; + + public channels: number; + + public touchLocked: bool = false; + + private _unlockSource = null; + + public unlock() { + + if (this.touchLocked == false) + { + return; + } + + console.log('SoundManager touch unlocked'); + + if (this.game.device.webAudio && (window['PhaserGlobal'] && window['PhaserGlobal'].disableWebAudio == false)) + { + console.log('create empty buffer'); + // Create empty buffer and play it + var buffer = this.context.createBuffer(1, 1, 22050); + this._unlockSource = this.context.createBufferSource(); + this._unlockSource.buffer = buffer; + this._unlockSource.connect(this.context.destination); + this._unlockSource.noteOn(0); + } + else + { + // Create an Audio tag? + console.log('create audio tag'); + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + this.game.input.mouse.callbackContext = null; + this.game.input.mouse.mouseDownCallback = null; + } } /** - * Enable sounds. + * A global audio mute toggle. */ - public unmute() { + public get mute():bool { + return this._muted; + } - this._gainNode.gain.value = this._volume; + public set mute(value: bool) { + + if (value) + { + if (this._muted) + { + return; + } + + this._muted = true; + + if (this.usingWebAudio) + { + this._muteVolume = this.masterGain.gain.value; + this.masterGain.gain.value = 0; + } + + // Loop through sounds + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].mute = true; + } + } + } + else + { + if (this._muted == false) + { + return; + } + + this._muted = false; + + if (this.usingWebAudio) + { + this.masterGain.gain.value = this._muteVolume; + } + + // Loop through sounds + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].mute = false; + } + } + } } + /** + * The global audio volume. A value between 0 (silence) and 1 (full volume) + */ public set volume(value: number) { + value = this.game.math.clamp(value, 1, 0); + this._volume = value; - this._gainNode.gain.value = this._volume; + + if (this.usingWebAudio) + { + this.masterGain.gain.value = value; + } + + // Loop through the sound cache and change the volume of all html audio tags + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i].usingAudioTag) + { + this._sounds[i].volume = this._sounds[i].volume * value; + } + } } public get volume(): number { - return this._volume; + + if (this.usingWebAudio) + { + return this.masterGain.gain.value; + } + else + { + return this._volume; + } + + } + + public stopAll() { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].stop(); + } + } + + } + + public pauseAll() { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].pause(); + } + } + + } + + public resumeAll() { + + for (var i = 0; i < this._sounds.length; i++) + { + if (this._sounds[i]) + { + this._sounds[i].resume(); + } + } + } /** * Decode a sound with its assets key. * @param key {string} Assets key of the sound to be decoded. - * @param callback {function} This will be invoked when finished decoding. * @param [sound] {Sound} its bufer will be set to decoded data. */ - public decode(key: string, callback = null, sound?: Sound = null) { + public decode(key: string, sound?: Sound = null) { - var soundData = this._game.cache.getSound(key); + var soundData = this.game.cache.getSoundData(key); if (soundData) { - if (this._game.cache.isSoundDecoded(key) === false) + if (this.game.cache.isSoundDecoded(key) === false) { + this.game.cache.updateSound(key, 'isDecoding', true); + var that = this; - this._context.decodeAudioData(soundData, function (buffer) { - that._game.cache.decodedSound(key, buffer); + this.context.decodeAudioData(soundData, function (buffer) { + + that.game.cache.decodedSound(key, buffer); if (sound) { - sound.setDecodedBuffer(buffer); + that.onSoundDecode.dispatch(sound); } - - callback(); }); } } } - /** - * Play a sound with its assets key. - * @param key {string} Assets key of the sound you want to play. - * @param [volume] {number} volume of the sound you want to play. - * @param [loop] {boolean} loop when it finished playing? (Default to false) - * @return {Sound} The playing sound object. - */ - public play(key: string, volume?: number = 1, loop?: bool = false): Sound { + public onSoundDecode: Phaser.Signal = new Phaser.Signal; - if (this._context === null) + public update() { + + if (this.touchLocked) { - return; - } - - var soundData = this._game.cache.getSound(key); - - if (soundData) - { - // Does the sound need decoding? - if (this._game.cache.isSoundDecoded(key) === true) + if (this.game.device.webAudio && this._unlockSource !== null) { - return new Sound(this._context, this._gainNode, soundData, volume, loop); - } - else - { - var tempSound: Sound = new Sound(this._context, this._gainNode, null, volume, loop); - - // this is an async process, so we can return the Sound object anyway, it just won't be playing yet - this.decode(key, () => tempSound.play(), tempSound); - - return tempSound; + if ((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE)) + { + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + } } } + for (var i = 0; i < this._sounds.length; i++) + { + this._sounds[i].update(); + } + + } + + public add(key: string, volume?: number = 1, loop?: bool = false): Sound { + + var sound: Phaser.Sound = new Sound(this.game, key, volume, loop); + + this._sounds.push(sound); + + return sound; + } } diff --git a/Phaser/system/Device.ts b/Phaser/system/Device.ts index 2132bea1..cb6d71b5 100644 --- a/Phaser/system/Device.ts +++ b/Phaser/system/Device.ts @@ -191,41 +191,53 @@ module Phaser { // Audio /** - * Is audioData available? + * Are Audio tags available? * @type {boolean} */ public audioData: bool = false; /** - * Is webaudio available? + * Is the WebAudio API available? * @type {boolean} */ - public webaudio: bool = false; + public webAudio: bool = false; /** - * Is ogg available? + * Can this device play ogg files? * @type {boolean} */ public ogg: bool = false; /** - * Is mp3 available? + * Can this device play opus files? + * @type {boolean} + */ + public opus: bool = false; + + /** + * Can this device play mp3 files? * @type {boolean} */ public mp3: bool = false; /** - * Is wav available? + * Can this device play wav files? * @type {boolean} */ public wav: bool = false; /** - * Is m4a available? + * Can this device play m4a files? * @type {boolean} */ public m4a: bool = false; + /** + * Can this device play webm files? + * @type {boolean} + */ + public webm: bool = false; + // Device /** @@ -355,7 +367,7 @@ module Phaser { this.mobileSafari = true; } else if (/MSIE (\d+\.\d+);/.test(ua)) - { + { this.ie = true; this.ieVersion = parseInt(RegExp.$1); } @@ -380,6 +392,33 @@ module Phaser { } + public canPlayAudio(type: string): bool { + + if (type == 'mp3' && this.mp3) + { + return true; + } + else if (type == 'ogg' && (this.ogg || this.opus)) + { + return true; + } + else if (type == 'm4a' && this.m4a) + { + return true; + } + else if (type == 'wav' && this.wav) + { + return true; + } + else if (type == 'webm' && this.webm) + { + return true; + } + + return false; + + } + /** * Check audio support. * @private @@ -387,7 +426,7 @@ module Phaser { private _checkAudio() { this.audioData = !!(window['Audio']); - this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']); + this.webAudio = !!(window['webkitAudioContext'] || window['AudioContext']); var audioElement: HTMLAudioElement = document.createElement('audio'); var result = false; @@ -401,6 +440,11 @@ module Phaser { this.ogg = true; } + if (audioElement.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, '')) + { + this.opus = true; + } + if (audioElement.canPlayType('audio/mpeg;').replace(/^no$/, '')) { this.mp3 = true; @@ -418,6 +462,11 @@ module Phaser { { this.m4a = true; } + + if (audioElement.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')) + { + this.webm = true; + } } } catch (e) { } diff --git a/Phaser/tweens/TweenManager.ts b/Phaser/tweens/TweenManager.ts index 91d0ad31..9430cd1a 100644 --- a/Phaser/tweens/TweenManager.ts +++ b/Phaser/tweens/TweenManager.ts @@ -56,19 +56,28 @@ module Phaser { } /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param object {object} Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - public create(object): Phaser.Tween { + public create(object, localReference?:bool = false): Phaser.Tween { - return new Phaser.Tween(object, this._game); + if (localReference) + { + object['tween'] = new Phaser.Tween(object, this._game); + return object['tween']; + } + else + { + return new Phaser.Tween(object, this._game); + } } /** - * Add an exist tween object to the manager. + * Add a new tween into the TweenManager. * * @param tween {Phaser.Tween} The tween object you want to add. * @return {Phaser.Tween} The tween object you added to the manager. diff --git a/Phaser/utils/DebugUtils.ts b/Phaser/utils/DebugUtils.ts index 7ad62249..a4a522e3 100644 --- a/Phaser/utils/DebugUtils.ts +++ b/Phaser/utils/DebugUtils.ts @@ -58,13 +58,11 @@ module Phaser { if (body.shapes[0].verts.length > 0) { - DebugUtils.context.fillText('Vert 1 x: ' + (body.shapes[0].verts[0].x * 50) + ' y: ' + (body.shapes[0].verts[0].y * 50), x, y + 56); DebugUtils.context.fillText('Vert 2 x: ' + (body.shapes[0].verts[1].x * 50) + ' y: ' + (body.shapes[0].verts[1].y * 50), x, y + 70); DebugUtils.context.fillText('Vert 3 x: ' + (body.shapes[0].tverts[2].x * 50) + ' y: ' + (body.shapes[0].tverts[2].y * 50), x, y + 84); DebugUtils.context.fillText('Vert 4 x: ' + (body.shapes[0].tverts[3].x * 50) + ' y: ' + (body.shapes[0].tverts[3].y * 50), x, y + 98); - /* DebugUtils.context.fillText('Vert 1 x: ' + body.shapes[0].verts[0].x.toFixed(1) + ' y: ' + body.shapes[0].verts[0].y.toFixed(1), x, y + 56); DebugUtils.context.fillText('Vert 2 x: ' + body.shapes[0].verts[1].x.toFixed(1) + ' y: ' + body.shapes[0].verts[1].y.toFixed(1), x, y + 70); diff --git a/README.md b/README.md index 5f623da6..705e77a8 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,16 @@ TODO: * Camera control method (touch/keyboard) * Tilemap.destroy needs doing * Look at the input targetObject - it doesn't seem to get cleared down all the time, maybe just a priority/alpha issue (check vis/alpha?) +* Sprite.transform.bottomRight/Left doesn't seem to take origin into account +* When you toggle visible of a button that is over, it doesn't disable that 'over' state (should it? probably yes) +* Stage.opaqueBackground = 'rgb()' or null, alpha, blendMode, filters, mask, rotation+XYZ,scaleXYZ,visible + +* Need a way for Input event to redirect to audio to unlock playback +* AudioSprite object? +* How to get web audio to playback from an offset +* Stage lost to mute + + V1.0.0 @@ -142,6 +152,13 @@ V1.0.0 * Fixed bug where Sprite.alpha wasn't properly reflecting Sprite.texture.alpha. * Fixed bug where the hand cursor would be reset on input up, even if the mouse was still over the sprite. * Fixed bug where pressing down then moving out of the sprite area wouldn't properly reset the input state next time you moved over the sprite. +* Added the Sprite.tween property, really useful to avoid creating new tween vars in your local scope if you don't need them. +* Added support for pagehide and pageshow events to Stage, hooked in to the pause/resume game methods. +* Extended Device audio checks to include opus and webm. +* Updated Loader and Cache so they now support loading of Audio() tags as well as Web Audio. +* Set Input.recordPointerHistory to false, you now need to enable the pointer tracking if you wish to use it. +* SoundManager will now automatically handle iOS touch unlocking. + V0.9.6 diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index f57ac0db..bd659391 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -56,6 +56,14 @@ + + + + audio sprites 1.ts + + + play sound 1.ts + basic button 2.ts diff --git a/Tests/assets/mp3/oedipus_wizball_highscore.ogg b/Tests/assets/mp3/oedipus_wizball_highscore.ogg new file mode 100644 index 00000000..c9f7430f Binary files /dev/null and b/Tests/assets/mp3/oedipus_wizball_highscore.ogg differ diff --git a/Tests/assets/mp3/oedipus_wizball_highscore.xmp b/Tests/assets/mp3/oedipus_wizball_highscore.xmp new file mode 100644 index 00000000..cc00ef74 --- /dev/null +++ b/Tests/assets/mp3/oedipus_wizball_highscore.xmp @@ -0,0 +1,84 @@ + + + + + + + + CuePoint Markers + Cue + f44100 + + + Subclip Markers + InOut + f44100 + + + + + + 2013-07-16T02:39:59+01:00 + 2013-07-16T02:39:59+01:00 + + + xmp.iid:CF0377343CEDE211B46AA47009D9EF1D + xmp.did:CF0377343CEDE211B46AA47009D9EF1D + xmp.did:CE0377343CEDE211B46AA47009D9EF1D + + + + saved + xmp.iid:CE0377343CEDE211B46AA47009D9EF1D + 2013-07-16T02:39:59+01:00 + Adobe Audition CS6 (Windows) + /metadata + + + saved + xmp.iid:CF0377343CEDE211B46AA47009D9EF1D + 2013-07-16T02:39:59+01:00 + Adobe Audition CS6 (Windows) + / + + + + + xmp.iid:CE0377343CEDE211B46AA47009D9EF1D + xmp.did:CE0377343CEDE211B46AA47009D9EF1D + xmp.did:CE0377343CEDE211B46AA47009D9EF1D + + + + audio/ogg; codec="vorbis" + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/audio/audio sprites 1.js b/Tests/audio/audio sprites 1.js new file mode 100644 index 00000000..dd52c2ee --- /dev/null +++ b/Tests/audio/audio sprites 1.js @@ -0,0 +1,44 @@ +/// +/// +/// +//var PhaserGlobal = { disableWebAudio: true }; +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render); + function init() { + game.load.audio('rabbit', [ + 'assets/mp3/peter_rabbit.m4a', + 'assets/mp3/peter_rabbit.mp3', + 'assets/mp3/peter_rabbit.ogg' + ]); + game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71); + game.load.start(); + } + audioSprite: +Phaser.Sound + button: +Phaser.Button + pause: +Phaser.Button + function create() { + this.audioSprite = game.add.audio('rabbit'); + this.audioSprite.addMarker('title', 3.00, 5.00, 1, true); + this.audioSprite.addMarker('help', 6.00, 12.00); + this.audioSprite.addMarker('intro', 14.00, 19.00); + this.audioSprite.addMarker('peter', 20.00, 21.50); + this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0); + //this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0); + } + function playMusic() { + this.audioSprite.play('help'); + } + function render() { + this.audioSprite.renderDebug(32, 32); + } + function togglePause() { + if(this.music.paused) { + this.music.resume(); + } else { + this.music.pause(); + } + } +})(); diff --git a/Tests/audio/audio sprites 1.ts b/Tests/audio/audio sprites 1.ts new file mode 100644 index 00000000..e24f22da --- /dev/null +++ b/Tests/audio/audio sprites 1.ts @@ -0,0 +1,58 @@ +/// +/// +/// + +//var PhaserGlobal = { disableWebAudio: true }; + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render); + + function init() { + + game.load.audio('rabbit', ['assets/mp3/peter_rabbit.m4a', 'assets/mp3/peter_rabbit.mp3', 'assets/mp3/peter_rabbit.ogg']); + game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71); + game.load.start(); + + } + + audioSprite: Phaser.Sound; + button: Phaser.Button; + pause: Phaser.Button; + + function create() { + + this.audioSprite = game.add.audio('rabbit'); + this.audioSprite.addMarker('title', 3.00, 5.00, 1, true); + this.audioSprite.addMarker('help', 6.00, 12.00); + this.audioSprite.addMarker('intro', 14.00, 19.00); + this.audioSprite.addMarker('peter', 20.00, 21.50); + + this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0); + //this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0); + + } + + function playMusic() { + this.audioSprite.play('help'); + } + + function render() { + this.audioSprite.renderDebug(32, 32); + } + + function togglePause() { + + if (this.music.paused) + { + this.music.resume(); + } + else + { + this.music.pause(); + } + + } + + +})(); diff --git a/Tests/audio/play sound 1.js b/Tests/audio/play sound 1.js new file mode 100644 index 00000000..336e67e1 --- /dev/null +++ b/Tests/audio/play sound 1.js @@ -0,0 +1,55 @@ +/// +/// +/// +//var PhaserGlobal = { fakeiOSTouchLock: true, disableWebAudio: true }; +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render); + function init() { + //game.load.audio('wizball', ['assets/mp3/oedipus_wizball_highscore.ogg', 'assets/mp3/oedipus_wizball_highscore.mp3']); + game.load.audio('boden', [ + 'assets/mp3/bodenstaendig_2000_in_rock_4bit.mp3' + ]); + game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71); + game.load.start(); + } + button: +Phaser.Button + music: +Phaser.Sound + volumeUp: +Phaser.Button + volumeDown: +Phaser.Button + pause: +Phaser.Button + function create() { + this.music = game.add.audio('boden'); + this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0); + //this.volumeUp = game.add.button(0, 0, 'button', volUp, this, 2, 1, 0); + //this.volumeDown = game.add.button(700, 0, 'button', volDown, this, 2, 1, 0); + //this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0); + } + function render() { + this.music.renderDebug(0, 300); + } + function togglePause() { + if(this.music.paused) { + this.music.resume(); + } else { + this.music.pause(); + } + } + function volUp() { + //game.sound.volume += 0.1; + this.music.volume += 0.1; + console.log('vol up', game.sound.volume); + } + function volDown() { + //game.sound.volume -= 0.1; + this.music.volume -= 0.1; + console.log('vol down', game.sound.volume); + } + function playMusic() { + this.music.play(); + } +})(); diff --git a/Tests/audio/play sound 1.ts b/Tests/audio/play sound 1.ts new file mode 100644 index 00000000..59638671 --- /dev/null +++ b/Tests/audio/play sound 1.ts @@ -0,0 +1,70 @@ +/// +/// +/// + +//var PhaserGlobal = { fakeiOSTouchLock: true, disableWebAudio: true }; + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render); + + function init() { + + //game.load.audio('wizball', ['assets/mp3/oedipus_wizball_highscore.ogg', 'assets/mp3/oedipus_wizball_highscore.mp3']); + game.load.audio('boden', ['assets/mp3/bodenstaendig_2000_in_rock_4bit.mp3']); + game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71); + game.load.start(); + + } + + button: Phaser.Button; + music: Phaser.Sound; + volumeUp: Phaser.Button; + volumeDown: Phaser.Button; + pause: Phaser.Button; + + function create() { + + this.music = game.add.audio('boden'); + + this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0); + //this.volumeUp = game.add.button(0, 0, 'button', volUp, this, 2, 1, 0); + //this.volumeDown = game.add.button(700, 0, 'button', volDown, this, 2, 1, 0); + //this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0); + + } + + function render() { + this.music.renderDebug(0, 300); + } + + function togglePause() { + + if (this.music.paused) + { + this.music.resume(); + } + else + { + this.music.pause(); + } + + } + + function volUp() { + //game.sound.volume += 0.1; + this.music.volume += 0.1; + console.log('vol up', game.sound.volume); + } + + function volDown() { + //game.sound.volume -= 0.1; + this.music.volume -= 0.1; + console.log('vol down', game.sound.volume); + } + + function playMusic() { + this.music.play(); + } + +})(); diff --git a/Tests/phaser.js b/Tests/phaser.js index 788d7e91..18b3481f 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -1284,6 +1284,9 @@ var Phaser; Types.BODY_STATIC = 1; Types.BODY_KINETIC = 2; Types.BODY_DYNAMIC = 3; + Types.OUT_OF_BOUNDS_KILL = 0; + Types.OUT_OF_BOUNDS_DESTROY = 1; + Types.OUT_OF_BOUNDS_PERSIST = 2; Types.LEFT = 0x0001; Types.RIGHT = 0x0010; Types.UP = 0x0100; @@ -3961,6 +3964,7 @@ var Phaser; this.onRemovedFromGroup = new Phaser.Signal(); this.onKilled = new Phaser.Signal(); this.onRevived = new Phaser.Signal(); + this.onOutOfBounds = new Phaser.Signal(); } return Events; })(); @@ -6710,6 +6714,7 @@ var Phaser; this.categoryBits = 0x0001; this.maskBits = 0xFFFF; this.stepCount = 0; + this._newPosition = new Phaser.Vec2(); this.id = Phaser.Physics.Manager.bodyCounter++; this.name = 'body' + this.id; this.type = type; @@ -6888,6 +6893,10 @@ var Phaser; this.inertia = inertia; this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; }; + Body.prototype.setPosition = function (x, y) { + this._newPosition.setTo(this.game.physics.pixelsToMeters(x), this.game.physics.pixelsToMeters(y)); + this.setTransform(this._newPosition, this.angle); + }; Body.prototype.setTransform = function (pos, angle) { // inject the transform into this.position this.transform.setTo(pos, angle); @@ -7233,6 +7242,8 @@ var Phaser; this.worldView = new Phaser.Rectangle(x, y, this.width, this.height); this.cameraView = new Phaser.Rectangle(x, y, this.width, this.height); this.transform.setCache(); + this.outOfBounds = false; + this.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST; // Handy proxies this.scale = this.transform.scale; this.alpha = this.texture.alpha; @@ -7345,42 +7356,23 @@ var Phaser; function () { }; Sprite.prototype.postUpdate = /** - * Automatically called after update() by the game loop. + * Automatically called after update() by the game loop for all 'alive' objects. */ function () { this.animations.update(); - /* - if (this.worldBounds != null) - { - if (this.outOfBoundsAction == GameObject.OUT_OF_BOUNDS_KILL) - { - if (this.x < this.worldBounds.x || this.x > this.worldBounds.right || this.y < this.worldBounds.y || this.y > this.worldBounds.bottom) - { - this.kill(); + if(Phaser.RectangleUtils.intersects(this.worldView, this.game.world.bounds)) { + this.outOfBounds = false; + } else { + if(this.outOfBounds == false) { + this.events.onOutOfBounds.dispatch(this); + } + this.outOfBounds = true; + if(this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_KILL) { + this.kill(); + } else if(this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_DESTROY) { + this.destroy(); + } } - } - else - { - if (this.x < this.worldBounds.x) - { - this.x = this.worldBounds.x; - } - else if (this.x > this.worldBounds.right) - { - this.x = this.worldBounds.right; - } - - if (this.y < this.worldBounds.y) - { - this.y = this.worldBounds.y; - } - else if (this.y > this.worldBounds.bottom) - { - this.y = this.worldBounds.bottom; - } - } - } - */ if(this.modified == true && this.transform.scale.equals(1) && this.transform.skew.equals(0) && this.transform.rotation == 0 && this.transform.rotationOffset == 0 && this.texture.flippedX == false && this.texture.flippedY == false) { this.modified = false; } @@ -9142,19 +9134,22 @@ var Phaser; Loader.prototype.audio = /** * Add a new audio file loading request. * @param key {string} Unique asset key of the audio file. - * @param url {string} URL of audio file. + * @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ] + * @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process. */ - function (key, url) { + function (key, urls, autoDecode) { + if (typeof autoDecode === "undefined") { autoDecode = true; } if(this.checkKeyExists(key) === false) { this._queueSize++; this._fileList[key] = { type: 'audio', key: key, - url: url, + url: urls, data: null, buffer: null, error: false, - loaded: false + loaded: false, + autoDecode: autoDecode }; this._keys.push(key); } @@ -9242,15 +9237,46 @@ var Phaser; file.data.src = file.url; break; case 'audio': - this._xhr.open("GET", file.url, true); - this._xhr.responseType = "arraybuffer"; - this._xhr.onload = function () { - return _this.fileComplete(file.key); - }; - this._xhr.onerror = function () { - return _this.fileError(file.key); - }; - this._xhr.send(); + file.url = this.getAudioURL(file.url); + console.log('Loader audio'); + console.log(file.url); + if(file.url !== null) { + // WebAudio or Audio Tag? + if(this._game.sound.usingWebAudio) { + this._xhr.open("GET", file.url, true); + this._xhr.responseType = "arraybuffer"; + this._xhr.onload = function () { + return _this.fileComplete(file.key); + }; + this._xhr.onerror = function () { + return _this.fileError(file.key); + }; + this._xhr.send(); + } else if(this._game.sound.usingAudioTag) { + if(this._game.sound.touchLocked) { + // If audio is locked we can't do this yet, so need to queue this load request somehow. Bum. + console.log('Audio is touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.preload = 'auto'; + file.data.src = file.url; + this.fileComplete(file.key); + } else { + console.log('Audio not touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.onerror = function () { + return _this.fileError(file.key); + }; + file.data.preload = 'auto'; + file.data.src = file.url; + file.data.addEventListener('canplaythrough', function () { + return _this.fileComplete(file.key); + }, false); + file.data.load(); + } + } + } break; case 'text': this._xhr.open("GET", file.url, true); @@ -9265,6 +9291,19 @@ var Phaser; break; } }; + Loader.prototype.getAudioURL = function (urls) { + var extension; + for(var i = 0; i < urls.length; i++) { + extension = urls[i].toLowerCase(); + extension = extension.substr((Math.max(0, extension.lastIndexOf(".")) || Infinity) + 1); + if(this._game.device.canPlayAudio(extension)) { + console.log('getAudioURL', urls[i]); + console.log(urls[i]); + return urls[i]; + } + } + return null; + }; Loader.prototype.fileError = /** * Error occured when load a file. * @param key {string} Key of the error loading file. @@ -9314,8 +9353,22 @@ var Phaser; } break; case 'audio': - file.data = this._xhr.response; - this._game.cache.addSound(file.key, file.url, file.data); + if(this._game.sound.usingWebAudio) { + file.data = this._xhr.response; + this._game.cache.addSound(file.key, file.url, file.data, true, false); + if(file.autoDecode) { + this._game.cache.updateSound(key, 'isDecoding', true); + var that = this; + var key = file.key; + this._game.sound.context.decodeAudioData(file.data, function (buffer) { + if(buffer) { + that._game.cache.decodedSound(key, buffer); + } + }); + } + } else { + this._game.cache.addSound(file.key, file.url, file.data, false, true); + } break; case 'text': file.data = this._xhr.response; @@ -9421,6 +9474,7 @@ var Phaser; * Cache constructor */ function Cache(game) { + this.onSoundUnlock = new Phaser.Signal(); this._game = game; this._canvases = { }; @@ -9500,22 +9554,58 @@ var Phaser; * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ - function (key, url, data) { + function (key, url, data, webAudio, audioTag) { + if (typeof webAudio === "undefined") { webAudio = true; } + if (typeof audioTag === "undefined") { audioTag = false; } + console.log('Cache addSound: ' + key + ' url: ' + url, webAudio, audioTag); + var locked = this._game.sound.touchLocked; + var decoded = false; + if(audioTag) { + decoded = true; + } this._sounds[key] = { url: url, data: data, - decoded: false + locked: locked, + isDecoding: false, + decoded: decoded, + webAudio: webAudio, + audioTag: audioTag }; }; + Cache.prototype.reloadSound = function (key) { + var _this = this; + console.log('reloadSound', key); + if(this._sounds[key]) { + this._sounds[key].data.src = this._sounds[key].url; + this._sounds[key].data.addEventListener('canplaythrough', function () { + return _this.reloadSoundComplete(key); + }, false); + this._sounds[key].data.load(); + } + }; + Cache.prototype.reloadSoundComplete = function (key) { + console.log('reloadSoundComplete', key); + if(this._sounds[key]) { + this._sounds[key].locked = false; + this.onSoundUnlock.dispatch(key); + } + }; + Cache.prototype.updateSound = function (key, property, value) { + if(this._sounds[key]) { + this._sounds[key][property] = value; + } + }; Cache.prototype.decodedSound = /** * Add a new decoded sound. * @param key {string} Asset key for the sound. - * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ function (key, data) { + console.log('decoded sound', key); this._sounds[key].data = data; this._sounds[key].decoded = true; + this._sounds[key].isDecoding = false; }; Cache.prototype.addText = /** * Add a new text data. @@ -9563,6 +9653,17 @@ var Phaser; return null; }; Cache.prototype.getSound = /** + * Get sound by key. + * @param key Asset key of the sound you want. + * @return {object} The sound you want. + */ + function (key) { + if(this._sounds[key]) { + return this._sounds[key]; + } + return null; + }; + Cache.prototype.getSoundData = /** * Get sound data by key. * @param key Asset key of the sound you want. * @return {object} The sound data you want. @@ -9583,6 +9684,17 @@ var Phaser; return this._sounds[key].decoded; } }; + Cache.prototype.isSoundReady = /** + * Check whether an asset is decoded sound. + * @param key Asset key of the sound you want. + * @return {object} The sound data you want. + */ + function (key) { + if(this._sounds[key] && this._sounds[key].decoded == true && this._sounds[key].locked == false) { + return true; + } + return false; + }; Cache.prototype.isSpriteSheet = /** * Check whether an asset is sprite sheet. * @param key Asset key of the sprite sheet you want. @@ -12502,9 +12614,14 @@ var Phaser; * Create a new Button object. * * @param game {Phaser.Game} Current game instance. - * @param [x] {number} the initial x position of the button. - * @param [y] {number} the initial y position of the button. - * @param [key] {string} Key of the graphic you want to load for this button. + * @param [x] {number} X position of the button. + * @param [y] {number} Y position of the button. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this button. + * @param [callback] {function} The function to call when this button is pressed + * @param [callbackContext] {object} The context in which the callback will be called (usually 'this') + * @param [overFrame] {string|number} This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name. + * @param [outFrame] {string|number} This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name. + * @param [downFrame] {string|number} This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name. */ function Button(game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame) { if (typeof x === "undefined") { x = 0; } @@ -13797,6 +13914,11 @@ var Phaser; if (typeof bodyType === "undefined") { bodyType = Phaser.Types.BODY_DISABLED; } return this._world.group.add(new Phaser.Sprite(this._game, x, y, key, frame, bodyType)); }; + GameObjectFactory.prototype.audio = function (key, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + return this._game.sound.add(key, volume, loop); + }; GameObjectFactory.prototype.physicsSprite = /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -13892,13 +14014,15 @@ var Phaser; return this._world.group.add(new Phaser.Tilemap(this._game, key, mapData, format, resizeWorld, tileWidth, tileHeight)); }; GameObjectFactory.prototype.tween = /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param obj Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - function (obj) { - return this._game.tweens.create(obj); + function (obj, localReference) { + if (typeof localReference === "undefined") { localReference = false; } + return this._game.tweens.create(obj, localReference); }; GameObjectFactory.prototype.existingSprite = /** * Add an existing Sprite to the current world. @@ -13910,6 +14034,16 @@ var Phaser; function (sprite) { return this._world.group.add(sprite); }; + GameObjectFactory.prototype.existingButton = /** + * Add an existing Button to the current world. + * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. + * + * @param button The Button to add to the Game World + * @return {Phaser.Button} The Button object + */ + function (button) { + return this._world.group.add(button); + }; GameObjectFactory.prototype.existingEmitter = /** * Add an existing GeomSprite to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. @@ -13976,90 +14110,340 @@ var Phaser; var Sound = (function () { /** * Sound constructor - * @param context {object} The AudioContext instance. - * @param gainNode {object} Gain node instance. - * @param data {object} Sound data. * @param [volume] {number} volume of this sound when playing. * @param [loop] {boolean} loop this sound when playing? (Default to false) */ - function Sound(context, gainNode, data, volume, loop) { + function Sound(game, key, volume, loop) { if (typeof volume === "undefined") { volume = 1; } if (typeof loop === "undefined") { loop = false; } + /** + * Reference to AudioContext instance. + */ + this.context = null; + this._muted = false; + this.usingWebAudio = false; + this.usingAudioTag = false; + this.name = ''; + this.autoplay = false; + this.totalDuration = 0; + this.startTime = 0; + this.currentTime = 0; + this.duration = 0; + this.stopTime = 0; + this.paused = false; this.loop = false; this.isPlaying = false; - this.isDecoding = false; - this._context = context; - this._gainNode = gainNode; - this._buffer = data; + this.currentMarker = ''; + this.pendingPlayback = false; + this.game = game; + this.usingWebAudio = this.game.sound.usingWebAudio; + this.usingAudioTag = this.game.sound.usingAudioTag; + this.key = key; + if(this.usingWebAudio) { + this.context = this.game.sound.context; + this.masterGainNode = this.game.sound.masterGain; + if(typeof this.context.createGain === 'undefined') { + this.gainNode = this.context.createGainNode(); + } else { + this.gainNode = this.context.createGain(); + } + this.gainNode.gain.value = volume * this.game.sound.volume; + this.gainNode.connect(this.masterGainNode); + } else { + if(this.game.cache.getSound(key).locked == false) { + this._sound = this.game.cache.getSoundData(key); + this.totalDuration = this._sound.duration; + } else { + this.game.cache.onSoundUnlock.add(this.soundHasUnlocked, this); + } + } this._volume = volume; this.loop = loop; - // Local volume control - if(this._context !== null) { - this._localGainNode = this._context.createGainNode(); - this._localGainNode.connect(this._gainNode); - this._localGainNode.gain.value = this._volume; - } - if(this._buffer === null) { - this.isDecoding = true; - } else { - this.play(); - } + this.markers = { + }; + this.onDecoded = new Phaser.Signal(); + this.onPlay = new Phaser.Signal(); + this.onPause = new Phaser.Signal(); + this.onResume = new Phaser.Signal(); + this.onLoop = new Phaser.Signal(); + this.onStop = new Phaser.Signal(); + this.onMute = new Phaser.Signal(); } - Sound.prototype.setDecodedBuffer = function (data) { - this._buffer = data; - this.isDecoding = false; - //this.play(); - }; + Sound.prototype.soundHasUnlocked = function (key) { + if(key == this.key) { + this._sound = this.game.cache.getSoundData(this.key); + this.totalDuration = this._sound.duration; + console.log('sound has unlocked', this._sound); + } + }; + Object.defineProperty(Sound.prototype, "isDecoding", { + get: function () { + return this.game.cache.getSound(this.key).isDecoding; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Sound.prototype, "isDecoded", { + get: function () { + return this.game.cache.isSoundDecoded(this.key); + }, + enumerable: true, + configurable: true + }); + Sound.prototype.addMarker = function (name, start, stop, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + this.markers[name] = { + name: name, + start: start, + stop: stop, + volume: volume, + duration: stop - start, + loop: loop + }; + }; + Sound.prototype.removeMarker = function (name) { + delete this.markers[name]; + }; + Sound.prototype.update = function () { + if(this.pendingPlayback && this.game.cache.isSoundReady(this.key)) { + console.log('pending over'); + this.pendingPlayback = false; + this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop); + } + if(this.isPlaying) { + this.currentTime = this.game.time.now - this.startTime; + if(this.currentTime >= this.duration) { + if(this.usingWebAudio) { + if(this.loop) { + this.onLoop.dispatch(this); + this.currentTime = 0; + this.startTime = this.game.time.now; + } else { + this.stop(); + } + } else { + if(this.loop) { + this.onLoop.dispatch(this); + this.play(this.currentMarker, 0, this.volume, true, true); + } else { + this.stop(); + } + } + } + } + }; Sound.prototype.play = /** - * Play this sound. + * Play this sound, or a marked section of it. + * @param marker {string} Assets key of the sound you want to play. + * @param [volume] {number} volume of the sound you want to play. + * @param [loop] {boolean} loop when it finished playing? (Default to false) + * @return {Sound} The playing sound object. */ - function () { - if(this._buffer === null || this.isDecoding === true) { + function (marker, position, volume, loop, forceRestart) { + if (typeof marker === "undefined") { marker = ''; } + if (typeof position === "undefined") { position = 0; } + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + if (typeof forceRestart === "undefined") { forceRestart = false; } + if(this.isPlaying == true && forceRestart == false) { + // Use Restart instead return; } - this._sound = this._context.createBufferSource(); - this._sound.buffer = this._buffer; - this._sound.connect(this._localGainNode); - if(this.loop) { - this._sound.loop = true; + this.currentMarker = marker; + if(marker !== '' && this.markers[marker]) { + this.position = this.markers[marker].start; + this.volume = this.markers[marker].volume; + this.loop = this.markers[marker].loop; + this.duration = this.markers[marker].duration * 1000; + } else { + this.position = position; + this.volume = volume; + this.loop = loop; + this.duration = 0; + } + this._tempMarker = marker; + this._tempPosition = position; + this._tempVolume = volume; + this._tempLoop = loop; + if(this.usingWebAudio) { + // Does the sound need decoding? + if(this.game.cache.isSoundDecoded(this.key)) { + // Do we need to do this every time we play? How about just if the buffer is empty? + this._buffer = this.game.cache.getSoundData(this.key); + this._sound = this.context.createBufferSource(); + this._sound.buffer = this._buffer; + this._sound.connect(this.gainNode); + this.totalDuration = this._sound.buffer.duration; + if(this.duration == 0) { + this.duration = this.totalDuration * 1000; + } + if(this.loop) { + this._sound.loop = true; + } + // Useful to cache this somewhere perhaps? + if(typeof this._sound.start === 'undefined') { + this._sound.noteGrainOn(0, this.position, this.duration / 1000); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } else { + this._sound.start(0, this.position, this.duration / 1000); + } + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } else { + this.pendingPlayback = true; + if(this.game.cache.getSound(this.key).isDecoding == false) { + this.game.sound.decode(this.key, this); + } + } + } else { + console.log('Sound play Audio'); + if(this.game.cache.getSound(this.key).locked) { + console.log('tried playing locked sound, pending set, reload started'); + this.game.cache.reloadSound(this.key); + this.pendingPlayback = true; + } else { + console.log('sound not locked, state?', this._sound.readyState); + if(this._sound && this._sound.readyState == 4) { + if(this.duration == 0) { + this.duration = this.totalDuration * 1000; + } + console.log('playing', this._sound); + this._sound.currentTime = this.position; + this._sound.muted = this._muted; + this._sound.volume = this._volume; + this._sound.play(); + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } else { + this.pendingPlayback = true; + } + } + } + }; + Sound.prototype.restart = function (marker, position, volume, loop) { + if (typeof marker === "undefined") { marker = ''; } + if (typeof position === "undefined") { position = 0; } + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + this.play(marker, position, volume, loop, true); + }; + Sound.prototype.pause = function () { + if(this.isPlaying && this._sound) { + this.stop(); + this.isPlaying = false; + this.paused = true; + this.onPause.dispatch(this); + } + }; + Sound.prototype.resume = function () { + //if (this.isPlaying == false && this._sound) + if(this.paused && this._sound) { + if(this.usingWebAudio) { + if(typeof this._sound.start === 'undefined') { + this._sound.noteGrainOn(0, this.position, this.duration); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } else { + this._sound.start(0, this.position, this.duration); + } + } else { + this._sound.play(); + } + this.isPlaying = true; + this.paused = false; + this.onResume.dispatch(this); } - this._sound.noteOn(0)// the zero is vitally important, crashes iOS6 without it - ; - this.duration = this._sound.buffer.duration; - this.isPlaying = true; }; Sound.prototype.stop = /** * Stop playing this sound. */ function () { - if(this.isPlaying === true) { + if(this.isPlaying && this._sound) { + if(this.usingWebAudio) { + if(typeof this._sound.stop === 'undefined') { + this._sound.noteOff(0); + } else { + this._sound.stop(0); + } + } else if(this.usingAudioTag) { + this._sound.pause(); + this._sound.currentTime = 0; + } this.isPlaying = false; - this._sound.noteOff(0); + this.currentMarker = ''; + this.onStop.dispatch(this); } }; - Sound.prototype.mute = /** - * Mute the sound. - */ - function () { - this._localGainNode.gain.value = 0; - }; - Sound.prototype.unmute = /** - * Enable the sound. - */ - function () { - this._localGainNode.gain.value = this._volume; - }; + Object.defineProperty(Sound.prototype, "mute", { + get: /** + * Mute sounds. + */ + function () { + return this._muted; + }, + set: function (value) { + if(value) { + this._muted = true; + if(this.usingWebAudio) { + this._muteVolume = this.gainNode.gain.value; + this.gainNode.gain.value = 0; + } else if(this.usingAudioTag && this._sound) { + this._muteVolume = this._sound.volume; + this._sound.volume = 0; + } + } else { + this._muted = false; + if(this.usingWebAudio) { + this.gainNode.gain.value = this._muteVolume; + } else if(this.usingAudioTag && this._sound) { + this._sound.volume = this._muteVolume; + } + } + this.onMute.dispatch(this); + }, + enumerable: true, + configurable: true + }); Object.defineProperty(Sound.prototype, "volume", { get: function () { return this._volume; }, set: function (value) { this._volume = value; - this._localGainNode.gain.value = this._volume; + if(this.usingWebAudio) { + this.gainNode.gain.value = value; + } else if(this.usingAudioTag && this._sound) { + this._sound.volume = value; + } }, enumerable: true, configurable: true }); + Sound.prototype.renderDebug = /** + * Renders the Pointer.circle object onto the stage in green if down or red if up. + * @method renderDebug + */ + function (x, y) { + this.game.stage.context.fillStyle = 'rgb(255,255,255)'; + this.game.stage.context.font = '16px Courier'; + this.game.stage.context.fillText('Sound: ' + this.key + ' Locked: ' + this.game.sound.touchLocked + ' Pending Playback: ' + this.pendingPlayback, x, y); + this.game.stage.context.fillText('Decoded: ' + this.isDecoded + ' Decoding: ' + this.isDecoding, x, y + 20); + this.game.stage.context.fillText('Total Duration: ' + this.totalDuration + ' Playing: ' + this.isPlaying, x, y + 40); + this.game.stage.context.fillText('Time: ' + this.currentTime, x, y + 60); + this.game.stage.context.fillText('Volume: ' + this.volume + ' Muted: ' + this.mute, x, y + 80); + this.game.stage.context.fillText('WebAudio: ' + this.usingWebAudio + ' Audio: ' + this.usingAudioTag, x, y + 100); + if(this.currentMarker !== '') { + this.game.stage.context.fillText('Marker: ' + this.currentMarker + ' Duration: ' + this.duration, x, y + 120); + this.game.stage.context.fillText('Start: ' + this.markers[this.currentMarker].start + ' Stop: ' + this.markers[this.currentMarker].stop, x, y + 140); + this.game.stage.context.fillText('Position: ' + this.position, x, y + 160); + } + }; return Sound; })(); Phaser.Sound = Sound; @@ -14079,100 +14463,229 @@ var Phaser; * Create a new SoundManager. */ function SoundManager(game) { + this.usingWebAudio = false; + this.usingAudioTag = false; + this.noAudio = false; /** * Reference to AudioContext instance. */ - this._context = null; - this._game = game; - if(window['PhaserGlobal'] && window['PhaserGlobal'].disableAudio == true) { - return; + this.context = null; + this._muted = false; + this.touchLocked = false; + this._unlockSource = null; + this.onSoundDecode = new Phaser.Signal(); + this.game = game; + this._volume = 1; + this._muted = false; + this._sounds = []; + if(this.game.device.iOS && this.game.device.webAudio == false) { + this.channels = 1; } - if(game.device.webaudio == true) { - if(!!window['AudioContext']) { - this._context = new window['AudioContext'](); - } else if(!!window['webkitAudioContext']) { - this._context = new window['webkitAudioContext'](); + if(this.game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock)) { + console.log('iOS Touch Locked'); + this.game.input.touch.callbackContext = this; + this.game.input.touch.touchStartCallback = this.unlock; + this.game.input.mouse.callbackContext = this; + this.game.input.mouse.mouseDownCallback = this.unlock; + this.touchLocked = true; + } else { + // What about iOS5? + this.touchLocked = false; + } + if(window['PhaserGlobal']) { + // Check to see if all audio playback is disabled (i.e. handled by a 3rd party class) + if(window['PhaserGlobal'].disableAudio == true) { + this.usingWebAudio = false; + this.noAudio = true; + return; } - if(this._context !== null) { - this._gainNode = this._context.createGainNode(); - this._gainNode.connect(this._context.destination); - this._volume = 1; + // Check if the Web Audio API is disabled (for testing Audio Tag playback during development) + if(window['PhaserGlobal'].disableWebAudio == true) { + this.usingWebAudio = false; + this.usingAudioTag = true; + this.noAudio = false; + return; } } + this.usingWebAudio = true; + this.noAudio = false; + if(!!window['AudioContext']) { + this.context = new window['AudioContext'](); + } else if(!!window['webkitAudioContext']) { + this.context = new window['webkitAudioContext'](); + } else if(!!window['Audio']) { + this.usingWebAudio = false; + this.usingAudioTag = true; + } else { + this.usingWebAudio = false; + this.noAudio = true; + } + if(this.context !== null) { + if(typeof this.context.createGain === 'undefined') { + this.masterGain = this.context.createGainNode(); + } else { + this.masterGain = this.context.createGain(); + } + this.masterGain.gain.value = 1; + this.masterGain.connect(this.context.destination); + } } - SoundManager.prototype.mute = /** - * Mute sounds. - */ - function () { - this._gainNode.gain.value = 0; + SoundManager.prototype.unlock = function () { + if(this.touchLocked == false) { + return; + } + console.log('SoundManager touch unlocked'); + if(this.game.device.webAudio && (window['PhaserGlobal'] && window['PhaserGlobal'].disableWebAudio == false)) { + console.log('create empty buffer'); + // Create empty buffer and play it + var buffer = this.context.createBuffer(1, 1, 22050); + this._unlockSource = this.context.createBufferSource(); + this._unlockSource.buffer = buffer; + this._unlockSource.connect(this.context.destination); + this._unlockSource.noteOn(0); + } else { + // Create an Audio tag? + console.log('create audio tag'); + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + this.game.input.mouse.callbackContext = null; + this.game.input.mouse.mouseDownCallback = null; + } }; - SoundManager.prototype.unmute = /** - * Enable sounds. - */ - function () { - this._gainNode.gain.value = this._volume; - }; - Object.defineProperty(SoundManager.prototype, "volume", { - get: function () { - return this._volume; + Object.defineProperty(SoundManager.prototype, "mute", { + get: /** + * A global audio mute toggle. + */ + function () { + return this._muted; }, set: function (value) { - this._volume = value; - this._gainNode.gain.value = this._volume; + if(value) { + if(this._muted) { + return; + } + this._muted = true; + if(this.usingWebAudio) { + this._muteVolume = this.masterGain.gain.value; + this.masterGain.gain.value = 0; + } + // Loop through sounds + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].mute = true; + } + } + } else { + if(this._muted == false) { + return; + } + this._muted = false; + if(this.usingWebAudio) { + this.masterGain.gain.value = this._muteVolume; + } + // Loop through sounds + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].mute = false; + } + } + } }, enumerable: true, configurable: true }); + Object.defineProperty(SoundManager.prototype, "volume", { + get: function () { + if(this.usingWebAudio) { + return this.masterGain.gain.value; + } else { + return this._volume; + } + }, + set: /** + * The global audio volume. A value between 0 (silence) and 1 (full volume) + */ + function (value) { + value = this.game.math.clamp(value, 1, 0); + this._volume = value; + if(this.usingWebAudio) { + this.masterGain.gain.value = value; + } + // Loop through the sound cache and change the volume of all html audio tags + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i].usingAudioTag) { + this._sounds[i].volume = this._sounds[i].volume * value; + } + } + }, + enumerable: true, + configurable: true + }); + SoundManager.prototype.stopAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].stop(); + } + } + }; + SoundManager.prototype.pauseAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].pause(); + } + } + }; + SoundManager.prototype.resumeAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].resume(); + } + } + }; SoundManager.prototype.decode = /** * Decode a sound with its assets key. * @param key {string} Assets key of the sound to be decoded. - * @param callback {function} This will be invoked when finished decoding. * @param [sound] {Sound} its bufer will be set to decoded data. */ - function (key, callback, sound) { - if (typeof callback === "undefined") { callback = null; } + function (key, sound) { if (typeof sound === "undefined") { sound = null; } - var soundData = this._game.cache.getSound(key); + var soundData = this.game.cache.getSoundData(key); if(soundData) { - if(this._game.cache.isSoundDecoded(key) === false) { + if(this.game.cache.isSoundDecoded(key) === false) { + this.game.cache.updateSound(key, 'isDecoding', true); var that = this; - this._context.decodeAudioData(soundData, function (buffer) { - that._game.cache.decodedSound(key, buffer); + this.context.decodeAudioData(soundData, function (buffer) { + that.game.cache.decodedSound(key, buffer); if(sound) { - sound.setDecodedBuffer(buffer); + that.onSoundDecode.dispatch(sound); } - callback(); }); } } }; - SoundManager.prototype.play = /** - * Play a sound with its assets key. - * @param key {string} Assets key of the sound you want to play. - * @param [volume] {number} volume of the sound you want to play. - * @param [loop] {boolean} loop when it finished playing? (Default to false) - * @return {Sound} The playing sound object. - */ - function (key, volume, loop) { - if (typeof volume === "undefined") { volume = 1; } - if (typeof loop === "undefined") { loop = false; } - if(this._context === null) { - return; - } - var soundData = this._game.cache.getSound(key); - if(soundData) { - // Does the sound need decoding? - if(this._game.cache.isSoundDecoded(key) === true) { - return new Phaser.Sound(this._context, this._gainNode, soundData, volume, loop); - } else { - var tempSound = new Phaser.Sound(this._context, this._gainNode, null, volume, loop); - // this is an async process, so we can return the Sound object anyway, it just won't be playing yet - this.decode(key, function () { - return tempSound.play(); - }, tempSound); - return tempSound; + SoundManager.prototype.update = function () { + if(this.touchLocked) { + if(this.game.device.webAudio && this._unlockSource !== null) { + if((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE)) { + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + } } } + for(var i = 0; i < this._sounds.length; i++) { + this._sounds[i].update(); + } + }; + SoundManager.prototype.add = function (key, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + var sound = new Phaser.Sound(this.game, key, volume, loop); + this._sounds.push(sound); + return sound; }; return SoundManager; })(); @@ -14861,6 +15374,12 @@ var Phaser; document.addEventListener('webkitvisibilitychange', function (event) { return _this.visibilityChange(event); }, false); + document.addEventListener('pagehide', function (event) { + return _this.visibilityChange(event); + }, false); + document.addEventListener('pageshow', function (event) { + return _this.visibilityChange(event); + }, false); window.onblur = function (event) { return _this.visibilityChange(event); }; @@ -14905,16 +15424,17 @@ var Phaser; * This method is called when the canvas elements visibility is changed. */ function (event) { - if(this.disablePauseScreen) { - return; - } - if(event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) { - if(this._game.paused == false) { + if(event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) { + if(this._game.paused == false && this.disablePauseScreen == false) { this.pauseGame(); + } else { + this._game.paused = true; } } else { - if(this._game.paused == true) { + if(this._game.paused == true && this.disablePauseScreen == false) { this.resumeGame(); + } else { + this._game.paused = false; } } }; @@ -15244,16 +15764,23 @@ var Phaser; this._tweens.length = 0; }; TweenManager.prototype.create = /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param object {object} Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - function (object) { - return new Phaser.Tween(object, this._game); + function (object, localReference) { + if (typeof localReference === "undefined") { localReference = false; } + if(localReference) { + object['tween'] = new Phaser.Tween(object, this._game); + return object['tween']; + } else { + return new Phaser.Tween(object, this._game); + } }; TweenManager.prototype.add = /** - * Add an exist tween object to the manager. + * Add a new tween into the TweenManager. * * @param tween {Phaser.Tween} The tween object you want to add. * @return {Phaser.Tween} The tween object you added to the manager. @@ -15892,35 +16419,45 @@ var Phaser; this.webApp = false; // Audio /** - * Is audioData available? + * Are Audio tags available? * @type {boolean} */ this.audioData = false; /** - * Is webaudio available? + * Is the WebAudio API available? * @type {boolean} */ - this.webaudio = false; + this.webAudio = false; /** - * Is ogg available? + * Can this device play ogg files? * @type {boolean} */ this.ogg = false; /** - * Is mp3 available? + * Can this device play opus files? + * @type {boolean} + */ + this.opus = false; + /** + * Can this device play mp3 files? * @type {boolean} */ this.mp3 = false; /** - * Is wav available? + * Can this device play wav files? * @type {boolean} */ this.wav = false; /** - * Is m4a available? + * Can this device play m4a files? * @type {boolean} */ this.m4a = false; + /** + * Can this device play webm files? + * @type {boolean} + */ + this.webm = false; // Device /** * Is running on iPhone? @@ -16025,13 +16562,27 @@ var Phaser; this.webApp = true; } }; + Device.prototype.canPlayAudio = function (type) { + if(type == 'mp3' && this.mp3) { + return true; + } else if(type == 'ogg' && (this.ogg || this.opus)) { + return true; + } else if(type == 'm4a' && this.m4a) { + return true; + } else if(type == 'wav' && this.wav) { + return true; + } else if(type == 'webm' && this.webm) { + return true; + } + return false; + }; Device.prototype._checkAudio = /** * Check audio support. * @private */ function () { this.audioData = !!(window['Audio']); - this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']); + this.webAudio = !!(window['webkitAudioContext'] || window['AudioContext']); var audioElement = document.createElement('audio'); var result = false; try { @@ -16039,6 +16590,9 @@ var Phaser; if(audioElement.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '')) { this.ogg = true; } + if(audioElement.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, '')) { + this.opus = true; + } if(audioElement.canPlayType('audio/mpeg;').replace(/^no$/, '')) { this.mp3 = true; } @@ -16051,6 +16605,9 @@ var Phaser; if(audioElement.canPlayType('audio/x-m4a;') || audioElement.canPlayType('audio/aac;').replace(/^no$/, '')) { this.m4a = true; } + if(audioElement.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')) { + this.webm = true; + } } } catch (e) { } @@ -16637,6 +17194,12 @@ var Phaser; **/ this.totalTouches = 0; /** + * The number of miliseconds since the last click + * @property msSinceLastClick + * @type {Number} + **/ + this.msSinceLastClick = Number.MAX_VALUE; + /** * The Game Object this Pointer is currently over / touching / dragging. * @property targetObject * @type {Any} @@ -16704,6 +17267,8 @@ var Phaser; this.withinGame = true; this.isDown = true; this.isUp = false; + // Work out how long it has been since the last click + this.msSinceLastClick = this.game.time.now - this.timeDown; this.timeDown = this.game.time.now; this._holdSent = false; // This sets the x/y and other local values @@ -17761,7 +18326,7 @@ var Phaser; * @property recordPointerHistory * @type {Boolean} **/ - this.recordPointerHistory = true; + this.recordPointerHistory = false; /** * The rate in milliseconds at which the Pointer objects should update their tracking history * @property recordRate @@ -17770,7 +18335,7 @@ var Phaser; this.recordRate = 100; /** * The total number of entries that can be recorded into the Pointer objects tracking history. - * The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. + * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. * @property recordLimit * @type {Number} */ @@ -19013,6 +19578,7 @@ var Phaser; /// /// /// +/// /// /// /// @@ -19183,12 +19749,12 @@ var Phaser; this.stage = new Phaser.Stage(this, parent, width, height); this.world = new Phaser.World(this, width, height); this.add = new Phaser.GameObjectFactory(this); - this.sound = new Phaser.SoundManager(this); this.cache = new Phaser.Cache(this); this.load = new Phaser.Loader(this, this.loadComplete); this.time = new Phaser.Time(this); this.tweens = new Phaser.TweenManager(this); this.input = new Phaser.Input(this); + this.sound = new Phaser.SoundManager(this); this.rnd = new Phaser.RandomDataGenerator([ (Date.now() * Math.random()).toString() ]); @@ -19250,6 +19816,7 @@ var Phaser; this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); if(this.onPausedCallback !== null) { this.onPausedCallback.call(this.callbackContext); } @@ -19261,6 +19828,7 @@ var Phaser; this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); this.physics.update(); this.world.update(); if(this._loadComplete && this.onUpdateCallback) { @@ -19424,11 +19992,13 @@ var Phaser; set: function (value) { if(value == true && this._paused == false) { this._paused = true; + this.sound.pauseAll(); this._raf.callback = this.pausedLoop; } else if(value == false && this._paused == true) { this._paused = false; //this.time.time = window.performance.now ? (performance.now() + performance.timing.navigationStart) : Date.now(); this.input.reset(); + this.sound.resumeAll(); if(this.isRunning == false) { this._raf.callback = this.bootLoop; } else { diff --git a/Tests/physics/simple test 1.js b/Tests/physics/simple test 1.js index ca4c0736..f00fa461 100644 --- a/Tests/physics/simple test 1.js +++ b/Tests/physics/simple test 1.js @@ -16,15 +16,26 @@ atari = game.add.physicsSprite(300, 450, 'atari', null, Phaser.Types.BODY_STATIC); //atari.rotation = 10; //atari.body.transform.setRotation(1); - atari.body.angle = 1; + //atari.body.angle = 1; + atari.body.setTransform(atari.body.position, 1); + atari.body.syncTransform(); // atari = 220px width (110 = center x) // ball = 32px width (16 = center x) // Ball will be a dynamic body and fall based on gravity ball = game.add.physicsSprite(300 - 20, 0, 'ball'); - ball.body.angle = 1; + //ball.body.angle = 1; //ball.body.transform.setRotation(1); //ball.body.fixedRotation = true; - } + // limit the velocity or things can go nuts + ball.body.linearDamping = 0.5; + ball.body.angularDamping = 0.5; + // when the ball bounces out of the game world, call the resetBall function + ball.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST; + ball.events.onOutOfBounds.add(resetBall, this); + } + function resetBall() { + ball.body.setPosition(300, 0); + } function render() { Phaser.DebugUtils.renderPhysicsBodyInfo(atari.body, 32, 32); Phaser.DebugUtils.renderPhysicsBodyInfo(ball.body, 320, 32); diff --git a/Tests/physics/simple test 1.ts b/Tests/physics/simple test 1.ts index a34964d3..594a3d8b 100644 --- a/Tests/physics/simple test 1.ts +++ b/Tests/physics/simple test 1.ts @@ -25,17 +25,31 @@ atari = game.add.physicsSprite(300, 450, 'atari', null, Phaser.Types.BODY_STATIC); //atari.rotation = 10; //atari.body.transform.setRotation(1); - atari.body.angle = 1; + //atari.body.angle = 1; + atari.body.setTransform(atari.body.position, 1); + atari.body.syncTransform(); // atari = 220px width (110 = center x) // ball = 32px width (16 = center x) // Ball will be a dynamic body and fall based on gravity ball = game.add.physicsSprite(300-20, 0, 'ball'); - ball.body.angle = 1; + //ball.body.angle = 1; //ball.body.transform.setRotation(1); //ball.body.fixedRotation = true; + // limit the velocity or things can go nuts + ball.body.linearDamping = 0.5; + ball.body.angularDamping = 0.5; + + // when the ball bounces out of the game world, call the resetBall function + ball.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST; + ball.events.onOutOfBounds.add(resetBall, this); + + } + + function resetBall() { + ball.body.setPosition(300, 0); } function render() { diff --git a/Tests/sprites/alpha.js b/Tests/sprites/alpha.js index 72292ae2..c462970f 100644 --- a/Tests/sprites/alpha.js +++ b/Tests/sprites/alpha.js @@ -6,10 +6,10 @@ game.load.image('bunny', 'assets/sprites/bunny.png'); game.load.start(); } - var bunny; function create() { - // Here we'll assign the new sprite to the local smallBunny variable - bunny = game.add.sprite(0, 0, 'bunny'); + // Here we'll assign the new sprite to the local bunny variable + var bunny = game.add.sprite(0, 0, 'bunny'); + // And this sets the alpha of the sprite to 0.5, which is 50% opaque bunny.alpha = 0.5; } })(); diff --git a/build/phaser.d.ts b/build/phaser.d.ts index cff3d03d..8d3bc6f1 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -856,6 +856,9 @@ module Phaser { static BODY_STATIC: number; static BODY_KINETIC: number; static BODY_DYNAMIC: number; + static OUT_OF_BOUNDS_KILL: number; + static OUT_OF_BOUNDS_DESTROY: number; + static OUT_OF_BOUNDS_PERSIST: number; /** * Flag used to allow GameObjects to collide on their left side * @type {number} @@ -2384,6 +2387,9 @@ module Phaser.Components.Sprite { * Dispatched by the Animation component when the Sprite animation loops */ public onAnimationLoop: Signal; + /** + * Dispatched by the Sprite when it first leaves the world bounds + */ public onOutOfBounds: Signal; } } @@ -3176,6 +3182,8 @@ module Phaser.Physics { public removeShape(shape): void; private setMass(mass); private setInertia(inertia); + private _newPosition; + public setPosition(x: number, y: number): void; public setTransform(pos: Vec2, angle: number): void; public syncTransform(): void; public getWorldPoint(p: Vec2): Vec2; @@ -3252,6 +3260,15 @@ module Phaser { */ public alive: bool; /** + * Is the Sprite out of the world bounds or not? + */ + public outOfBounds: bool; + /** + * The action to be taken when the sprite is fully out of the world bounds + * Defaults to Phaser.Types.OUT_OF_BOUNDS_KILL + */ + public outOfBoundsAction: number; + /** * Sprite physics body. */ public body: Physics.Body; @@ -3287,6 +3304,10 @@ module Phaser { */ public cameraView: Rectangle; /** + * A local tween variable. Used by the TweenManager when setting a tween on this sprite or a property of it. + */ + public tween: Tween; + /** * A boolean representing if the Sprite has been modified in any way via a scale, rotate, flip or skew. */ public modified: bool; @@ -3356,7 +3377,7 @@ module Phaser { */ public update(): void; /** - * Automatically called after update() by the game loop. + * Automatically called after update() by the game loop for all 'alive' objects. */ public postUpdate(): void; /** @@ -4278,9 +4299,10 @@ module Phaser { /** * Add a new audio file loading request. * @param key {string} Unique asset key of the audio file. - * @param url {string} URL of audio file. + * @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ] + * @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process. */ - public audio(key: string, url: string): void; + public audio(key: string, urls: string[], autoDecode?: bool): void; /** * Add a new text file loading request. * @param key {string} Unique asset key of the text file. @@ -4306,6 +4328,7 @@ module Phaser { * Load files. Private method ONLY used by loader. */ private loadFile(); + private getAudioURL(urls); /** * Error occured when load a file. * @param key {string} Key of the error loading file. @@ -4415,11 +4438,14 @@ module Phaser { * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ - public addSound(key: string, url: string, data): void; + public addSound(key: string, url: string, data, webAudio?: bool, audioTag?: bool): void; + public reloadSound(key: string): void; + public onSoundUnlock: Signal; + public reloadSoundComplete(key: string): void; + public updateSound(key: string, property: string, value): void; /** * Add a new decoded sound. * @param key {string} Asset key for the sound. - * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ public decodedSound(key: string, data): void; @@ -4449,11 +4475,17 @@ module Phaser { */ public getFrameData(key: string): FrameData; /** + * Get sound by key. + * @param key Asset key of the sound you want. + * @return {object} The sound you want. + */ + public getSound(key: string); + /** * Get sound data by key. * @param key Asset key of the sound you want. * @return {object} The sound data you want. */ - public getSound(key: string); + public getSoundData(key: string); /** * Check whether an asset is decoded sound. * @param key Asset key of the sound you want. @@ -4461,6 +4493,12 @@ module Phaser { */ public isSoundDecoded(key: string): bool; /** + * Check whether an asset is decoded sound. + * @param key Asset key of the sound you want. + * @return {object} The sound data you want. + */ + public isSoundReady(key: string): bool; + /** * Check whether an asset is sprite sheet. * @param key Asset key of the sprite sheet you want. * @return {object} The sprite sheet data you want. @@ -5998,9 +6036,14 @@ module Phaser { * Create a new Button object. * * @param game {Phaser.Game} Current game instance. - * @param [x] {number} the initial x position of the button. - * @param [y] {number} the initial y position of the button. - * @param [key] {string} Key of the graphic you want to load for this button. + * @param [x] {number} X position of the button. + * @param [y] {number} Y position of the button. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this button. + * @param [callback] {function} The function to call when this button is pressed + * @param [callbackContext] {object} The context in which the callback will be called (usually 'this') + * @param [overFrame] {string|number} This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name. + * @param [outFrame] {string|number} This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name. + * @param [downFrame] {string|number} This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name. */ constructor(game: Game, x?: number, y?: number, key?: string, callback?, callbackContext?, overFrame?, outFrame?, downFrame?); private _onOverFrameName; @@ -6780,6 +6823,7 @@ module Phaser { * @returns {Sprite} The newly created sprite object. */ public sprite(x: number, y: number, key?: string, frame?, bodyType?: number): Sprite; + public audio(key: string, volume?: number, loop?: bool): Sound; /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -6846,12 +6890,13 @@ module Phaser { */ public tilemap(key: string, mapData: string, format: number, resizeWorld?: bool, tileWidth?: number, tileHeight?: number): Tilemap; /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param obj Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - public tween(obj): Tween; + public tween(obj, localReference?: bool): Tween; /** * Add an existing Sprite to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. @@ -6861,6 +6906,14 @@ module Phaser { */ public existingSprite(sprite: Sprite): Sprite; /** + * Add an existing Button to the current world. + * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. + * + * @param button The Button to add to the Game World + * @return {Phaser.Button} The Button object + */ + public existingButton(button: Button): Button; + /** * Add an existing Emitter to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. * @@ -6903,27 +6956,29 @@ module Phaser { class Sound { /** * Sound constructor - * @param context {object} The AudioContext instance. - * @param gainNode {object} Gain node instance. - * @param data {object} Sound data. * @param [volume] {number} volume of this sound when playing. * @param [loop] {boolean} loop this sound when playing? (Default to false) */ - constructor(context, gainNode, data, volume?: number, loop?: bool); + constructor(game: Game, key: string, volume?: number, loop?: bool); + private soundHasUnlocked(key); /** - * Local private reference to AudioContext. + * Local reference to the current Phaser.Game. */ - private _context; + public game: Game; + /** + * Reference to AudioContext instance. + */ + public context; /** * Reference to gain node of SoundManager. */ - private _gainNode; + public masterGainNode; /** * GainNode of this sound. */ - private _localGainNode; + public gainNode; /** - * Decoded data buffer. + * Decoded data buffer / Audio tag. */ private _buffer; /** @@ -6934,28 +6989,66 @@ module Phaser { * The real sound object (buffer source). */ private _sound; - public loop: bool; + private _muteVolume; + private _muted; + private _tempPosition; + private _tempVolume; + private _tempLoop; + private _tempMarker; + public usingWebAudio: bool; + public usingAudioTag: bool; + public name: string; + public autoplay: bool; + public totalDuration: number; + public startTime: number; + public currentTime: number; public duration: number; + public stopTime: number; + public position: number; + public paused: bool; + public loop: bool; public isPlaying: bool; - public isDecoding: bool; - public setDecodedBuffer(data): void; + public key: string; + public markers; + public currentMarker: string; + public onDecoded: Signal; + public onPlay: Signal; + public onPause: Signal; + public onResume: Signal; + public onLoop: Signal; + public onStop: Signal; + public onMute: Signal; + public pendingPlayback: bool; + public isDecoding : bool; + public isDecoded : bool; + public addMarker(name: string, start: number, stop: number, volume?: number, loop?: bool): void; + public removeMarker(name: string): void; + public update(): void; /** - * Play this sound. + * Play this sound, or a marked section of it. + * @param marker {string} Assets key of the sound you want to play. + * @param [volume] {number} volume of the sound you want to play. + * @param [loop] {boolean} loop when it finished playing? (Default to false) + * @return {Sound} The playing sound object. */ - public play(): void; + public play(marker?: string, position?: number, volume?: number, loop?: bool, forceRestart?: bool): void; + public restart(marker?: string, position?: number, volume?: number, loop?: bool): void; + public pause(): void; + public resume(): void; /** * Stop playing this sound. */ public stop(): void; /** - * Mute the sound. + * Mute sounds. */ - public mute(): void; - /** - * Enable the sound. - */ - public unmute(): void; + public mute : bool; public volume : number; + /** + * Renders the Pointer.circle object onto the stage in green if down or red if up. + * @method renderDebug + */ + public renderDebug(x: number, y: number): void; } } /** @@ -6970,47 +7063,53 @@ module Phaser { * Create a new SoundManager. */ constructor(game: Game); + public usingWebAudio: bool; + public usingAudioTag: bool; + public noAudio: bool; /** - * Local private reference to game. + * Local reference to the current Phaser.Game. */ - private _game; + public game: Game; /** * Reference to AudioContext instance. */ - private _context; + public context; /** - * Gain node created from audio context. + * The Master Gain node through which all sounds */ - private _gainNode; + public masterGain; /** * Volume of sounds. * @type {number} */ private _volume; + private _sounds; + private _muteVolume; + private _muted; + public channels: number; + public touchLocked: bool; + private _unlockSource; + public unlock(): void; /** - * Mute sounds. + * A global audio mute toggle. */ - public mute(): void; + public mute : bool; /** - * Enable sounds. + * The global audio volume. A value between 0 (silence) and 1 (full volume) */ - public unmute(): void; public volume : number; + public stopAll(): void; + public pauseAll(): void; + public resumeAll(): void; /** * Decode a sound with its assets key. * @param key {string} Assets key of the sound to be decoded. - * @param callback {function} This will be invoked when finished decoding. * @param [sound] {Sound} its bufer will be set to decoded data. */ - public decode(key: string, callback?, sound?: Sound): void; - /** - * Play a sound with its assets key. - * @param key {string} Assets key of the sound you want to play. - * @param [volume] {number} volume of the sound you want to play. - * @param [loop] {boolean} loop when it finished playing? (Default to false) - * @return {Sound} The playing sound object. - */ - public play(key: string, volume?: number, loop?: bool): Sound; + public decode(key: string, sound?: Sound): void; + public onSoundDecode: Signal; + public update(): void; + public add(key: string, volume?: number, loop?: bool): Sound; } } /** @@ -7656,14 +7755,15 @@ module Phaser { */ public removeAll(): void; /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param object {object} Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - public create(object): Tween; + public create(object, localReference?: bool): Tween; /** - * Add an exist tween object to the manager. + * Add a new tween into the TweenManager. * * @param tween {Phaser.Tween} The tween object you want to add. * @return {Phaser.Tween} The tween object you added to the manager. @@ -8070,36 +8170,46 @@ module Phaser { public safari: bool; public webApp: bool; /** - * Is audioData available? + * Are Audio tags available? * @type {boolean} */ public audioData: bool; /** - * Is webaudio available? + * Is the WebAudio API available? * @type {boolean} */ - public webaudio: bool; + public webAudio: bool; /** - * Is ogg available? + * Can this device play ogg files? * @type {boolean} */ public ogg: bool; /** - * Is mp3 available? + * Can this device play opus files? + * @type {boolean} + */ + public opus: bool; + /** + * Can this device play mp3 files? * @type {boolean} */ public mp3: bool; /** - * Is wav available? + * Can this device play wav files? * @type {boolean} */ public wav: bool; /** - * Is m4a available? + * Can this device play m4a files? * @type {boolean} */ public m4a: bool; /** + * Can this device play webm files? + * @type {boolean} + */ + public webm: bool; + /** * Is running on iPhone? * @type {boolean} */ @@ -8134,6 +8244,7 @@ module Phaser { * @private */ private _checkBrowser(); + public canPlayAudio(type: string): bool; /** * Check audio support. * @private @@ -8369,6 +8480,9 @@ module Phaser { * @return {Phaser.Pointer} This object. */ constructor(game: Game, id: number); + private _highestRenderOrderID; + private _highestRenderObject; + private _highestInputPriorityID; /** * Local private reference to game. * @property game @@ -8546,6 +8660,12 @@ module Phaser { **/ public totalTouches: number; /** + * The number of miliseconds since the last click + * @property msSinceLastClick + * @type {Number} + **/ + public msSinceLastClick: number; + /** * How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1. * @property duration * @type {Number} @@ -8574,9 +8694,6 @@ module Phaser { */ public start(event): Pointer; public update(): void; - private _highestRenderOrderID; - private _highestRenderObject; - private _highestInputPriorityID; /** * Called when the Pointer is moved on the touchscreen * @method move @@ -9229,7 +9346,7 @@ module Phaser { public recordRate: number; /** * The total number of entries that can be recorded into the Pointer objects tracking history. - * The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. + * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. * @property recordLimit * @type {Number} */ diff --git a/build/phaser.js b/build/phaser.js index 788d7e91..18b3481f 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -1284,6 +1284,9 @@ var Phaser; Types.BODY_STATIC = 1; Types.BODY_KINETIC = 2; Types.BODY_DYNAMIC = 3; + Types.OUT_OF_BOUNDS_KILL = 0; + Types.OUT_OF_BOUNDS_DESTROY = 1; + Types.OUT_OF_BOUNDS_PERSIST = 2; Types.LEFT = 0x0001; Types.RIGHT = 0x0010; Types.UP = 0x0100; @@ -3961,6 +3964,7 @@ var Phaser; this.onRemovedFromGroup = new Phaser.Signal(); this.onKilled = new Phaser.Signal(); this.onRevived = new Phaser.Signal(); + this.onOutOfBounds = new Phaser.Signal(); } return Events; })(); @@ -6710,6 +6714,7 @@ var Phaser; this.categoryBits = 0x0001; this.maskBits = 0xFFFF; this.stepCount = 0; + this._newPosition = new Phaser.Vec2(); this.id = Phaser.Physics.Manager.bodyCounter++; this.name = 'body' + this.id; this.type = type; @@ -6888,6 +6893,10 @@ var Phaser; this.inertia = inertia; this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; }; + Body.prototype.setPosition = function (x, y) { + this._newPosition.setTo(this.game.physics.pixelsToMeters(x), this.game.physics.pixelsToMeters(y)); + this.setTransform(this._newPosition, this.angle); + }; Body.prototype.setTransform = function (pos, angle) { // inject the transform into this.position this.transform.setTo(pos, angle); @@ -7233,6 +7242,8 @@ var Phaser; this.worldView = new Phaser.Rectangle(x, y, this.width, this.height); this.cameraView = new Phaser.Rectangle(x, y, this.width, this.height); this.transform.setCache(); + this.outOfBounds = false; + this.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST; // Handy proxies this.scale = this.transform.scale; this.alpha = this.texture.alpha; @@ -7345,42 +7356,23 @@ var Phaser; function () { }; Sprite.prototype.postUpdate = /** - * Automatically called after update() by the game loop. + * Automatically called after update() by the game loop for all 'alive' objects. */ function () { this.animations.update(); - /* - if (this.worldBounds != null) - { - if (this.outOfBoundsAction == GameObject.OUT_OF_BOUNDS_KILL) - { - if (this.x < this.worldBounds.x || this.x > this.worldBounds.right || this.y < this.worldBounds.y || this.y > this.worldBounds.bottom) - { - this.kill(); + if(Phaser.RectangleUtils.intersects(this.worldView, this.game.world.bounds)) { + this.outOfBounds = false; + } else { + if(this.outOfBounds == false) { + this.events.onOutOfBounds.dispatch(this); + } + this.outOfBounds = true; + if(this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_KILL) { + this.kill(); + } else if(this.outOfBoundsAction == Phaser.Types.OUT_OF_BOUNDS_DESTROY) { + this.destroy(); + } } - } - else - { - if (this.x < this.worldBounds.x) - { - this.x = this.worldBounds.x; - } - else if (this.x > this.worldBounds.right) - { - this.x = this.worldBounds.right; - } - - if (this.y < this.worldBounds.y) - { - this.y = this.worldBounds.y; - } - else if (this.y > this.worldBounds.bottom) - { - this.y = this.worldBounds.bottom; - } - } - } - */ if(this.modified == true && this.transform.scale.equals(1) && this.transform.skew.equals(0) && this.transform.rotation == 0 && this.transform.rotationOffset == 0 && this.texture.flippedX == false && this.texture.flippedY == false) { this.modified = false; } @@ -9142,19 +9134,22 @@ var Phaser; Loader.prototype.audio = /** * Add a new audio file loading request. * @param key {string} Unique asset key of the audio file. - * @param url {string} URL of audio file. + * @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ] + * @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process. */ - function (key, url) { + function (key, urls, autoDecode) { + if (typeof autoDecode === "undefined") { autoDecode = true; } if(this.checkKeyExists(key) === false) { this._queueSize++; this._fileList[key] = { type: 'audio', key: key, - url: url, + url: urls, data: null, buffer: null, error: false, - loaded: false + loaded: false, + autoDecode: autoDecode }; this._keys.push(key); } @@ -9242,15 +9237,46 @@ var Phaser; file.data.src = file.url; break; case 'audio': - this._xhr.open("GET", file.url, true); - this._xhr.responseType = "arraybuffer"; - this._xhr.onload = function () { - return _this.fileComplete(file.key); - }; - this._xhr.onerror = function () { - return _this.fileError(file.key); - }; - this._xhr.send(); + file.url = this.getAudioURL(file.url); + console.log('Loader audio'); + console.log(file.url); + if(file.url !== null) { + // WebAudio or Audio Tag? + if(this._game.sound.usingWebAudio) { + this._xhr.open("GET", file.url, true); + this._xhr.responseType = "arraybuffer"; + this._xhr.onload = function () { + return _this.fileComplete(file.key); + }; + this._xhr.onerror = function () { + return _this.fileError(file.key); + }; + this._xhr.send(); + } else if(this._game.sound.usingAudioTag) { + if(this._game.sound.touchLocked) { + // If audio is locked we can't do this yet, so need to queue this load request somehow. Bum. + console.log('Audio is touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.preload = 'auto'; + file.data.src = file.url; + this.fileComplete(file.key); + } else { + console.log('Audio not touch locked'); + file.data = new Audio(); + file.data.name = file.key; + file.data.onerror = function () { + return _this.fileError(file.key); + }; + file.data.preload = 'auto'; + file.data.src = file.url; + file.data.addEventListener('canplaythrough', function () { + return _this.fileComplete(file.key); + }, false); + file.data.load(); + } + } + } break; case 'text': this._xhr.open("GET", file.url, true); @@ -9265,6 +9291,19 @@ var Phaser; break; } }; + Loader.prototype.getAudioURL = function (urls) { + var extension; + for(var i = 0; i < urls.length; i++) { + extension = urls[i].toLowerCase(); + extension = extension.substr((Math.max(0, extension.lastIndexOf(".")) || Infinity) + 1); + if(this._game.device.canPlayAudio(extension)) { + console.log('getAudioURL', urls[i]); + console.log(urls[i]); + return urls[i]; + } + } + return null; + }; Loader.prototype.fileError = /** * Error occured when load a file. * @param key {string} Key of the error loading file. @@ -9314,8 +9353,22 @@ var Phaser; } break; case 'audio': - file.data = this._xhr.response; - this._game.cache.addSound(file.key, file.url, file.data); + if(this._game.sound.usingWebAudio) { + file.data = this._xhr.response; + this._game.cache.addSound(file.key, file.url, file.data, true, false); + if(file.autoDecode) { + this._game.cache.updateSound(key, 'isDecoding', true); + var that = this; + var key = file.key; + this._game.sound.context.decodeAudioData(file.data, function (buffer) { + if(buffer) { + that._game.cache.decodedSound(key, buffer); + } + }); + } + } else { + this._game.cache.addSound(file.key, file.url, file.data, false, true); + } break; case 'text': file.data = this._xhr.response; @@ -9421,6 +9474,7 @@ var Phaser; * Cache constructor */ function Cache(game) { + this.onSoundUnlock = new Phaser.Signal(); this._game = game; this._canvases = { }; @@ -9500,22 +9554,58 @@ var Phaser; * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ - function (key, url, data) { + function (key, url, data, webAudio, audioTag) { + if (typeof webAudio === "undefined") { webAudio = true; } + if (typeof audioTag === "undefined") { audioTag = false; } + console.log('Cache addSound: ' + key + ' url: ' + url, webAudio, audioTag); + var locked = this._game.sound.touchLocked; + var decoded = false; + if(audioTag) { + decoded = true; + } this._sounds[key] = { url: url, data: data, - decoded: false + locked: locked, + isDecoding: false, + decoded: decoded, + webAudio: webAudio, + audioTag: audioTag }; }; + Cache.prototype.reloadSound = function (key) { + var _this = this; + console.log('reloadSound', key); + if(this._sounds[key]) { + this._sounds[key].data.src = this._sounds[key].url; + this._sounds[key].data.addEventListener('canplaythrough', function () { + return _this.reloadSoundComplete(key); + }, false); + this._sounds[key].data.load(); + } + }; + Cache.prototype.reloadSoundComplete = function (key) { + console.log('reloadSoundComplete', key); + if(this._sounds[key]) { + this._sounds[key].locked = false; + this.onSoundUnlock.dispatch(key); + } + }; + Cache.prototype.updateSound = function (key, property, value) { + if(this._sounds[key]) { + this._sounds[key][property] = value; + } + }; Cache.prototype.decodedSound = /** * Add a new decoded sound. * @param key {string} Asset key for the sound. - * @param url {string} URL of this sound file. * @param data {object} Extra sound data. */ function (key, data) { + console.log('decoded sound', key); this._sounds[key].data = data; this._sounds[key].decoded = true; + this._sounds[key].isDecoding = false; }; Cache.prototype.addText = /** * Add a new text data. @@ -9563,6 +9653,17 @@ var Phaser; return null; }; Cache.prototype.getSound = /** + * Get sound by key. + * @param key Asset key of the sound you want. + * @return {object} The sound you want. + */ + function (key) { + if(this._sounds[key]) { + return this._sounds[key]; + } + return null; + }; + Cache.prototype.getSoundData = /** * Get sound data by key. * @param key Asset key of the sound you want. * @return {object} The sound data you want. @@ -9583,6 +9684,17 @@ var Phaser; return this._sounds[key].decoded; } }; + Cache.prototype.isSoundReady = /** + * Check whether an asset is decoded sound. + * @param key Asset key of the sound you want. + * @return {object} The sound data you want. + */ + function (key) { + if(this._sounds[key] && this._sounds[key].decoded == true && this._sounds[key].locked == false) { + return true; + } + return false; + }; Cache.prototype.isSpriteSheet = /** * Check whether an asset is sprite sheet. * @param key Asset key of the sprite sheet you want. @@ -12502,9 +12614,14 @@ var Phaser; * Create a new Button object. * * @param game {Phaser.Game} Current game instance. - * @param [x] {number} the initial x position of the button. - * @param [y] {number} the initial y position of the button. - * @param [key] {string} Key of the graphic you want to load for this button. + * @param [x] {number} X position of the button. + * @param [y] {number} Y position of the button. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this button. + * @param [callback] {function} The function to call when this button is pressed + * @param [callbackContext] {object} The context in which the callback will be called (usually 'this') + * @param [overFrame] {string|number} This is the frame or frameName that will be set when this button is in an over state. Give either a number to use a frame ID or a string for a frame name. + * @param [outFrame] {string|number} This is the frame or frameName that will be set when this button is in an out state. Give either a number to use a frame ID or a string for a frame name. + * @param [downFrame] {string|number} This is the frame or frameName that will be set when this button is in a down state. Give either a number to use a frame ID or a string for a frame name. */ function Button(game, x, y, key, callback, callbackContext, overFrame, outFrame, downFrame) { if (typeof x === "undefined") { x = 0; } @@ -13797,6 +13914,11 @@ var Phaser; if (typeof bodyType === "undefined") { bodyType = Phaser.Types.BODY_DISABLED; } return this._world.group.add(new Phaser.Sprite(this._game, x, y, key, frame, bodyType)); }; + GameObjectFactory.prototype.audio = function (key, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + return this._game.sound.add(key, volume, loop); + }; GameObjectFactory.prototype.physicsSprite = /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -13892,13 +14014,15 @@ var Phaser; return this._world.group.add(new Phaser.Tilemap(this._game, key, mapData, format, resizeWorld, tileWidth, tileHeight)); }; GameObjectFactory.prototype.tween = /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param obj Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - function (obj) { - return this._game.tweens.create(obj); + function (obj, localReference) { + if (typeof localReference === "undefined") { localReference = false; } + return this._game.tweens.create(obj, localReference); }; GameObjectFactory.prototype.existingSprite = /** * Add an existing Sprite to the current world. @@ -13910,6 +14034,16 @@ var Phaser; function (sprite) { return this._world.group.add(sprite); }; + GameObjectFactory.prototype.existingButton = /** + * Add an existing Button to the current world. + * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. + * + * @param button The Button to add to the Game World + * @return {Phaser.Button} The Button object + */ + function (button) { + return this._world.group.add(button); + }; GameObjectFactory.prototype.existingEmitter = /** * Add an existing GeomSprite to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. @@ -13976,90 +14110,340 @@ var Phaser; var Sound = (function () { /** * Sound constructor - * @param context {object} The AudioContext instance. - * @param gainNode {object} Gain node instance. - * @param data {object} Sound data. * @param [volume] {number} volume of this sound when playing. * @param [loop] {boolean} loop this sound when playing? (Default to false) */ - function Sound(context, gainNode, data, volume, loop) { + function Sound(game, key, volume, loop) { if (typeof volume === "undefined") { volume = 1; } if (typeof loop === "undefined") { loop = false; } + /** + * Reference to AudioContext instance. + */ + this.context = null; + this._muted = false; + this.usingWebAudio = false; + this.usingAudioTag = false; + this.name = ''; + this.autoplay = false; + this.totalDuration = 0; + this.startTime = 0; + this.currentTime = 0; + this.duration = 0; + this.stopTime = 0; + this.paused = false; this.loop = false; this.isPlaying = false; - this.isDecoding = false; - this._context = context; - this._gainNode = gainNode; - this._buffer = data; + this.currentMarker = ''; + this.pendingPlayback = false; + this.game = game; + this.usingWebAudio = this.game.sound.usingWebAudio; + this.usingAudioTag = this.game.sound.usingAudioTag; + this.key = key; + if(this.usingWebAudio) { + this.context = this.game.sound.context; + this.masterGainNode = this.game.sound.masterGain; + if(typeof this.context.createGain === 'undefined') { + this.gainNode = this.context.createGainNode(); + } else { + this.gainNode = this.context.createGain(); + } + this.gainNode.gain.value = volume * this.game.sound.volume; + this.gainNode.connect(this.masterGainNode); + } else { + if(this.game.cache.getSound(key).locked == false) { + this._sound = this.game.cache.getSoundData(key); + this.totalDuration = this._sound.duration; + } else { + this.game.cache.onSoundUnlock.add(this.soundHasUnlocked, this); + } + } this._volume = volume; this.loop = loop; - // Local volume control - if(this._context !== null) { - this._localGainNode = this._context.createGainNode(); - this._localGainNode.connect(this._gainNode); - this._localGainNode.gain.value = this._volume; - } - if(this._buffer === null) { - this.isDecoding = true; - } else { - this.play(); - } + this.markers = { + }; + this.onDecoded = new Phaser.Signal(); + this.onPlay = new Phaser.Signal(); + this.onPause = new Phaser.Signal(); + this.onResume = new Phaser.Signal(); + this.onLoop = new Phaser.Signal(); + this.onStop = new Phaser.Signal(); + this.onMute = new Phaser.Signal(); } - Sound.prototype.setDecodedBuffer = function (data) { - this._buffer = data; - this.isDecoding = false; - //this.play(); - }; + Sound.prototype.soundHasUnlocked = function (key) { + if(key == this.key) { + this._sound = this.game.cache.getSoundData(this.key); + this.totalDuration = this._sound.duration; + console.log('sound has unlocked', this._sound); + } + }; + Object.defineProperty(Sound.prototype, "isDecoding", { + get: function () { + return this.game.cache.getSound(this.key).isDecoding; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Sound.prototype, "isDecoded", { + get: function () { + return this.game.cache.isSoundDecoded(this.key); + }, + enumerable: true, + configurable: true + }); + Sound.prototype.addMarker = function (name, start, stop, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + this.markers[name] = { + name: name, + start: start, + stop: stop, + volume: volume, + duration: stop - start, + loop: loop + }; + }; + Sound.prototype.removeMarker = function (name) { + delete this.markers[name]; + }; + Sound.prototype.update = function () { + if(this.pendingPlayback && this.game.cache.isSoundReady(this.key)) { + console.log('pending over'); + this.pendingPlayback = false; + this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop); + } + if(this.isPlaying) { + this.currentTime = this.game.time.now - this.startTime; + if(this.currentTime >= this.duration) { + if(this.usingWebAudio) { + if(this.loop) { + this.onLoop.dispatch(this); + this.currentTime = 0; + this.startTime = this.game.time.now; + } else { + this.stop(); + } + } else { + if(this.loop) { + this.onLoop.dispatch(this); + this.play(this.currentMarker, 0, this.volume, true, true); + } else { + this.stop(); + } + } + } + } + }; Sound.prototype.play = /** - * Play this sound. + * Play this sound, or a marked section of it. + * @param marker {string} Assets key of the sound you want to play. + * @param [volume] {number} volume of the sound you want to play. + * @param [loop] {boolean} loop when it finished playing? (Default to false) + * @return {Sound} The playing sound object. */ - function () { - if(this._buffer === null || this.isDecoding === true) { + function (marker, position, volume, loop, forceRestart) { + if (typeof marker === "undefined") { marker = ''; } + if (typeof position === "undefined") { position = 0; } + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + if (typeof forceRestart === "undefined") { forceRestart = false; } + if(this.isPlaying == true && forceRestart == false) { + // Use Restart instead return; } - this._sound = this._context.createBufferSource(); - this._sound.buffer = this._buffer; - this._sound.connect(this._localGainNode); - if(this.loop) { - this._sound.loop = true; + this.currentMarker = marker; + if(marker !== '' && this.markers[marker]) { + this.position = this.markers[marker].start; + this.volume = this.markers[marker].volume; + this.loop = this.markers[marker].loop; + this.duration = this.markers[marker].duration * 1000; + } else { + this.position = position; + this.volume = volume; + this.loop = loop; + this.duration = 0; + } + this._tempMarker = marker; + this._tempPosition = position; + this._tempVolume = volume; + this._tempLoop = loop; + if(this.usingWebAudio) { + // Does the sound need decoding? + if(this.game.cache.isSoundDecoded(this.key)) { + // Do we need to do this every time we play? How about just if the buffer is empty? + this._buffer = this.game.cache.getSoundData(this.key); + this._sound = this.context.createBufferSource(); + this._sound.buffer = this._buffer; + this._sound.connect(this.gainNode); + this.totalDuration = this._sound.buffer.duration; + if(this.duration == 0) { + this.duration = this.totalDuration * 1000; + } + if(this.loop) { + this._sound.loop = true; + } + // Useful to cache this somewhere perhaps? + if(typeof this._sound.start === 'undefined') { + this._sound.noteGrainOn(0, this.position, this.duration / 1000); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } else { + this._sound.start(0, this.position, this.duration / 1000); + } + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } else { + this.pendingPlayback = true; + if(this.game.cache.getSound(this.key).isDecoding == false) { + this.game.sound.decode(this.key, this); + } + } + } else { + console.log('Sound play Audio'); + if(this.game.cache.getSound(this.key).locked) { + console.log('tried playing locked sound, pending set, reload started'); + this.game.cache.reloadSound(this.key); + this.pendingPlayback = true; + } else { + console.log('sound not locked, state?', this._sound.readyState); + if(this._sound && this._sound.readyState == 4) { + if(this.duration == 0) { + this.duration = this.totalDuration * 1000; + } + console.log('playing', this._sound); + this._sound.currentTime = this.position; + this._sound.muted = this._muted; + this._sound.volume = this._volume; + this._sound.play(); + this.isPlaying = true; + this.startTime = this.game.time.now; + this.currentTime = 0; + this.stopTime = this.startTime + this.duration; + this.onPlay.dispatch(this); + } else { + this.pendingPlayback = true; + } + } + } + }; + Sound.prototype.restart = function (marker, position, volume, loop) { + if (typeof marker === "undefined") { marker = ''; } + if (typeof position === "undefined") { position = 0; } + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + this.play(marker, position, volume, loop, true); + }; + Sound.prototype.pause = function () { + if(this.isPlaying && this._sound) { + this.stop(); + this.isPlaying = false; + this.paused = true; + this.onPause.dispatch(this); + } + }; + Sound.prototype.resume = function () { + //if (this.isPlaying == false && this._sound) + if(this.paused && this._sound) { + if(this.usingWebAudio) { + if(typeof this._sound.start === 'undefined') { + this._sound.noteGrainOn(0, this.position, this.duration); + //this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it + } else { + this._sound.start(0, this.position, this.duration); + } + } else { + this._sound.play(); + } + this.isPlaying = true; + this.paused = false; + this.onResume.dispatch(this); } - this._sound.noteOn(0)// the zero is vitally important, crashes iOS6 without it - ; - this.duration = this._sound.buffer.duration; - this.isPlaying = true; }; Sound.prototype.stop = /** * Stop playing this sound. */ function () { - if(this.isPlaying === true) { + if(this.isPlaying && this._sound) { + if(this.usingWebAudio) { + if(typeof this._sound.stop === 'undefined') { + this._sound.noteOff(0); + } else { + this._sound.stop(0); + } + } else if(this.usingAudioTag) { + this._sound.pause(); + this._sound.currentTime = 0; + } this.isPlaying = false; - this._sound.noteOff(0); + this.currentMarker = ''; + this.onStop.dispatch(this); } }; - Sound.prototype.mute = /** - * Mute the sound. - */ - function () { - this._localGainNode.gain.value = 0; - }; - Sound.prototype.unmute = /** - * Enable the sound. - */ - function () { - this._localGainNode.gain.value = this._volume; - }; + Object.defineProperty(Sound.prototype, "mute", { + get: /** + * Mute sounds. + */ + function () { + return this._muted; + }, + set: function (value) { + if(value) { + this._muted = true; + if(this.usingWebAudio) { + this._muteVolume = this.gainNode.gain.value; + this.gainNode.gain.value = 0; + } else if(this.usingAudioTag && this._sound) { + this._muteVolume = this._sound.volume; + this._sound.volume = 0; + } + } else { + this._muted = false; + if(this.usingWebAudio) { + this.gainNode.gain.value = this._muteVolume; + } else if(this.usingAudioTag && this._sound) { + this._sound.volume = this._muteVolume; + } + } + this.onMute.dispatch(this); + }, + enumerable: true, + configurable: true + }); Object.defineProperty(Sound.prototype, "volume", { get: function () { return this._volume; }, set: function (value) { this._volume = value; - this._localGainNode.gain.value = this._volume; + if(this.usingWebAudio) { + this.gainNode.gain.value = value; + } else if(this.usingAudioTag && this._sound) { + this._sound.volume = value; + } }, enumerable: true, configurable: true }); + Sound.prototype.renderDebug = /** + * Renders the Pointer.circle object onto the stage in green if down or red if up. + * @method renderDebug + */ + function (x, y) { + this.game.stage.context.fillStyle = 'rgb(255,255,255)'; + this.game.stage.context.font = '16px Courier'; + this.game.stage.context.fillText('Sound: ' + this.key + ' Locked: ' + this.game.sound.touchLocked + ' Pending Playback: ' + this.pendingPlayback, x, y); + this.game.stage.context.fillText('Decoded: ' + this.isDecoded + ' Decoding: ' + this.isDecoding, x, y + 20); + this.game.stage.context.fillText('Total Duration: ' + this.totalDuration + ' Playing: ' + this.isPlaying, x, y + 40); + this.game.stage.context.fillText('Time: ' + this.currentTime, x, y + 60); + this.game.stage.context.fillText('Volume: ' + this.volume + ' Muted: ' + this.mute, x, y + 80); + this.game.stage.context.fillText('WebAudio: ' + this.usingWebAudio + ' Audio: ' + this.usingAudioTag, x, y + 100); + if(this.currentMarker !== '') { + this.game.stage.context.fillText('Marker: ' + this.currentMarker + ' Duration: ' + this.duration, x, y + 120); + this.game.stage.context.fillText('Start: ' + this.markers[this.currentMarker].start + ' Stop: ' + this.markers[this.currentMarker].stop, x, y + 140); + this.game.stage.context.fillText('Position: ' + this.position, x, y + 160); + } + }; return Sound; })(); Phaser.Sound = Sound; @@ -14079,100 +14463,229 @@ var Phaser; * Create a new SoundManager. */ function SoundManager(game) { + this.usingWebAudio = false; + this.usingAudioTag = false; + this.noAudio = false; /** * Reference to AudioContext instance. */ - this._context = null; - this._game = game; - if(window['PhaserGlobal'] && window['PhaserGlobal'].disableAudio == true) { - return; + this.context = null; + this._muted = false; + this.touchLocked = false; + this._unlockSource = null; + this.onSoundDecode = new Phaser.Signal(); + this.game = game; + this._volume = 1; + this._muted = false; + this._sounds = []; + if(this.game.device.iOS && this.game.device.webAudio == false) { + this.channels = 1; } - if(game.device.webaudio == true) { - if(!!window['AudioContext']) { - this._context = new window['AudioContext'](); - } else if(!!window['webkitAudioContext']) { - this._context = new window['webkitAudioContext'](); + if(this.game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock)) { + console.log('iOS Touch Locked'); + this.game.input.touch.callbackContext = this; + this.game.input.touch.touchStartCallback = this.unlock; + this.game.input.mouse.callbackContext = this; + this.game.input.mouse.mouseDownCallback = this.unlock; + this.touchLocked = true; + } else { + // What about iOS5? + this.touchLocked = false; + } + if(window['PhaserGlobal']) { + // Check to see if all audio playback is disabled (i.e. handled by a 3rd party class) + if(window['PhaserGlobal'].disableAudio == true) { + this.usingWebAudio = false; + this.noAudio = true; + return; } - if(this._context !== null) { - this._gainNode = this._context.createGainNode(); - this._gainNode.connect(this._context.destination); - this._volume = 1; + // Check if the Web Audio API is disabled (for testing Audio Tag playback during development) + if(window['PhaserGlobal'].disableWebAudio == true) { + this.usingWebAudio = false; + this.usingAudioTag = true; + this.noAudio = false; + return; } } + this.usingWebAudio = true; + this.noAudio = false; + if(!!window['AudioContext']) { + this.context = new window['AudioContext'](); + } else if(!!window['webkitAudioContext']) { + this.context = new window['webkitAudioContext'](); + } else if(!!window['Audio']) { + this.usingWebAudio = false; + this.usingAudioTag = true; + } else { + this.usingWebAudio = false; + this.noAudio = true; + } + if(this.context !== null) { + if(typeof this.context.createGain === 'undefined') { + this.masterGain = this.context.createGainNode(); + } else { + this.masterGain = this.context.createGain(); + } + this.masterGain.gain.value = 1; + this.masterGain.connect(this.context.destination); + } } - SoundManager.prototype.mute = /** - * Mute sounds. - */ - function () { - this._gainNode.gain.value = 0; + SoundManager.prototype.unlock = function () { + if(this.touchLocked == false) { + return; + } + console.log('SoundManager touch unlocked'); + if(this.game.device.webAudio && (window['PhaserGlobal'] && window['PhaserGlobal'].disableWebAudio == false)) { + console.log('create empty buffer'); + // Create empty buffer and play it + var buffer = this.context.createBuffer(1, 1, 22050); + this._unlockSource = this.context.createBufferSource(); + this._unlockSource.buffer = buffer; + this._unlockSource.connect(this.context.destination); + this._unlockSource.noteOn(0); + } else { + // Create an Audio tag? + console.log('create audio tag'); + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + this.game.input.mouse.callbackContext = null; + this.game.input.mouse.mouseDownCallback = null; + } }; - SoundManager.prototype.unmute = /** - * Enable sounds. - */ - function () { - this._gainNode.gain.value = this._volume; - }; - Object.defineProperty(SoundManager.prototype, "volume", { - get: function () { - return this._volume; + Object.defineProperty(SoundManager.prototype, "mute", { + get: /** + * A global audio mute toggle. + */ + function () { + return this._muted; }, set: function (value) { - this._volume = value; - this._gainNode.gain.value = this._volume; + if(value) { + if(this._muted) { + return; + } + this._muted = true; + if(this.usingWebAudio) { + this._muteVolume = this.masterGain.gain.value; + this.masterGain.gain.value = 0; + } + // Loop through sounds + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].mute = true; + } + } + } else { + if(this._muted == false) { + return; + } + this._muted = false; + if(this.usingWebAudio) { + this.masterGain.gain.value = this._muteVolume; + } + // Loop through sounds + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].mute = false; + } + } + } }, enumerable: true, configurable: true }); + Object.defineProperty(SoundManager.prototype, "volume", { + get: function () { + if(this.usingWebAudio) { + return this.masterGain.gain.value; + } else { + return this._volume; + } + }, + set: /** + * The global audio volume. A value between 0 (silence) and 1 (full volume) + */ + function (value) { + value = this.game.math.clamp(value, 1, 0); + this._volume = value; + if(this.usingWebAudio) { + this.masterGain.gain.value = value; + } + // Loop through the sound cache and change the volume of all html audio tags + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i].usingAudioTag) { + this._sounds[i].volume = this._sounds[i].volume * value; + } + } + }, + enumerable: true, + configurable: true + }); + SoundManager.prototype.stopAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].stop(); + } + } + }; + SoundManager.prototype.pauseAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].pause(); + } + } + }; + SoundManager.prototype.resumeAll = function () { + for(var i = 0; i < this._sounds.length; i++) { + if(this._sounds[i]) { + this._sounds[i].resume(); + } + } + }; SoundManager.prototype.decode = /** * Decode a sound with its assets key. * @param key {string} Assets key of the sound to be decoded. - * @param callback {function} This will be invoked when finished decoding. * @param [sound] {Sound} its bufer will be set to decoded data. */ - function (key, callback, sound) { - if (typeof callback === "undefined") { callback = null; } + function (key, sound) { if (typeof sound === "undefined") { sound = null; } - var soundData = this._game.cache.getSound(key); + var soundData = this.game.cache.getSoundData(key); if(soundData) { - if(this._game.cache.isSoundDecoded(key) === false) { + if(this.game.cache.isSoundDecoded(key) === false) { + this.game.cache.updateSound(key, 'isDecoding', true); var that = this; - this._context.decodeAudioData(soundData, function (buffer) { - that._game.cache.decodedSound(key, buffer); + this.context.decodeAudioData(soundData, function (buffer) { + that.game.cache.decodedSound(key, buffer); if(sound) { - sound.setDecodedBuffer(buffer); + that.onSoundDecode.dispatch(sound); } - callback(); }); } } }; - SoundManager.prototype.play = /** - * Play a sound with its assets key. - * @param key {string} Assets key of the sound you want to play. - * @param [volume] {number} volume of the sound you want to play. - * @param [loop] {boolean} loop when it finished playing? (Default to false) - * @return {Sound} The playing sound object. - */ - function (key, volume, loop) { - if (typeof volume === "undefined") { volume = 1; } - if (typeof loop === "undefined") { loop = false; } - if(this._context === null) { - return; - } - var soundData = this._game.cache.getSound(key); - if(soundData) { - // Does the sound need decoding? - if(this._game.cache.isSoundDecoded(key) === true) { - return new Phaser.Sound(this._context, this._gainNode, soundData, volume, loop); - } else { - var tempSound = new Phaser.Sound(this._context, this._gainNode, null, volume, loop); - // this is an async process, so we can return the Sound object anyway, it just won't be playing yet - this.decode(key, function () { - return tempSound.play(); - }, tempSound); - return tempSound; + SoundManager.prototype.update = function () { + if(this.touchLocked) { + if(this.game.device.webAudio && this._unlockSource !== null) { + if((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE)) { + this.touchLocked = false; + this._unlockSource = null; + this.game.input.touch.callbackContext = null; + this.game.input.touch.touchStartCallback = null; + } } } + for(var i = 0; i < this._sounds.length; i++) { + this._sounds[i].update(); + } + }; + SoundManager.prototype.add = function (key, volume, loop) { + if (typeof volume === "undefined") { volume = 1; } + if (typeof loop === "undefined") { loop = false; } + var sound = new Phaser.Sound(this.game, key, volume, loop); + this._sounds.push(sound); + return sound; }; return SoundManager; })(); @@ -14861,6 +15374,12 @@ var Phaser; document.addEventListener('webkitvisibilitychange', function (event) { return _this.visibilityChange(event); }, false); + document.addEventListener('pagehide', function (event) { + return _this.visibilityChange(event); + }, false); + document.addEventListener('pageshow', function (event) { + return _this.visibilityChange(event); + }, false); window.onblur = function (event) { return _this.visibilityChange(event); }; @@ -14905,16 +15424,17 @@ var Phaser; * This method is called when the canvas elements visibility is changed. */ function (event) { - if(this.disablePauseScreen) { - return; - } - if(event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) { - if(this._game.paused == false) { + if(event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) { + if(this._game.paused == false && this.disablePauseScreen == false) { this.pauseGame(); + } else { + this._game.paused = true; } } else { - if(this._game.paused == true) { + if(this._game.paused == true && this.disablePauseScreen == false) { this.resumeGame(); + } else { + this._game.paused = false; } } }; @@ -15244,16 +15764,23 @@ var Phaser; this._tweens.length = 0; }; TweenManager.prototype.create = /** - * Create a tween object for a specific object. + * Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite. * - * @param object {object} Object you wish the tween will affect. + * @param obj {object} Object the tween will be run on. + * @param [localReference] {bool} If true the tween will be stored in the object.tween property so long as it exists. If already set it'll be over-written. * @return {Phaser.Tween} The newly created tween object. */ - function (object) { - return new Phaser.Tween(object, this._game); + function (object, localReference) { + if (typeof localReference === "undefined") { localReference = false; } + if(localReference) { + object['tween'] = new Phaser.Tween(object, this._game); + return object['tween']; + } else { + return new Phaser.Tween(object, this._game); + } }; TweenManager.prototype.add = /** - * Add an exist tween object to the manager. + * Add a new tween into the TweenManager. * * @param tween {Phaser.Tween} The tween object you want to add. * @return {Phaser.Tween} The tween object you added to the manager. @@ -15892,35 +16419,45 @@ var Phaser; this.webApp = false; // Audio /** - * Is audioData available? + * Are Audio tags available? * @type {boolean} */ this.audioData = false; /** - * Is webaudio available? + * Is the WebAudio API available? * @type {boolean} */ - this.webaudio = false; + this.webAudio = false; /** - * Is ogg available? + * Can this device play ogg files? * @type {boolean} */ this.ogg = false; /** - * Is mp3 available? + * Can this device play opus files? + * @type {boolean} + */ + this.opus = false; + /** + * Can this device play mp3 files? * @type {boolean} */ this.mp3 = false; /** - * Is wav available? + * Can this device play wav files? * @type {boolean} */ this.wav = false; /** - * Is m4a available? + * Can this device play m4a files? * @type {boolean} */ this.m4a = false; + /** + * Can this device play webm files? + * @type {boolean} + */ + this.webm = false; // Device /** * Is running on iPhone? @@ -16025,13 +16562,27 @@ var Phaser; this.webApp = true; } }; + Device.prototype.canPlayAudio = function (type) { + if(type == 'mp3' && this.mp3) { + return true; + } else if(type == 'ogg' && (this.ogg || this.opus)) { + return true; + } else if(type == 'm4a' && this.m4a) { + return true; + } else if(type == 'wav' && this.wav) { + return true; + } else if(type == 'webm' && this.webm) { + return true; + } + return false; + }; Device.prototype._checkAudio = /** * Check audio support. * @private */ function () { this.audioData = !!(window['Audio']); - this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']); + this.webAudio = !!(window['webkitAudioContext'] || window['AudioContext']); var audioElement = document.createElement('audio'); var result = false; try { @@ -16039,6 +16590,9 @@ var Phaser; if(audioElement.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '')) { this.ogg = true; } + if(audioElement.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, '')) { + this.opus = true; + } if(audioElement.canPlayType('audio/mpeg;').replace(/^no$/, '')) { this.mp3 = true; } @@ -16051,6 +16605,9 @@ var Phaser; if(audioElement.canPlayType('audio/x-m4a;') || audioElement.canPlayType('audio/aac;').replace(/^no$/, '')) { this.m4a = true; } + if(audioElement.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')) { + this.webm = true; + } } } catch (e) { } @@ -16637,6 +17194,12 @@ var Phaser; **/ this.totalTouches = 0; /** + * The number of miliseconds since the last click + * @property msSinceLastClick + * @type {Number} + **/ + this.msSinceLastClick = Number.MAX_VALUE; + /** * The Game Object this Pointer is currently over / touching / dragging. * @property targetObject * @type {Any} @@ -16704,6 +17267,8 @@ var Phaser; this.withinGame = true; this.isDown = true; this.isUp = false; + // Work out how long it has been since the last click + this.msSinceLastClick = this.game.time.now - this.timeDown; this.timeDown = this.game.time.now; this._holdSent = false; // This sets the x/y and other local values @@ -17761,7 +18326,7 @@ var Phaser; * @property recordPointerHistory * @type {Boolean} **/ - this.recordPointerHistory = true; + this.recordPointerHistory = false; /** * The rate in milliseconds at which the Pointer objects should update their tracking history * @property recordRate @@ -17770,7 +18335,7 @@ var Phaser; this.recordRate = 100; /** * The total number of entries that can be recorded into the Pointer objects tracking history. - * The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. + * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history. * @property recordLimit * @type {Number} */ @@ -19013,6 +19578,7 @@ var Phaser; /// /// /// +/// /// /// /// @@ -19183,12 +19749,12 @@ var Phaser; this.stage = new Phaser.Stage(this, parent, width, height); this.world = new Phaser.World(this, width, height); this.add = new Phaser.GameObjectFactory(this); - this.sound = new Phaser.SoundManager(this); this.cache = new Phaser.Cache(this); this.load = new Phaser.Loader(this, this.loadComplete); this.time = new Phaser.Time(this); this.tweens = new Phaser.TweenManager(this); this.input = new Phaser.Input(this); + this.sound = new Phaser.SoundManager(this); this.rnd = new Phaser.RandomDataGenerator([ (Date.now() * Math.random()).toString() ]); @@ -19250,6 +19816,7 @@ var Phaser; this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); if(this.onPausedCallback !== null) { this.onPausedCallback.call(this.callbackContext); } @@ -19261,6 +19828,7 @@ var Phaser; this.tweens.update(); this.input.update(); this.stage.update(); + this.sound.update(); this.physics.update(); this.world.update(); if(this._loadComplete && this.onUpdateCallback) { @@ -19424,11 +19992,13 @@ var Phaser; set: function (value) { if(value == true && this._paused == false) { this._paused = true; + this.sound.pauseAll(); this._raf.callback = this.pausedLoop; } else if(value == false && this._paused == true) { this._paused = false; //this.time.time = window.performance.now ? (performance.now() + performance.timing.navigationStart) : Date.now(); this.input.reset(); + this.sound.resumeAll(); if(this.isRunning == false) { this._raf.callback = this.bootLoop; } else { diff --git a/todo/docChanges.zip b/todo/docChanges.zip new file mode 100644 index 00000000..8967d912 Binary files /dev/null and b/todo/docChanges.zip differ diff --git a/todo/phaser clean up/20130112_arkanoid_sdl_v1.0.0.01_(pandora_game_port).png b/todo/phaser clean up/20130112_arkanoid_sdl_v1.0.0.01_(pandora_game_port).png new file mode 100644 index 00000000..2aeaeae4 Binary files /dev/null and b/todo/phaser clean up/20130112_arkanoid_sdl_v1.0.0.01_(pandora_game_port).png differ diff --git a/todo/phaser clean up/Collision.js b/todo/phaser clean up/Collision.js new file mode 100644 index 00000000..d22312a9 --- /dev/null +++ b/todo/phaser clean up/Collision.js @@ -0,0 +1,777 @@ +var Phaser; +(function (Phaser) { + var Collision = (function () { + function Collision(game) { + this._game = game; + Collision.T_VECTORS = []; + for(var i = 0; i < 10; i++) { + Collision.T_VECTORS.push(new Vector2()); + } + Collision.T_ARRAYS = []; + for(var i = 0; i < 5; i++) { + Collision.T_ARRAYS.push([]); + } + } + Collision.LEFT = 0x0001; + Collision.RIGHT = 0x0010; + Collision.UP = 0x0100; + Collision.DOWN = 0x1000; + Collision.NONE = 0; + Collision.CEILING = Phaser.Collision.UP; + Collision.FLOOR = Phaser.Collision.DOWN; + Collision.WALL = Phaser.Collision.LEFT | Phaser.Collision.RIGHT; + Collision.ANY = Phaser.Collision.LEFT | Phaser.Collision.RIGHT | Phaser.Collision.UP | Phaser.Collision.DOWN; + Collision.OVERLAP_BIAS = 4; + Collision.TILE_OVERLAP = false; + Collision.lineToLine = function lineToLine(line1, line2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var denominator = (line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 - line2.x2); + if(denominator !== 0) { + output.result = true; + output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.x1 - line2.x2) - (line1.x1 - line1.x2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; + output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; + } + return output; + }; + Collision.lineToLineSegment = function lineToLineSegment(line, seg, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var denominator = (line.x1 - line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 - seg.x2); + if(denominator !== 0) { + output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.x1 - seg.x2) - (line.x1 - line.x2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; + output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; + var maxX = Math.max(seg.x1, seg.x2); + var minX = Math.min(seg.x1, seg.x2); + var maxY = Math.max(seg.y1, seg.y2); + var minY = Math.min(seg.y1, seg.y2); + if((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) { + output.result = true; + } + } + return output; + }; + Collision.lineToRawSegment = function lineToRawSegment(line, x1, y1, x2, y2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var denominator = (line.x1 - line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 - x2); + if(denominator !== 0) { + output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (x1 - x2) - (line.x1 - line.x2) * (x1 * y2 - y1 * x2)) / denominator; + output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 * y2 - y1 * x2)) / denominator; + var maxX = Math.max(x1, x2); + var minX = Math.min(x1, x2); + var maxY = Math.max(y1, y2); + var minY = Math.min(y1, y2); + if((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) { + output.result = true; + } + } + return output; + }; + Collision.lineToRay = function lineToRay(line1, ray, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var denominator = (line1.x1 - line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 - ray.x2); + if(denominator !== 0) { + output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.x1 - ray.x2) - (line1.x1 - line1.x2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; + output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; + output.result = true; + if(!(ray.x1 >= ray.x2) && output.x < ray.x1) { + output.result = false; + } + if(!(ray.y1 >= ray.y2) && output.y < ray.y1) { + output.result = false; + } + } + return output; + }; + Collision.lineToCircle = function lineToCircle(line, circle, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + if(line.perp(circle.x, circle.y).length <= circle.radius) { + output.result = true; + } + return output; + }; + Collision.lineToRectangle = function lineToRectangle(line, rect, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + Phaser.Collision.lineToRawSegment(line, rect.x, rect.y, rect.right, rect.y, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(line, rect.x, rect.y, rect.x, rect.bottom, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(line, rect.x, rect.bottom, rect.right, rect.bottom, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(line, rect.right, rect.y, rect.right, rect.bottom, output); + return output; + }; + Collision.lineSegmentToLineSegment = function lineSegmentToLineSegment(line1, line2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + Phaser.Collision.lineToLineSegment(line1, line2); + if(output.result === true) { + if(!(output.x >= Math.min(line1.x1, line1.x2) && output.x <= Math.max(line1.x1, line1.x2) && output.y >= Math.min(line1.y1, line1.y2) && output.y <= Math.max(line1.y1, line1.y2))) { + output.result = false; + } + } + return output; + }; + Collision.lineSegmentToRay = function lineSegmentToRay(line, ray, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + Phaser.Collision.lineToRay(line, ray, output); + if(output.result === true) { + if(!(output.x >= Math.min(line.x1, line.x2) && output.x <= Math.max(line.x1, line.x2) && output.y >= Math.min(line.y1, line.y2) && output.y <= Math.max(line.y1, line.y2))) { + output.result = false; + } + } + return output; + }; + Collision.lineSegmentToCircle = function lineSegmentToCircle(seg, circle, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var perp = seg.perp(circle.x, circle.y); + if(perp.length <= circle.radius) { + var maxX = Math.max(seg.x1, seg.x2); + var minX = Math.min(seg.x1, seg.x2); + var maxY = Math.max(seg.y1, seg.y2); + var minY = Math.min(seg.y1, seg.y2); + if((perp.x2 <= maxX && perp.x2 >= minX) && (perp.y2 <= maxY && perp.y2 >= minY)) { + output.result = true; + } else { + if(Phaser.Collision.circleContainsPoint(circle, { + x: seg.x1, + y: seg.y1 + }) || Phaser.Collision.circleContainsPoint(circle, { + x: seg.x2, + y: seg.y2 + })) { + output.result = true; + } + } + } + return output; + }; + Collision.lineSegmentToRectangle = function lineSegmentToRectangle(seg, rect, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + if(rect.contains(seg.x1, seg.y1) && rect.contains(seg.x2, seg.y2)) { + output.result = true; + } else { + Phaser.Collision.lineToRawSegment(seg, rect.x, rect.y, rect.right, rect.bottom, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(seg, rect.x, rect.y, rect.x, rect.bottom, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(seg, rect.x, rect.bottom, rect.right, rect.bottom, output); + if(output.result === true) { + return output; + } + Phaser.Collision.lineToRawSegment(seg, rect.right, rect.y, rect.right, rect.bottom, output); + return output; + } + return output; + }; + Collision.rayToRectangle = function rayToRectangle(ray, rect, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + Phaser.Collision.lineToRectangle(ray, rect, output); + return output; + }; + Collision.rayToLineSegment = function rayToLineSegment(rayX1, rayY1, rayX2, rayY2, lineX1, lineY1, lineX2, lineY2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var r; + var s; + var d; + if((rayY2 - rayY1) / (rayX2 - rayX1) != (lineY2 - lineY1) / (lineX2 - lineX1)) { + d = (((rayX2 - rayX1) * (lineY2 - lineY1)) - (rayY2 - rayY1) * (lineX2 - lineX1)); + if(d != 0) { + r = (((rayY1 - lineY1) * (lineX2 - lineX1)) - (rayX1 - lineX1) * (lineY2 - lineY1)) / d; + s = (((rayY1 - lineY1) * (rayX2 - rayX1)) - (rayX1 - lineX1) * (rayY2 - rayY1)) / d; + if(r >= 0) { + if(s >= 0 && s <= 1) { + output.result = true; + output.x = rayX1 + r * (rayX2 - rayX1); + output.y = rayY1 + r * (rayY2 - rayY1); + } + } + } + } + return output; + }; + Collision.pointToRectangle = function pointToRectangle(point, rect, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + output.setTo(point.x, point.y); + output.result = rect.containsPoint(point); + return output; + }; + Collision.rectangleToRectangle = function rectangleToRectangle(rect1, rect2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var leftX = Math.max(rect1.x, rect2.x); + var rightX = Math.min(rect1.right, rect2.right); + var topY = Math.max(rect1.y, rect2.y); + var bottomY = Math.min(rect1.bottom, rect2.bottom); + output.setTo(leftX, topY, rightX - leftX, bottomY - topY, rightX - leftX, bottomY - topY); + var cx = output.x + output.width * .5; + var cy = output.y + output.height * .5; + if((cx > rect1.x && cx < rect1.right) && (cy > rect1.y && cy < rect1.bottom)) { + output.result = true; + } + return output; + }; + Collision.rectangleToCircle = function rectangleToCircle(rect, circle, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + return Phaser.Collision.circleToRectangle(circle, rect, output); + }; + Collision.circleToCircle = function circleToCircle(circle1, circle2, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + output.result = ((circle1.radius + circle2.radius) * (circle1.radius + circle2.radius)) >= Phaser.Collision.distanceSquared(circle1.x, circle1.y, circle2.x, circle2.y); + return output; + }; + Collision.circleToRectangle = function circleToRectangle(circle, rect, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + var inflatedRect = rect.clone(); + inflatedRect.inflate(circle.radius, circle.radius); + output.result = inflatedRect.contains(circle.x, circle.y); + return output; + }; + Collision.circleContainsPoint = function circleContainsPoint(circle, point, output) { + if (typeof output === "undefined") { output = new Phaser.IntersectResult(); } + output.result = circle.radius * circle.radius >= Phaser.Collision.distanceSquared(circle.x, circle.y, point.x, point.y); + return output; + }; + Collision.prototype.overlap = function (object1, object2, notifyCallback, processCallback, context) { + if (typeof object1 === "undefined") { object1 = null; } + if (typeof object2 === "undefined") { object2 = null; } + if (typeof notifyCallback === "undefined") { notifyCallback = null; } + if (typeof processCallback === "undefined") { processCallback = null; } + if (typeof context === "undefined") { context = null; } + if(object1 == null) { + object1 = this._game.world.group; + } + if(object2 == object1) { + object2 = null; + } + Phaser.QuadTree.divisions = this._game.world.worldDivisions; + var quadTree = new Phaser.QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); + quadTree.load(object1, object2, notifyCallback, processCallback, context); + var result = quadTree.execute(); + quadTree.destroy(); + quadTree = null; + return result; + }; + Collision.separate = function separate(object1, object2) { + object1.collisionMask.update(); + object2.collisionMask.update(); + var separatedX = Phaser.Collision.separateX(object1, object2); + var separatedY = Phaser.Collision.separateY(object1, object2); + return separatedX || separatedY; + }; + Collision.separateTile = function separateTile(object, x, y, width, height, mass, collideLeft, collideRight, collideUp, collideDown, separateX, separateY) { + object.collisionMask.update(); + var separatedX = Phaser.Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); + var separatedY = Phaser.Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); + return separatedX || separatedY; + }; + Collision.separateTileX = function separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separate) { + if(object.immovable) { + return false; + } + var overlap = 0; + var objDelta = object.x - object.last.x; + if(objDelta != 0) { + var objDeltaAbs = (objDelta > 0) ? objDelta : -objDelta; + var objBounds = new Phaser.Quad(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); + if((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) { + var maxOverlap = objDeltaAbs + Phaser.Collision.OVERLAP_BIAS; + if(objDelta > 0) { + overlap = object.x + object.width - x; + if((overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.RIGHT) || collideLeft == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.RIGHT; + } + } else if(objDelta < 0) { + overlap = object.x - width - x; + if((-overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.LEFT) || collideRight == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.LEFT; + } + } + } + } + if(overlap != 0) { + if(separate == true) { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } + Phaser.Collision.TILE_OVERLAP = true; + return true; + } else { + return false; + } + }; + Collision.separateTileY = function separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separate) { + if(object.immovable) { + return false; + } + var overlap = 0; + var objDelta = object.y - object.last.y; + if(objDelta != 0) { + var objDeltaAbs = (objDelta > 0) ? objDelta : -objDelta; + var objBounds = new Phaser.Quad(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); + if((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) { + var maxOverlap = objDeltaAbs + Phaser.Collision.OVERLAP_BIAS; + if(objDelta > 0) { + overlap = object.y + object.height - y; + if((overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.DOWN) || collideUp == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.DOWN; + } + } else if(objDelta < 0) { + overlap = object.y - height - y; + if((-overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.UP) || collideDown == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.UP; + } + } + } + } + if(overlap != 0) { + if(separate == true) { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } + Phaser.Collision.TILE_OVERLAP = true; + return true; + } else { + return false; + } + }; + Collision.NEWseparateTileX = function NEWseparateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separate) { + if(object.immovable) { + return false; + } + var overlap = 0; + if(object.collisionMask.deltaX != 0) { + if(object.collisionMask.intersectsRaw(x, x + width, y, y + height)) { + var maxOverlap = object.collisionMask.deltaXAbs + Phaser.Collision.OVERLAP_BIAS; + if(object.collisionMask.deltaX > 0) { + overlap = object.collisionMask.right - x; + if((overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.RIGHT) || collideLeft == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.RIGHT; + } + } else if(object.collisionMask.deltaX < 0) { + overlap = object.collisionMask.x - width - x; + if((-overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.LEFT) || collideRight == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.LEFT; + } + } + } + } + if(overlap != 0) { + if(separate == true) { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } + Phaser.Collision.TILE_OVERLAP = true; + return true; + } else { + return false; + } + }; + Collision.NEWseparateTileY = function NEWseparateTileY(object, x, y, width, height, mass, collideUp, collideDown, separate) { + if(object.immovable) { + return false; + } + var overlap = 0; + if(object.collisionMask.deltaY != 0) { + if(object.collisionMask.intersectsRaw(x, x + width, y, y + height)) { + var maxOverlap = object.collisionMask.deltaYAbs + Phaser.Collision.OVERLAP_BIAS; + if(object.collisionMask.deltaY > 0) { + overlap = object.collisionMask.bottom - y; + if((overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.DOWN) || collideUp == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.DOWN; + } + } else if(object.collisionMask.deltaY < 0) { + overlap = object.collisionMask.y - height - y; + if((-overlap > maxOverlap) || !(object.allowCollisions & Phaser.Collision.UP) || collideDown == false) { + overlap = 0; + } else { + object.touching |= Phaser.Collision.UP; + } + } + } + } + if(overlap != 0) { + if(separate == true) { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } + Phaser.Collision.TILE_OVERLAP = true; + return true; + } else { + return false; + } + }; + Collision.separateX = function separateX(object1, object2) { + if(object1.immovable && object2.immovable) { + return false; + } + var overlap = 0; + if(object1.collisionMask.deltaX != object2.collisionMask.deltaX) { + if(object1.collisionMask.intersects(object2.collisionMask)) { + var maxOverlap = object1.collisionMask.deltaXAbs + object2.collisionMask.deltaXAbs + Phaser.Collision.OVERLAP_BIAS; + if(object1.collisionMask.deltaX > object2.collisionMask.deltaX) { + overlap = object1.collisionMask.right - object2.collisionMask.x; + if((overlap > maxOverlap) || !(object1.allowCollisions & Phaser.Collision.RIGHT) || !(object2.allowCollisions & Phaser.Collision.LEFT)) { + overlap = 0; + } else { + object1.touching |= Phaser.Collision.RIGHT; + object2.touching |= Phaser.Collision.LEFT; + } + } else if(object1.collisionMask.deltaX < object2.collisionMask.deltaX) { + overlap = object1.collisionMask.x - object2.collisionMask.width - object2.collisionMask.x; + if((-overlap > maxOverlap) || !(object1.allowCollisions & Phaser.Collision.LEFT) || !(object2.allowCollisions & Phaser.Collision.RIGHT)) { + overlap = 0; + } else { + object1.touching |= Phaser.Collision.LEFT; + object2.touching |= Phaser.Collision.RIGHT; + } + } + } + } + if(overlap != 0) { + var obj1Velocity = object1.velocity.x; + var obj2Velocity = object2.velocity.x; + if(!object1.immovable && !object2.immovable) { + overlap *= 0.5; + object1.x = object1.x - overlap; + object2.x += overlap; + var obj1NewVelocity = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); + var obj2NewVelocity = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); + var average = (obj1NewVelocity + obj2NewVelocity) * 0.5; + obj1NewVelocity -= average; + obj2NewVelocity -= average; + object1.velocity.x = average + obj1NewVelocity * object1.elasticity; + object2.velocity.x = average + obj2NewVelocity * object2.elasticity; + } else if(!object1.immovable) { + object1.x = object1.x - overlap; + object1.velocity.x = obj2Velocity - obj1Velocity * object1.elasticity; + } else if(!object2.immovable) { + object2.x += overlap; + object2.velocity.x = obj1Velocity - obj2Velocity * object2.elasticity; + } + return true; + } else { + return false; + } + }; + Collision.separateY = function separateY(object1, object2) { + if(object1.immovable && object2.immovable) { + return false; + } + var overlap = 0; + if(object1.collisionMask.deltaY != object2.collisionMask.deltaY) { + if(object1.collisionMask.intersects(object2.collisionMask)) { + var maxOverlap = object1.collisionMask.deltaYAbs + object2.collisionMask.deltaYAbs + Phaser.Collision.OVERLAP_BIAS; + if(object1.collisionMask.deltaY > object2.collisionMask.deltaY) { + overlap = object1.collisionMask.bottom - object2.collisionMask.y; + if((overlap > maxOverlap) || !(object1.allowCollisions & Phaser.Collision.DOWN) || !(object2.allowCollisions & Phaser.Collision.UP)) { + overlap = 0; + } else { + object1.touching |= Phaser.Collision.DOWN; + object2.touching |= Phaser.Collision.UP; + } + } else if(object1.collisionMask.deltaY < object2.collisionMask.deltaY) { + overlap = object1.collisionMask.y - object2.collisionMask.height - object2.collisionMask.y; + if((-overlap > maxOverlap) || !(object1.allowCollisions & Phaser.Collision.UP) || !(object2.allowCollisions & Phaser.Collision.DOWN)) { + overlap = 0; + } else { + object1.touching |= Phaser.Collision.UP; + object2.touching |= Phaser.Collision.DOWN; + } + } + } + } + if(overlap != 0) { + var obj1Velocity = object1.velocity.y; + var obj2Velocity = object2.velocity.y; + if(!object1.immovable && !object2.immovable) { + overlap *= 0.5; + object1.y = object1.y - overlap; + object2.y += overlap; + var obj1NewVelocity = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); + var obj2NewVelocity = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); + var average = (obj1NewVelocity + obj2NewVelocity) * 0.5; + obj1NewVelocity -= average; + obj2NewVelocity -= average; + object1.velocity.y = average + obj1NewVelocity * object1.elasticity; + object2.velocity.y = average + obj2NewVelocity * object2.elasticity; + } else if(!object1.immovable) { + object1.y = object1.y - overlap; + object1.velocity.y = obj2Velocity - obj1Velocity * object1.elasticity; + if(object2.active && object2.moves && (object1.collisionMask.deltaY > object2.collisionMask.deltaY)) { + object1.x += object2.x - object2.last.x; + } + } else if(!object2.immovable) { + object2.y += overlap; + object2.velocity.y = obj1Velocity - obj2Velocity * object2.elasticity; + if(object1.active && object1.moves && (object1.collisionMask.deltaY < object2.collisionMask.deltaY)) { + object2.x += object1.x - object1.last.x; + } + } + return true; + } else { + return false; + } + }; + Collision.distance = function distance(x1, y1, x2, y2) { + return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + }; + Collision.distanceSquared = function distanceSquared(x1, y1, x2, y2) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + }; + Collision.flattenPointsOn = function flattenPointsOn(points, normal, result) { + var min = Number.MAX_VALUE; + var max = -Number.MAX_VALUE; + var len = points.length; + for(var i = 0; i < len; i++) { + var dot = points[i].dot(normal); + if(dot < min) { + min = dot; + } + if(dot > max) { + max = dot; + } + } + result[0] = min; + result[1] = max; + }; + Collision.isSeparatingAxis = function isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response) { + if (typeof response === "undefined") { response = null; } + var rangeA = Phaser.Collision.T_ARRAYS.pop(); + var rangeB = Phaser.Collision.T_ARRAYS.pop(); + var offsetV = Phaser.Collision.T_VECTORS.pop().copyFrom(bPos).sub(aPos); + var projectedOffset = offsetV.dot(axis); + Phaser.Collision.flattenPointsOn(aPoints, axis, rangeA); + Phaser.Collision.flattenPointsOn(bPoints, axis, rangeB); + rangeB[0] += projectedOffset; + rangeB[1] += projectedOffset; + if(rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) { + Phaser.Collision.T_VECTORS.push(offsetV); + Phaser.Collision.T_ARRAYS.push(rangeA); + Phaser.Collision.T_ARRAYS.push(rangeB); + return true; + } + if(response) { + var overlap = 0; + if(rangeA[0] < rangeB[0]) { + response.aInB = false; + if(rangeA[1] < rangeB[1]) { + overlap = rangeA[1] - rangeB[0]; + response.bInA = false; + } else { + var option1 = rangeA[1] - rangeB[0]; + var option2 = rangeB[1] - rangeA[0]; + overlap = option1 < option2 ? option1 : -option2; + } + } else { + response.bInA = false; + if(rangeA[1] > rangeB[1]) { + overlap = rangeA[0] - rangeB[1]; + response.aInB = false; + } else { + var option1 = rangeA[1] - rangeB[0]; + var option2 = rangeB[1] - rangeA[0]; + overlap = option1 < option2 ? option1 : -option2; + } + } + var absOverlap = Math.abs(overlap); + if(absOverlap < response.overlap) { + response.overlap = absOverlap; + response.overlapN.copyFrom(axis); + if(overlap < 0) { + response.overlapN.reverse(); + } + } + } + Phaser.Collision.T_VECTORS.push(offsetV); + Phaser.Collision.T_ARRAYS.push(rangeA); + Phaser.Collision.T_ARRAYS.push(rangeB); + return false; + }; + Collision.LEFT_VORNOI_REGION = -1; + Collision.MIDDLE_VORNOI_REGION = 0; + Collision.RIGHT_VORNOI_REGION = 1; + Collision.vornoiRegion = function vornoiRegion(line, point) { + var len2 = line.length2(); + var dp = point.dot(line); + if(dp < 0) { + return Phaser.Collision.LEFT_VORNOI_REGION; + } else if(dp > len2) { + return Phaser.Collision.RIGHT_VORNOI_REGION; + } else { + return Phaser.Collision.MIDDLE_VORNOI_REGION; + } + }; + Collision.testCircleCircle = function testCircleCircle(a, b, response) { + if (typeof response === "undefined") { response = null; } + var differenceV = Phaser.Collision.T_VECTORS.pop().copyFrom(b.pos).sub(a.pos); + var totalRadius = a.radius + b.radius; + var totalRadiusSq = totalRadius * totalRadius; + var distanceSq = differenceV.length2(); + if(distanceSq > totalRadiusSq) { + Phaser.Collision.T_VECTORS.push(differenceV); + return false; + } + if(response) { + var dist = Math.sqrt(distanceSq); + response.a = a; + response.b = b; + response.overlap = totalRadius - dist; + response.overlapN.copyFrom(differenceV.normalize()); + response.overlapV.copyFrom(differenceV).scale(response.overlap); + response.aInB = a.radius <= b.radius && dist <= b.radius - a.radius; + response.bInA = b.radius <= a.radius && dist <= a.radius - b.radius; + } + Phaser.Collision.T_VECTORS.push(differenceV); + return true; + }; + Collision.testPolygonCircle = function testPolygonCircle(polygon, circle, response) { + if (typeof response === "undefined") { response = null; } + var circlePos = Phaser.Collision.T_VECTORS.pop().copyFrom(circle.pos).sub(polygon.pos); + var radius = circle.radius; + var radius2 = radius * radius; + var points = polygon.points; + var len = points.length; + var edge = Collision.T_VECTORS.pop(); + var point = Collision.T_VECTORS.pop(); + for(var i = 0; i < len; i++) { + var next = i === len - 1 ? 0 : i + 1; + var prev = i === 0 ? len - 1 : i - 1; + var overlap = 0; + var overlapN = null; + edge.copyFrom(polygon.edges[i]); + point.copyFrom(circlePos).sub(points[i]); + if(response && point.length2() > radius2) { + response.aInB = false; + } + var region = Collision.vornoiRegion(edge, point); + if(region === Phaser.Collision.LEFT_VORNOI_REGION) { + edge.copyFrom(polygon.edges[prev]); + var point2 = Phaser.Collision.T_VECTORS.pop().copyFrom(circlePos).sub(points[prev]); + region = Collision.vornoiRegion(edge, point2); + if(region === Phaser.Collision.RIGHT_VORNOI_REGION) { + var dist = point.length2(); + if(dist > radius) { + Phaser.Collision.T_VECTORS.push(circlePos); + Phaser.Collision.T_VECTORS.push(edge); + Phaser.Collision.T_VECTORS.push(point); + Phaser.Collision.T_VECTORS.push(point2); + return false; + } else if(response) { + response.bInA = false; + overlapN = point.normalize(); + overlap = radius - dist; + } + } + Phaser.Collision.T_VECTORS.push(point2); + } else if(region === Phaser.Collision.RIGHT_VORNOI_REGION) { + edge.copyFrom(polygon.edges[next]); + point.copyFrom(circlePos).sub(points[next]); + region = Collision.vornoiRegion(edge, point); + if(region === Phaser.Collision.LEFT_VORNOI_REGION) { + var dist = point.length2(); + if(dist > radius) { + Phaser.Collision.T_VECTORS.push(circlePos); + Phaser.Collision.T_VECTORS.push(edge); + Phaser.Collision.T_VECTORS.push(point); + return false; + } else if(response) { + response.bInA = false; + overlapN = point.normalize(); + overlap = radius - dist; + } + } + } else { + var normal = edge.perp().normalize(); + var dist = point.dot(normal); + var distAbs = Math.abs(dist); + if(dist > 0 && distAbs > radius) { + Phaser.Collision.T_VECTORS.push(circlePos); + Phaser.Collision.T_VECTORS.push(normal); + Phaser.Collision.T_VECTORS.push(point); + return false; + } else if(response) { + overlapN = normal; + overlap = radius - dist; + if(dist >= 0 || overlap < 2 * radius) { + response.bInA = false; + } + } + } + if(overlapN && response && Math.abs(overlap) < Math.abs(response.overlap)) { + response.overlap = overlap; + response.overlapN.copyFrom(overlapN); + } + } + if(response) { + response.a = polygon; + response.b = circle; + response.overlapV.copyFrom(response.overlapN).scale(response.overlap); + } + Phaser.Collision.T_VECTORS.push(circlePos); + Phaser.Collision.T_VECTORS.push(edge); + Phaser.Collision.T_VECTORS.push(point); + return true; + }; + Collision.testCirclePolygon = function testCirclePolygon(circle, polygon, response) { + if (typeof response === "undefined") { response = null; } + var result = Phaser.Collision.testPolygonCircle(polygon, circle, response); + if(result && response) { + var a = response.a; + var aInB = response.aInB; + response.overlapN.reverse(); + response.overlapV.reverse(); + response.a = response.b; + response.b = a; + response.aInB = response.bInA; + response.bInA = aInB; + } + return result; + }; + Collision.testPolygonPolygon = function testPolygonPolygon(a, b, response) { + if (typeof response === "undefined") { response = null; } + var aPoints = a.points; + var aLen = aPoints.length; + var bPoints = b.points; + var bLen = bPoints.length; + for(var i = 0; i < aLen; i++) { + if(Phaser.Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, a.normals[i], response)) { + return false; + } + } + for(var i = 0; i < bLen; i++) { + if(Phaser.Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, b.normals[i], response)) { + return false; + } + } + if(response) { + response.a = a; + response.b = b; + response.overlapV.copyFrom(response.overlapN).scale(response.overlap); + } + return true; + }; + return Collision; + })(); + Phaser.Collision = Collision; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/Collision.ts b/todo/phaser clean up/Collision.ts new file mode 100644 index 00000000..13684b81 --- /dev/null +++ b/todo/phaser clean up/Collision.ts @@ -0,0 +1,1702 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// + +/** +* Phaser - Collision +* +* A set of extremely useful collision and geometry intersection functions. +*/ + +module Phaser { + + export class Collision { + + /** + * Collision constructor + * @param game A reference to the current Game + */ + constructor(game: Game) { + + this._game = game; + + Collision.T_VECTORS = []; + + for (var i = 0; i < 10; i++) + { + Collision.T_VECTORS.push(new Vec2); + } + + Collision.T_ARRAYS = []; + + for (var i = 0; i < 5; i++) + { + Collision.T_ARRAYS.push([]); + } + + } + + /** + * Local private reference to Game + */ + private _game: Game; + + /** + * Flag used to allow GameObjects to collide on their left side + * @type {number} + */ + public static LEFT: number = 0x0001; + + /** + * Flag used to allow GameObjects to collide on their right side + * @type {number} + */ + public static RIGHT: number = 0x0010; + + /** + * Flag used to allow GameObjects to collide on their top side + * @type {number} + */ + public static UP: number = 0x0100; + + /** + * Flag used to allow GameObjects to collide on their bottom side + * @type {number} + */ + public static DOWN: number = 0x1000; + + /** + * Flag used with GameObjects to disable collision + * @type {number} + */ + public static NONE: number = 0; + + /** + * Flag used to allow GameObjects to collide with a ceiling + * @type {number} + */ + public static CEILING: number = Collision.UP; + + /** + * Flag used to allow GameObjects to collide with a floor + * @type {number} + */ + public static FLOOR: number = Collision.DOWN; + + /** + * Flag used to allow GameObjects to collide with a wall (same as LEFT+RIGHT) + * @type {number} + */ + public static WALL: number = Collision.LEFT | Collision.RIGHT; + + /** + * Flag used to allow GameObjects to collide on any face + * @type {number} + */ + public static ANY: number = Collision.LEFT | Collision.RIGHT | Collision.UP | Collision.DOWN; + + /** + * The overlap bias is used when calculating hull overlap before separation - change it if you have especially small or large GameObjects + * @type {number} + */ + public static OVERLAP_BIAS: number = 4; + + /** + * This holds the result of the tile separation check, true if the object was moved, otherwise false + * @type {boolean} + */ + public static TILE_OVERLAP: bool = false; + + /** + * A temporary Rectangle used in the separation process to help avoid gc spikes + * @type {Rectangle} + */ + public static _tempBounds: Rectangle; + + /** + * Checks for Line to Line intersection and returns an IntersectResult object containing the results of the intersection. + * @param line1 The first Line object to check + * @param line2 The second Line object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToLine(line1: Line, line2: Line, output?: IntersectResult = new IntersectResult): IntersectResult { + + var denominator = (line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 - line2.x2); + + if (denominator !== 0) + { + output.result = true; + output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.x1 - line2.x2) - (line1.x1 - line1.x2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; + output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x1 * line2.y2 - line2.y1 * line2.x2)) / denominator; + } + + return output; + } + + /** + * Checks for Line to Line Segment intersection and returns an IntersectResult object containing the results of the intersection. + * @param line The Line object to check + * @param seg The Line segment object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToLineSegment(line: Line, seg: Line, output?: IntersectResult = new IntersectResult): IntersectResult { + + var denominator = (line.x1 - line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 - seg.x2); + + if (denominator !== 0) + { + output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.x1 - seg.x2) - (line.x1 - line.x2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; + output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (seg.y1 - seg.y2) - (line.y1 - line.y2) * (seg.x1 * seg.y2 - seg.y1 * seg.x2)) / denominator; + + var maxX = Math.max(seg.x1, seg.x2); + var minX = Math.min(seg.x1, seg.x2); + var maxY = Math.max(seg.y1, seg.y2); + var minY = Math.min(seg.y1, seg.y2); + + if ((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) + { + output.result = true; + } + + } + + return output; + + } + + /** + * Checks for Line to Raw Line Segment intersection and returns the result in the IntersectResult object. + * @param line The Line object to check + * @param x1 The start x coordinate of the raw segment + * @param y1 The start y coordinate of the raw segment + * @param x2 The end x coordinate of the raw segment + * @param y2 The end y coordinate of the raw segment + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToRawSegment(line: Line, x1: number, y1: number, x2: number, y2: number, output?: IntersectResult = new IntersectResult): IntersectResult { + + var denominator = (line.x1 - line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 - x2); + + if (denominator !== 0) + { + output.x = ((line.x1 * line.y2 - line.y1 * line.x2) * (x1 - x2) - (line.x1 - line.x2) * (x1 * y2 - y1 * x2)) / denominator; + output.y = ((line.x1 * line.y2 - line.y1 * line.x2) * (y1 - y2) - (line.y1 - line.y2) * (x1 * y2 - y1 * x2)) / denominator; + + var maxX = Math.max(x1, x2); + var minX = Math.min(x1, x2); + var maxY = Math.max(y1, y2); + var minY = Math.min(y1, y2); + + if ((output.x <= maxX && output.x >= minX) === true || (output.y <= maxY && output.y >= minY) === true) + { + output.result = true; + } + + } + + return output; + + } + + /** + * Checks for Line to Ray intersection and returns the result in an IntersectResult object. + * @param line1 The Line object to check + * @param ray The Ray object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToRay(line1: Line, ray: Line, output?: IntersectResult = new IntersectResult): IntersectResult { + + var denominator = (line1.x1 - line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 - ray.x2); + + if (denominator !== 0) + { + output.x = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.x1 - ray.x2) - (line1.x1 - line1.x2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; + output.y = ((line1.x1 * line1.y2 - line1.y1 * line1.x2) * (ray.y1 - ray.y2) - (line1.y1 - line1.y2) * (ray.x1 * ray.y2 - ray.y1 * ray.x2)) / denominator; + output.result = true; // true unless either of the 2 following conditions are met + + if (!(ray.x1 >= ray.x2) && output.x < ray.x1) + { + output.result = false; + } + + if (!(ray.y1 >= ray.y2) && output.y < ray.y1) + { + output.result = false; + } + } + + return output; + + } + + + /** + * Check if the Line and Circle objects intersect + * @param line The Line object to check + * @param circle The Circle object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToCircle(line: Line, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { + + // Get a perpendicular line running to the center of the circle + if (line.perp(circle.x, circle.y).length <= circle.radius) + { + output.result = true; + } + + return output; + + } + + /** + * Check if the Line intersects each side of the Rectangle + * @param line The Line object to check + * @param rect The Rectangle object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineToRectangle(line: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + // Top of the Rectangle vs the Line + Collision.lineToRawSegment(line, rect.x, rect.y, rect.right, rect.y, output); + + if (output.result === true) + { + return output; + } + + // Left of the Rectangle vs the Line + Collision.lineToRawSegment(line, rect.x, rect.y, rect.x, rect.bottom, output); + + if (output.result === true) + { + return output; + } + + // Bottom of the Rectangle vs the Line + Collision.lineToRawSegment(line, rect.x, rect.bottom, rect.right, rect.bottom, output); + + if (output.result === true) + { + return output; + } + + // Right of the Rectangle vs the Line + Collision.lineToRawSegment(line, rect.right, rect.y, rect.right, rect.bottom, output); + + return output; + + } + + /** + * Check if the two Line Segments intersect and returns the result in an IntersectResult object. + * @param line1 The first Line Segment to check + * @param line2 The second Line Segment to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineSegmentToLineSegment(line1: Line, line2: Line, output?: IntersectResult = new IntersectResult): IntersectResult { + + Collision.lineToLineSegment(line1, line2); + + if (output.result === true) + { + if (!(output.x >= Math.min(line1.x1, line1.x2) && output.x <= Math.max(line1.x1, line1.x2) + && output.y >= Math.min(line1.y1, line1.y2) && output.y <= Math.max(line1.y1, line1.y2))) + { + output.result = false; + } + } + + return output; + } + + /** + * Check if the Line Segment intersects with the Ray and returns the result in an IntersectResult object. + * @param line The Line Segment to check. + * @param ray The Ray to check. + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineSegmentToRay(line: Line, ray: Line, output?: IntersectResult = new IntersectResult): IntersectResult { + + Collision.lineToRay(line, ray, output); + + if (output.result === true) + { + if (!(output.x >= Math.min(line.x1, line.x2) && output.x <= Math.max(line.x1, line.x2) + && output.y >= Math.min(line.y1, line.y2) && output.y <= Math.max(line.y1, line.y2))) + { + output.result = false; + } + } + + return output; + + } + + /** + * Check if the Line Segment intersects with the Circle and returns the result in an IntersectResult object. + * @param seg The Line Segment to check. + * @param circle The Circle to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineSegmentToCircle(seg: Line, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { + + var perp = seg.perp(circle.x, circle.y); + + if (perp.length <= circle.radius) + { + // Line intersects circle - check if segment does + var maxX = Math.max(seg.x1, seg.x2); + var minX = Math.min(seg.x1, seg.x2); + var maxY = Math.max(seg.y1, seg.y2); + var minY = Math.min(seg.y1, seg.y2); + + if ((perp.x2 <= maxX && perp.x2 >= minX) && (perp.y2 <= maxY && perp.y2 >= minY)) + { + output.result = true; + } + else + { + // Worst case - segment doesn't traverse center, so no perpendicular connection. + if (Collision.circleContainsPoint(circle, { x: seg.x1, y: seg.y1 }) || Collision.circleContainsPoint(circle, { x: seg.x2, y: seg.y2 })) + { + output.result = true; + } + } + + } + + return output; + } + + /** + * Check if the Line Segment intersects with the Rectangle and returns the result in an IntersectResult object. + * @param seg The Line Segment to check. + * @param rect The Rectangle to check. + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static lineSegmentToRectangle(seg: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + if (rect.contains(seg.x1, seg.y1) && rect.contains(seg.x2, seg.y2)) + { + output.result = true; + } + else + { + // Top of the Rectangle vs the Line + Collision.lineToRawSegment(seg, rect.x, rect.y, rect.right, rect.bottom, output); + + if (output.result === true) + { + return output; + } + + // Left of the Rectangle vs the Line + Collision.lineToRawSegment(seg, rect.x, rect.y, rect.x, rect.bottom, output); + + if (output.result === true) + { + return output; + } + + // Bottom of the Rectangle vs the Line + Collision.lineToRawSegment(seg, rect.x, rect.bottom, rect.right, rect.bottom, output); + + if (output.result === true) + { + return output; + } + + // Right of the Rectangle vs the Line + Collision.lineToRawSegment(seg, rect.right, rect.y, rect.right, rect.bottom, output); + + return output; + + } + + return output; + + } + + /** + * Check for Ray to Rectangle intersection and returns the result in an IntersectResult object. + * @param ray The Ray to check. + * @param rect The Rectangle to check. + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static rayToRectangle(ray: Line, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + // Currently just finds first intersection - might not be closest to ray pt1 + Collision.lineToRectangle(ray, rect, output); + + return output; + + } + + + /** + * Check whether a Ray intersects a Line segment and returns the parametric value where the intersection occurs in an IntersectResult object. + * @param rayX1 + * @param rayY1 + * @param rayX2 + * @param rayY2 + * @param lineX1 + * @param lineY1 + * @param lineX2 + * @param lineY2 + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static rayToLineSegment(rayX1, rayY1, rayX2, rayY2, lineX1, lineY1, lineX2, lineY2, output?: IntersectResult = new IntersectResult): IntersectResult { + + var r:number; + var s:number; + var d:number; + + // Check lines are not parallel + if ((rayY2 - rayY1) / (rayX2 - rayX1) != (lineY2 - lineY1) / (lineX2 - lineX1)) + { + d = (((rayX2 - rayX1) * (lineY2 - lineY1)) - (rayY2 - rayY1) * (lineX2 - lineX1)); + + if (d != 0) + { + r = (((rayY1 - lineY1) * (lineX2 - lineX1)) - (rayX1 - lineX1) * (lineY2 - lineY1)) / d; + s = (((rayY1 - lineY1) * (rayX2 - rayX1)) - (rayX1 - lineX1) * (rayY2 - rayY1)) / d; + + if (r >= 0) + { + if (s >= 0 && s <= 1) + { + output.result = true; + output.x = rayX1 + r * (rayX2 - rayX1); + output.y = rayY1 + r * (rayY2 - rayY1); + } + } + } + } + + return output; + + } + + /** + * Determines whether the specified point is contained within the rectangular region defined by the Rectangle object and returns the result in an IntersectResult object. + * @param point The Point or Point object to check, or any object with x and y properties. + * @param rect The Rectangle object to check the point against + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static pointToRectangle(point, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + output.setTo(point.x, point.y); + + //output.result = rect.containsPoint(point); + + + return output; + + } + + /** + * Check whether two axis aligned Rectangles intersect and returns the intersecting rectangle dimensions in an IntersectResult object if they do. + * @param rect1 The first Rectangle object. + * @param rect2 The second Rectangle object. + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static rectangleToRectangle(rect1: Rectangle, rect2: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + var leftX = Math.max(rect1.x, rect2.x); + var rightX = Math.min(rect1.right, rect2.right); + var topY = Math.max(rect1.y, rect2.y); + var bottomY = Math.min(rect1.bottom, rect2.bottom); + + output.setTo(leftX, topY, rightX - leftX, bottomY - topY, rightX - leftX, bottomY - topY); + + var cx = output.x + output.width * .5; + var cy = output.y + output.height * .5; + + if ((cx > rect1.x && cx < rect1.right) && (cy > rect1.y && cy < rect1.bottom)) + { + output.result = true; + } + + return output; + + } + + /** + * Checks if the Rectangle and Circle objects intersect and returns the result in an IntersectResult object. + * @param rect The Rectangle object to check + * @param circle The Circle object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static rectangleToCircle(rect: Rectangle, circle: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { + + return Collision.circleToRectangle(circle, rect, output); + + } + + /** + * Checks if the two Circle objects intersect and returns the result in an IntersectResult object. + * @param circle1 The first Circle object to check + * @param circle2 The second Circle object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static circleToCircle(circle1: Circle, circle2: Circle, output?: IntersectResult = new IntersectResult): IntersectResult { + + output.result = ((circle1.radius + circle2.radius) * (circle1.radius + circle2.radius)) >= Collision.distanceSquared(circle1.x, circle1.y, circle2.x, circle2.y); + + return output; + + } + + /** + * Checks if the Circle object intersects with the Rectangle and returns the result in an IntersectResult object. + * @param circle The Circle object to check + * @param rect The Rectangle object to check + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static circleToRectangle(circle: Circle, rect: Rectangle, output?: IntersectResult = new IntersectResult): IntersectResult { + + var inflatedRect: Rectangle = rect.clone(); + + inflatedRect.inflate(circle.radius, circle.radius); + + output.result = inflatedRect.contains(circle.x, circle.y); + + return output; + + } + + /** + * Checks if the Point object is contained within the Circle and returns the result in an IntersectResult object. + * @param circle The Circle object to check + * @param point A Point or Point object to check, or any object with x and y properties + * @param [output] An optional IntersectResult object to store the intersection values in. One is created if none given. + * @returns {IntersectResult=} An IntersectResult object containing the results of the intersection + */ + public static circleContainsPoint(circle: Circle, point, output?: IntersectResult = new IntersectResult): IntersectResult { + + output.result = circle.radius * circle.radius >= Collision.distanceSquared(circle.x, circle.y, point.x, point.y); + + return output; + + } + + /** + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. + */ + public overlap(object1: Basic = null, object2: Basic = null, notifyCallback = null, processCallback = null, context = null): bool { + + if (object1 == null) + { + object1 = this._game.world.group; + } + + if (object2 == object1) + { + object2 = null; + } + + QuadTree.divisions = this._game.world.worldDivisions; + + var quadTree: QuadTree = new QuadTree(this._game.world.bounds.x, this._game.world.bounds.y, this._game.world.bounds.width, this._game.world.bounds.height); + + quadTree.load(object1, object2, notifyCallback, processCallback, context); + + var result: bool = quadTree.execute(); + + quadTree.destroy(); + + quadTree = null; + + return result; + + } + + /** + * The core Collision separation function used by Collision.overlap. + * @param object1 The first GameObject to separate + * @param object2 The second GameObject to separate + * @returns {boolean} Returns true if the objects were separated, otherwise false. + */ + public static separate(object1, object2): bool { + + object1.collisionMask.update(); + object2.collisionMask.update(); + + var separatedX: bool = Collision.separateX(object1, object2); + var separatedY: bool = Collision.separateY(object1, object2); + + return separatedX || separatedY; + + } + + /** + * Collision resolution specifically for GameObjects vs. Tiles. + * @param object The GameObject to separate + * @param tile The Tile to separate + * @returns {boolean} Whether the objects in fact touched and were separated + */ + public static separateTile(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, collideUp: bool, collideDown: bool, separateX: bool, separateY: bool): bool { + + object.collisionMask.update(); + + var separatedX: bool = Collision.separateTileX(object, x, y, width, height, mass, collideLeft, collideRight, separateX); + var separatedY: bool = Collision.separateTileY(object, x, y, width, height, mass, collideUp, collideDown, separateY); + + return separatedX || separatedY; + + } + + /** + * Separates the two objects on their x axis + * @param object The GameObject to separate + * @param tile The Tile to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. + */ + public static separateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { + + // Can't separate two immovable objects (tiles are always immovable) + if (object.immovable) + { + return false; + } + + // First, get the object delta + var overlap: number = 0; + var objDelta: number = object.x - object.last.x; + //var objDelta: number = object.collisionMask.deltaX; + + if (objDelta != 0) + { + // Check if the X hulls actually overlap + var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; + //var objDeltaAbs: number = object.collisionMask.deltaXAbs; + var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); + + if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + { + var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (objDelta > 0) + { + overlap = object.x + object.width - x; + + if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.RIGHT; + } + } + else if (objDelta < 0) + { + overlap = object.x - width - x; + + if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.LEFT; + } + + } + + } + } + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + if (separate == true) + { + //console.log(' + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } + + Collision.TILE_OVERLAP = true; + return true; + } + else + { + return false; + } + + } + + /** + * Separates the two objects on their y axis + * @param object The first GameObject to separate + * @param tile The second GameObject to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. + */ + public static separateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { + + // Can't separate two immovable objects (tiles are always immovable) + if (object.immovable) + { + return false; + } + + // First, get the two object deltas + var overlap: number = 0; + var objDelta: number = object.y - object.last.y; + + if (objDelta != 0) + { + // Check if the Y hulls actually overlap + var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; + var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); + + if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + { + var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (objDelta > 0) + { + overlap = object.y + object.height - y; + + if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.DOWN; + } + } + else if (objDelta < 0) + { + overlap = object.y - height - y; + + if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.UP; + } + } + } + } + + // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + if (separate == true) + { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } + + Collision.TILE_OVERLAP = true; + return true; + } + else + { + return false; + } + } + + + /** + * Separates the two objects on their x axis + * @param object The GameObject to separate + * @param tile The Tile to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. + */ + public static NEWseparateTileX(object:GameObject, x: number, y: number, width: number, height: number, mass: number, collideLeft: bool, collideRight: bool, separate: bool): bool { + + // Can't separate two immovable objects (tiles are always immovable) + if (object.immovable) + { + return false; + } + + // First, get the object delta + var overlap: number = 0; + + if (object.collisionMask.deltaX != 0) + { + // Check if the X hulls actually overlap + //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; + //var objBounds: Rectangle = new Rectangle(object.x - ((objDelta > 0) ? objDelta : 0), object.last.y, object.width + ((objDelta > 0) ? objDelta : -objDelta), object.height); + + //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) + { + var maxOverlap: number = object.collisionMask.deltaXAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (object.collisionMask.deltaX > 0) + { + //overlap = object.x + object.width - x; + overlap = object.collisionMask.right - x; + + if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.RIGHT) || collideLeft == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.RIGHT; + } + } + else if (object.collisionMask.deltaX < 0) + { + //overlap = object.x - width - x; + overlap = object.collisionMask.x - width - x; + + if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.LEFT) || collideRight == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.LEFT; + } + + } + + } + } + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + if (separate == true) + { + object.x = object.x - overlap; + object.velocity.x = -(object.velocity.x * object.elasticity); + } + + Collision.TILE_OVERLAP = true; + return true; + } + else + { + return false; + } + + } + + /** + * Separates the two objects on their y axis + * @param object The first GameObject to separate + * @param tile The second GameObject to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. + */ + public static NEWseparateTileY(object: GameObject, x: number, y: number, width: number, height: number, mass: number, collideUp: bool, collideDown: bool, separate: bool): bool { + + // Can't separate two immovable objects (tiles are always immovable) + if (object.immovable) + { + return false; + } + + // First, get the two object deltas + var overlap: number = 0; + //var objDelta: number = object.y - object.last.y; + + if (object.collisionMask.deltaY != 0) + { + // Check if the Y hulls actually overlap + //var objDeltaAbs: number = (objDelta > 0) ? objDelta : -objDelta; + //var objBounds: Rectangle = new Rectangle(object.x, object.y - ((objDelta > 0) ? objDelta : 0), object.width, object.height + objDeltaAbs); + + //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + if (object.collisionMask.intersectsRaw(x, x + width, y, y + height)) + { + //var maxOverlap: number = objDeltaAbs + Collision.OVERLAP_BIAS; + var maxOverlap: number = object.collisionMask.deltaYAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (object.collisionMask.deltaY > 0) + { + //overlap = object.y + object.height - y; + overlap = object.collisionMask.bottom - y; + + if ((overlap > maxOverlap) || !(object.allowCollisions & Collision.DOWN) || collideUp == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.DOWN; + } + } + else if (object.collisionMask.deltaY < 0) + { + //overlap = object.y - height - y; + overlap = object.collisionMask.y - height - y; + + if ((-overlap > maxOverlap) || !(object.allowCollisions & Collision.UP) || collideDown == false) + { + overlap = 0; + } + else + { + object.touching |= Collision.UP; + } + } + } + } + + // TODO - with super low velocities you get lots of stuttering, set some kind of base minimum here + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + if (separate == true) + { + object.y = object.y - overlap; + object.velocity.y = -(object.velocity.y * object.elasticity); + } + + Collision.TILE_OVERLAP = true; + return true; + } + else + { + return false; + } + } + + /** + * Separates the two objects on their x axis + * @param object1 The first GameObject to separate + * @param object2 The second GameObject to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the X axis. + */ + public static separateX(object1, object2): bool { + + // Can't separate two immovable objects + if (object1.immovable && object2.immovable) + { + return false; + } + + // First, get the two object deltas + var overlap: number = 0; + + if (object1.collisionMask.deltaX != object2.collisionMask.deltaX) + { + if (object1.collisionMask.intersects(object2.collisionMask)) + { + var maxOverlap: number = object1.collisionMask.deltaXAbs + object2.collisionMask.deltaXAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (object1.collisionMask.deltaX > object2.collisionMask.deltaX) + { + overlap = object1.collisionMask.right - object2.collisionMask.x; + + if ((overlap > maxOverlap) || !(object1.allowCollisions & Collision.RIGHT) || !(object2.allowCollisions & Collision.LEFT)) + { + overlap = 0; + } + else + { + object1.touching |= Collision.RIGHT; + object2.touching |= Collision.LEFT; + } + } + else if (object1.collisionMask.deltaX < object2.collisionMask.deltaX) + { + overlap = object1.collisionMask.x - object2.collisionMask.width - object2.collisionMask.x; + + if ((-overlap > maxOverlap) || !(object1.allowCollisions & Collision.LEFT) || !(object2.allowCollisions & Collision.RIGHT)) + { + overlap = 0; + } + else + { + object1.touching |= Collision.LEFT; + object2.touching |= Collision.RIGHT; + } + + } + + } + } + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + var obj1Velocity: number = object1.velocity.x; + var obj2Velocity: number = object2.velocity.x; + + if (!object1.immovable && !object2.immovable) + { + overlap *= 0.5; + object1.x = object1.x - overlap; + object2.x += overlap; + + var obj1NewVelocity: number = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); + var obj2NewVelocity: number = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); + var average: number = (obj1NewVelocity + obj2NewVelocity) * 0.5; + obj1NewVelocity -= average; + obj2NewVelocity -= average; + object1.velocity.x = average + obj1NewVelocity * object1.elasticity; + object2.velocity.x = average + obj2NewVelocity * object2.elasticity; + } + else if (!object1.immovable) + { + object1.x = object1.x - overlap; + object1.velocity.x = obj2Velocity - obj1Velocity * object1.elasticity; + } + else if (!object2.immovable) + { + object2.x += overlap; + object2.velocity.x = obj1Velocity - obj2Velocity * object2.elasticity; + } + + return true; + } + else + { + return false; + } + + } + + /** + * Separates the two objects on their y axis + * @param object1 The first GameObject to separate + * @param object2 The second GameObject to separate + * @returns {boolean} Whether the objects in fact touched and were separated along the Y axis. + */ + public static separateY(object1, object2): bool { + + // Can't separate two immovable objects + if (object1.immovable && object2.immovable) { + return false; + } + + // First, get the two object deltas + var overlap: number = 0; + + if (object1.collisionMask.deltaY != object2.collisionMask.deltaY) + { + if (object1.collisionMask.intersects(object2.collisionMask)) + { + // This is the only place to use the DeltaAbs values + var maxOverlap: number = object1.collisionMask.deltaYAbs + object2.collisionMask.deltaYAbs + Collision.OVERLAP_BIAS; + + // If they did overlap (and can), figure out by how much and flip the corresponding flags + if (object1.collisionMask.deltaY > object2.collisionMask.deltaY) + { + overlap = object1.collisionMask.bottom - object2.collisionMask.y; + + if ((overlap > maxOverlap) || !(object1.allowCollisions & Collision.DOWN) || !(object2.allowCollisions & Collision.UP)) + { + overlap = 0; + } + else + { + object1.touching |= Collision.DOWN; + object2.touching |= Collision.UP; + } + } + else if (object1.collisionMask.deltaY < object2.collisionMask.deltaY) + { + overlap = object1.collisionMask.y - object2.collisionMask.height - object2.collisionMask.y; + + if ((-overlap > maxOverlap) || !(object1.allowCollisions & Collision.UP) || !(object2.allowCollisions & Collision.DOWN)) + { + overlap = 0; + } + else + { + object1.touching |= Collision.UP; + object2.touching |= Collision.DOWN; + } + } + } + } + + // Then adjust their positions and velocities accordingly (if there was any overlap) + if (overlap != 0) + { + var obj1Velocity: number = object1.velocity.y; + var obj2Velocity: number = object2.velocity.y; + + if (!object1.immovable && !object2.immovable) + { + overlap *= 0.5; + object1.y = object1.y - overlap; + object2.y += overlap; + + var obj1NewVelocity: number = Math.sqrt((obj2Velocity * obj2Velocity * object2.mass) / object1.mass) * ((obj2Velocity > 0) ? 1 : -1); + var obj2NewVelocity: number = Math.sqrt((obj1Velocity * obj1Velocity * object1.mass) / object2.mass) * ((obj1Velocity > 0) ? 1 : -1); + var average: number = (obj1NewVelocity + obj2NewVelocity) * 0.5; + obj1NewVelocity -= average; + obj2NewVelocity -= average; + object1.velocity.y = average + obj1NewVelocity * object1.elasticity; + object2.velocity.y = average + obj2NewVelocity * object2.elasticity; + } + else if (!object1.immovable) + { + object1.y = object1.y - overlap; + object1.velocity.y = obj2Velocity - obj1Velocity * object1.elasticity; + // This is special case code that handles things like horizontal moving platforms you can ride + if (object2.active && object2.moves && (object1.collisionMask.deltaY > object2.collisionMask.deltaY)) + { + object1.x += object2.x - object2.last.x; + } + } + else if (!object2.immovable) + { + object2.y += overlap; + object2.velocity.y = obj1Velocity - obj2Velocity * object2.elasticity; + // This is special case code that handles things like horizontal moving platforms you can ride + if (object1.active && object1.moves && (object1.collisionMask.deltaY < object2.collisionMask.deltaY)) + { + object2.x += object1.x - object1.last.x; + } + } + + return true; + } + else + { + return false; + } + } + + /** + * Returns the distance between the two given coordinates. + * @param x1 The X value of the first coordinate + * @param y1 The Y value of the first coordinate + * @param x2 The X value of the second coordinate + * @param y2 The Y value of the second coordinate + * @returns {number} The distance between the two coordinates + */ + public static distance(x1: number, y1: number, x2: number, y2: number) { + return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + } + + /** + * Returns the distanced squared between the two given coordinates. + * @param x1 The X value of the first coordinate + * @param y1 The Y value of the first coordinate + * @param x2 The X value of the second coordinate + * @param y2 The Y value of the second coordinate + * @returns {number} The distance between the two coordinates + */ + public static distanceSquared(x1: number, y1: number, x2: number, y2: number) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + } + + + + // SAT + + /** + * Flattens the specified array of points onto a unit vector axis, + * resulting in a one dimensional range of the minimum and + * maximum value on that axis. + * + * @param {Array.} points The points to flatten. + * @param {Vector} normal The unit vector axis to flatten on. + * @param {Array.} result An array. After calling this function, + * result[0] will be the minimum value, + * result[1] will be the maximum value. + */ + public static flattenPointsOn(points, normal, result) { + + var min = Number.MAX_VALUE; + var max = -Number.MAX_VALUE; + var len = points.length; + + for (var i = 0; i < len; i++) + { + // Get the magnitude of the projection of the point onto the normal + var dot = points[i].dot(normal); + if (dot < min) { min = dot; } + if (dot > max) { max = dot; } + } + + result[0] = min; result[1] = max; + + } + + /** + * Pool of Vectors used in calculations. + * + * @type {Array.} + */ + public static T_VECTORS: Vec2[]; + + /** + * Pool of Arrays used in calculations. + * + * @type {Array.>} + */ + public static T_ARRAYS; + + /** + * Check whether two convex clockwise polygons are separated by the specified + * axis (must be a unit vector). + * + * @param {Vector} aPos The position of the first polygon. + * @param {Vector} bPos The position of the second polygon. + * @param {Array.} aPoints The points in the first polygon. + * @param {Array.} bPoints The points in the second polygon. + * @param {Vector} axis The axis (unit sized) to test against. The points of both polygons + * will be projected onto this axis. + * @param {Response=} response A Response object (optional) which will be populated + * if the axis is not a separating axis. + * @return {boolean} true if it is a separating axis, false otherwise. If false, + * and a response is passed in, information about how much overlap and + * the direction of the overlap will be populated. + */ + public static isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response?:Response = null): bool { + + var rangeA = Collision.T_ARRAYS.pop(); + var rangeB = Collision.T_ARRAYS.pop(); + + // Get the magnitude of the offset between the two polygons + var offsetV = Collision.T_VECTORS.pop().copyFrom(bPos).sub(aPos); + var projectedOffset = offsetV.dot(axis); + + // Project the polygons onto the axis. + Collision.flattenPointsOn(aPoints, axis, rangeA); + Collision.flattenPointsOn(bPoints, axis, rangeB); + + // Move B's range to its position relative to A. + rangeB[0] += projectedOffset; + rangeB[1] += projectedOffset; + + // Check if there is a gap. If there is, this is a separating axis and we can stop + if (rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) + { + Collision.T_VECTORS.push(offsetV); + Collision.T_ARRAYS.push(rangeA); + Collision.T_ARRAYS.push(rangeB); + return true; + } + + // If we're calculating a response, calculate the overlap. + if (response) + { + var overlap = 0; + + // A starts further left than B + if (rangeA[0] < rangeB[0]) + { + response.aInB = false; + + // A ends before B does. We have to pull A out of B + if (rangeA[1] < rangeB[1]) + { + overlap = rangeA[1] - rangeB[0]; + response.bInA = false; + // B is fully inside A. Pick the shortest way out. + } + else + { + var option1 = rangeA[1] - rangeB[0]; + var option2 = rangeB[1] - rangeA[0]; + overlap = option1 < option2 ? option1 : -option2; + } + // B starts further left than A + } + else + { + response.bInA = false; + + // B ends before A ends. We have to push A out of B + if (rangeA[1] > rangeB[1]) + { + overlap = rangeA[0] - rangeB[1]; + response.aInB = false; + // A is fully inside B. Pick the shortest way out. + } + else + { + var option1 = rangeA[1] - rangeB[0]; + var option2 = rangeB[1] - rangeA[0]; + overlap = option1 < option2 ? option1 : -option2; + } + } + + // If this is the smallest amount of overlap we've seen so far, set it as the minimum overlap. + var absOverlap = Math.abs(overlap); + + if (absOverlap < response.overlap) + { + response.overlap = absOverlap; + response.overlapN.copyFrom(axis); + + if (overlap < 0) + { + response.overlapN.reverse(); + } + } + } + + Collision.T_VECTORS.push(offsetV); + Collision.T_ARRAYS.push(rangeA); + Collision.T_ARRAYS.push(rangeB); + + return false; + + } + + public static LEFT_VORNOI_REGION:number = -1; + public static MIDDLE_VORNOI_REGION:number = 0; + public static RIGHT_VORNOI_REGION:number = 1; + + /** + * Calculates which Vornoi region a point is on a line segment. + * It is assumed that both the line and the point are relative to (0, 0) + * + * | (0) | + * (-1) [0]--------------[1] (1) + * | (0) | + * + * @param {Vector} line The line segment. + * @param {Vector} point The point. + * @return {number} LEFT_VORNOI_REGION (-1) if it is the left region, + * MIDDLE_VORNOI_REGION (0) if it is the middle region, + * RIGHT_VORNOI_REGION (1) if it is the right region. + */ + public static vornoiRegion(line: Vec2, point: Vec2): number { + + var len2 = line.length2(); + var dp = point.dot(line); + + if (dp < 0) { return Collision.LEFT_VORNOI_REGION; } + else if (dp > len2) { return Collision.RIGHT_VORNOI_REGION; } + else { return Collision.MIDDLE_VORNOI_REGION; } + + } + + /** + * Check if two circles intersect. + * + * @param {Circle} a The first circle. + * @param {Circle} b The second circle. + * @param {Response=} response Response object (optional) that will be populated if + * the circles intersect. + * @return {boolean} true if the circles intersect, false if they don't. + */ + public static testCircleCircle(a: Circle, b: Circle, response?: Response = null): bool { + + var differenceV = Collision.T_VECTORS.pop().copyFrom(b.pos).sub(a.pos); + var totalRadius = a.radius + b.radius; + var totalRadiusSq = totalRadius * totalRadius; + var distanceSq = differenceV.length2(); + + if (distanceSq > totalRadiusSq) + { + // They do not intersect + Collision.T_VECTORS.push(differenceV); + return false; + } + + // They intersect. If we're calculating a response, calculate the overlap. + if (response) + { + var dist = Math.sqrt(distanceSq); + response.a = a; + response.b = b; + response.overlap = totalRadius - dist; + response.overlapN.copyFrom(differenceV.normalize()); + response.overlapV.copyFrom(differenceV).scale(response.overlap); + response.aInB = a.radius <= b.radius && dist <= b.radius - a.radius; + response.bInA = b.radius <= a.radius && dist <= a.radius - b.radius; + } + + Collision.T_VECTORS.push(differenceV); + return true; + + } + + /** + * Check if a polygon and a circle intersect. + * + * @param {Polygon} polygon The polygon. + * @param {Circle} circle The circle. + * @param {Response=} response Response object (optional) that will be populated if + * they interset. + * @return {boolean} true if they intersect, false if they don't. + */ + public static testPolygonCircle(polygon: Polygon, circle: Circle, response?: Response = null): bool { + + var circlePos = Collision.T_VECTORS.pop().copyFrom(circle.pos).sub(polygon.pos); + var radius = circle.radius; + var radius2 = radius * radius; + var points = polygon.points; + var len = points.length; + var edge = T_VECTORS.pop(); + var point = T_VECTORS.pop(); + + // For each edge in the polygon + for (var i = 0; i < len; i++) + { + var next = i === len - 1 ? 0 : i + 1; + var prev = i === 0 ? len - 1 : i - 1; + var overlap = 0; + var overlapN = null; + + // Get the edge + edge.copyFrom(polygon.edges[i]); + // Calculate the center of the cirble relative to the starting point of the edge + point.copyFrom(circlePos).sub(points[i]); + + // If the distance between the center of the circle and the point + // is bigger than the radius, the polygon is definitely not fully in + // the circle. + if (response && point.length2() > radius2) + { + response.aInB = false; + } + + // Calculate which Vornoi region the center of the circle is in. + var region = vornoiRegion(edge, point); + + if (region === Collision.LEFT_VORNOI_REGION) + { + // Need to make sure we're in the RIGHT_VORNOI_REGION of the previous edge. + edge.copyFrom(polygon.edges[prev]); + + // Calculate the center of the circle relative the starting point of the previous edge + var point2 = Collision.T_VECTORS.pop().copyFrom(circlePos).sub(points[prev]); + region = vornoiRegion(edge, point2); + + if (region === Collision.RIGHT_VORNOI_REGION) + { + // It's in the region we want. Check if the circle intersects the point. + var dist = point.length2(); + + if (dist > radius) + { + // No intersection + Collision.T_VECTORS.push(circlePos); + Collision.T_VECTORS.push(edge); + Collision.T_VECTORS.push(point); + Collision.T_VECTORS.push(point2); + return false; + } + else if (response) + { + // It intersects, calculate the overlap + response.bInA = false; + overlapN = point.normalize(); + overlap = radius - dist; + } + } + + Collision.T_VECTORS.push(point2); + + } + else if (region === Collision.RIGHT_VORNOI_REGION) + { + // Need to make sure we're in the left region on the next edge + edge.copyFrom(polygon.edges[next]); + + // Calculate the center of the circle relative to the starting point of the next edge + point.copyFrom(circlePos).sub(points[next]); + region = vornoiRegion(edge, point); + + if (region === Collision.LEFT_VORNOI_REGION) + { + // It's in the region we want. Check if the circle intersects the point. + var dist = point.length2(); + + if (dist > radius) + { + // No intersection + Collision.T_VECTORS.push(circlePos); + Collision.T_VECTORS.push(edge); + Collision.T_VECTORS.push(point); + return false; + } + else if (response) + { + // It intersects, calculate the overlap + response.bInA = false; + overlapN = point.normalize(); + overlap = radius - dist; + } + } + // MIDDLE_VORNOI_REGION + } + else + { + // Need to check if the circle is intersecting the edge, + // Change the edge into its "edge normal". + var normal = edge.perp().normalize(); + + // Find the perpendicular distance between the center of the + // circle and the edge. + var dist = point.dot(normal); + var distAbs = Math.abs(dist); + + // If the circle is on the outside of the edge, there is no intersection + if (dist > 0 && distAbs > radius) + { + Collision.T_VECTORS.push(circlePos); + Collision.T_VECTORS.push(normal); + Collision.T_VECTORS.push(point); + return false; + } + else if (response) + { + // It intersects, calculate the overlap. + overlapN = normal; + overlap = radius - dist; + // If the center of the circle is on the outside of the edge, or part of the + // circle is on the outside, the circle is not fully inside the polygon. + if (dist >= 0 || overlap < 2 * radius) + { + response.bInA = false; + } + } + } + + // If this is the smallest overlap we've seen, keep it. + // (overlapN may be null if the circle was in the wrong Vornoi region) + if (overlapN && response && Math.abs(overlap) < Math.abs(response.overlap)) + { + response.overlap = overlap; + response.overlapN.copyFrom(overlapN); + } + } + + // Calculate the final overlap vector - based on the smallest overlap. + if (response) + { + response.a = polygon; + response.b = circle; + response.overlapV.copyFrom(response.overlapN).scale(response.overlap); + } + + Collision.T_VECTORS.push(circlePos); + Collision.T_VECTORS.push(edge); + Collision.T_VECTORS.push(point); + + return true; + } + + /** + * Check if a circle and a polygon intersect. + * + * NOTE: This runs slightly slower than polygonCircle as it just + * runs polygonCircle and reverses everything at the end. + * + * @param {Circle} circle The circle. + * @param {Polygon} polygon The polygon. + * @param {Response=} response Response object (optional) that will be populated if + * they interset. + * @return {boolean} true if they intersect, false if they don't. + */ + public static testCirclePolygon(circle: Circle, polygon: Polygon, response?: Response = null): bool { + + var result = Collision.testPolygonCircle(polygon, circle, response); + + if (result && response) + { + // Swap A and B in the response. + var a = response.a; + var aInB = response.aInB; + response.overlapN.reverse(); + response.overlapV.reverse(); + response.a = response.b; + response.b = a; + response.aInB = response.bInA; + response.bInA = aInB; + } + + return result; + } + + /** + * Checks whether two convex, clockwise polygons intersect. + * + * @param {Polygon} a The first polygon. + * @param {Polygon} b The second polygon. + * @param {Response=} response Response object (optional) that will be populated if + * they interset. + * @return {boolean} true if they intersect, false if they don't. + */ + public static testPolygonPolygon(a: Polygon, b: Polygon, response?: Response = null): bool { + + var aPoints = a.points; + var aLen = aPoints.length; + var bPoints = b.points; + var bLen = bPoints.length; + + // If any of the edge normals of A is a separating axis, no intersection. + for (var i = 0; i < aLen; i++) + { + if (Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, a.normals[i], response)) + { + return false; + } + } + + // If any of the edge normals of B is a separating axis, no intersection. + for (var i = 0; i < bLen; i++) + { + if (Collision.isSeparatingAxis(a.pos, b.pos, aPoints, bPoints, b.normals[i], response)) + { + return false; + } + } + + // Since none of the edge normals of A or B are a separating axis, there is an intersection + // and we've already calculated the smallest overlap (in isSeparatingAxis). Calculate the + // final overlap vector. + if (response) + { + response.a = a; + response.b = b; + response.overlapV.copyFrom(response.overlapN).scale(response.overlap); + } + + return true; + } + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/CollisionMask.js b/todo/phaser clean up/CollisionMask.js new file mode 100644 index 00000000..037d78c3 --- /dev/null +++ b/todo/phaser clean up/CollisionMask.js @@ -0,0 +1,365 @@ +/// +/** +* Phaser - CollisionMask +*/ +var Phaser; +(function (Phaser) { + var CollisionMask = (function () { + /** + * CollisionMask constructor. Creates a new CollisionMask for the given GameObject. + * + * @param game {Phaser.Game} Current game instance. + * @param parent {Phaser.GameObject} The GameObject this CollisionMask belongs to. + * @param x {number} The initial x position of the CollisionMask. + * @param y {number} The initial y position of the CollisionMask. + * @param width {number} The width of the CollisionMask. + * @param height {number} The height of the CollisionMask. + */ + function CollisionMask(game, parent, x, y, width, height) { + /** + * Geom type of this sprite. (available: QUAD, POINT, CIRCLE, LINE, RECTANGLE, POLYGON) + * @type {number} + */ + this.type = 0; + this._game = game; + this._parent = parent; + // By default the CollisionMask is a quad + this.type = CollisionMask.QUAD; + this.quad = new Phaser.Quad(this._parent.x, this._parent.y, this._parent.width, this._parent.height); + this.offset = new Phaser.MicroPoint(0, 0); + this.last = new Phaser.MicroPoint(0, 0); + this._ref = this.quad; + return this; + } + CollisionMask.QUAD = 0; + CollisionMask.POINT = 1; + CollisionMask.CIRCLE = 2; + CollisionMask.LINE = 3; + CollisionMask.RECTANGLE = 4; + CollisionMask.POLYGON = 5; + CollisionMask.prototype.createCircle = /** + * Create a circle shape with specific diameter. + * @param diameter {number} Diameter of the circle. + * @return {CollisionMask} This + */ + function (diameter) { + this.type = CollisionMask.CIRCLE; + this.circle = new Phaser.Circle(this.last.x, this.last.y, diameter); + this._ref = this.circle; + return this; + }; + CollisionMask.prototype.preUpdate = /** + * Pre-update is called right before update() on each object in the game loop. + */ + function () { + this.last.x = this.x; + this.last.y = this.y; + }; + CollisionMask.prototype.update = function () { + this._ref.x = this._parent.x + this.offset.x; + this._ref.y = this._parent.y + this.offset.y; + }; + CollisionMask.prototype.render = /** + * Renders the bounding box around this Sprite and the contact points. Useful for visually debugging. + * @param camera {Camera} Camera the bound will be rendered to. + * @param cameraOffsetX {number} X offset of bound to the camera. + * @param cameraOffsetY {number} Y offset of bound to the camera. + */ + function (camera, cameraOffsetX, cameraOffsetY) { + var _dx = cameraOffsetX + (this.x - camera.worldView.x); + var _dy = cameraOffsetY + (this.y - camera.worldView.y); + this._parent.context.fillStyle = this._parent.renderDebugColor; + if(this.type == CollisionMask.QUAD) { + this._parent.context.fillRect(_dx, _dy, this.width, this.height); + } else if(this.type == CollisionMask.CIRCLE) { + this._parent.context.beginPath(); + this._parent.context.arc(_dx, _dy, this.circle.radius, 0, Math.PI * 2); + this._parent.context.fill(); + this._parent.context.closePath(); + } + }; + CollisionMask.prototype.destroy = /** + * Destroy all objects and references belonging to this CollisionMask + */ + function () { + this._game = null; + this._parent = null; + this._ref = null; + this.quad = null; + this.point = null; + this.circle = null; + this.rect = null; + this.line = null; + this.offset = null; + }; + CollisionMask.prototype.intersectsRaw = function (left, right, top, bottom) { + //if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + return true; + }; + CollisionMask.prototype.intersectsVector = function (vector) { + if(this.type == CollisionMask.QUAD) { + return this.quad.contains(vector.x, vector.y); + } + }; + CollisionMask.prototype.intersects = /** + * Gives a basic boolean response to a geometric collision. + * If you need the details of the collision use the Collision functions instead and inspect the IntersectResult object. + * @param source {GeomSprite} Sprite you want to check. + * @return {boolean} Whether they overlaps or not. + */ + function (source) { + // Quad vs. Quad + if(this.type == CollisionMask.QUAD && source.type == CollisionMask.QUAD) { + return this.quad.intersects(source.quad); + } + // Circle vs. Circle + if(this.type == CollisionMask.CIRCLE && source.type == CollisionMask.CIRCLE) { + return Phaser.Collision.circleToCircle(this.circle, source.circle).result; + } + // Circle vs. Rect + if(this.type == CollisionMask.CIRCLE && source.type == CollisionMask.RECTANGLE) { + return Phaser.Collision.circleToRectangle(this.circle, source.rect).result; + } + // Circle vs. Point + if(this.type == CollisionMask.CIRCLE && source.type == CollisionMask.POINT) { + return Phaser.Collision.circleContainsPoint(this.circle, source.point).result; + } + // Circle vs. Line + if(this.type == CollisionMask.CIRCLE && source.type == CollisionMask.LINE) { + return Phaser.Collision.lineToCircle(source.line, this.circle).result; + } + // Rect vs. Rect + if(this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.RECTANGLE) { + return Phaser.Collision.rectangleToRectangle(this.rect, source.rect).result; + } + // Rect vs. Circle + if(this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.CIRCLE) { + return Phaser.Collision.circleToRectangle(source.circle, this.rect).result; + } + // Rect vs. Point + if(this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.POINT) { + return Phaser.Collision.pointToRectangle(source.point, this.rect).result; + } + // Rect vs. Line + if(this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.LINE) { + return Phaser.Collision.lineToRectangle(source.line, this.rect).result; + } + // Point vs. Point + if(this.type == CollisionMask.POINT && source.type == CollisionMask.POINT) { + return this.point.equals(source.point); + } + // Point vs. Circle + if(this.type == CollisionMask.POINT && source.type == CollisionMask.CIRCLE) { + return Phaser.Collision.circleContainsPoint(source.circle, this.point).result; + } + // Point vs. Rect + if(this.type == CollisionMask.POINT && source.type == CollisionMask.RECTANGLE) { + return Phaser.Collision.pointToRectangle(this.point, source.rect).result; + } + // Point vs. Line + if(this.type == CollisionMask.POINT && source.type == CollisionMask.LINE) { + return source.line.isPointOnLine(this.point.x, this.point.y); + } + // Line vs. Line + if(this.type == CollisionMask.LINE && source.type == CollisionMask.LINE) { + return Phaser.Collision.lineSegmentToLineSegment(this.line, source.line).result; + } + // Line vs. Circle + if(this.type == CollisionMask.LINE && source.type == CollisionMask.CIRCLE) { + return Phaser.Collision.lineToCircle(this.line, source.circle).result; + } + // Line vs. Rect + if(this.type == CollisionMask.LINE && source.type == CollisionMask.RECTANGLE) { + return Phaser.Collision.lineSegmentToRectangle(this.line, source.rect).result; + } + // Line vs. Point + if(this.type == CollisionMask.LINE && source.type == CollisionMask.POINT) { + return this.line.isPointOnLine(source.point.x, source.point.y); + } + return false; + }; + CollisionMask.prototype.checkHullIntersection = function (mask) { + if((this.hullX + this.hullWidth > mask.hullX) && (this.hullX < mask.hullX + mask.width) && (this.hullY + this.hullHeight > mask.hullY) && (this.hullY < mask.hullY + mask.hullHeight)) { + return true; + } else { + return false; + } + }; + Object.defineProperty(CollisionMask.prototype, "hullWidth", { + get: function () { + if(this.deltaX > 0) { + return this.width + this.deltaX; + } else { + return this.width - this.deltaX; + } + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "hullHeight", { + get: function () { + if(this.deltaY > 0) { + return this.height + this.deltaY; + } else { + return this.height - this.deltaY; + } + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "hullX", { + get: function () { + if(this.x < this.last.x) { + return this.x; + } else { + return this.last.x; + } + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "hullY", { + get: function () { + if(this.y < this.last.y) { + return this.y; + } else { + return this.last.y; + } + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "deltaXAbs", { + get: function () { + return (this.deltaX > 0 ? this.deltaX : -this.deltaX); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "deltaYAbs", { + get: function () { + return (this.deltaY > 0 ? this.deltaY : -this.deltaY); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "deltaX", { + get: function () { + return this.x - this.last.x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "deltaY", { + get: function () { + return this.y - this.last.y; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "x", { + get: function () { + return this._ref.x; + //return this.quad.x; + }, + set: function (value) { + this._ref.x = value; + //this.quad.x = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "y", { + get: function () { + return this._ref.y; + //return this.quad.y; + }, + set: function (value) { + this._ref.y = value; + //this.quad.y = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "width", { + get: function () { + //return this.quad.width; + return this._ref.width; + }, + set: //public get rotation(): number { + // return this._angle; + //} + //public set rotation(value: number) { + // this._angle = this._game.math.wrap(value, 360, 0); + //} + //public get angle(): number { + // return this._angle; + //} + //public set angle(value: number) { + // this._angle = this._game.math.wrap(value, 360, 0); + //} + function (value) { + //this.quad.width = value; + this._ref.width = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "height", { + get: function () { + //return this.quad.height; + return this._ref.height; + }, + set: function (value) { + //this.quad.height = value; + this._ref.height = value; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "left", { + get: function () { + return this.x; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "right", { + get: function () { + return this.x + this.width; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "top", { + get: function () { + return this.y; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "bottom", { + get: function () { + return this.y + this.height; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "halfWidth", { + get: function () { + return this.width / 2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(CollisionMask.prototype, "halfHeight", { + get: function () { + return this.height / 2; + }, + enumerable: true, + configurable: true + }); + return CollisionMask; + })(); + Phaser.CollisionMask = CollisionMask; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/CollisionMask.ts b/todo/phaser clean up/CollisionMask.ts new file mode 100644 index 00000000..f0186ae0 --- /dev/null +++ b/todo/phaser clean up/CollisionMask.ts @@ -0,0 +1,501 @@ +/// + +/** +* Phaser - CollisionMask +*/ + +module Phaser { + + export class CollisionMask { + + /** + * CollisionMask constructor. Creates a new CollisionMask for the given GameObject. + * + * @param game {Phaser.Game} Current game instance. + * @param parent {Phaser.GameObject} The GameObject this CollisionMask belongs to. + * @param x {number} The initial x position of the CollisionMask. + * @param y {number} The initial y position of the CollisionMask. + * @param width {number} The width of the CollisionMask. + * @param height {number} The height of the CollisionMask. + */ + constructor(game: Game, parent: GameObject, x: number, y: number, width: number, height: number) { + + this._game = game; + this._parent = parent; + + // By default the CollisionMask is a quad + this.type = CollisionMask.QUAD; + + this.quad = new Phaser.Quad(this._parent.x, this._parent.y, this._parent.width, this._parent.height); + this.offset = new MicroPoint(0, 0); + this.last = new MicroPoint(0, 0); + + this._ref = this.quad; + + return this; + + } + + private _game; + private _parent; + + // An internal reference to the active collision shape + private _ref; + + /** + * Geom type of this sprite. (available: QUAD, POINT, CIRCLE, LINE, RECTANGLE, POLYGON) + * @type {number} + */ + public type: number = 0; + + /** + * Quad (a smaller version of Rectangle). + * @type {number} + */ + public static QUAD: number = 0; + + /** + * Point. + * @type {number} + */ + public static POINT: number = 1; + + /** + * Circle. + * @type {number} + */ + public static CIRCLE: number = 2; + + /** + * Line. + * @type {number} + */ + public static LINE: number = 3; + + /** + * Rectangle. + * @type {number} + */ + public static RECTANGLE: number = 4; + + /** + * Polygon. + * @type {number} + */ + public static POLYGON: number = 5; + + /** + * Rectangle shape container. A Rectangle instance. + * @type {Rectangle} + */ + public quad: Quad; + + /** + * Point shape container. A Point instance. + * @type {Point} + */ + public point: Point; + + /** + * Circle shape container. A Circle instance. + * @type {Circle} + */ + public circle: Circle; + + /** + * Line shape container. A Line instance. + * @type {Line} + */ + public line: Line; + + /** + * Rectangle shape container. A Rectangle instance. + * @type {Rectangle} + */ + public rect: Rectangle; + + /** + * A value from the top-left of the GameObject frame that this collisionMask is offset to. + * If the CollisionMask is a Quad/Rectangle the offset relates to the top-left of that Quad. + * If the CollisionMask is a Circle the offset relates to the center of the circle. + * @type {MicroPoint} + */ + public offset: MicroPoint; + + /** + * The previous x/y coordinates of the CollisionMask, used for hull calculations + * @type {MicroPoint} + */ + public last: MicroPoint; + + /** + * Create a circle shape with specific diameter. + * @param diameter {number} Diameter of the circle. + * @return {CollisionMask} This + */ + createCircle(diameter: number): CollisionMask { + + this.type = CollisionMask.CIRCLE; + this.circle = new Circle(this.last.x, this.last.y, diameter); + this._ref = this.circle; + + return this; + + } + + /** + * Pre-update is called right before update() on each object in the game loop. + */ + public preUpdate() { + + this.last.x = this.x; + this.last.y = this.y; + + } + + public update() { + + this._ref.x = this._parent.x + this.offset.x; + this._ref.y = this._parent.y + this.offset.y; + + } + + /** + * Renders the bounding box around this Sprite and the contact points. Useful for visually debugging. + * @param camera {Camera} Camera the bound will be rendered to. + * @param cameraOffsetX {number} X offset of bound to the camera. + * @param cameraOffsetY {number} Y offset of bound to the camera. + */ + public render(camera:Camera, cameraOffsetX:number, cameraOffsetY:number) { + + var _dx = cameraOffsetX + (this.x - camera.worldView.x); + var _dy = cameraOffsetY + (this.y - camera.worldView.y); + + this._parent.context.fillStyle = this._parent.renderDebugColor; + + if (this.type == CollisionMask.QUAD) + { + this._parent.context.fillRect(_dx, _dy, this.width, this.height); + } + else if (this.type == CollisionMask.CIRCLE) + { + this._parent.context.beginPath(); + this._parent.context.arc(_dx, _dy, this.circle.radius, 0, Math.PI * 2); + this._parent.context.fill(); + this._parent.context.closePath(); + } + + } + + /** + * Destroy all objects and references belonging to this CollisionMask + */ + public destroy() { + + this._game = null; + this._parent = null; + this._ref = null; + this.quad = null; + this.point = null; + this.circle = null; + this.rect = null; + this.line = null; + this.offset = null; + + } + + public intersectsRaw(left: number, right: number, top: number, bottom: number): bool { + +//if ((objBounds.x + objBounds.width > x) && (objBounds.x < x + width) && (objBounds.y + objBounds.height > y) && (objBounds.y < y + height)) + + return true; + + } + + public intersectsVector(vector: Phaser.Vector2): bool { + + if (this.type == CollisionMask.QUAD) + { + return this.quad.contains(vector.x, vector.y); + } + + } + + /** + * Gives a basic boolean response to a geometric collision. + * If you need the details of the collision use the Collision functions instead and inspect the IntersectResult object. + * @param source {GeomSprite} Sprite you want to check. + * @return {boolean} Whether they overlaps or not. + */ + public intersects(source: CollisionMask): bool { + + // Quad vs. Quad + if (this.type == CollisionMask.QUAD && source.type == CollisionMask.QUAD) + { + return this.quad.intersects(source.quad); + } + + // Circle vs. Circle + if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.CIRCLE) + { + return Collision.circleToCircle(this.circle, source.circle).result; + } + + // Circle vs. Rect + if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.RECTANGLE) + { + return Collision.circleToRectangle(this.circle, source.rect).result; + } + + // Circle vs. Point + if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.POINT) + { + return Collision.circleContainsPoint(this.circle, source.point).result; + } + + // Circle vs. Line + if (this.type == CollisionMask.CIRCLE && source.type == CollisionMask.LINE) + { + return Collision.lineToCircle(source.line, this.circle).result; + } + + // Rect vs. Rect + if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.RECTANGLE) + { + return Collision.rectangleToRectangle(this.rect, source.rect).result; + } + + // Rect vs. Circle + if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.CIRCLE) + { + return Collision.circleToRectangle(source.circle, this.rect).result; + } + + // Rect vs. Point + if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.POINT) + { + return Collision.pointToRectangle(source.point, this.rect).result; + } + + // Rect vs. Line + if (this.type == CollisionMask.RECTANGLE && source.type == CollisionMask.LINE) + { + return Collision.lineToRectangle(source.line, this.rect).result; + } + + // Point vs. Point + if (this.type == CollisionMask.POINT && source.type == CollisionMask.POINT) + { + return this.point.equals(source.point); + } + + // Point vs. Circle + if (this.type == CollisionMask.POINT && source.type == CollisionMask.CIRCLE) + { + return Collision.circleContainsPoint(source.circle, this.point).result; + } + + // Point vs. Rect + if (this.type == CollisionMask.POINT && source.type == CollisionMask.RECTANGLE) + { + return Collision.pointToRectangle(this.point, source.rect).result; + } + + // Point vs. Line + if (this.type == CollisionMask.POINT && source.type == CollisionMask.LINE) + { + return source.line.isPointOnLine(this.point.x, this.point.y); + } + + // Line vs. Line + if (this.type == CollisionMask.LINE && source.type == CollisionMask.LINE) + { + return Collision.lineSegmentToLineSegment(this.line, source.line).result; + } + + // Line vs. Circle + if (this.type == CollisionMask.LINE && source.type == CollisionMask.CIRCLE) + { + return Collision.lineToCircle(this.line, source.circle).result; + } + + // Line vs. Rect + if (this.type == CollisionMask.LINE && source.type == CollisionMask.RECTANGLE) + { + return Collision.lineSegmentToRectangle(this.line, source.rect).result; + } + + // Line vs. Point + if (this.type == CollisionMask.LINE && source.type == CollisionMask.POINT) + { + return this.line.isPointOnLine(source.point.x, source.point.y); + } + + return false; + + } + + public checkHullIntersection(mask: CollisionMask): bool { + + if ((this.hullX + this.hullWidth > mask.hullX) && (this.hullX < mask.hullX + mask.width) && (this.hullY + this.hullHeight > mask.hullY) && (this.hullY < mask.hullY + mask.hullHeight)) + { + return true; + } + else + { + return false; + } + + } + + public get hullWidth(): number { + + if (this.deltaX > 0) + { + return this.width + this.deltaX; + } + else + { + return this.width - this.deltaX; + } + + } + + public get hullHeight(): number { + + if (this.deltaY > 0) + { + return this.height + this.deltaY; + } + else + { + return this.height - this.deltaY; + } + + } + + public get hullX(): number { + + if (this.x < this.last.x) + { + return this.x; + } + else + { + return this.last.x; + } + + } + + public get hullY(): number { + + if (this.y < this.last.y) + { + return this.y; + } + else + { + return this.last.y; + } + + } + + public get deltaXAbs(): number { + return (this.deltaX > 0 ? this.deltaX : -this.deltaX); + } + + public get deltaYAbs(): number { + return (this.deltaY > 0 ? this.deltaY : -this.deltaY); + } + + public get deltaX(): number { + return this.x - this.last.x; + } + + public get deltaY(): number { + return this.y - this.last.y; + } + + public get x(): number { + return this._ref.x; + //return this.quad.x; + } + + public set x(value: number) { + this._ref.x = value; + //this.quad.x = value; + } + + public get y(): number { + return this._ref.y; + //return this.quad.y; + } + + public set y(value: number) { + this._ref.y = value; + //this.quad.y = value; + } + + //public get rotation(): number { + // return this._angle; + //} + + //public set rotation(value: number) { + // this._angle = this._game.math.wrap(value, 360, 0); + //} + + //public get angle(): number { + // return this._angle; + //} + + //public set angle(value: number) { + // this._angle = this._game.math.wrap(value, 360, 0); + //} + + public set width(value:number) { + //this.quad.width = value; + this._ref.width = value; + } + + public set height(value:number) { + //this.quad.height = value; + this._ref.height = value; + } + + public get width(): number { + //return this.quad.width; + return this._ref.width; + } + + public get height(): number { + //return this.quad.height; + return this._ref.height; + } + + public get left(): number { + return this.x; + } + + public get right(): number { + return this.x + this.width; + } + + public get top(): number { + return this.y; + } + + public get bottom(): number { + return this.y + this.height; + } + + public get halfWidth(): number { + return this.width / 2; + } + + public get halfHeight(): number { + return this.height / 2; + } + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/Debug.js b/todo/phaser clean up/Debug.js new file mode 100644 index 00000000..2f9b5688 --- /dev/null +++ b/todo/phaser clean up/Debug.js @@ -0,0 +1,19 @@ +var Shapes; +(function (Shapes) { + + var Point = Shapes.Point = (function () { + function Point(x, y) { + this.x = x; + this.y = y; + } + Point.prototype.getDist = function () { + return Math.sqrt((this.x * this.x) + (this.y * this.y)); + }; + Point.origin = new Point(0, 0); + return Point; + })(); + +})(Shapes || (Shapes = {})); + +var p = new Shapes.Point(3, 4); +var dist = p.getDist(); diff --git a/todo/phaser clean up/Debug.ts b/todo/phaser clean up/Debug.ts new file mode 100644 index 00000000..5ffa22eb --- /dev/null +++ b/todo/phaser clean up/Debug.ts @@ -0,0 +1,90 @@ +/** +* Phaser - Components - Debug +* +* +*/ + +module Phaser.Components { + + export class Debug { + + /** + * Render bound of this sprite for debugging? (default to false) + * @type {boolean} + */ + public renderDebug: bool = false; + + /** + * Color of the Sprite when no image is present. Format is a css color string. + * @type {string} + */ + public fillColor: string = 'rgb(255,255,255)'; + + /** + * Color of bound when render debug. (see renderDebug) Format is a css color string. + * @type {string} + */ + public renderDebugColor: string = 'rgba(0,255,0,0.5)'; + + /** + * Color of points when render debug. (see renderDebug) Format is a css color string. + * @type {string} + */ + public renderDebugPointColor: string = 'rgba(255,255,255,1)'; + + /** + * Renders the bounding box around this Sprite and the contact points. Useful for visually debugging. + * @param camera {Camera} Camera the bound will be rendered to. + * @param cameraOffsetX {number} X offset of bound to the camera. + * @param cameraOffsetY {number} Y offset of bound to the camera. + */ + /* + private renderBounds(camera: Camera, cameraOffsetX: number, cameraOffsetY: number) { + + this._dx = cameraOffsetX + (this.frameBounds.topLeft.x - camera.worldView.x); + this._dy = cameraOffsetY + (this.frameBounds.topLeft.y - camera.worldView.y); + + this.context.fillStyle = this.renderDebugColor; + this.context.fillRect(this._dx, this._dy, this.frameBounds.width, this.frameBounds.height); + + //this.context.fillStyle = this.renderDebugPointColor; + + //var hw = this.frameBounds.halfWidth * this.scale.x; + //var hh = this.frameBounds.halfHeight * this.scale.y; + //var sw = (this.frameBounds.width * this.scale.x) - 1; + //var sh = (this.frameBounds.height * this.scale.y) - 1; + + //this.context.fillRect(this._dx, this._dy, 1, 1); // top left + //this.context.fillRect(this._dx + hw, this._dy, 1, 1); // top center + //this.context.fillRect(this._dx + sw, this._dy, 1, 1); // top right + //this.context.fillRect(this._dx, this._dy + hh, 1, 1); // left center + //this.context.fillRect(this._dx + hw, this._dy + hh, 1, 1); // center + //this.context.fillRect(this._dx + sw, this._dy + hh, 1, 1); // right center + //this.context.fillRect(this._dx, this._dy + sh, 1, 1); // bottom left + //this.context.fillRect(this._dx + hw, this._dy + sh, 1, 1); // bottom center + //this.context.fillRect(this._dx + sw, this._dy + sh, 1, 1); // bottom right + + } + */ + + /** + * Render debug infos. (including name, bounds info, position and some other properties) + * @param x {number} X position of the debug info to be rendered. + * @param y {number} Y position of the debug info to be rendered. + * @param [color] {number} color of the debug info to be rendered. (format is css color string) + */ + /* + public renderDebugInfo(x: number, y: number, color?: string = 'rgb(255,255,255)') { + + this.context.fillStyle = color; + this.context.fillText('Sprite: ' + this.name + ' (' + this.frameBounds.width + ' x ' + this.frameBounds.height + ')', x, y); + this.context.fillText('x: ' + this.frameBounds.x.toFixed(1) + ' y: ' + this.frameBounds.y.toFixed(1) + ' rotation: ' + this.angle.toFixed(1), x, y + 14); + this.context.fillText('dx: ' + this._dx.toFixed(1) + ' dy: ' + this._dy.toFixed(1) + ' dw: ' + this._dw.toFixed(1) + ' dh: ' + this._dh.toFixed(1), x, y + 28); + this.context.fillText('sx: ' + this._sx.toFixed(1) + ' sy: ' + this._sy.toFixed(1) + ' sw: ' + this._sw.toFixed(1) + ' sh: ' + this._sh.toFixed(1), x, y + 42); + + } + */ + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/Emitter.js b/todo/phaser clean up/Emitter.js new file mode 100644 index 00000000..694a8b78 --- /dev/null +++ b/todo/phaser clean up/Emitter.js @@ -0,0 +1,183 @@ +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + var Emitter = (function (_super) { + __extends(Emitter, _super); + function Emitter(game, x, y, size) { + if (typeof x === "undefined") { x = 0; } + if (typeof y === "undefined") { y = 0; } + if (typeof size === "undefined") { size = 0; } + _super.call(this, game, size); + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + this.minParticleSpeed = new MicroPoint(-100, -100); + this.maxParticleSpeed = new MicroPoint(100, 100); + this.minRotation = -360; + this.maxRotation = 360; + this.gravity = 0; + this.particleClass = null; + this.particleDrag = new MicroPoint(); + this.frequency = 0.1; + this.lifespan = 3; + this.bounce = 0; + this._quantity = 0; + this._counter = 0; + this._explode = true; + this.on = false; + this._point = new MicroPoint(); + } + Emitter.prototype.destroy = function () { + this.minParticleSpeed = null; + this.maxParticleSpeed = null; + this.particleDrag = null; + this.particleClass = null; + this._point = null; + _super.prototype.destroy.call(this); + }; + Emitter.prototype.makeParticles = function (graphics, quantity, multiple, collide) { + if (typeof quantity === "undefined") { quantity = 50; } + if (typeof multiple === "undefined") { multiple = false; } + if (typeof collide === "undefined") { collide = 0; } + this.maxSize = quantity; + var totalFrames = 1; + var randomFrame; + var particle; + var i = 0; + while(i < quantity) { + if(this.particleClass == null) { + particle = new Phaser.Particle(this._game); + } else { + particle = new this.particleClass(this._game); + } + if(multiple) { + } else { + if(graphics) { + particle.loadGraphic(graphics); + } + } + if(collide > 0) { + particle.allowCollisions = Phaser.Collision.ANY; + particle.width *= collide; + particle.height *= collide; + } else { + particle.allowCollisions = Phaser.Collision.NONE; + } + particle.exists = false; + this.add(particle); + i++; + } + return this; + }; + Emitter.prototype.update = function () { + if(this.on) { + if(this._explode) { + this.on = false; + var i = 0; + var l = this._quantity; + if((l <= 0) || (l > this.length)) { + l = this.length; + } + while(i < l) { + this.emitParticle(); + i++; + } + this._quantity = 0; + } else { + this._timer += this._game.time.elapsed; + while((this.frequency > 0) && (this._timer > this.frequency) && this.on) { + this._timer -= this.frequency; + this.emitParticle(); + if((this._quantity > 0) && (++this._counter >= this._quantity)) { + this.on = false; + this._quantity = 0; + } + } + } + } + _super.prototype.update.call(this); + }; + Emitter.prototype.kill = function () { + this.on = false; + _super.prototype.kill.call(this); + }; + Emitter.prototype.start = function (explode, lifespan, frequency, quantity) { + if (typeof explode === "undefined") { explode = true; } + if (typeof lifespan === "undefined") { lifespan = 0; } + if (typeof frequency === "undefined") { frequency = 0.1; } + if (typeof quantity === "undefined") { quantity = 0; } + this.revive(); + this.visible = true; + this.on = true; + this._explode = explode; + this.lifespan = lifespan; + this.frequency = frequency; + this._quantity += quantity; + this._counter = 0; + this._timer = 0; + }; + Emitter.prototype.emitParticle = function () { + var particle = this.recycle(Phaser.Particle); + particle.lifespan = this.lifespan; + particle.elasticity = this.bounce; + particle.reset(this.x - (particle.width >> 1) + this._game.math.random() * this.width, this.y - (particle.height >> 1) + this._game.math.random() * this.height); + particle.visible = true; + if(this.minParticleSpeed.x != this.maxParticleSpeed.x) { + particle.velocity.x = this.minParticleSpeed.x + this._game.math.random() * (this.maxParticleSpeed.x - this.minParticleSpeed.x); + } else { + particle.velocity.x = this.minParticleSpeed.x; + } + if(this.minParticleSpeed.y != this.maxParticleSpeed.y) { + particle.velocity.y = this.minParticleSpeed.y + this._game.math.random() * (this.maxParticleSpeed.y - this.minParticleSpeed.y); + } else { + particle.velocity.y = this.minParticleSpeed.y; + } + particle.acceleration.y = this.gravity; + if(this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) { + particle.angularVelocity = this.minRotation + this._game.math.random() * (this.maxRotation - this.minRotation); + } else { + particle.angularVelocity = this.minRotation; + } + if(particle.angularVelocity != 0) { + particle.angle = this._game.math.random() * 360 - 180; + } + particle.drag.x = this.particleDrag.x; + particle.drag.y = this.particleDrag.y; + particle.onEmit(); + }; + Emitter.prototype.setSize = function (width, height) { + this.width = width; + this.height = height; + }; + Emitter.prototype.setXSpeed = function (min, max) { + if (typeof min === "undefined") { min = 0; } + if (typeof max === "undefined") { max = 0; } + this.minParticleSpeed.x = min; + this.maxParticleSpeed.x = max; + }; + Emitter.prototype.setYSpeed = function (min, max) { + if (typeof min === "undefined") { min = 0; } + if (typeof max === "undefined") { max = 0; } + this.minParticleSpeed.y = min; + this.maxParticleSpeed.y = max; + }; + Emitter.prototype.setRotation = function (min, max) { + if (typeof min === "undefined") { min = 0; } + if (typeof max === "undefined") { max = 0; } + this.minRotation = min; + this.maxRotation = max; + }; + Emitter.prototype.at = function (object) { + object.getMidpoint(this._point); + this.x = this._point.x - (this.width >> 1); + this.y = this._point.y - (this.height >> 1); + }; + return Emitter; + })(Phaser.Group); + Phaser.Emitter = Emitter; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/Emitter.ts b/todo/phaser clean up/Emitter.ts new file mode 100644 index 00000000..1aa5d2b1 --- /dev/null +++ b/todo/phaser clean up/Emitter.ts @@ -0,0 +1,454 @@ +/// +/// + +/** +* Phaser - Emitter +* +* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for +* continuous effects like rain and fire. All it really does is launch Particle objects out +* at set intervals, and fixes their positions and velocities accorindgly. +*/ + +module Phaser { + + export class Emitter extends Group { + + /** + * Creates a new Emitter object at a specific position. + * Does NOT automatically generate or attach particles! + * + * @param x {number} The X position of the emitter. + * @param y {number} The Y position of the emitter. + * @param [size] {number} Specifies a maximum capacity for this emitter. + */ + constructor(game: Game, x: number = 0, y: number = 0, size: number = 0) { + + super(game, size); + + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + this.minParticleSpeed = new MicroPoint(-100, -100); + this.maxParticleSpeed = new MicroPoint(100, 100); + this.minRotation = -360; + this.maxRotation = 360; + this.gravity = 0; + this.particleClass = null; + this.particleDrag = new MicroPoint(); + this.frequency = 0.1; + this.lifespan = 3; + this.bounce = 0; + this._quantity = 0; + this._counter = 0; + this._explode = true; + this.on = false; + this._point = new MicroPoint(); + + } + + /** + * The X position of the top left corner of the emitter in world space. + */ + public x: number; + + /** + * The Y position of the top left corner of emitter in world space. + */ + public y: number; + + /** + * The width of the emitter. Particles can be randomly generated from anywhere within this box. + */ + public width: number; + + /** + * The height of the emitter. Particles can be randomly generated from anywhere within this box. + */ + public height: number; + + /** + * The minimum possible velocity of a particle. + * The default value is (-100,-100). + */ + public minParticleSpeed: MicroPoint; + + /** + * The maximum possible velocity of a particle. + * The default value is (100,100). + */ + public maxParticleSpeed: MicroPoint; + + /** + * The X and Y drag component of particles launched from the emitter. + */ + public particleDrag: MicroPoint; + + /** + * The minimum possible angular velocity of a particle. The default value is -360. + * NOTE: rotating particles are more expensive to draw than non-rotating ones! + */ + public minRotation: number; + + /** + * The maximum possible angular velocity of a particle. The default value is 360. + * NOTE: rotating particles are more expensive to draw than non-rotating ones! + */ + public maxRotation: number; + + /** + * Sets the acceleration.y member of each particle to this value on launch. + */ + public gravity: number; + + /** + * Determines whether the emitter is currently emitting particles. + * It is totally safe to directly toggle this. + */ + public on: bool; + + /** + * How often a particle is emitted (if emitter is started with Explode == false). + */ + public frequency: number; + + /** + * How long each particle lives once it is emitted. + * Set lifespan to 'zero' for particles to live forever. + */ + public lifespan: number; + + /** + * How much each particle should bounce. 1 = full bounce, 0 = no bounce. + */ + public bounce: number; + + /** + * Set your own particle class type here. + * Default is Particle. + */ + public particleClass; + + /** + * Internal helper for deciding how many particles to launch. + */ + private _quantity: number; + + /** + * Internal helper for the style of particle emission (all at once, or one at a time). + */ + private _explode: bool; + + /** + * Internal helper for deciding when to launch particles or kill them. + */ + private _timer: number; + + /** + * Internal counter for figuring out how many particles to launch. + */ + private _counter: number; + + /** + * Internal point object, handy for reusing for memory mgmt purposes. + */ + private _point: MicroPoint; + + /** + * Clean up memory. + */ + public destroy() { + this.minParticleSpeed = null; + this.maxParticleSpeed = null; + this.particleDrag = null; + this.particleClass = null; + this._point = null; + super.destroy(); + } + + /** + * This function generates a new array of particle sprites to attach to the emitter. + * + * @param graphics If you opted to not pre-configure an array of Sprite objects, you can simply pass in a particle image or sprite sheet. + * @param quantity {number} The number of particles to generate when using the "create from image" option. + * @param multiple {boolean} Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). + * @param collide {number} Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box. + * + * @return This Emitter instance (nice for chaining stuff together, if you're into that). + */ + public makeParticles(graphics, quantity: number = 50, multiple: bool = false, collide: number = 0): Emitter { + + this.maxSize = quantity; + + var totalFrames: number = 1; + + /* + if(Multiple) + { + var sprite:Sprite = new Sprite(this._game); + sprite.loadGraphic(Graphics,true); + totalFrames = sprite.frames; + sprite.destroy(); + } + */ + + var randomFrame: number; + var particle: Particle; + var i: number = 0; + + while (i < quantity) + { + if (this.particleClass == null) + { + particle = new Particle(this._game); + } + else + { + particle = new this.particleClass(this._game); + } + + if (multiple) + { + /* + randomFrame = this._game.math.random()*totalFrames; + if(BakedRotations > 0) + particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame); + else + { + particle.loadGraphic(Graphics,true); + particle.frame = randomFrame; + } + */ + } + else + { + /* + if (BakedRotations > 0) + particle.loadRotatedGraphic(Graphics,BakedRotations); + else + particle.loadGraphic(Graphics); + */ + + if (graphics) + { + particle.loadGraphic(graphics); + } + + } + + if (collide > 0) + { + particle.allowCollisions = Collision.ANY; + particle.width *= collide; + particle.height *= collide; + //particle.centerOffsets(); + } + else + { + particle.allowCollisions = Collision.NONE; + } + + particle.exists = false; + + this.add(particle); + + i++; + } + + return this; + } + + /** + * Called automatically by the game loop, decides when to launch particles and when to "die". + */ + public update() { + + if (this.on) + { + if (this._explode) + { + this.on = false; + + var i: number = 0; + var l: number = this._quantity; + + if ((l <= 0) || (l > this.length)) + { + l = this.length; + } + + while (i < l) + { + this.emitParticle(); + i++; + } + + this._quantity = 0; + } + else + { + this._timer += this._game.time.elapsed; + + while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) + { + this._timer -= this.frequency; + this.emitParticle(); + + if ((this._quantity > 0) && (++this._counter >= this._quantity)) + { + this.on = false; + this._quantity = 0; + } + } + } + } + + super.update(); + + } + + /** + * Call this function to turn off all the particles and the emitter. + */ + public kill() { + + this.on = false; + + super.kill(); + + } + + /** + * Call this function to start emitting particles. + * + * @param explode {boolean} Whether the particles should all burst out at once. + * @param lifespan {number} How long each particle lives once emitted. 0 = forever. + * @param frequency {number} Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. + * @param quantity {number} How many particles to launch. 0 = "all of the particles". + */ + public start(explode: bool = true, lifespan: number = 0, frequency: number = 0.1, quantity: number = 0) { + + this.revive(); + + this.visible = true; + this.on = true; + + this._explode = explode; + this.lifespan = lifespan; + this.frequency = frequency; + this._quantity += quantity; + + this._counter = 0; + this._timer = 0; + + } + + /** + * This function can be used both internally and externally to emit the next particle. + */ + public emitParticle() { + + var particle: Particle = this.recycle(Particle); + + particle.lifespan = this.lifespan; + particle.elasticity = this.bounce; + particle.reset(this.x - (particle.width >> 1) + this._game.math.random() * this.width, this.y - (particle.height >> 1) + this._game.math.random() * this.height); + particle.visible = true; + + if (this.minParticleSpeed.x != this.maxParticleSpeed.x) + { + particle.velocity.x = this.minParticleSpeed.x + this._game.math.random() * (this.maxParticleSpeed.x - this.minParticleSpeed.x); + } + else + { + particle.velocity.x = this.minParticleSpeed.x; + } + + if (this.minParticleSpeed.y != this.maxParticleSpeed.y) + { + particle.velocity.y = this.minParticleSpeed.y + this._game.math.random() * (this.maxParticleSpeed.y - this.minParticleSpeed.y); + } + else + { + particle.velocity.y = this.minParticleSpeed.y; + } + + particle.acceleration.y = this.gravity; + + if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) + { + particle.angularVelocity = this.minRotation + this._game.math.random() * (this.maxRotation - this.minRotation); + } + else + { + particle.angularVelocity = this.minRotation; + } + + if (particle.angularVelocity != 0) + { + particle.angle = this._game.math.random() * 360 - 180; + } + + particle.drag.x = this.particleDrag.x; + particle.drag.y = this.particleDrag.y; + particle.onEmit(); + + } + + /** + * A more compact way of setting the width and height of the emitter. + * + * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). + * @param height {number} The desired height of the emitter. + */ + public setSize(width: number, height: number) { + this.width = width; + this.height = height; + } + + /** + * A more compact way of setting the X velocity range of the emitter. + * + * @param Min {number} The minimum value for this range. + * @param Max {number} The maximum value for this range. + */ + public setXSpeed(min: number = 0, max: number = 0) { + this.minParticleSpeed.x = min; + this.maxParticleSpeed.x = max; + } + + /** + * A more compact way of setting the Y velocity range of the emitter. + * + * @param Min {number} The minimum value for this range. + * @param Max {number} The maximum value for this range. + */ + public setYSpeed(min: number = 0, max: number = 0) { + this.minParticleSpeed.y = min; + this.maxParticleSpeed.y = max; + } + + /** + * A more compact way of setting the angular velocity constraints of the emitter. + * + * @param Min {number} The minimum value for this range. + * @param Max {number} The maximum value for this range. + */ + public setRotation(min: number = 0, max: number = 0) { + this.minRotation = min; + this.maxRotation = max; + } + + /** + * Change the emitter's midpoint to match the midpoint of a Object. + * + * @param Object {object} The Object that you want to sync up with. + */ + public at(object) { + object.getMidpoint(this._point); + this.x = this._point.x - (this.width >> 1); + this.y = this._point.y - (this.height >> 1); + } + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/GeomSprite.js b/todo/phaser clean up/GeomSprite.js new file mode 100644 index 00000000..9c59b103 --- /dev/null +++ b/todo/phaser clean up/GeomSprite.js @@ -0,0 +1,271 @@ +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + var GeomSprite = (function (_super) { + __extends(GeomSprite, _super); + function GeomSprite(game, x, y) { + if (typeof x === "undefined") { x = 0; } + if (typeof y === "undefined") { y = 0; } + _super.call(this, game, x, y); + this._dx = 0; + this._dy = 0; + this._dw = 0; + this._dh = 0; + this.type = 0; + this.renderOutline = true; + this.renderFill = true; + this.lineWidth = 1; + this.lineColor = 'rgb(0,255,0)'; + this.fillColor = 'rgb(0,100,0)'; + this.type = GeomSprite.UNASSIGNED; + return this; + } + GeomSprite.UNASSIGNED = 0; + GeomSprite.CIRCLE = 1; + GeomSprite.LINE = 2; + GeomSprite.POINT = 3; + GeomSprite.RECTANGLE = 4; + GeomSprite.POLYGON = 5; + GeomSprite.prototype.loadCircle = function (circle) { + this.refresh(); + this.circle = circle; + this.type = Phaser.GeomSprite.CIRCLE; + return this; + }; + GeomSprite.prototype.loadLine = function (line) { + this.refresh(); + this.line = line; + this.type = Phaser.GeomSprite.LINE; + return this; + }; + GeomSprite.prototype.loadPoint = function (point) { + this.refresh(); + this.point = point; + this.type = Phaser.GeomSprite.POINT; + return this; + }; + GeomSprite.prototype.loadRectangle = function (rect) { + this.refresh(); + this.rect = rect; + this.type = Phaser.GeomSprite.RECTANGLE; + return this; + }; + GeomSprite.prototype.createCircle = function (diameter) { + this.refresh(); + this.circle = new Phaser.Circle(this.x, this.y, diameter); + this.type = Phaser.GeomSprite.CIRCLE; + this.frameBounds.setTo(this.circle.x - this.circle.radius, this.circle.y - this.circle.radius, this.circle.diameter, this.circle.diameter); + return this; + }; + GeomSprite.prototype.createLine = function (x, y) { + this.refresh(); + this.line = new Phaser.Line(this.x, this.y, x, y); + this.type = Phaser.GeomSprite.LINE; + this.frameBounds.setTo(this.x, this.y, this.line.width, this.line.height); + return this; + }; + GeomSprite.prototype.createPoint = function () { + this.refresh(); + this.point = new Phaser.Point(this.x, this.y); + this.type = Phaser.GeomSprite.POINT; + this.frameBounds.width = 1; + this.frameBounds.height = 1; + return this; + }; + GeomSprite.prototype.createRectangle = function (width, height) { + this.refresh(); + this.rect = new Phaser.Rectangle(this.x, this.y, width, height); + this.type = Phaser.GeomSprite.RECTANGLE; + this.frameBounds.copyFrom(this.rect); + return this; + }; + GeomSprite.prototype.createPolygon = function (points) { + if (typeof points === "undefined") { points = []; } + this.refresh(); + this.polygon = new Phaser.Polygon(new Vector2(this.x, this.y), points); + this.type = Phaser.GeomSprite.POLYGON; + return this; + }; + GeomSprite.prototype.refresh = function () { + this.circle = null; + this.line = null; + this.point = null; + this.rect = null; + }; + GeomSprite.prototype.update = function () { + if(this.type == Phaser.GeomSprite.UNASSIGNED) { + return; + } else if(this.type == Phaser.GeomSprite.CIRCLE) { + this.circle.x = this.x; + this.circle.y = this.y; + this.frameBounds.width = this.circle.diameter; + this.frameBounds.height = this.circle.diameter; + } else if(this.type == Phaser.GeomSprite.LINE) { + this.line.x1 = this.x; + this.line.y1 = this.y; + this.frameBounds.setTo(this.x, this.y, this.line.width, this.line.height); + } else if(this.type == Phaser.GeomSprite.POINT) { + this.point.x = this.x; + this.point.y = this.y; + } else if(this.type == Phaser.GeomSprite.RECTANGLE) { + this.rect.x = this.x; + this.rect.y = this.y; + this.frameBounds.copyFrom(this.rect); + } + }; + GeomSprite.prototype.inCamera = function (camera) { + if(this.scrollFactor.x !== 1.0 || this.scrollFactor.y !== 1.0) { + this._dx = this.frameBounds.x - (camera.x * this.scrollFactor.x); + this._dy = this.frameBounds.y - (camera.y * this.scrollFactor.x); + this._dw = this.frameBounds.width * this.scale.x; + this._dh = this.frameBounds.height * this.scale.y; + return (camera.right > this._dx) && (camera.x < this._dx + this._dw) && (camera.bottom > this._dy) && (camera.y < this._dy + this._dh); + } else { + return camera.intersects(this.frameBounds); + } + }; + GeomSprite.prototype.render = function (camera, cameraOffsetX, cameraOffsetY) { + if(this.type == Phaser.GeomSprite.UNASSIGNED || this.visible === false || this.scale.x == 0 || this.scale.y == 0 || this.alpha < 0.1 || this.cameraBlacklist.indexOf(camera.ID) !== -1 || this.inCamera(camera.worldView) == false) { + return false; + } + if(this.alpha !== 1) { + var globalAlpha = this.context.globalAlpha; + this.context.globalAlpha = this.alpha; + } + this._dx = cameraOffsetX + (this.frameBounds.x - camera.worldView.x); + this._dy = cameraOffsetY + (this.frameBounds.y - camera.worldView.y); + this._dw = this.frameBounds.width * this.scale.x; + this._dh = this.frameBounds.height * this.scale.y; + if(this.scrollFactor.x !== 1.0 || this.scrollFactor.y !== 1.0) { + this._dx -= (camera.worldView.x * this.scrollFactor.x); + this._dy -= (camera.worldView.y * this.scrollFactor.y); + } + this._dx = Math.round(this._dx); + this._dy = Math.round(this._dy); + this._dw = Math.round(this._dw); + this._dh = Math.round(this._dh); + this._game.stage.saveCanvasValues(); + this.context.lineWidth = this.lineWidth; + this.context.strokeStyle = this.lineColor; + this.context.fillStyle = this.fillColor; + if(this._game.stage.fillStyle !== this.fillColor) { + } + if(this.type == Phaser.GeomSprite.CIRCLE) { + this.context.beginPath(); + this.context.arc(this._dx, this._dy, this.circle.radius, 0, Math.PI * 2); + if(this.renderOutline) { + this.context.stroke(); + } + if(this.renderFill) { + this.context.fill(); + } + this.context.closePath(); + } else if(this.type == Phaser.GeomSprite.LINE) { + this.context.beginPath(); + this.context.moveTo(this._dx, this._dy); + this.context.lineTo(this.line.x2, this.line.y2); + this.context.stroke(); + this.context.closePath(); + } else if(this.type == Phaser.GeomSprite.POINT) { + this.context.fillRect(this._dx, this._dy, 2, 2); + } else if(this.type == Phaser.GeomSprite.RECTANGLE) { + if(this.renderOutline == false) { + this.context.fillRect(this._dx, this._dy, this.rect.width, this.rect.height); + } else { + this.context.beginPath(); + this.context.rect(this._dx, this._dy, this.rect.width, this.rect.height); + this.context.stroke(); + if(this.renderFill) { + this.context.fill(); + } + this.context.closePath(); + } + this.context.fillStyle = 'rgb(255,255,255)'; + this.renderPoint(this.rect.topLeft, 0, 0, 2); + this.renderPoint(this.rect.topCenter, 0, 0, 2); + this.renderPoint(this.rect.topRight, 0, 0, 2); + this.renderPoint(this.rect.leftCenter, 0, 0, 2); + this.renderPoint(this.rect.center, 0, 0, 2); + this.renderPoint(this.rect.rightCenter, 0, 0, 2); + this.renderPoint(this.rect.bottomLeft, 0, 0, 2); + this.renderPoint(this.rect.bottomCenter, 0, 0, 2); + this.renderPoint(this.rect.bottomRight, 0, 0, 2); + } + this._game.stage.restoreCanvasValues(); + if(this.rotation !== 0) { + this.context.translate(0, 0); + this.context.restore(); + } + if(globalAlpha > -1) { + this.context.globalAlpha = globalAlpha; + } + return true; + }; + GeomSprite.prototype.renderPoint = function (point, offsetX, offsetY, size) { + if (typeof offsetX === "undefined") { offsetX = 0; } + if (typeof offsetY === "undefined") { offsetY = 0; } + if (typeof size === "undefined") { size = 1; } + this.context.fillRect(offsetX + point.x, offsetY + point.y, size, size); + }; + GeomSprite.prototype.renderDebugInfo = function (x, y, color) { + if (typeof color === "undefined") { color = 'rgb(255,255,255)'; } + }; + GeomSprite.prototype.collide = function (source) { + if(this.type == Phaser.GeomSprite.CIRCLE && source.type == Phaser.GeomSprite.CIRCLE) { + return Phaser.Collision.circleToCircle(this.circle, source.circle).result; + } + if(this.type == Phaser.GeomSprite.CIRCLE && source.type == Phaser.GeomSprite.RECTANGLE) { + return Phaser.Collision.circleToRectangle(this.circle, source.rect).result; + } + if(this.type == Phaser.GeomSprite.CIRCLE && source.type == Phaser.GeomSprite.POINT) { + return Phaser.Collision.circleContainsPoint(this.circle, source.point).result; + } + if(this.type == Phaser.GeomSprite.CIRCLE && source.type == Phaser.GeomSprite.LINE) { + return Phaser.Collision.lineToCircle(source.line, this.circle).result; + } + if(this.type == Phaser.GeomSprite.RECTANGLE && source.type == Phaser.GeomSprite.RECTANGLE) { + return Phaser.Collision.rectangleToRectangle(this.rect, source.rect).result; + } + if(this.type == Phaser.GeomSprite.RECTANGLE && source.type == Phaser.GeomSprite.CIRCLE) { + return Phaser.Collision.circleToRectangle(source.circle, this.rect).result; + } + if(this.type == Phaser.GeomSprite.RECTANGLE && source.type == Phaser.GeomSprite.POINT) { + return Phaser.Collision.pointToRectangle(source.point, this.rect).result; + } + if(this.type == Phaser.GeomSprite.RECTANGLE && source.type == Phaser.GeomSprite.LINE) { + return Phaser.Collision.lineToRectangle(source.line, this.rect).result; + } + if(this.type == Phaser.GeomSprite.POINT && source.type == Phaser.GeomSprite.POINT) { + return this.point.equals(source.point); + } + if(this.type == Phaser.GeomSprite.POINT && source.type == Phaser.GeomSprite.CIRCLE) { + return Phaser.Collision.circleContainsPoint(source.circle, this.point).result; + } + if(this.type == Phaser.GeomSprite.POINT && source.type == Phaser.GeomSprite.RECTANGLE) { + return Phaser.Collision.pointToRectangle(this.point, source.rect).result; + } + if(this.type == Phaser.GeomSprite.POINT && source.type == Phaser.GeomSprite.LINE) { + return source.line.isPointOnLine(this.point.x, this.point.y); + } + if(this.type == Phaser.GeomSprite.LINE && source.type == Phaser.GeomSprite.LINE) { + return Phaser.Collision.lineSegmentToLineSegment(this.line, source.line).result; + } + if(this.type == Phaser.GeomSprite.LINE && source.type == Phaser.GeomSprite.CIRCLE) { + return Phaser.Collision.lineToCircle(this.line, source.circle).result; + } + if(this.type == Phaser.GeomSprite.LINE && source.type == Phaser.GeomSprite.RECTANGLE) { + return Phaser.Collision.lineSegmentToRectangle(this.line, source.rect).result; + } + if(this.type == Phaser.GeomSprite.LINE && source.type == Phaser.GeomSprite.POINT) { + return this.line.isPointOnLine(source.point.x, source.point.y); + } + return false; + }; + return GeomSprite; + })(Phaser.GameObject); + Phaser.GeomSprite = GeomSprite; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/GeomSprite.ts b/todo/phaser clean up/GeomSprite.ts new file mode 100644 index 00000000..81ac326e --- /dev/null +++ b/todo/phaser clean up/GeomSprite.ts @@ -0,0 +1,645 @@ +/// +/// +/// + +/** +* Phaser - GeomSprite +* +* A GeomSprite is a special kind of GameObject that contains a base geometry class (Circle, Line, Point, Rectangle). +* They can be rendered in the game and used for collision just like any other game object. Display of them is controlled +* via the lineWidth / lineColor / fillColor and renderOutline / renderFill properties. +*/ + +module Phaser { + + export class GeomSprite extends Sprite { + + /** + * GeomSprite constructor + * Create a new GeomSprite. + * + * @param game {Phaser.Game} Current game instance. + * @param [x] {number} the initial x position of the sprite. + * @param [y] {number} the initial y position of the sprite. + */ + constructor(game: Game, x?: number = 0, y?: number = 0) { + + super(game, x, y); + + this.type = GeomSprite.UNASSIGNED; + + return this; + + } + + // local rendering related temp vars to help avoid gc spikes + private _dx: number = 0; + private _dy: number = 0; + private _dw: number = 0; + private _dh: number = 0; + + /** + * Geom type of this sprite. (available: UNASSIGNED, CIRCLE, LINE, POINT, RECTANGLE) + * @type {number} + */ + public type: number = 0; + + /** + * Not completely set yet. (the default type) + */ + public static UNASSIGNED: number = 0; + + /** + * Circle. + * @type {number} + */ + public static CIRCLE: number = 1; + + /** + * Line. + * @type {number} + */ + public static LINE: number = 2; + + /** + * Point. + * @type {number} + */ + public static POINT: number = 3; + + /** + * Rectangle. + * @type {number} + */ + public static RECTANGLE: number = 4; + + /** + * Polygon. + * @type {number} + */ + public static POLYGON: number = 5; + + /** + * Circle shape container. A Circle instance. + * @type {Circle} + */ + public circle: Circle; + + /** + * Line shape container. A Line instance. + * @type {Line} + */ + public line: Line; + + /** + * Point shape container. A Point instance. + * @type {Point} + */ + public point: Point; + + /** + * Rectangle shape container. A Rectangle instance. + * @type {Rectangle} + */ + public rect: Rectangle; + + /** + * Polygon shape container. A Polygon instance. + * @type {Polygon} + */ + public polygon: Polygon; + + /** + * Render outline of this sprite or not. (default is true) + * @type {boolean} + */ + public renderOutline: bool = true; + + /** + * Fill the shape or not. (default is true) + * @type {boolean} + */ + public renderFill: bool = true; + + /** + * Width of outline. (default is 1) + * @type {number} + */ + public lineWidth: number = 1; + + /** + * Width of outline. (default is 1) + * @type {number} + */ + public lineColor: string = 'rgb(0,255,0)'; + + /** + * The color of the filled area in rgb or rgba string format + * @type {string} Defaults to rgb(0,100,0) - a green color + */ + public fillColor: string = 'rgb(0,100,0)'; + + /** + * Just like Sprite.loadGraphic(), this will load a circle and set its shape to Circle. + * @param circle {Circle} Circle geometry define. + * @return {GeomSprite} GeomSprite instance itself. + */ + loadCircle(circle:Circle): GeomSprite { + + this.refresh(); + this.circle = circle; + this.type = GeomSprite.CIRCLE; + return this; + + } + + /** + * Just like Sprite.loadGraphic(), this will load a line and set its shape to Line. + * @param line {Line} Line geometry define. + * @return {GeomSprite} GeomSprite instance itself. + */ + loadLine(line:Line): GeomSprite { + + this.refresh(); + this.line = line; + this.type = GeomSprite.LINE; + return this; + + } + + /** + * Just like Sprite.loadGraphic(), this will load a point and set its shape to Point. + * @param point {Point} Point geometry define. + * @return {GeomSprite} GeomSprite instance itself. + */ + loadPoint(point:Point): GeomSprite { + + this.refresh(); + this.point = point; + this.type = GeomSprite.POINT; + return this; + + } + + /** + * Just like Sprite.loadGraphic(), this will load a rect and set its shape to Rectangle. + * @param rect {Rectangle} Rectangle geometry define. + * @return {GeomSprite} GeomSprite instance itself. + */ + loadRectangle(rect:Rectangle): GeomSprite { + + this.refresh(); + this.rect = rect; + this.type = GeomSprite.RECTANGLE; + return this; + + } + + /** + * Create a circle shape with specific diameter. + * @param diameter {number} Diameter of the circle. + * @return {GeomSprite} GeomSprite instance itself. + */ + createCircle(diameter: number): GeomSprite { + + this.refresh(); + this.circle = new Circle(this.x, this.y, diameter); + this.type = GeomSprite.CIRCLE; + this.frameBounds.setTo(this.circle.x - this.circle.radius, this.circle.y - this.circle.radius, this.circle.diameter, this.circle.diameter); + return this; + + } + + /** + * Create a line shape with specific end point. + * @param x {number} X position of the end point. + * @param y {number} Y position of the end point. + * @return {GeomSprite} GeomSprite instance itself. + */ + createLine(x: number, y: number): GeomSprite { + + this.refresh(); + this.line = new Line(this.x, this.y, x, y); + this.type = GeomSprite.LINE; + this.frameBounds.setTo(this.x, this.y, this.line.width, this.line.height); + return this; + + } + + /** + * Create a point shape at spriter's position. + * @return {GeomSprite} GeomSprite instance itself. + */ + createPoint(): GeomSprite { + + this.refresh(); + this.point = new Point(this.x, this.y); + this.type = GeomSprite.POINT; + this.frameBounds.width = 1; + this.frameBounds.height = 1; + return this; + + } + + /** + * Create a rectangle shape of the given width and height size + * @param width {Number} Width of the rectangle + * @param height {Number} Height of the rectangle + * @return {GeomSprite} GeomSprite instance. + */ + createRectangle(width: number, height: number): GeomSprite { + + this.refresh(); + this.rect = new Rectangle(this.x, this.y, width, height); + this.type = GeomSprite.RECTANGLE; + this.frameBounds.copyFrom(this.rect); + return this; + + } + + /** + * Create a polygon object + * @param width {Number} Width of the rectangle + * @param height {Number} Height of the rectangle + * @return {GeomSprite} GeomSprite instance. + */ + createPolygon(points?: Vec2[] = []): GeomSprite { + + //this.refresh(); + //this.polygon = new Polygon(new Vec2(this.x, this.y), points); + //this.type = GeomSprite.POLYGON; + //this.frameBounds.copyFrom(this.rect); + return this; + + } + + /** + * Destroy all geom shapes of this sprite. + */ + refresh() { + + this.circle = null; + this.line = null; + this.point = null; + this.rect = null; + + } + + /** + * Update bounds. + */ + update() { + + // Update bounds and position? + if (this.type == GeomSprite.UNASSIGNED) + { + return; + } + else if (this.type == GeomSprite.CIRCLE) + { + this.circle.x = this.x; + this.circle.y = this.y; + this.frameBounds.width = this.circle.diameter; + this.frameBounds.height = this.circle.diameter; + } + else if (this.type == GeomSprite.LINE) + { + this.line.x1 = this.x; + this.line.y1 = this.y; + this.frameBounds.setTo(this.x, this.y, this.line.width, this.line.height); + } + else if (this.type == GeomSprite.POINT) + { + this.point.x = this.x; + this.point.y = this.y; + } + else if (this.type == GeomSprite.RECTANGLE) + { + this.rect.x = this.x; + this.rect.y = this.y; + this.frameBounds.copyFrom(this.rect); + } + + } + + /** + * Check whether this object is visible in a specific camera rectangle. + * @param camera {Rectangle} The rectangle you want to check. + * @return {boolean} Return true if bounds of this sprite intersects the given rectangle, otherwise return false. + */ + /* + public inCamera(camera: Rectangle): bool { + + if (this.scrollFactor.x !== 1.0 || this.scrollFactor.y !== 1.0) + { + this._dx = this.frameBounds.x - (camera.x * this.scrollFactor.x); + this._dy = this.frameBounds.y - (camera.y * this.scrollFactor.x); + this._dw = this.frameBounds.width * this.scale.x; + this._dh = this.frameBounds.height * this.scale.y; + + return (camera.right > this._dx) && (camera.x < this._dx + this._dw) && (camera.bottom > this._dy) && (camera.y < this._dy + this._dh); + } + else + { + return camera.intersects(this.frameBounds); + } + + } + */ + + /** + * Render this sprite to specific camera. Called by game loop after update(). + * @param camera {Camera} Camera this sprite will be rendered to. + * @cameraOffsetX {number} X offset to the camera. + * @cameraOffsetY {number} Y offset to the camera. + * @return {boolean} Return false if not rendered, otherwise return true. + */ + public render(camera: Camera, cameraOffsetX: number, cameraOffsetY: number): bool { + + // Render checks + //if (this.type == GeomSprite.UNASSIGNED || this.visible === false || this.scale.x == 0 || this.scale.y == 0 || this.alpha < 0.1 || this.cameraBlacklist.indexOf(camera.ID) !== -1 || this.inCamera(camera.worldView) == false) + //{ + // return false; + //} + + // Alpha + if (this.alpha !== 1) + { + var globalAlpha = this.context.globalAlpha; + this.context.globalAlpha = this.alpha; + } + + this._dx = cameraOffsetX + (this.frameBounds.x - camera.worldView.x); + this._dy = cameraOffsetY + (this.frameBounds.y - camera.worldView.y); + this._dw = this.frameBounds.width * this.scale.x; + this._dh = this.frameBounds.height * this.scale.y; + + // Apply camera difference + if (this.scrollFactor.x !== 1.0 || this.scrollFactor.y !== 1.0) + { + this._dx -= (camera.worldView.x * this.scrollFactor.x); + this._dy -= (camera.worldView.y * this.scrollFactor.y); + } + + // Rotation is disabled for now as I don't want it to be misleading re: collision + /* + if (this.angle !== 0) + { + this.context.save(); + this.context.translate(this._dx + (this._dw / 2) - this.origin.x, this._dy + (this._dh / 2) - this.origin.y); + this.context.rotate(this.angle * (Math.PI / 180)); + this._dx = -(this._dw / 2); + this._dy = -(this._dh / 2); + } + */ + + this._dx = Math.round(this._dx); + this._dy = Math.round(this._dy); + this._dw = Math.round(this._dw); + this._dh = Math.round(this._dh); + + this._game.stage.saveCanvasValues(); + + // Debug + //this.context.fillStyle = 'rgba(255,0,0,0.5)'; + //this.context.fillRect(this.frameBounds.x, this.frameBounds.y, this.frameBounds.width, this.frameBounds.height); + + this.context.lineWidth = this.lineWidth; + this.context.strokeStyle = this.lineColor; + this.context.fillStyle = this.fillColor; + + if (this._game.stage.fillStyle !== this.fillColor) + { + } + + // Primitive Renderer + if (this.type == GeomSprite.CIRCLE) + { + this.context.beginPath(); + this.context.arc(this._dx, this._dy, this.circle.radius, 0, Math.PI * 2); + + if (this.renderOutline) + { + this.context.stroke(); + } + + if (this.renderFill) + { + this.context.fill(); + } + + this.context.closePath(); + } + else if (this.type == GeomSprite.LINE) + { + this.context.beginPath(); + this.context.moveTo(this._dx, this._dy); + this.context.lineTo(this.line.x2, this.line.y2); + this.context.stroke(); + this.context.closePath(); + } + else if (this.type == GeomSprite.POINT) + { + this.context.fillRect(this._dx, this._dy, 2, 2); + } + else if (this.type == GeomSprite.RECTANGLE) + { + // We can use the faster fillRect if we don't need the outline + if (this.renderOutline == false) + { + this.context.fillRect(this._dx, this._dy, this.rect.width, this.rect.height); + } + else + { + this.context.beginPath(); + this.context.rect(this._dx, this._dy, this.rect.width, this.rect.height); + this.context.stroke(); + + if (this.renderFill) + { + this.context.fill(); + } + + this.context.closePath(); + } + + // And now the edge points + this.context.fillStyle = 'rgb(255,255,255)'; + //this.renderPoint(this.rect.topLeft, this._dx, this._dy, 2); + //this.renderPoint(this.rect.topCenter, this._dx, this._dy, 2); + //this.renderPoint(this.rect.topRight, this._dx, this._dy, 2); + //this.renderPoint(this.rect.leftCenter, this._dx, this._dy, 2); + //this.renderPoint(this.rect.center, this._dx, this._dy, 2); + //this.renderPoint(this.rect.rightCenter, this._dx, this._dy, 2); + //this.renderPoint(this.rect.bottomLeft, this._dx, this._dy, 2); + //this.renderPoint(this.rect.bottomCenter, this._dx, this._dy, 2); + //this.renderPoint(this.rect.bottomRight, this._dx, this._dy, 2); + this.renderPoint(this.rect.topLeft, 0, 0, 2); + this.renderPoint(this.rect.topCenter, 0, 0, 2); + this.renderPoint(this.rect.topRight, 0, 0, 2); + this.renderPoint(this.rect.leftCenter, 0, 0, 2); + this.renderPoint(this.rect.center, 0, 0, 2); + this.renderPoint(this.rect.rightCenter, 0, 0, 2); + this.renderPoint(this.rect.bottomLeft, 0, 0, 2); + this.renderPoint(this.rect.bottomCenter, 0, 0, 2); + this.renderPoint(this.rect.bottomRight, 0, 0, 2); + + } + + this._game.stage.restoreCanvasValues(); + + if (this.rotation !== 0) + { + this.context.translate(0, 0); + this.context.restore(); + } + + if (globalAlpha > -1) + { + this.context.globalAlpha = globalAlpha; + } + + return true; + + } + + /** + * Render a point of geometry. + * @param point {Point} Position of the point. + * @param offsetX {number} X offset to its position. + * @param offsetY {number} Y offset to its position. + * @param [size] {number} point size. + */ + public renderPoint(point, offsetX?: number = 0, offsetY?: number = 0, size?: number = 1) { + + this.context.fillRect(offsetX + point.x, offsetY + point.y, size, size); + + } + + /** + * Render debug infos. (this method does not work now) + * @param x {number} X position of the debug info to be rendered. + * @param y {number} Y position of the debug info to be rendered. + * @param [color] {number} color of the debug info to be rendered. (format is css color string) + */ + public renderDebugInfo(x: number, y: number, color?: string = 'rgb(255,255,255)') { + + //this.context.fillStyle = color; + //this.context.fillText('Sprite: ' + this.name + ' (' + this.frameBounds.width + ' x ' + this.frameBounds.height + ')', x, y); + //this.context.fillText('x: ' + this.frameBounds.x.toFixed(1) + ' y: ' + this.frameBounds.y.toFixed(1) + ' rotation: ' + this.angle.toFixed(1), x, y + 14); + //this.context.fillText('dx: ' + this._dx.toFixed(1) + ' dy: ' + this._dy.toFixed(1) + ' dw: ' + this._dw.toFixed(1) + ' dh: ' + this._dh.toFixed(1), x, y + 28); + //this.context.fillText('sx: ' + this._sx.toFixed(1) + ' sy: ' + this._sy.toFixed(1) + ' sw: ' + this._sw.toFixed(1) + ' sh: ' + this._sh.toFixed(1), x, y + 42); + + } + + /** + * Gives a basic boolean response to a geometric collision. + * If you need the details of the collision use the Collision functions instead and inspect the IntersectResult object. + * @param source {GeomSprite} Sprite you want to check. + * @return {boolean} Whether they overlaps or not. + */ + public collide(source: GeomSprite): bool { + + // Circle vs. Circle + if (this.type == GeomSprite.CIRCLE && source.type == GeomSprite.CIRCLE) + { + return Collision.circleToCircle(this.circle, source.circle).result; + } + + // Circle vs. Rect + if (this.type == GeomSprite.CIRCLE && source.type == GeomSprite.RECTANGLE) + { + return Collision.circleToRectangle(this.circle, source.rect).result; + } + + // Circle vs. Point + if (this.type == GeomSprite.CIRCLE && source.type == GeomSprite.POINT) + { + return Collision.circleContainsPoint(this.circle, source.point).result; + } + + // Circle vs. Line + if (this.type == GeomSprite.CIRCLE && source.type == GeomSprite.LINE) + { + return Collision.lineToCircle(source.line, this.circle).result; + } + + // Rect vs. Rect + if (this.type == GeomSprite.RECTANGLE && source.type == GeomSprite.RECTANGLE) + { + return Collision.rectangleToRectangle(this.rect, source.rect).result; + } + + // Rect vs. Circle + if (this.type == GeomSprite.RECTANGLE && source.type == GeomSprite.CIRCLE) + { + return Collision.circleToRectangle(source.circle, this.rect).result; + } + + // Rect vs. Point + if (this.type == GeomSprite.RECTANGLE && source.type == GeomSprite.POINT) + { + return Collision.pointToRectangle(source.point, this.rect).result; + } + + // Rect vs. Line + if (this.type == GeomSprite.RECTANGLE && source.type == GeomSprite.LINE) + { + return Collision.lineToRectangle(source.line, this.rect).result; + } + + // Point vs. Point + if (this.type == GeomSprite.POINT && source.type == GeomSprite.POINT) + { + return this.point.equals(source.point); + } + + // Point vs. Circle + if (this.type == GeomSprite.POINT && source.type == GeomSprite.CIRCLE) + { + return Collision.circleContainsPoint(source.circle, this.point).result; + } + + // Point vs. Rect + if (this.type == GeomSprite.POINT && source.type == GeomSprite.RECTANGLE) + { + return Collision.pointToRectangle(this.point, source.rect).result; + } + + // Point vs. Line + if (this.type == GeomSprite.POINT && source.type == GeomSprite.LINE) + { + return source.line.isPointOnLine(this.point.x, this.point.y); + } + + // Line vs. Line + if (this.type == GeomSprite.LINE && source.type == GeomSprite.LINE) + { + return Collision.lineSegmentToLineSegment(this.line, source.line).result; + } + + // Line vs. Circle + if (this.type == GeomSprite.LINE && source.type == GeomSprite.CIRCLE) + { + return Collision.lineToCircle(this.line, source.circle).result; + } + + // Line vs. Rect + if (this.type == GeomSprite.LINE && source.type == GeomSprite.RECTANGLE) + { + return Collision.lineSegmentToRectangle(this.line, source.rect).result; + } + + // Line vs. Point + if (this.type == GeomSprite.LINE && source.type == GeomSprite.POINT) + { + return this.line.isPointOnLine(source.point.x, source.point.y); + } + + return false; + + } + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/Input.js b/todo/phaser clean up/Input.js new file mode 100644 index 00000000..2f9b5688 --- /dev/null +++ b/todo/phaser clean up/Input.js @@ -0,0 +1,19 @@ +var Shapes; +(function (Shapes) { + + var Point = Shapes.Point = (function () { + function Point(x, y) { + this.x = x; + this.y = y; + } + Point.prototype.getDist = function () { + return Math.sqrt((this.x * this.x) + (this.y * this.y)); + }; + Point.origin = new Point(0, 0); + return Point; + })(); + +})(Shapes || (Shapes = {})); + +var p = new Shapes.Point(3, 4); +var dist = p.getDist(); diff --git a/todo/phaser clean up/Input.ts b/todo/phaser clean up/Input.ts new file mode 100644 index 00000000..75a23b04 --- /dev/null +++ b/todo/phaser clean up/Input.ts @@ -0,0 +1,29 @@ +/** +* Phaser - Components - Input +* +* +*/ + +module Phaser.Components { + + export class Input { + + // Input + //public inputEnabled: bool = false; + //private _inputOver: bool = false; + + //public onInputOver: Phaser.Signal; + //public onInputOut: Phaser.Signal; + //public onInputDown: Phaser.Signal; + //public onInputUp: Phaser.Signal; + + /** + * Update input. + */ + private updateInput() { + } + + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/Motion.js b/todo/phaser clean up/Motion.js new file mode 100644 index 00000000..2b42dab0 --- /dev/null +++ b/todo/phaser clean up/Motion.js @@ -0,0 +1,161 @@ +var Phaser; +(function (Phaser) { + var Motion = (function () { + function Motion(game) { + this._game = game; + } + Motion.prototype.computeVelocity = function (Velocity, Acceleration, Drag, Max) { + if (typeof Acceleration === "undefined") { Acceleration = 0; } + if (typeof Drag === "undefined") { Drag = 0; } + if (typeof Max === "undefined") { Max = 10000; } + if(Acceleration !== 0) { + Velocity += Acceleration * this._game.time.elapsed; + } else if(Drag !== 0) { + var drag = Drag * this._game.time.elapsed; + if(Velocity - drag > 0) { + Velocity = Velocity - drag; + } else if(Velocity + drag < 0) { + Velocity += drag; + } else { + Velocity = 0; + } + } + if((Velocity != 0) && (Max != 10000)) { + if(Velocity > Max) { + Velocity = Max; + } else if(Velocity < -Max) { + Velocity = -Max; + } + } + return Velocity; + }; + Motion.prototype.velocityFromAngle = function (angle, speed) { + if(isNaN(speed)) { + speed = 0; + } + var a = this._game.math.degreesToRadians(angle); + return new Phaser.Point((Math.cos(a) * speed), (Math.sin(a) * speed)); + }; + Motion.prototype.moveTowardsObject = function (source, dest, speed, maxTime) { + if (typeof speed === "undefined") { speed = 60; } + if (typeof maxTime === "undefined") { maxTime = 0; } + var a = this.angleBetween(source, dest); + if(maxTime > 0) { + var d = this.distanceBetween(source, dest); + speed = d / (maxTime / 1000); + } + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + }; + Motion.prototype.accelerateTowardsObject = function (source, dest, speed, xSpeedMax, ySpeedMax) { + var a = this.angleBetween(source, dest); + source.velocity.x = 0; + source.velocity.y = 0; + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + }; + Motion.prototype.moveTowardsMouse = function (source, speed, maxTime) { + if (typeof speed === "undefined") { speed = 60; } + if (typeof maxTime === "undefined") { maxTime = 0; } + var a = this.angleBetweenMouse(source); + if(maxTime > 0) { + var d = this.distanceToMouse(source); + speed = d / (maxTime / 1000); + } + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + }; + Motion.prototype.accelerateTowardsMouse = function (source, speed, xSpeedMax, ySpeedMax) { + var a = this.angleBetweenMouse(source); + source.velocity.x = 0; + source.velocity.y = 0; + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + }; + Motion.prototype.moveTowardsPoint = function (source, target, speed, maxTime) { + if (typeof speed === "undefined") { speed = 60; } + if (typeof maxTime === "undefined") { maxTime = 0; } + var a = this.angleBetweenPoint(source, target); + if(maxTime > 0) { + var d = this.distanceToPoint(source, target); + speed = d / (maxTime / 1000); + } + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + }; + Motion.prototype.accelerateTowardsPoint = function (source, target, speed, xSpeedMax, ySpeedMax) { + var a = this.angleBetweenPoint(source, target); + source.velocity.x = 0; + source.velocity.y = 0; + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + }; + Motion.prototype.distanceBetween = function (a, b) { + var dx = (a.x + a.origin.x) - (b.x + b.origin.x); + var dy = (a.y + a.origin.y) - (b.y + b.origin.y); + return this._game.math.vectorLength(dx, dy); + }; + Motion.prototype.distanceToPoint = function (a, target) { + var dx = (a.x + a.origin.x) - (target.x); + var dy = (a.y + a.origin.y) - (target.y); + return this._game.math.vectorLength(dx, dy); + }; + Motion.prototype.distanceToMouse = function (a) { + var dx = (a.x + a.origin.x) - this._game.input.x; + var dy = (a.y + a.origin.y) - this._game.input.y; + return this._game.math.vectorLength(dx, dy); + }; + Motion.prototype.angleBetweenPoint = function (a, target, asDegrees) { + if (typeof asDegrees === "undefined") { asDegrees = false; } + var dx = (target.x) - (a.x + a.origin.x); + var dy = (target.y) - (a.y + a.origin.y); + if(asDegrees) { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } else { + return Math.atan2(dy, dx); + } + }; + Motion.prototype.angleBetween = function (a, b, asDegrees) { + if (typeof asDegrees === "undefined") { asDegrees = false; } + var dx = (b.x + b.origin.x) - (a.x + a.origin.x); + var dy = (b.y + b.origin.y) - (a.y + a.origin.y); + if(asDegrees) { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } else { + return Math.atan2(dy, dx); + } + }; + Motion.prototype.velocityFromFacing = function (parent, speed) { + var a; + if(parent.facing == Phaser.Collision.LEFT) { + a = this._game.math.degreesToRadians(180); + } else if(parent.facing == Phaser.Collision.RIGHT) { + a = this._game.math.degreesToRadians(0); + } else if(parent.facing == Phaser.Collision.UP) { + a = this._game.math.degreesToRadians(-90); + } else if(parent.facing == Phaser.Collision.DOWN) { + a = this._game.math.degreesToRadians(90); + } + return new Phaser.Point(Math.cos(a) * speed, Math.sin(a) * speed); + }; + Motion.prototype.angleBetweenMouse = function (a, asDegrees) { + if (typeof asDegrees === "undefined") { asDegrees = false; } + var p = a.getScreenXY(); + var dx = a._game.input.x - p.x; + var dy = a._game.input.y - p.y; + if(asDegrees) { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } else { + return Math.atan2(dy, dx); + } + }; + return Motion; + })(); + Phaser.Motion = Motion; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/Motion.ts b/todo/phaser clean up/Motion.ts new file mode 100644 index 00000000..6d91c688 --- /dev/null +++ b/todo/phaser clean up/Motion.ts @@ -0,0 +1,409 @@ +/// +/// + +/** +* Phaser - Motion +* +* The Motion class contains lots of useful functions for moving game objects around in world space. +*/ + +module Phaser { + + export class Motion { + + constructor(game: Game) { + + this._game = game; + + } + + private _game: Game; + + /** + * A tween-like function that takes a starting velocity and some other factors and returns an altered velocity. + * + * @param {number} Velocity Any component of velocity (e.g. 20). + * @param {number} Acceleration Rate at which the velocity is changing. + * @param {number} Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. + * @param {number} Max An absolute value cap for the velocity. + * + * @return {number} The altered Velocity value. + */ + public computeVelocity(Velocity: number, Acceleration: number = 0, Drag: number = 0, Max: number = 10000): number { + + if (Acceleration !== 0) + { + Velocity += Acceleration * this._game.time.elapsed; + } + else if (Drag !== 0) + { + var drag: number = Drag * this._game.time.elapsed; + + if (Velocity - drag > 0) + { + Velocity = Velocity - drag; + } + else if (Velocity + drag < 0) + { + Velocity += drag; + } + else + { + Velocity = 0; + } + } + + if ((Velocity != 0) && (Max != 10000)) + { + if (Velocity > Max) + { + Velocity = Max; + } + else if (Velocity < -Max) + { + Velocity = -Max; + } + } + + return Velocity; + + } + + /** + * Given the angle and speed calculate the velocity and return it as a Point + * + * @param {number} angle The angle (in degrees) calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) + * @param {number} speed The speed it will move, in pixels per second sq + * + * @return {Point} A Point where Point.x contains the velocity x value and Point.y contains the velocity y value + */ + public velocityFromAngle(angle: number, speed: number): Point { + + if (isNaN(speed)) + { + speed = 0; + } + + var a: number = this._game.math.degreesToRadians(angle); + + return new Point((Math.cos(a) * speed), (Math.sin(a) * speed)); + + } + + /** + * Sets the source Sprite x/y velocity so it will move directly towards the destination Sprite at the speed given (in pixels per second)
+ * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
+ * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
+ * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
+ * If you need the object to accelerate, see accelerateTowardsObject() instead + * Note: Doesn't take into account acceleration, maxVelocity or drag (if you set drag or acceleration too high this object may not move at all) + * + * @param {GameObject} source The Sprite on which the velocity will be set + * @param {GameObject} dest The Sprite where the source object will move to + * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms + */ + public moveTowardsObject(source:GameObject, dest:GameObject, speed:number= 60, maxTime:number = 0) + { + var a:number = this.angleBetween(source, dest); + + if (maxTime > 0) + { + var d:number = this.distanceBetween(source, dest); + + // We know how many pixels we need to move, but how fast? + speed = d / (maxTime / 1000); + } + + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + + } + + /** + * Sets the x/y acceleration on the source Sprite so it will move towards the destination Sprite at the speed given (in pixels per second)
+ * You must give a maximum speed value, beyond which the Sprite won't go any faster.
+ * If you don't need acceleration look at moveTowardsObject() instead. + * + * @param {GameObject} source The Sprite on which the acceleration will be set + * @param {GameObject} dest The Sprite where the source object will move towards + * @param {number} speed The speed it will accelerate in pixels per second + * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally + * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically + */ + public accelerateTowardsObject(source:GameObject, dest:GameObject, speed:number, xSpeedMax:number, ySpeedMax:number) + { + var a:number = this.angleBetween(source, dest); + + source.velocity.x = 0; + source.velocity.y = 0; + + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + + } + + /** + * Move the given Sprite towards the mouse pointer coordinates at a steady velocity + * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
+ * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
+ * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
+ * + * @param {GameObject} source The Sprite to move + * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms + */ + public moveTowardsMouse(source:GameObject, speed:number = 60, maxTime:number = 0) + { + var a:number = this.angleBetweenMouse(source); + + if (maxTime > 0) + { + var d:number = this.distanceToMouse(source); + + // We know how many pixels we need to move, but how fast? + speed = d / (maxTime / 1000); + } + + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + + } + + /** + * Sets the x/y acceleration on the source Sprite so it will move towards the mouse coordinates at the speed given (in pixels per second)
+ * You must give a maximum speed value, beyond which the Sprite won't go any faster.
+ * If you don't need acceleration look at moveTowardsMouse() instead. + * + * @param {GameObject} source The Sprite on which the acceleration will be set + * @param {number} speed The speed it will accelerate in pixels per second + * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally + * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically + */ + public accelerateTowardsMouse(source:GameObject, speed:number, xSpeedMax:number, ySpeedMax:number) + { + var a:number = this.angleBetweenMouse(source); + + source.velocity.x = 0; + source.velocity.y = 0; + + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + } + + /** + * Sets the x/y velocity on the source Sprite so it will move towards the target coordinates at the speed given (in pixels per second)
+ * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
+ * Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
+ * The source object doesn't stop moving automatically should it ever reach the destination coordinates.
+ * + * @param {GameObject} source The Sprite to move + * @param {Point} target The Point coordinates to move the source Sprite towards + * @param {number} speed The speed it will move, in pixels per second (default is 60 pixels/sec) + * @param {number} maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms + */ + public moveTowardsPoint(source:GameObject, target:Point, speed:number = 60, maxTime:number = 0) + { + var a:number = this.angleBetweenPoint(source, target); + + if (maxTime > 0) + { + var d:number = this.distanceToPoint(source, target); + + // We know how many pixels we need to move, but how fast? + speed = d / (maxTime / 1000); + } + + source.velocity.x = Math.cos(a) * speed; + source.velocity.y = Math.sin(a) * speed; + } + + /** + * Sets the x/y acceleration on the source Sprite so it will move towards the target coordinates at the speed given (in pixels per second)
+ * You must give a maximum speed value, beyond which the Sprite won't go any faster.
+ * If you don't need acceleration look at moveTowardsPoint() instead. + * + * @param {GameObject} source The Sprite on which the acceleration will be set + * @param {Point} target The Point coordinates to move the source Sprite towards + * @param {number} speed The speed it will accelerate in pixels per second + * @param {number} xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally + * @param {number} ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically + */ + public accelerateTowardsPoint(source:GameObject, target:Point, speed:number, xSpeedMax:number, ySpeedMax:number) + { + var a:number = this.angleBetweenPoint(source, target); + + source.velocity.x = 0; + source.velocity.y = 0; + + source.acceleration.x = Math.cos(a) * speed; + source.acceleration.y = Math.sin(a) * speed; + + source.maxVelocity.x = xSpeedMax; + source.maxVelocity.y = ySpeedMax; + } + + /** + * Find the distance (in pixels, rounded) between two Sprites, taking their origin into account + * + * @param {GameObject} a The first Sprite + * @param {GameObject} b The second Sprite + * @return {number} int Distance (in pixels) + */ + public distanceBetween(a:GameObject, b:GameObject):number + { + var dx:number = (a.x + a.origin.x) - (b.x + b.origin.x); + var dy:number = (a.y + a.origin.y) - (b.y + b.origin.y); + + return this._game.math.vectorLength(dx, dy); + + } + + /** + * Find the distance (in pixels, rounded) from an Sprite to the given Point, taking the source origin into account + * + * @param {GameObject} a The Sprite + * @param {Point} target The Point + * @return {number} Distance (in pixels) + */ + public distanceToPoint(a:GameObject, target:Point):number + { + var dx:number = (a.x + a.origin.x) - (target.x); + var dy:number = (a.y + a.origin.y) - (target.y); + + return this._game.math.vectorLength(dx, dy); + } + + /** + * Find the distance (in pixels, rounded) from the object x/y and the mouse x/y + * + * @param {GameObject} a Sprite to test against + * @return {number} The distance between the given sprite and the mouse coordinates + */ + public distanceToMouse(a:GameObject):number + { + var dx: number = (a.x + a.origin.x) - this._game.input.x; + var dy: number = (a.y + a.origin.y) - this._game.input.y; + + return this._game.math.vectorLength(dx, dy); + } + + /** + * Find the angle (in radians) between an Sprite and an Point. The source sprite takes its x/y and origin into account. + * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) + * + * @param {GameObject} a The Sprite to test from + * @param {Point} target The Point to angle the Sprite towards + * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true + * + * @return {number} The angle (in radians unless asDegrees is true) + */ + public angleBetweenPoint(a:GameObject, target:Point, asDegrees:bool = false):number + { + var dx:number = (target.x) - (a.x + a.origin.x); + var dy:number = (target.y) - (a.y + a.origin.y); + + if (asDegrees) + { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } + else + { + return Math.atan2(dy, dx); + } + } + + /** + * Find the angle (in radians) between the two Sprite, taking their x/y and origin into account. + * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) + * + * @param {GameObject} a The Sprite to test from + * @param {GameObject} b The Sprite to test to + * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true + * + * @return {number} The angle (in radians unless asDegrees is true) + */ + public angleBetween(a:GameObject, b:GameObject, asDegrees:bool = false):number + { + var dx:number = (b.x + b.origin.x) - (a.x + a.origin.x); + var dy:number = (b.y + b.origin.y) - (a.y + a.origin.y); + + if (asDegrees) + { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } + else + { + return Math.atan2(dy, dx); + } + } + + /** + * Given the GameObject and speed calculate the velocity and return it as an Point based on the direction the sprite is facing + * + * @param {GameObject} parent The Sprite to get the facing value from + * @param {number} speed The speed it will move, in pixels per second sq + * + * @return {Point} An Point where Point.x contains the velocity x value and Point.y contains the velocity y value + */ + public velocityFromFacing(parent:GameObject, speed:number):Point + { + var a:number; + + if (parent.facing == Collision.LEFT) + { + a = this._game.math.degreesToRadians(180); + } + else if (parent.facing == Collision.RIGHT) + { + a = this._game.math.degreesToRadians(0); + } + else if (parent.facing == Collision.UP) + { + a = this._game.math.degreesToRadians(-90); + } + else if (parent.facing == Collision.DOWN) + { + a = this._game.math.degreesToRadians(90); + } + + return new Point(Math.cos(a) * speed, Math.sin(a) * speed); + + } + + /** + * Find the angle (in radians) between an Sprite and the mouse, taking their x/y and origin into account. + * The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative) + * + * @param {GameObject} a The Object to test from + * @param {boolean} asDegrees If you need the value in degrees instead of radians, set to true + * + * @return {number} The angle (in radians unless asDegrees is true) + */ + public angleBetweenMouse(a:GameObject, asDegrees:bool = false):number + { + // In order to get the angle between the object and mouse, we need the objects screen coordinates (rather than world coordinates) + var p:MicroPoint = a.getScreenXY(); + + var dx:number = a._game.input.x - p.x; + var dy:number = a._game.input.y - p.y; + + if (asDegrees) + { + return this._game.math.radiansToDegrees(Math.atan2(dy, dx)); + } + else + { + return Math.atan2(dy, dx); + } + } + + } + +} diff --git a/todo/phaser clean up/N_tutorialAsrc.zip b/todo/phaser clean up/N_tutorialAsrc.zip new file mode 100644 index 00000000..15f6b503 Binary files /dev/null and b/todo/phaser clean up/N_tutorialAsrc.zip differ diff --git a/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.fla b/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.fla new file mode 100644 index 00000000..1a82aade Binary files /dev/null and b/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.fla differ diff --git a/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.swf b/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.swf new file mode 100644 index 00000000..4f6b991c Binary files /dev/null and b/todo/phaser clean up/N_tutorialAsrc/tutA_demo05.swf differ diff --git a/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.fla b/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.fla new file mode 100644 index 00000000..f60efeb4 Binary files /dev/null and b/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.fla differ diff --git a/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.swf b/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.swf new file mode 100644 index 00000000..f5627908 Binary files /dev/null and b/todo/phaser clean up/N_tutorialBsrc/tutB_demo04.swf differ diff --git a/todo/phaser clean up/OldSprite.ts b/todo/phaser clean up/OldSprite.ts new file mode 100644 index 00000000..1e28f513 --- /dev/null +++ b/todo/phaser clean up/OldSprite.ts @@ -0,0 +1,355 @@ +/// +/// +/// +/// + +/** +* Phaser - Sprite +* +* The Sprite GameObject is an extension of the core GameObject that includes support for animation and dynamic textures. +* It's probably the most used GameObject of all. +*/ + +module Phaser { + + export class OldSprite { + + /** + * Sprite constructor + * Create a new Sprite. + * + * @param game {Phaser.Game} Current game instance. + * @param [x] {number} the initial x position of the sprite. + * @param [y] {number} the initial y position of the sprite. + * @param [key] {string} Key of the graphic you want to load for this sprite. + * @param [width] {number} The width of the object. + * @param [height] {number} The height of the object. + */ + constructor(game: Game, x?: number = 0, y?: number = 0, key?: string = null, width?: number = 16, height?: number = 16) { + + this.canvas = game.stage.canvas; + this.context = game.stage.context; + + this.frameBounds = new Rectangle(x, y, width, height); + this.exists = true; + this.active = true; + this.visible = true; + this.alive = true; + this.isGroup = false; + this.alpha = 1; + this.scale = new MicroPoint(1, 1); + + this.last = new MicroPoint(x, y); + this.align = GameObject.ALIGN_TOP_LEFT; + this.mass = 1; + this.elasticity = 0; + this.health = 1; + this.immovable = false; + this.moves = true; + this.worldBounds = null; + + this.touching = Collision.NONE; + this.wasTouching = Collision.NONE; + this.allowCollisions = Collision.ANY; + + this.velocity = new MicroPoint(); + this.acceleration = new MicroPoint(); + this.drag = new MicroPoint(); + this.maxVelocity = new MicroPoint(10000, 10000); + + this.angle = 0; + this.angularVelocity = 0; + this.angularAcceleration = 0; + this.angularDrag = 0; + this.maxAngular = 10000; + + this.cameraBlacklist = []; + this.scrollFactor = new MicroPoint(1, 1); + this.collisionMask = new CollisionMask(game, this, x, y, width, height); + + + this._texture = null; + + this.animations = new AnimationManager(this._game, this); + + if (key !== null) + { + this.cacheKey = key; + this.loadGraphic(key); + } + else + { + this.frameBounds.width = 16; + this.frameBounds.height = 16; + } + + } + + /** + * The essential reference to the main game object + */ + public _game: Game; + + /** + * Controls whether update() is automatically called by State/Group. + */ + public active: bool; + + /** + * Controls whether draw() is automatically called by State/Group. + */ + public visible: bool; + + /** + * Setting this to true will prevent the object from being updated during the main game loop (you will have to call update on it yourself) + */ + public ignoreGlobalUpdate: bool; + + /** + * Setting this to true will prevent the object from being rendered during the main game loop (you will have to call render on it yourself) + */ + public ignoreGlobalRender: bool; + + /** + * An Array of Cameras to which this GameObject won't render + * @type {Array} + */ + public cameraBlacklist: number[]; + + /** + * Orientation of the object. + * @type {number} + */ + public facing: number; + + /** + * Set alpha to a number between 0 and 1 to change the opacity. + * @type {number} + */ + public alpha: number; + + /** + * Scale factor of the object. + * @type {MicroPoint} + */ + public scale: MicroPoint; + + /** + * Controls if the GameObject is rendered rotated or not. + * If renderRotation is false then the object can still rotate but it will never be rendered rotated. + * @type {boolean} + */ + public renderRotation: bool = true; + + /** + * A point that can store numbers from 0 to 1 (for X and Y independently) + * which governs how much this object is affected by the camera . + * @type {MicroPoint} + */ + public scrollFactor: MicroPoint; + + /** + * Rectangle container of this object. + * @type {Rectangle} + */ + public frameBounds: Rectangle; + + /** + * This objects CollisionMask + * @type {CollisionMask} + */ + public collisionMask: CollisionMask; + + /** + * A rectangular area which is object is allowed to exist within. If it travels outside of this area it will perform the outOfBoundsAction. + * @type {Quad} + */ + public worldBounds: Quad; + + /** + * A reference to the Canvas this GameObject will render to + * @type {HTMLCanvasElement} + */ + public canvas: HTMLCanvasElement; + + /** + * A reference to the Canvas Context2D this GameObject will render to + * @type {CanvasRenderingContext2D} + */ + public context: CanvasRenderingContext2D; + + /** + * Texture of this sprite to be rendered. + */ + private _texture; + + /** + * Texture of this sprite is DynamicTexture? (default to false) + * @type {boolean} + */ + private _dynamicTexture: bool = false; + + + /** + * This manages animations of the sprite. You can modify animations though it. (see AnimationManager) + * @type AnimationManager + */ + public animations: AnimationManager; + + /** + * The cache key that was used for this texture (if any) + */ + public cacheKey: string; + + + /** + * Flip the graphic horizontally? (defaults to false) + * @type {boolean} + */ + public flipped: bool = false; + + + + /** + * Pre-update is called right before update() on each object in the game loop. + */ + public preUpdate() { + + this.last.x = this.frameBounds.x; + this.last.y = this.frameBounds.y; + + this.collisionMask.preUpdate(); + + } + + /** + * Override this function to update your class's position and appearance. + */ + public update() { + } + + /** + * Automatically called after update() by the game loop. + */ + public postUpdate() { + + this.animations.update(); + + if (this.moves) + { + this.updateMotion(); + } + + if (this.worldBounds != null) + { + if (this.outOfBoundsAction == GameObject.OUT_OF_BOUNDS_KILL) + { + if (this.x < this.worldBounds.x || this.x > this.worldBounds.right || this.y < this.worldBounds.y || this.y > this.worldBounds.bottom) + { + this.kill(); + } + } + else + { + if (this.x < this.worldBounds.x) + { + this.x = this.worldBounds.x; + } + else if (this.x > this.worldBounds.right) + { + this.x = this.worldBounds.right; + } + + if (this.y < this.worldBounds.y) + { + this.y = this.worldBounds.y; + } + else if (this.y > this.worldBounds.bottom) + { + this.y = this.worldBounds.bottom; + } + } + } + + this.collisionMask.update(); + + if (this.inputEnabled) + { + this.updateInput(); + } + + this.wasTouching = this.touching; + this.touching = Collision.NONE; + + } + + /** + * Clean up memory. + */ + public destroy() { + } + + public set width(value:number) { + this.frameBounds.width = value; + } + + public set height(value:number) { + this.frameBounds.height = value; + } + + public get width(): number { + return this.frameBounds.width; + } + + public get height(): number { + return this.frameBounds.height; + } + + public set frame(value: number) { + this.animations.frame = value; + } + + public get frame(): number { + return this.animations.frame; + } + + public set frameName(value: string) { + this.animations.frameName = value; + } + + public get frameName(): string { + return this.animations.frameName; + } + + /** + * Handy for "killing" game objects. + * Default behavior is to flag them as nonexistent AND dead. + * However, if you want the "corpse" to remain in the game, + * like to animate an effect or whatever, you should override this, + * setting only alive to false, and leaving exists true. + */ + public kill() { + this.alive = false; + this.exists = false; + } + + /** + * Handy for bringing game objects "back to life". Just sets alive and exists back to true. + * In practice, this is most often called by Object.reset(). + */ + public revive() { + this.alive = true; + this.exists = true; + } + + /** + * Convert object to readable string name. Useful for debugging, save games, etc. + */ + public toString(): string { + return ""; + } + + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/Properties.js b/todo/phaser clean up/Properties.js new file mode 100644 index 00000000..2f9b5688 --- /dev/null +++ b/todo/phaser clean up/Properties.js @@ -0,0 +1,19 @@ +var Shapes; +(function (Shapes) { + + var Point = Shapes.Point = (function () { + function Point(x, y) { + this.x = x; + this.y = y; + } + Point.prototype.getDist = function () { + return Math.sqrt((this.x * this.x) + (this.y * this.y)); + }; + Point.origin = new Point(0, 0); + return Point; + })(); + +})(Shapes || (Shapes = {})); + +var p = new Shapes.Point(3, 4); +var dist = p.getDist(); diff --git a/todo/phaser clean up/Properties.ts b/todo/phaser clean up/Properties.ts new file mode 100644 index 00000000..3171afe9 --- /dev/null +++ b/todo/phaser clean up/Properties.ts @@ -0,0 +1,36 @@ +/** +* Phaser - Components - Properties +* +* +*/ + +module Phaser.Components { + + export class Properties { + + /** + * Handy for storing health percentage or armor points or whatever. + * @type {number} + */ + public health: number; + + /** + * Reduces the "health" variable of this sprite by the amount specified in Damage. + * Calls kill() if health drops to or below zero. + * + * @param Damage {number} How much health to take away (use a negative number to give a health bonus). + */ + public hurt(damage: number) { + + this.health = this.health - damage; + + if (this.health <= 0) + { + //this.kill(); + } + + } + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/SAT.zip b/todo/phaser clean up/SAT.zip new file mode 100644 index 00000000..ac8a2960 Binary files /dev/null and b/todo/phaser clean up/SAT.zip differ diff --git a/todo/phaser clean up/Tilemap.js b/todo/phaser clean up/Tilemap.js new file mode 100644 index 00000000..08b7d6cc --- /dev/null +++ b/todo/phaser clean up/Tilemap.js @@ -0,0 +1,187 @@ +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + var Tilemap = (function (_super) { + __extends(Tilemap, _super); + function Tilemap(game, key, mapData, format, resizeWorld, tileWidth, tileHeight) { + if (typeof resizeWorld === "undefined") { resizeWorld = true; } + if (typeof tileWidth === "undefined") { tileWidth = 0; } + if (typeof tileHeight === "undefined") { tileHeight = 0; } + _super.call(this, game); + this.collisionCallback = null; + this.isGroup = false; + this.tiles = []; + this.layers = []; + this.mapFormat = format; + switch(format) { + case Tilemap.FORMAT_CSV: + this.parseCSV(game.cache.getText(mapData), key, tileWidth, tileHeight); + break; + case Tilemap.FORMAT_TILED_JSON: + this.parseTiledJSON(game.cache.getText(mapData), key); + break; + } + if(this.currentLayer && resizeWorld) { + this._game.world.setSize(this.currentLayer.widthInPixels, this.currentLayer.heightInPixels, true); + } + } + Tilemap.FORMAT_CSV = 0; + Tilemap.FORMAT_TILED_JSON = 1; + Tilemap.prototype.update = function () { + }; + Tilemap.prototype.render = function (camera, cameraOffsetX, cameraOffsetY) { + if(this.cameraBlacklist.indexOf(camera.ID) == -1) { + for(var i = 0; i < this.layers.length; i++) { + this.layers[i].render(camera, cameraOffsetX, cameraOffsetY); + } + } + }; + Tilemap.prototype.parseCSV = function (data, key, tileWidth, tileHeight) { + var layer = new Phaser.TilemapLayer(this._game, this, key, Phaser.Tilemap.FORMAT_CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight); + data = data.trim(); + var rows = data.split("\n"); + for(var i = 0; i < rows.length; i++) { + var column = rows[i].split(","); + if(column.length > 0) { + layer.addColumn(column); + } + } + layer.updateBounds(); + var tileQuantity = layer.parseTileOffsets(); + this.currentLayer = layer; + this.collisionLayer = layer; + this.layers.push(layer); + this.generateTiles(tileQuantity); + }; + Tilemap.prototype.parseTiledJSON = function (data, key) { + data = data.trim(); + var json = JSON.parse(data); + for(var i = 0; i < json.layers.length; i++) { + var layer = new Phaser.TilemapLayer(this._game, this, key, Phaser.Tilemap.FORMAT_TILED_JSON, json.layers[i].name, json.tilewidth, json.tileheight); + layer.alpha = json.layers[i].opacity; + layer.visible = json.layers[i].visible; + layer.tileMargin = json.tilesets[0].margin; + layer.tileSpacing = json.tilesets[0].spacing; + var c = 0; + var row; + for(var t = 0; t < json.layers[i].data.length; t++) { + if(c == 0) { + row = []; + } + row.push(json.layers[i].data[t]); + c++; + if(c == json.layers[i].width) { + layer.addColumn(row); + c = 0; + } + } + layer.updateBounds(); + var tileQuantity = layer.parseTileOffsets(); + this.currentLayer = layer; + this.collisionLayer = layer; + this.layers.push(layer); + } + this.generateTiles(tileQuantity); + }; + Tilemap.prototype.generateTiles = function (qty) { + for(var i = 0; i < qty; i++) { + this.tiles.push(new Phaser.Tile(this._game, this, i, this.currentLayer.tileWidth, this.currentLayer.tileHeight)); + } + }; + Object.defineProperty(Tilemap.prototype, "widthInPixels", { + get: function () { + return this.currentLayer.widthInPixels; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Tilemap.prototype, "heightInPixels", { + get: function () { + return this.currentLayer.heightInPixels; + }, + enumerable: true, + configurable: true + }); + Tilemap.prototype.setCollisionCallback = function (context, callback) { + this.collisionCallbackContext = context; + this.collisionCallback = callback; + }; + Tilemap.prototype.setCollisionRange = function (start, end, collision, resetCollisions, separateX, separateY) { + if (typeof collision === "undefined") { collision = Phaser.Collision.ANY; } + if (typeof resetCollisions === "undefined") { resetCollisions = false; } + if (typeof separateX === "undefined") { separateX = true; } + if (typeof separateY === "undefined") { separateY = true; } + for(var i = start; i < end; i++) { + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); + } + }; + Tilemap.prototype.setCollisionByIndex = function (values, collision, resetCollisions, separateX, separateY) { + if (typeof collision === "undefined") { collision = Phaser.Collision.ANY; } + if (typeof resetCollisions === "undefined") { resetCollisions = false; } + if (typeof separateX === "undefined") { separateX = true; } + if (typeof separateY === "undefined") { separateY = true; } + for(var i = 0; i < values.length; i++) { + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); + } + }; + Tilemap.prototype.getTileByIndex = function (value) { + if(this.tiles[value]) { + return this.tiles[value]; + } + return null; + }; + Tilemap.prototype.getTile = function (x, y, layer) { + if (typeof layer === "undefined") { layer = 0; } + return this.tiles[this.layers[layer].getTileIndex(x, y)]; + }; + Tilemap.prototype.getTileFromWorldXY = function (x, y, layer) { + if (typeof layer === "undefined") { layer = 0; } + return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; + }; + Tilemap.prototype.getTileFromInputXY = function (layer) { + if (typeof layer === "undefined") { layer = 0; } + return this.tiles[this.layers[layer].getTileFromWorldXY(this._game.input.getWorldX(), this._game.input.getWorldY())]; + }; + Tilemap.prototype.getTileOverlaps = function (object) { + return this.currentLayer.getTileOverlaps(object); + }; + Tilemap.prototype.collide = function (objectOrGroup, callback, context) { + if (typeof objectOrGroup === "undefined") { objectOrGroup = null; } + if (typeof callback === "undefined") { callback = null; } + if (typeof context === "undefined") { context = null; } + if(callback !== null && context !== null) { + this.collisionCallback = callback; + this.collisionCallbackContext = context; + } + if(objectOrGroup == null) { + objectOrGroup = this._game.world.group; + } + if(objectOrGroup.isGroup == false) { + this.collideGameObject(objectOrGroup); + } else { + objectOrGroup.forEachAlive(this, this.collideGameObject, true); + } + }; + Tilemap.prototype.collideGameObject = function (object) { + if(object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Phaser.Collision.NONE) { + this._tempCollisionData = this.collisionLayer.getTileOverlaps(object); + if(this.collisionCallback !== null && this._tempCollisionData.length > 0) { + this.collisionCallback.call(this.collisionCallbackContext, object, this._tempCollisionData); + } + return true; + } else { + return false; + } + }; + Tilemap.prototype.putTile = function (x, y, index, layer) { + if (typeof layer === "undefined") { layer = 0; } + this.layers[layer].putTile(x, y, index); + }; + return Tilemap; + })(Phaser.GameObject); + Phaser.Tilemap = Tilemap; +})(Phaser || (Phaser = {})); diff --git a/todo/phaser clean up/Tilemap.ts b/todo/phaser clean up/Tilemap.ts new file mode 100644 index 00000000..746d7df5 --- /dev/null +++ b/todo/phaser clean up/Tilemap.ts @@ -0,0 +1,434 @@ +/// +/// +/// + +/** +* Phaser - Tilemap +* +* This GameObject allows for the display of a tilemap within the game world. Tile maps consist of an image, tile data and a size. +* Internally it creates a TilemapLayer for each layer in the tilemap. +*/ + +module Phaser { + + export class Tilemap { + + /** + * Tilemap constructor + * Create a new Tilemap. + * + * @param game {Phaser.Game} Current game instance. + * @param key {string} Asset key for this map. + * @param mapData {string} Data of this map. (a big 2d array, normally in csv) + * @param format {number} Format of this map data, available: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. + * @param resizeWorld {boolean} Resize the world bound automatically based on this tilemap? + * @param tileWidth {number} Width of tiles in this map. + * @param tileHeight {number} Height of tiles in this map. + */ + constructor(game: Game, key: string, mapData: string, format: number, resizeWorld: bool = true, tileWidth?: number = 0, tileHeight?: number = 0) { + + //super(game); + + this.isGroup = false; + + this.tiles = []; + this.layers = []; + + this.mapFormat = format; + + switch (format) + { + case Tilemap.FORMAT_CSV: + this.parseCSV(game.cache.getText(mapData), key, tileWidth, tileHeight); + break; + + case Tilemap.FORMAT_TILED_JSON: + this.parseTiledJSON(game.cache.getText(mapData), key); + break; + } + + if (this.currentLayer && resizeWorld) + { + this._game.world.setSize(this.currentLayer.widthInPixels, this.currentLayer.heightInPixels, true); + } + + } + + private _tempCollisionData; + + /** + * Tilemap data format enum: CSV. + * @type {number} + */ + public static FORMAT_CSV: number = 0; + /** + * Tilemap data format enum: Tiled JSON. + * @type {number} + */ + public static FORMAT_TILED_JSON: number = 1; + + /** + * Array contains tile objects of this map. + * @type {Tile[]} + */ + public tiles : Tile[]; + /** + * Array contains tilemap layer objects of this map. + * @type {TilemapLayer[]} + */ + public layers : TilemapLayer[]; + /** + * Current tilemap layer. + * @type {TilemapLayer} + */ + public currentLayer: TilemapLayer; + /** + * The tilemap layer for collision. + * @type {TilemapLayer} + */ + public collisionLayer: TilemapLayer; + /** + * Tilemap collision callback. + * @type {function} + */ + public collisionCallback = null; + /** + * Context for the collision callback called with. + */ + public collisionCallbackContext; + /** + * Format of this tilemap data. Available values: Tilemap.FORMAT_CSV or Tilemap.FORMAT_TILED_JSON. + * @type {number} + */ + public mapFormat: number; + + /** + * Inherited update method. + */ + public update() { + } + + /** + * Render this tilemap to a specific camera with specific offset. + * @param camera {Camera} The camera this tilemap will be rendered to. + * @param cameraOffsetX {number} X offset of the camera. + * @param cameraOffsetY {number} Y offset of the camera. + */ + public render(camera: Camera, cameraOffsetX: number, cameraOffsetY: number) { + + if (this.cameraBlacklist.indexOf(camera.ID) == -1) + { + // Loop through the layers + for (var i = 0; i < this.layers.length; i++) + { + this.layers[i].render(camera, cameraOffsetX, cameraOffsetY); + } + } + + } + + /** + * Parset csv map data and generate tiles. + * @param data {string} CSV map data. + * @param key {string} Asset key for tileset image. + * @param tileWidth {number} Width of its tile. + * @param tileHeight {number} Height of its tile. + */ + private parseCSV(data: string, key: string, tileWidth: number, tileHeight: number) { + + var layer: TilemapLayer = new TilemapLayer(this._game, this, key, Tilemap.FORMAT_CSV, 'TileLayerCSV' + this.layers.length.toString(), tileWidth, tileHeight); + + // Trim any rogue whitespace from the data + data = data.trim(); + + var rows = data.split("\n"); + + for (var i = 0; i < rows.length; i++) + { + var column = rows[i].split(","); + + if (column.length > 0) + { + layer.addColumn(column); + } + } + + layer.updateBounds(); + var tileQuantity = layer.parseTileOffsets(); + + this.currentLayer = layer; + this.collisionLayer = layer; + + this.layers.push(layer); + + this.generateTiles(tileQuantity); + + } + + /** + * Parset JSON map data and generate tiles. + * @param data {string} JSON map data. + * @param key {string} Asset key for tileset image. + */ + private parseTiledJSON(data: string, key: string) { + + // Trim any rogue whitespace from the data + data = data.trim(); + + var json = JSON.parse(data); + + for (var i = 0; i < json.layers.length; i++) + { + var layer: TilemapLayer = new TilemapLayer(this._game, this, key, Tilemap.FORMAT_TILED_JSON, json.layers[i].name, json.tilewidth, json.tileheight); + + layer.alpha = json.layers[i].opacity; + layer.visible = json.layers[i].visible; + layer.tileMargin = json.tilesets[0].margin; + layer.tileSpacing = json.tilesets[0].spacing; + + var c = 0; + var row; + + for (var t = 0; t < json.layers[i].data.length; t++) + { + if (c == 0) + { + row = []; + } + + row.push(json.layers[i].data[t]); + + c++; + + if (c == json.layers[i].width) + { + layer.addColumn(row); + c = 0; + } + } + + layer.updateBounds(); + + var tileQuantity = layer.parseTileOffsets(); + + this.currentLayer = layer; + this.collisionLayer = layer; + + this.layers.push(layer); + + } + + this.generateTiles(tileQuantity); + + } + + /** + * Create tiles of given quantity. + * @param qty {number} Quentity of tiles to be generated. + */ + private generateTiles(qty:number) { + + for (var i = 0; i < qty; i++) + { + this.tiles.push(new Tile(this._game, this, i, this.currentLayer.tileWidth, this.currentLayer.tileHeight)); + } + + } + + public get widthInPixels(): number { + return this.currentLayer.widthInPixels; + } + + public get heightInPixels(): number { + return this.currentLayer.heightInPixels; + } + + // Tile Collision + + /** + * Set callback to be called when this tilemap collides. + * @param context {object} Callback will be called with this context. + * @param callback {function} Callback function. + */ + public setCollisionCallback(context, callback) { + + this.collisionCallbackContext = context; + this.collisionCallback = callback; + + } + + /** + * Set collision configs of tiles in a range index. + * @param start {number} First index of tiles. + * @param end {number} Last index of tiles. + * @param collision {number} Bit field of flags. (see Tile.allowCollision) + * @param resetCollisions {boolean} Reset collision flags before set. + * @param separateX {boolean} Enable seprate at x-axis. + * @param separateY {boolean} Enable seprate at y-axis. + */ + public setCollisionRange(start: number, end: number, collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { + + for (var i = start; i < end; i++) + { + this.tiles[i].setCollision(collision, resetCollisions, separateX, separateY); + } + + } + + /** + * Set collision configs of tiles with given index. + * @param values {number[]} Index array which contains all tile indexes. The tiles with those indexes will be setup with rest parameters. + * @param collision {number} Bit field of flags. (see Tile.allowCollision) + * @param resetCollisions {boolean} Reset collision flags before set. + * @param separateX {boolean} Enable seprate at x-axis. + * @param separateY {boolean} Enable seprate at y-axis. + */ + public setCollisionByIndex(values:number[], collision?:number = Collision.ANY, resetCollisions?: bool = false, separateX?: bool = true, separateY?: bool = true) { + + for (var i = 0; i < values.length; i++) + { + this.tiles[values[i]].setCollision(collision, resetCollisions, separateX, separateY); + } + + } + + // Tile Management + + /** + * Get the tile by its index. + * @param value {number} Index of the tile you want to get. + * @return {Tile} The tile with given index. + */ + public getTileByIndex(value: number):Tile { + + if (this.tiles[value]) + { + return this.tiles[value]; + } + + return null; + + } + + /** + * Get the tile located at specific position and layer. + * @param x {number} X position of this tile located. + * @param y {number} Y position of this tile located. + * @param [layer] {number} layer of this tile located. + * @return {Tile} The tile with specific properties. + */ + public getTile(x: number, y: number, layer?: number = 0):Tile { + + return this.tiles[this.layers[layer].getTileIndex(x, y)]; + + } + + /** + * Get the tile located at specific position (in world coordinate) and layer. (thus you give a position of a point which is within the tile) + * @param x {number} X position of the point in target tile. + * @param x {number} Y position of the point in target tile. + * @param [layer] {number} layer of this tile located. + * @return {Tile} The tile with specific properties. + */ + public getTileFromWorldXY(x: number, y: number, layer?: number = 0):Tile { + + return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; + + } + + public getTileFromInputXY(layer?: number = 0):Tile { + + return this.tiles[this.layers[layer].getTileFromWorldXY(this._game.input.getWorldX(), this._game.input.getWorldY())]; + + } + + /** + * Get tiles overlaps the given object. + * @param object {GameObject} Tiles you want to get that overlaps this. + * @return {array} Array with tiles informations. (Each contains x, y and the tile.) + */ + public getTileOverlaps(object: GameObject) { + + return this.currentLayer.getTileOverlaps(object); + + } + + // COLLIDE + /** + * Check whether this tilemap collides with the given game object or group of objects. + * @param objectOrGroup {function} Target object of group you want to check. + * @param callback {function} This is called if objectOrGroup collides the tilemap. + * @param context {object} Callback will be called with this context. + * @return {boolean} Return true if this collides with given object, otherwise return false. + */ + public collide(objectOrGroup = null, callback = null, context = null) { + + if (callback !== null && context !== null) + { + this.collisionCallback = callback; + this.collisionCallbackContext = context; + } + + if (objectOrGroup == null) + { + objectOrGroup = this._game.world.group; + } + + // Group? + if (objectOrGroup.isGroup == false) + { + this.collideGameObject(objectOrGroup); + } + else + { + objectOrGroup.forEachAlive(this, this.collideGameObject, true); + } + + } + + /** + * Check whether this tilemap collides with the given game object. + * @param object {GameObject} Target object you want to check. + * @return {boolean} Return true if this collides with given object, otherwise return false. + */ + public collideGameObject(object: GameObject): bool { + + if (object !== this && object.immovable == false && object.exists == true && object.allowCollisions != Collision.NONE) + { + this._tempCollisionData = this.collisionLayer.getTileOverlaps(object); + + if (this.collisionCallback !== null && this._tempCollisionData.length > 0) + { + this.collisionCallback.call(this.collisionCallbackContext, object, this._tempCollisionData); + } + + return true; + } + else + { + return false; + } + + } + + /** + * Set a tile to a specific layer. + * @param x {number} X position of this tile. + * @param y {number} Y position of this tile. + * @param index {number} The index of this tile type in the core map data. + * @param [layer] {number} which layer you want to set the tile to. + */ + public putTile(x: number, y: number, index: number, layer?: number = 0) { + + this.layers[layer].putTile(x, y, index); + + } + + // Set current layer + // Set layer order? + // Delete tiles of certain type + // Erase tiles + + } + +} \ No newline at end of file diff --git a/todo/phaser clean up/fitc/FITC_beyondHittest.txt b/todo/phaser clean up/fitc/FITC_beyondHittest.txt new file mode 100644 index 00000000..7170cc06 --- /dev/null +++ b/todo/phaser clean up/fitc/FITC_beyondHittest.txt @@ -0,0 +1,715 @@ +PART -1: intro/preamble + +[3]** + -download the slides at: [++TODO: URL to slides.. metanet/tutorials/fitc05.zip?] + +who am i + -one of two people who made N + -we've been using flash since v5 (i.e since actionscript was useful and easy to use) + +** + + -promo blurb: "N is a platform/run-and-jump game which combines oldschool gameplay + with modern collision detection + physics simulation" + + -we made N with flash prove that flash/actionscript could be used for "real video games" + + +[4]** -"the curse of sylvaniah" (by Strille/Luxregina) is another great game which also proves our point: + games made in flash can be "real video games" + + +[5]** -collision detection is a vast feild of research + -impossible to cover properly in an hour. + -i'll present specific methods well-suited for use in flash/actionscript + -some of the ideas we used in N, as well as some of the stuff we've learnt since making N + -there is a LOT of stuff to cover + + -i'm going to try to introduce you to the ideas involved without a lot of technical detail + -understand the concepts, independent from the implementation of the concept + -there WILL be some code and formulas and stuff + -it's more important to understand the meaning behind the code and formulas. + + -you can check out our online tutorials if you need to look at source. + + -warning: i'm a programmer. these slides count as "programmer art". + + -ask: + -how many people know what a vector is? + -how many people know what a dotproduct is? + + +[6]** +-to begin: + -this lecture is about collision detection + -implicitly, it's about game programming in flash + -game programming in flash feels a bit wrong. + -it certainly wasn't made for video games. + -there is a sense of "we're abusing this platform" + -but we're going to write games in flash anyway, so why not try to do it well. + -i'll start by trying to convince you about why collision detection matters + -then i'll discuss some solutions for dealing with collision detection + + + +MOTIVATION: + + -why does collision detection matter? is it only useful for ninja platformers? +[7]** -no. a lot of games would benefit from better collision detection/response + -everyone's favorite, the top-view racing game + -pool, pinball, + -basketball/sports (show McShitOut and/or scribball demo) + -platforming games +[8]** -most importantly: _new_ types of genre-defying games which you're going to invent next week +[9]** -having a good collision detection+response system enables a wide range of (otherwise inaccessible) gameplay possibilities. + + +[10]** +-HitTest: + -why do we need to learn about collision detection? + -doesn't flash already have built-in collision detection? + -yes, but it has two major weaknesses + 1: only returns a yes/no answer + 2: only works with axis-aligned rectangles + +[11]** -what can you do with a yes/no answer?: +[12]** -destroy one/both objects +[13]** -move one/both objects to old position +[14]** -reverse direction of one/both objects + -or any combination of the above + +[15]** -what can't you do with a yes/no answer?: +[16]** -friction and bounce +[17]** -sliding along obstacles +[18]** -rotating to orient to the ground + -etc. + +[19]** -what can you do with axis-aligned rectangles?: + -anything! ..as long as it involves unrotated rectangles + +[20]** -what can't you do with axis-aligned rectangles?: + -circles + -concave surfaces + -curved or angled surfaces + -rotating shapes + -etc. + +[21]** -using hittest, if you need more than a yes/no answer about a collision, you can use "tricks": + -sample multiple points on an object + -from the multiple yes/no results, infer information about the collision direction, etc. + + -if you work hard enough, you _might_ get it to work.. + +[22]** -it will be slower than it needs to be (due to the number of calls to hittest) + -it won't work 100% of the time (because you're relying on implied/approximated and not actual information) + -it just doesn't feel as solid as "the real thing" + + +[23]** -HitTest is awesome -- if you're living in the 80s + -lots of moving rectangles which explode when they touch each other + -if you want to actually respond to collisions in a meaningful way, HitTest isn't enough + -meaningful collision response requires meaningful information about the collision. + -better information, better response, better games, better "feel" + -ultimately, "real" (non-HitTest) collision detection enables "physics" + + +-physics?! +[24]** -in video games this year, "physics is the new graphics".. everyone wants more physics in their games. + -one common misconception is that "physics" means "slow and complex code". physics doesn't have to be complicated or slow. + +** + +[25]** -"physics" is a much-abused term. + -most of the time it just means "things moving". + -if you've ever written "position += velocity" or "newpos = oldpos + velocity", + you've already been using "physics". + -what are you afraid of? + +[26]** -the only math you need to know is some vector algebra/geometry which we'll cover + -no trig: trig is horrible + -it's very difficult to develop an intuition about it + -it's very hard to visualize when thinking about a problem + -vectors are your friends + -they can be used to replace trig in 99% of situations + -they are easy to visualize and develop intuitions about + +[27]** -most importantly: flash vs. SNES + -way less graphical power, way more processing power + + -flash can't compete with modern (or even classic) video games when it comes to graphical power + -consoles and PCs have dedicated graphics hardware; flash has only got the CPU + + -flash CAN compete in terms of processing power and memory + -the SNES (which came out in 1991) had the same processor as an apple IIgs.. + -even with the overhead of the flashplayer VM, flash outperforms the SNES in terms of both speed and complexity + -double-precision floating point, which lets us do a lot of things which simply weren't possible on an SNES + -because of the overhead of the flash VM, "expensive" ops like sqrt() aren't + really much more expensive than any other math op (+, -, etc.) + -this gives us flexibility to pursue different solutions (which might not be tractable on other platforms) + -flashplayer has built-in support for sophisticated structures like hashmaps (i.e "objects") + and dynamic memory management + -this makes it easier for us to explore complex ideas than possible on a SNES + +[28]** -so: flash games have the ability to compete with modern (or classic) video games on the battlefield known as "gameplay" + -collision detection and physics are two great tools for developing novel gameplay + +[29]** -some examples of games that are using collision detection and physics to create different types of gameplay +[30]** +[31]** +[32]** + + -hopefully you're now motivated + + +[33]** +PART 0: collision response + +-collision response is important to mention breifly because it provides the motivation for collision detection + -see our website for tutorials and links to more info + +-collision response is _why_ you need a collision detection solution that's more informative than HitTest + -collision detection provides you with information describing a collision; + -if you don't _use_ this information in a meaningful way, CD is moot + +[34]** -collision response "in a nutshell": + -there are many different ways to handle collision response + -they all of the need to know the same thing + +[35]** -penetration direction, penetration amount + -pdir*pamt = penetration vector, projection vector, etc. + -i may use "penetration vector" and "projection vector" interchangeably + +[36]** -one thing we want to do when notified of a collision is prevent the two colliding objects from continuing to collide + -a simple solution to this is "projection" + -given two colliding objects and their penetration vector + -translate (move) one object by the penetration vector + -OR: translate both objects by 1/2 the penetration vector + -etc.. playing with the ratios allows the simulation of objects with different relative mass + -i.e heavy objects aren't moved very much + +[37]** -aside from preventing the objects from continuing to overlap, you can modify their properties based on the direction of the collision surface + -penetration vector describes the surface direction of the collision + -you can measure the velocity of objects parallel to and perpendicular to the surface + -we'll cover the math later, it's simple vector projection +[38]** -you break the object's velocity down into those two components + -scale each component seperately/differently, and recombine them to get the post-collision velocity + -this lets you simulate friction/bounce + + -you can also let game logic make decisions based collision information + -rotating object graphics so that they align themselves to the ground + -inflicting damage for violent collisions + (the object's velocity perpendicular to the surface tells you how violent the collision was) + +** +** + + + + +[39]** +PART 1: narrow-phase collision detection, aka object-vs-object testing + +[40]** -so, what we need to do when performing collision detection on a pair of shapes is: + (a) determine if the two shapes are colliding, and if so + (b) calculate the penetration vector which describes the collision + +[41]** -actually.. that's only half of the collision detection problem. + -the other half is knowing which pairs of shapes to test in the first place + +[42]** -these two parts of the collision detection process are often called "narrow" and "broad" phase collision detection + -broad = figuring out which pairs of objects we must test + -narrow = testing each pair + -sort of like sifting through something: start with a coarse filter to elliminate most things, then use a fine-grained filter on the remaining stuff + +-we'll start with narrow-phase and then discuss broad-phase + +-i'll try to answer questions breifly after each section +-i'd prefer to stick to specific questions (i.e if i'm not explaining something in detail) +-leave larger questions for Q&A at the end of the presentation + + +-there are several different approaches to object-vs-object tests; the one i'm going to describe is based on something called "the method of seperating axes" + -it's well-suited for flash + -fast (faster than any other method we've seen) + -simple (easy to understand, easy to code) + -flexible (easy to adapt to many different situations) + +[43]** +-the method of seperating axes, in english + -there will be a lot of "axis" talk + -axis really just means "direction". any time you hear "axis", you can mentally substitute "direction". + -1 vector, many vertices + -1 axis, many axes + +[44]** -the Method of Seperating Axes is based on the Seperating Axis Theorem: + -a mathmatical theorem about the properties of convex polygons +[45]** -thus, this method only works for convex shapes + -what is a convex shape? probably you've at least got an intuitive understanding of convexity. + there are many "practical" definitions: + -no internal angles <= 180deg + -a line connecting any two points inside the shape is contained within the shape + -only lefthand turns when walking CCW around the surface + -etc. + + +[46]** -Seperating Axis Theorem, part 1: + -if two convex polygons don't overlap, then there must be a direction along which they are "seperated" + -the converse is also true: if there is no direction which seperates two objects, the objects must be overlapping + -"seperated" means that, if we look at the world along that direction, there is a space between the two shapes + -the direction we look along is called an axis + -if the two objects are seperated, it's a seperating axis + -you could think of it as a line through the objects; it's offset in diagrams to make it less cluttered/confusing + + + +[47]** -Seperating Axis Theorem, part 2: + -we don't have to test _all_ directions; if there is a seperating axis, then it must be perpendicular to an edge of one of the shapes + -so, instead of testing all possible directions to find a seperating axis, + we only test those directions which are perpendicular to the edges of our polygons + + + -there's a proof for the SAT, but that's (thankfully) outside the scope of this lecture + -we didn't invent this method, we just noticed that other game programmers were using it + + +[48]** -the Method of Seperating Axes is a method of collision detection which is based on SAT: + -given two shapes, determine all the potentially seperating axes for those shapes + -i.e determine the directions which are perpendicular to each edges of each polygon + -test each potentially seperating axis until we find an axis along which the objects are seperated + -if we've tested all potentially seperating axes and found none that are seperating the objects, + the objects are colliding + + -instead of a single, complicated 2D test (based on minimum distance or whatever), we perform a series of simple 1D tests + -each test is along a different direction, or "axis" + -each test boils down to a few lines of very simple (and fast) math + -we'll get to the math shortly + + -the strength of this method is the "early out" + -we don't need to test every potentially seperating axis; as soon as we find an axis which seperates the two shapes, + we know (thanks to the SAT) they can't be colliding and we can skip the rest of the axes + -this means when two objects aren't colliding, we do less work than when they are + -in a typical game, any two objects picked at random are likely NOT colliding, + so optimising the not-colliding case makes sense and really speeds things up + + + +[49]** -before we get into actual code, let's review some basic vector algebra/geometry + -basic vector math: nothing to be afraid of + +[50]** -vectors (difference of two points) + +[51]** -unit vectors/direction + -unit vectors: vectors whose length is 1 + -very useful to use these to represent directions, INSTEAD of angles/radians/etc. + -they've got some useful properties which we'll see in a moment + -formula/code: + -basically, you divide x and y components by the length of the vector + -null (0-length) vectors don't work + -when you multiple a unit vector by a scalar X, the result is a vector which points in the same + direction as the unit vector, but which is X units long + -in this presentation, whenever you hear "axis" (and/or "direction"), this means normalized unit vector + -the process of taking a vector and scaling it until it's unit-length is called "normalization" + -the vector has been normalized + -NOT to be confused with the following + + +[52]** -normal vectors (NOT to be confused with normalized vectors! althouth.. normal vectors are often normalized..) + -perpendicular to a vector + -formula/code: it's so simple it's weird that the results are meaningful/useful + -each vector has two of these, LH and RH + +[53]** -by convention, polygon edges are arranged in CCW order + -this means that each edge's RHnorm points "out" of the polygon edge + -these are of special importance to us: the RHnorms of each edge of a polygon _are_ the potentially seperating axes we must test + + +[54]** -dotproduct + -in highschool you may have learnt a definition of dotprod involving cosine: + -AdotB = |A|*|B|*cos(angle between A and B) + -this is irrelevant + -dotprod is a measure of the relative directions of two vectors + -formula/code: as with normal vectors, it's so simple it's weird that the results are meaningful/useful + -the important thing to note is that: + -when two vectors point in the same direction, their dp is at maximum value + -then they point in opposite directions, their dp is at minimum value + -when they're perpendicular to each other, their dp is 0 + +[55]** -if vector A is unit length and vector B isn't, the min/max values of their dp are -/+ the length of B + -the value of the dp is no longer just some value, it's now "meaningful": + -it's the _length_ of vector B when "measured along" the direction described by vector A + -"measured along, viewed along, the length of the image of B on A", etc. + -this is important for projection + + +[56]** -vector projection + -just one multiplication step added onto the end of a dotprod + -as we just learnt, when vector A is unit length, and vector B isn't, + AdotB gives you the length of the "image" of B, when measured along A + + -we can make a vector of length AdotB, which is parallel to A + -just multiply A (unit vector) by AdotB + -that vector is called the projection of B onto A + -you can think of it as "B when viewed/measured along the line containing A" + -mapping 2D to 1D + +[57]** -this is the core of our method: projecting shapes onto each potentially seperating axis + -if we only care about the length of the projected vector (i.e the dotproduct), we can use the dotproduct itself + -this is also what we use for friction/bounce + -as we saw earlier, you decompose object velocity into two perp. components + by projecting them onto the surface direction + + +[58]** -the method of seperating axes, revisited + -the if(overlap) step is "where all the action is" + +[59]** -example: Box vs. Box + -figure out which axes we have to test + -aside: normally, testing two quadrilaterals would involve testing 8 potentially seperating axes + (4 from each quad) + -boxes are a special case which exploit the SAT's rules + -we need to test all directions which are perpendicular to the surface of each shape + -since, for each box, a single direction is perp. to two surfaces, + we can perform a single axis test for each pair of opposite box edges + -anyway: + -calculate the object-object delta vector (vector from center of one object ot center of the other) + -for each potentially seperating axis (show all 4 axes, then show each step below on a single axis): + -calculate the projected halfsize of each object (projected = when measured along the axis) (BLUE) + -calculate the projected length of the delta vector (projected = when measured along the axis) (GREEN) + -calculate the difference between the sum of the halfsizes, and the delta vector + -if negative, we've found a seperating axis and we can stop: the shapes don't overlap + -if positive, objects overlap along the axis, continue testing (RED) +[60]** -if, after testing each axis, we still haven't found a seperating axis, then the objects must be colliding + + +[61]** -what??!? i'm confused.. + -why are we using this weird concept of "halfsize"? + -don't we still end up with only a yes/no answer? i thought this approach was supposed to give us more info + -..wait a minute. + -this might seem familiar. + +[62]** -one very useful insight for understanding this approach is that it's a generalisation of circle-circle collision detection + (which everyone is probably familiar with) + +[63]** -circle-circle + -calculate the object-object delta vector + -for each potentially seperating axis (in this case, there's only one: the axis which passes through both circles' centers) + -calculate distance between circles + -SAT: calculate the projected size of the delta vector (in this case, this is == the length of the delta vector) + -calculate sum of radii + -SAT: calculate the projected halfsize of each object (in this case, this is == their radius) + -calculate the difference between the sum of halfsizes/radii and the delta vector + -if positive, objects overlap along the axis + +[64]** -not only does this help us understand what's going on in our SAT test, + it also suggests how to get more than a yes/no answer from the results + -do whatever we do in the circle-circle case + + -if the circles ARE overlapping + -the penetration direction is parallel to the axis + -the amount is equal to the difference we calculated (sum-of-radii minus size-of-delta) + +[65]** -Box-vs-Box revisited + -it's the same process as with two circles, except that we have to test more than just a single potentially seperating axis + -each axis test is analogous to the circle-circle test, they just involve a bit more math, to project things onto the axis.. + -in the circle case, the object halfwidths and delta vectors are parallel to the axis + -as we saw with dotprods, when you project a vector onto an axis that's parallel to the vector, you get the + original vector + -like multiplying any number by 1 + -so, for circles, the values are basically "pre-projected" and we skip the projection math + -as soon as we find a seperating axis, we're done -- we know the objects don't overlap + +[66]** -as with circles, if the objects ARE overlapping (i.e we tested all axes and none are seperating the two objects), + we can extract collision info "for free" from the per-axis calculations we made + -the axis with the smallest "difference" (sum-of-halfsizes minus size-of-delta) is the projection direction + -the size of the difference is the projection amount + -projection direction + projection amount = projection vector. tada! + + +[67]** -so, we can use this method to collide any two shapes together, provided that for each shape we can quickly determine: + a) which directions are perpendicular to the shape's surface + (i.e which axes are potentially seperating directions for that shape) + b) how to project the shape onto an axis, i.e + -the shape's center (when projected onto an axis) + -the shape's size/halfsize (when projected onto an axis) + + +[68]** -example: projecting a box onto an axis + -this is code for an arbitrarily rotated box (in the diagrams, we're just keeping the box still and moving the axis..) + -it's the same thing/relativity + -an important insight for box projection is: + the sum of the projections of the halfwidth vectors = projected halfwidth of box + + +[69]** -box representation: + -position (of box center) + -width/height (along object axes) + -unit vector (describing orientation of box; we use the convention that the orientation vector points along local x) + + -see our tutorials for examples of how to project different shapes + -linesegs should be obvious + + +[70]** -step through Box-vs-Box code line-by-line, examining/explaining it + + -point out that you COULD make the code more generic/general + -each shape has an array of unit vectors representing potential seperating axes + -each shape has a function "Project" which return the object's halfsize when projected onto a given axis + -however, if you want to it run as fast as possible, writing special code for each possible combination of objects is the best solution + + +[71]** -what we left out: + [66]** might help when explaining this + -tracking minimum penetration axis + -store all pen amounts and compare them at the end, or keep a "running minimum" + -which direction to project? + -projdir is parallel to the axis of minimum projection, but could point in two possible directions + -use the sign of the deltaaxis + + +[72]**-what about circles? + -we haven't mentioned circles or curved surfaces yet, but they're very useful as collision shapes + -the main difference between circles and polygons is the behaviour around corners (vertices) + -circles "roll" around corners: the collision direction changes smoothly + -polygons slide across them: the collision direction remains the same (surface normal) +** +** + + -another way to look at this difference is that, unlike polygons, + there aren't a finite/discrete number of directions perpendicular to a circle's surface + -this is a problem, since our approach relies on knowing which directions might be potentially seperating directions + -but: since this SAT approach is a sort of generalization of circle-circle collision, + surely we can support circles without too much extra work + +[73]** -we can + -instead of reiterating what's presented in our online tutorials, i'll just refer you to them + -the basic idea is that for a circle you perform the exact same tests as you would for a box, except you sometimes + perform a single additional test at the end + -to handle the case where the circle is colliding with a "corner" (vertex) of a shape instead of an edge (lineseg) + -you don't always have to perform this test + -there is a fast and simple way to determine if you have to perform this additional test + -you can use information from the axes you've already tested + -if the overlap along the box axes is less than the circle's radius, we know the circle is in a corner region + + -if the circle's center is in the horiz/vertical regions, we only test the two box axes + -we can tell if the circle is in these regions by looking at the results of these 2 axis tests + -if the circle's NOT in these regions, we have to test it vs. the nearest box corner + -again, which corner to use is easy since part of the 2 axis tests was determining the box->circle delta vector + -this delta vector "points" at the box corner + +[74]** -what about non-convex shapes? + -this is important, especially for world/level shapes + -just represent them as a union of convex shapes + -several (possibly overlapping) convex shapes +[75]** -tilebased games use the same idea: decompose the world into small chunks +[76]** -a different decomposition is: linesegs + -this is leading into the next section into broad-phase + + + +[77]** -optimisations: + -the key is to precompute as much information as possible, so that you don't have to calculate it at runtime + -instead you just lookup your precomputed value + -almost every game subscribes to the rule: "the vast majority of things in the world are static" + -i.e the game code makes a distinction between (static) world/level geometry, and (dynamic) objects + -makings things static is a great optimisation because we can precompute almost everything about each static object + -triangles and more complex shapes are harder to project than a box + -more lines of code + -you _can_ still do it on the fly, it's just slower + -calculations for circles and boxes are very fast and simple; they make good candidates for dynamic objects + -more complex shapes can be used for static objects, since most of the calculations + (calculating surface normals aka axis directions, etc.) can be done beforehand instead of at runtime + + + + +[78]** QUESTIONS about Seperating Axis Thereom / Method of Seperating Axes + +[++TODO: figure out how much time we can spend here, and note it down so that, when presenting, we don't get stuck here for too long] + + + + +[79]** +PART 2: broad-phase collision detection, aka spatial database + -switching gears... + +[80]** -knowing how to test two objects for collision is only part of the solution to the CD problem +[81]** -if there are 10 objects in the world, we'd have to perform 10*10=100 tests + -it doesn't matter how fast your object-object test is if you're using it 100 times per frame + -for N objects, you have to use N^2 tests + +[82]** -we want to be able to very quickly determine which pairs of objects we should test, i.e which pairs MIGHT be colliding + -how to figure out what might be colliding? + -all the methods i know of for broad-phase CD use the assumption: + if two objects are near each other, then they might be colliding + -quickly == approximately; we don't need to know for sure, that's the job of the narrow-phase + -once we know which pairs MIGHT be colliding, we can pass them to our narrow-phase collision system + to figure out if they actually _are_ colliding, and if so, how. + -so, we just need to find a way to quickly determine, given an object or a position in the world, + all of the other objects nearby + + -this step is _very_ game specific + -there is no "magic bullet" + +[83]** -for small numbers of objects (<=5), using HitTest on each possible pair is often "good enough" + -you're still doing N^2 tests, but you're not doing too many of them, and they're not too expensive + -it's fast, and it tells you what you need to know (that the objects might be colliding) + -if HitTest returns true, you know their bounding boxes are overlapping, and can investigate further using complex tests + -however, for the sake of argument, SAT code for AABBs (i.e rectangles) can be just as fast as Hittest + -the main cost is the overhead of a function call + + +[84]** -for larger numbers of objects, you need a more sophisticated way to find all the objects near a given point + -this problem has been solved in many different ways: + -grid: divide space into uniform boxes +[85]** -quadtree: recursively subdivide space into 4 equal regions + +[86]** -they all amount to the same thing: partitioning space into discrete chunks, and building a database using those chunks + -the data in the database is the location of objects + -hence broad-phase collision systems are often called spatial databases + + -the two most important operations on this database are: + a) neighborhood query (quickly determining which objects are near a given position) + b) updating an entry in the database (i.e moving an object) + -optimising (a) is very simple if you don't care about (b) + -games which must support large numbers of moving objects must optimise (b) + -it's hard to do both at once; this is why broad-phase is so game-specific + + +[87]** -grids are best for flash + -there are many different ways to use grids, but ALL of them are better suited for flash than non-grid-based methods + + -why only grids? + + -grids allow for constant-time neighborhood queries, and constant-time updating + -the constant cost is extremely low + -for a neighborhood query, you simply find the cell which contains your query point, + then look in the surrounding cells + -to update the position of an object in the grid, you just need to find the cells which the object touches, + which is fast and simple + -more on this later + + -all other spatial database approaches are based on search-trees of some sort + -the quadtree i showed you LOOKED sort of like a grid, but the implementation looks + like a normal tree data structure + -each node has 4 children, etc. + -when you look something up from a tree, you have to descend to the leaves, performing tests at each level of the tree + -each test takes time + -grids only require one test, not one test per level of the heirarchy + -likewise, inserting something into a tree is more complex than with a grid + + -if grids are so good, why don't other games use them? + -most 2D games _do_ use grids + -obviously, any tile-based game is using some sort of grid + -even doom used grid-based collision + +[88]** -the problem with grids is that they take up a LOT of space, since the entire world must be covered in cells + -a 200x100-tile world means a grid with 20000 cells; if each cell needs 256bytes, that's 5MB + -in 3D, this only gets worse: 200x100x100 @ 256bytes = 500MB + -this is why trees are used; they require far less memory. instead of covering the entire world, + you only cover the non-empty parts of the world + -the tradeoff is that trees are slower and more complex to query and update + +[89]** -in flash, in 2D, you can afford the extra memory needed by a grid, but you _can't_ afford the extra processing time required by trees + + +[90]** -all grid operations involve a couple math ops + -you can't get any faster than that + +[91]** -using a grid for object-vs-object tests + -i'll just provide an outline of the process + -see our online tutorials for implementation details and source code + +[92]** -a grid is just an abstract data structure; there are many differnet ways to actually use the structure. + -two popular ways are "regular" and "loose" grids + + + -regular grid: + -every object knows which grid cell(s) it touches + -every grid cell knows which object(s) are touching it + + each blue cell must be tested for collision, and each blue cell must be updated when objects move + + -loose grid: + -every object knows which grid cell contains it's center + -every cell knows which object(s) it contains + + each red cell must be tested for collision, and each blue cell must be updated when objects move + + -regular vs. loose: + -less tests vs less time to update moving objects + -can handle any sized shape vs shapes must fit in a cell + + + +[93]** -using a grid for object-vs-world + -as mentioned earlier, most games divide objects into "static" and "dynamic" groups + -you CAN simply use the grid to collide dynamic vs. static objects exactly as you + would for dynamic vs. dynamic objects (as we just saw) + -however, if you know that most of the game world will consist of static shapes, + you might want to create a special system for colliding these static shapes against dynamic objects + + +[94]** -there isn't time to explore all of the possible ways to use a grid for object-vs-world tests + -i'll breifly cover 2 approaches + -tilebased and geometry-based + + -tilebased collision: + -each grid cell stores a tile shape + -each moving object collides against all other moving objects and tiles in it's neighborhood + -again, our online tutorials cover this topic very well (i'd rather not just repeat info that's already available) + -tilebased systems SEEM like a good choice (simple and fast) + -in practise they're quite hard to get good behaviour out of + -tilebased collision is usually rule-based instead of math-based; + rule-based works well for simple dynamic models, but when you start needing smooth + physics-based object movement, rule-based collision simply doesn't perform well + -you end up doing lots of extra work to get things behaving well + -doing lots of extra work defeates the purpose of using a tilebased world in the first place, which was speed and simplicity + + -a different grid-based approach is geometry/lineseg based + -the world is made out of polygons + -convex or concave, it doesn't matter + -as a preprocess, before runtime, you decompose your polygons into their component linesegs + -insert each lineseg into a grid + -determining which cells to insert a lineseg into can be done in several ways + -since it's a preprocess it doesn't really matter how slow this code is + -you can simply try lineseg-vs-AABB test for all cells touching the lineseg's bounding box + -or you can use raycast code (which we'll touch on soon) + -at runtime, object-vs-world collision involves one or two object-vs-lineseg tests + -linesegs are the simplest and fastest shapes to collide against + -they have only a single potentially seperating axis + -performing several object-vs-lineseg tests is MUCH faster than performing + several object-vs-tileshape tests + -this system can support the exact same levels and shapes as a tilebased system + -but it's far simpler + -instead of having many different possible tile-shapes to collide against, you have just one shape: lineseg + -it's also a lot faster + -testing vs. linesegs is faster than vs. timeshapes + + + +[95]** -another great advantage that grids have over other spatial databases is: raycasting is very easy + -rays are lines with one endpoint + -they're used to model fast-moving bullets in many games (doom, quake) + -they're also very useful for line-of-sight testing (AI uses these to determine what each enemy can "see") + +[96]** -a very efficient and simple to understand raycasting algorithm is presented in this paper + -it's very simple, and very fast + -yet again, please see our online tutorials for implementation details + + + +[97]** +PART 3: misc/conclusion + +-summary: + -collision detection is a powerful tool for making fun games + -the MSA is a useful tool for narrow-phase collision detection + -a uniform grid is a useful tool for broad-phase collision detection + +-QUESTIONS? + + + + + diff --git a/todo/phaser clean up/fitc/FITC_beyondHittest07.swf b/todo/phaser clean up/fitc/FITC_beyondHittest07.swf new file mode 100644 index 00000000..a2a6c210 Binary files /dev/null and b/todo/phaser clean up/fitc/FITC_beyondHittest07.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05.swf new file mode 100644 index 00000000..639e3d35 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxbox_projection.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxbox_projection.swf new file mode 100644 index 00000000..e436e8f2 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxbox_projection.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxcells.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxcells.swf new file mode 100644 index 00000000..e8d134bd Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxcells.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxprojection.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxprojection.swf new file mode 100644 index 00000000..e08eafb4 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_boxprojection.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_circlebox.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_circlebox.swf new file mode 100644 index 00000000..384aab0d Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_circlebox.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_frictionbounce.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_frictionbounce.swf new file mode 100644 index 00000000..639e3d35 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_frictionbounce.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_projdotprod.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_projdotprod.swf new file mode 100644 index 00000000..8ebb9e49 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_projdotprod.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_rawdotprod.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_rawdotprod.swf new file mode 100644 index 00000000..9eb2eede Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_rawdotprod.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitdotprod.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitdotprod.swf new file mode 100644 index 00000000..2c952281 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitdotprod.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitvector.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitvector.swf new file mode 100644 index 00000000..6663e792 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_unitvector.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vector.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vector.swf new file mode 100644 index 00000000..82e63958 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vector.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectornormals.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectornormals.swf new file mode 100644 index 00000000..1bdccbb0 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectornormals.swf differ diff --git a/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectorprojection.swf b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectorprojection.swf new file mode 100644 index 00000000..5aad8440 Binary files /dev/null and b/todo/phaser clean up/fitc/media/N_tutorial_diagrams05_vectorprojection.swf differ diff --git a/todo/phaser space paint s.png b/todo/phaser space paint s.png new file mode 100644 index 00000000..1c0a7d49 Binary files /dev/null and b/todo/phaser space paint s.png differ diff --git a/todo/phaser tests/camera fx/fade.js b/todo/phaser tests/camera fx/fade.js new file mode 100644 index 00000000..2fe678bc --- /dev/null +++ b/todo/phaser tests/camera fx/fade.js @@ -0,0 +1,46 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('background', 'assets/pics/large-color-wheel.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var fade; + function create() { + myGame.add.sprite(0, 0, 'background'); + car = myGame.add.sprite(400, 300, 'car'); + // Add our effect to the camera + fade = myGame.camera.fx.add(Phaser.FX.Camera.Fade); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + // Fade when the car hits the edges, a different colour per edge + if(car.x < 0) { + fade.start(0x330066, 3); + car.x = 0; + } else if(car.x > myGame.world.width) { + fade.start(0x000066, 3); + car.x = myGame.world.width - car.width; + } + if(car.y < 0) { + fade.start(0xffffff, 4); + car.y = 0; + } else if(car.y > myGame.world.height) { + fade.start(0x000000, 3); + car.y = myGame.world.height - car.height; + } + } +})(); diff --git a/todo/phaser tests/camera fx/fade.ts b/todo/phaser tests/camera fx/fade.ts new file mode 100644 index 00000000..4816b5d5 --- /dev/null +++ b/todo/phaser tests/camera fx/fade.ts @@ -0,0 +1,77 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('background', 'assets/pics/large-color-wheel.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var fade: Phaser.FX.Camera.Fade; + + function create() { + + myGame.add.sprite(0, 0, 'background'); + + car = myGame.add.sprite(400, 300, 'car'); + + // Add our effect to the camera + fade = myGame.camera.fx.add(Phaser.FX.Camera.Fade); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + // Fade when the car hits the edges, a different colour per edge + + if (car.x < 0) + { + fade.start(0x330066, 3); + car.x = 0; + } + else if (car.x > myGame.world.width) + { + fade.start(0x000066, 3); + car.x = myGame.world.width - car.width; + } + + if (car.y < 0) + { + fade.start(0xffffff, 4); + car.y = 0; + } + else if (car.y > myGame.world.height) + { + fade.start(0x000000, 3); + car.y = myGame.world.height - car.height; + } + + } + +})(); diff --git a/todo/phaser tests/camera fx/flash.js b/todo/phaser tests/camera fx/flash.js new file mode 100644 index 00000000..6c35dc00 --- /dev/null +++ b/todo/phaser tests/camera fx/flash.js @@ -0,0 +1,46 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('background', 'assets/pics/large-color-wheel.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var flash; + function create() { + myGame.add.sprite(0, 0, 'background'); + car = myGame.add.sprite(400, 300, 'car'); + // Add our effect to the camera + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + // Flash when the car hits the edges, a different colour per edge + if(car.x < 0) { + flash.start(0xffffff, 1); + car.x = 0; + } else if(car.x > myGame.world.width) { + flash.start(0xff0000, 2); + car.x = myGame.world.width - car.width; + } + if(car.y < 0) { + flash.start(0xffff00, 2); + car.y = 0; + } else if(car.y > myGame.world.height) { + flash.start(0xff00ff, 3); + car.y = myGame.world.height - car.height; + } + } +})(); diff --git a/todo/phaser tests/camera fx/flash.ts b/todo/phaser tests/camera fx/flash.ts new file mode 100644 index 00000000..f3a2e61a --- /dev/null +++ b/todo/phaser tests/camera fx/flash.ts @@ -0,0 +1,77 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('background', 'assets/pics/large-color-wheel.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var flash: Phaser.FX.Camera.Flash; + + function create() { + + myGame.add.sprite(0, 0, 'background'); + + car = myGame.add.sprite(400, 300, 'car'); + + // Add our effect to the camera + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + // Flash when the car hits the edges, a different colour per edge + + if (car.x < 0) + { + flash.start(0xffffff, 1); + car.x = 0; + } + else if (car.x > myGame.world.width) + { + flash.start(0xff0000, 2); + car.x = myGame.world.width - car.width; + } + + if (car.y < 0) + { + flash.start(0xffff00, 2); + car.y = 0; + } + else if (car.y > myGame.world.height) + { + flash.start(0xff00ff, 3); + car.y = myGame.world.height - car.height; + } + + } + +})(); diff --git a/todo/phaser tests/camera fx/mirror.js b/todo/phaser tests/camera fx/mirror.js new file mode 100644 index 00000000..e9abc4c5 --- /dev/null +++ b/todo/phaser tests/camera fx/mirror.js @@ -0,0 +1,41 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Just set the world to be the size of the image we're loading in + myGame.world.setSize(1216, 896); + myGame.loader.addImageFile('backdrop', 'assets/pics/ninja-masters2.png'); + myGame.loader.load(); + } + var mirror; + function create() { + // What we need is a camera 800x400 pixels in size as the mirror effect will be 200px tall and sit below it. + // So we resize our default camera to 400px + myGame.camera.height = 400; + // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect render. + myGame.camera.disableClipping = true; + // Add our effect to the camera + mirror = myGame.camera.fx.add(Phaser.FX.Camera.Mirror); + // The first 2 parameters are the x and y coordinates of where to display the effect. They are in STAGE coordinates, not World. + // The next is a Quad making up the rectangular region of the Camera that we'll create the effect from (in this case the whole camera). + // Finally we set the fill color that is put over the top of the mirror effect. + mirror.start(0, 400, new Phaser.Quad(0, 0, 800, 400), 'rgba(0, 0, 100, 0.7)'); + // Experiment with variations on these to see the different mirror effects that can be achieved. + //mirror.flipX = true; + //mirror.flipY = true; + myGame.add.sprite(0, 0, 'backdrop'); + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + } + } +})(); diff --git a/todo/phaser tests/camera fx/mirror.ts b/todo/phaser tests/camera fx/mirror.ts new file mode 100644 index 00000000..3a7cdc92 --- /dev/null +++ b/todo/phaser tests/camera fx/mirror.ts @@ -0,0 +1,68 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Just set the world to be the size of the image we're loading in + myGame.world.setSize(1216, 896); + + myGame.loader.addImageFile('backdrop', 'assets/pics/ninja-masters2.png'); + + myGame.loader.load(); + + } + + var mirror: Phaser.FX.Camera.Mirror; + + function create() { + + // What we need is a camera 800x400 pixels in size as the mirror effect will be 200px tall and sit below it. + // So we resize our default camera to 400px + myGame.camera.height = 400; + + // Because it's our default camera we need to tell it to disable clipping, otherwise we'll never see the mirror effect render. + myGame.camera.disableClipping = true; + + // Add our effect to the camera + mirror = myGame.camera.fx.add(Phaser.FX.Camera.Mirror); + + // The first 2 parameters are the x and y coordinates of where to display the effect. They are in STAGE coordinates, not World. + // The next is a Quad making up the rectangular region of the Camera that we'll create the effect from (in this case the whole camera). + // Finally we set the fill color that is put over the top of the mirror effect. + mirror.start(0, 400, new Phaser.Quad(0, 0, 800, 400), 'rgba(0, 0, 100, 0.7)'); + + // Experiment with variations on these to see the different mirror effects that can be achieved. + //mirror.flipX = true; + //mirror.flipY = true; + + myGame.add.sprite(0, 0, 'backdrop'); + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + } + + } + +})(); diff --git a/todo/phaser tests/camera fx/scanlines.js b/todo/phaser tests/camera fx/scanlines.js new file mode 100644 index 00000000..71d9bab5 --- /dev/null +++ b/todo/phaser tests/camera fx/scanlines.js @@ -0,0 +1,32 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1216, 896); + myGame.loader.addImageFile('backdrop', 'assets/pics/ninja-masters2.png'); + myGame.loader.load(); + } + var scanlines; + function create() { + // Add our effect to the camera + scanlines = myGame.camera.fx.add(Phaser.FX.Camera.Scanlines); + // We'll have the scanlines spaced out every 2 pixels + scanlines.spacing = 2; + // This is the color the lines will be drawn in + scanlines.color = 'rgba(0, 0, 0, 0.8)'; + myGame.add.sprite(0, 0, 'backdrop'); + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + } + } +})(); diff --git a/todo/phaser tests/camera fx/scanlines.ts b/todo/phaser tests/camera fx/scanlines.ts new file mode 100644 index 00000000..019f3024 --- /dev/null +++ b/todo/phaser tests/camera fx/scanlines.ts @@ -0,0 +1,57 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1216, 896); + + myGame.loader.addImageFile('backdrop', 'assets/pics/ninja-masters2.png'); + + myGame.loader.load(); + + } + + var scanlines: Phaser.FX.Camera.Scanlines; + + function create() { + + // Add our effect to the camera + scanlines = myGame.camera.fx.add(Phaser.FX.Camera.Scanlines); + + // We'll have the scanlines spaced out every 2 pixels + scanlines.spacing = 2; + + // This is the color the lines will be drawn in + scanlines.color = 'rgba(0, 0, 0, 0.8)'; + + myGame.add.sprite(0, 0, 'backdrop'); + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + } + + } + +})(); diff --git a/todo/phaser tests/camera fx/shake.js b/todo/phaser tests/camera fx/shake.js new file mode 100644 index 00000000..6c78a0da --- /dev/null +++ b/todo/phaser tests/camera fx/shake.js @@ -0,0 +1,50 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('background', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var shake; + function create() { + myGame.add.sprite(0, 0, 'background'); + car = myGame.add.sprite(400, 300, 'car'); + // Add our effect to the camera + shake = myGame.camera.fx.add(Phaser.FX.Camera.Shake); + myGame.onRenderCallback = render; + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + // Shake the camera when the car hits the edges, a different intensity per edge + if(car.x < 0) { + shake.start(); + car.x = 0; + } else if(car.x > myGame.world.width) { + shake.start(0.02); + car.x = myGame.world.width - car.width; + } + if(car.y < 0) { + shake.start(0.07, 1); + car.y = 0; + } else if(car.y > myGame.world.height) { + shake.start(0.1); + car.y = myGame.world.height - car.height; + } + } + function render() { + myGame.camera.renderDebugInfo(32, 32); + } +})(); diff --git a/todo/phaser tests/camera fx/shake.ts b/todo/phaser tests/camera fx/shake.ts new file mode 100644 index 00000000..eaa047a3 --- /dev/null +++ b/todo/phaser tests/camera fx/shake.ts @@ -0,0 +1,85 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('background', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var shake: Phaser.FX.Camera.Shake; + + function create() { + + myGame.add.sprite(0, 0, 'background'); + + car = myGame.add.sprite(400, 300, 'car'); + + // Add our effect to the camera + shake = myGame.camera.fx.add(Phaser.FX.Camera.Shake); + + myGame.onRenderCallback = render; + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + // Shake the camera when the car hits the edges, a different intensity per edge + + if (car.x < 0) + { + shake.start(); + car.x = 0; + } + else if (car.x > myGame.world.width) + { + shake.start(0.02); + car.x = myGame.world.width - car.width; + } + + if (car.y < 0) + { + shake.start(0.07, 1); + car.y = 0; + } + else if (car.y > myGame.world.height) + { + shake.start(0.1); + car.y = myGame.world.height - car.height; + } + + } + + function render() { + + myGame.camera.renderDebugInfo(32, 32); + + } + +})(); diff --git a/todo/phaser tests/cameras/camera alpha.js b/todo/phaser tests/cameras/camera alpha.js new file mode 100644 index 00000000..ff60bfe9 --- /dev/null +++ b/todo/phaser tests/cameras/camera alpha.js @@ -0,0 +1,38 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var miniCam; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car, Phaser.Camera.STYLE_TOPDOWN); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam = myGame.add.camera(0, 0, 300, 300); + miniCam.follow(car, Phaser.Camera.STYLE_TOPDOWN_TIGHT); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.alpha = 0.7; + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/camera alpha.ts b/todo/phaser tests/cameras/camera alpha.ts new file mode 100644 index 00000000..abf81700 --- /dev/null +++ b/todo/phaser tests/cameras/camera alpha.ts @@ -0,0 +1,63 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var miniCam: Phaser.Camera; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car, Phaser.Camera.STYLE_TOPDOWN); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + miniCam = myGame.add.camera(0, 0, 300, 300); + miniCam.follow(car, Phaser.Camera.STYLE_TOPDOWN_TIGHT); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.alpha = 0.7; + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/camera bounds.js b/todo/phaser tests/cameras/camera bounds.js new file mode 100644 index 00000000..6c5f0917 --- /dev/null +++ b/todo/phaser tests/cameras/camera bounds.js @@ -0,0 +1,33 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1920, 1200); + myGame.camera.setBounds(0, 0, 1920, 1200); + myGame.loader.addImageFile('backdrop', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + function create() { + myGame.add.sprite(0, 0, 'backdrop'); + for(var i = 0; i < 100; i++) { + myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'melon'); + } + myGame.onRenderCallback = render; + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + } + } + function render() { + myGame.camera.renderDebugInfo(32, 32); + } +})(); diff --git a/todo/phaser tests/cameras/camera bounds.ts b/todo/phaser tests/cameras/camera bounds.ts new file mode 100644 index 00000000..4c2c3a7b --- /dev/null +++ b/todo/phaser tests/cameras/camera bounds.ts @@ -0,0 +1,60 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1920, 1200); + myGame.camera.setBounds(0, 0, 1920, 1200); + + myGame.loader.addImageFile('backdrop', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + function create() { + + myGame.add.sprite(0, 0, 'backdrop'); + + for (var i = 0; i < 100; i++) + { + myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'melon'); + } + + myGame.onRenderCallback = render; + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + } + + } + + function render() { + + myGame.camera.renderDebugInfo(32, 32); + + } + +})(); diff --git a/todo/phaser tests/cameras/camera position.js b/todo/phaser tests/cameras/camera position.js new file mode 100644 index 00000000..295ac765 --- /dev/null +++ b/todo/phaser tests/cameras/camera position.js @@ -0,0 +1,40 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(3000, 3000); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + var cam2; + function create() { + for(var i = 0; i < 1000; i++) { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + myGame.camera.setPosition(16, 80); + myGame.camera.setSize(320, 320); + myGame.camera.showBorder = true; + myGame.camera.borderColor = 'rgb(255,0,0)'; + cam2 = myGame.add.camera(380, 100, 400, 400); + cam2.showBorder = true; + cam2.borderColor = 'rgb(255,255,0)'; + } + function update() { + myGame.camera.renderDebugInfo(16, 16); + cam2.renderDebugInfo(200, 16); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.x -= 4; + cam2.x -= 2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.x += 4; + cam2.x += 2; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.y -= 4; + cam2.y -= 2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.y += 4; + cam2.y += 2; + } + } +})(); diff --git a/todo/phaser tests/cameras/camera position.ts b/todo/phaser tests/cameras/camera position.ts new file mode 100644 index 00000000..d5f7384c --- /dev/null +++ b/todo/phaser tests/cameras/camera position.ts @@ -0,0 +1,66 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(3000, 3000); + + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + var cam2: Phaser.Camera; + + function create() { + + for (var i = 0; i < 1000; i++) + { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + + myGame.camera.setPosition(16, 80); + myGame.camera.setSize(320, 320); + myGame.camera.showBorder = true; + myGame.camera.borderColor = 'rgb(255,0,0)'; + + cam2 = myGame.add.camera(380, 100, 400, 400); + cam2.showBorder = true; + cam2.borderColor = 'rgb(255,255,0)'; + + } + + function update() { + + myGame.camera.renderDebugInfo(16, 16); + cam2.renderDebugInfo(200, 16); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.x -= 4; + cam2.x -= 2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.x += 4; + cam2.x += 2; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.y -= 4; + cam2.y -= 2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.y += 4; + cam2.y += 2; + } + + } + +})(); diff --git a/todo/phaser tests/cameras/camera rotation.js b/todo/phaser tests/cameras/camera rotation.js new file mode 100644 index 00000000..d7715798 --- /dev/null +++ b/todo/phaser tests/cameras/camera rotation.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(3000, 3000); + myGame.loader.addImageFile('car', 'assets/sprites/car.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + function create() { + for(var i = 0; i < 1000; i++) { + if(Math.random() > 0.5) { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'car'); + } else { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + } + myGame.camera.setPosition(100, 100); + myGame.camera.setSize(320, 320); + } + function update() { + myGame.camera.renderDebugInfo(600, 32); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.rotation -= 2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.rotation += 2; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + } + } +})(); diff --git a/todo/phaser tests/cameras/camera rotation.ts b/todo/phaser tests/cameras/camera rotation.ts new file mode 100644 index 00000000..92d41cc4 --- /dev/null +++ b/todo/phaser tests/cameras/camera rotation.ts @@ -0,0 +1,61 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(3000, 3000); + + myGame.loader.addImageFile('car', 'assets/sprites/car.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + function create() { + + for (var i = 0; i < 1000; i++) + { + if (Math.random() > 0.5) + { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'car'); + } + else + { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + } + + myGame.camera.setPosition(100, 100); + myGame.camera.setSize(320, 320); + + } + + function update() { + + myGame.camera.renderDebugInfo(600, 32); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.rotation -= 2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.rotation += 2; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + } + + } + +})(); diff --git a/todo/phaser tests/cameras/camera scale.js b/todo/phaser tests/cameras/camera scale.js new file mode 100644 index 00000000..d33ad8f6 --- /dev/null +++ b/todo/phaser tests/cameras/camera scale.js @@ -0,0 +1,45 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var miniCam; + var bigCam; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam = myGame.add.camera(600, 32, 200, 200); + miniCam.follow(car, Phaser.Camera.STYLE_LOCKON); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.scale.setTo(0.5, 0.5); + bigCam = myGame.add.camera(32, 32, 200, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + bigCam.showBorder = true; + bigCam.scale.setTo(2, 2); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/camera scale.ts b/todo/phaser tests/cameras/camera scale.ts new file mode 100644 index 00000000..254d0165 --- /dev/null +++ b/todo/phaser tests/cameras/camera scale.ts @@ -0,0 +1,71 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var miniCam: Phaser.Camera; + var bigCam: Phaser.Camera; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + miniCam = myGame.add.camera(600, 32, 200, 200); + miniCam.follow(car, Phaser.Camera.STYLE_LOCKON); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.scale.setTo(0.5, 0.5); + + bigCam = myGame.add.camera(32, 32, 200, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + bigCam.showBorder = true; + bigCam.scale.setTo(2, 2); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/camera shadow.js b/todo/phaser tests/cameras/camera shadow.js new file mode 100644 index 00000000..b66262d2 --- /dev/null +++ b/todo/phaser tests/cameras/camera shadow.js @@ -0,0 +1,47 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var miniCam; + var bigCam; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam = myGame.add.camera(600, 32, 200, 200); + miniCam.follow(car, Phaser.Camera.STYLE_LOCKON); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.scale.setTo(0.5, 0.5); + miniCam.showShadow = true; + bigCam = myGame.add.camera(32, 32, 200, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + bigCam.showBorder = true; + bigCam.scale.setTo(2, 2); + bigCam.showShadow = true; + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/camera shadow.ts b/todo/phaser tests/cameras/camera shadow.ts new file mode 100644 index 00000000..195e5227 --- /dev/null +++ b/todo/phaser tests/cameras/camera shadow.ts @@ -0,0 +1,73 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var miniCam: Phaser.Camera; + var bigCam: Phaser.Camera; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + miniCam = myGame.add.camera(600, 32, 200, 200); + miniCam.follow(car, Phaser.Camera.STYLE_LOCKON); + miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + miniCam.showBorder = true; + miniCam.scale.setTo(0.5, 0.5); + miniCam.showShadow = true; + + bigCam = myGame.add.camera(32, 32, 200, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + bigCam.showBorder = true; + bigCam.scale.setTo(2, 2); + bigCam.showShadow = true; + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/camera texture.js b/todo/phaser tests/cameras/camera texture.js new file mode 100644 index 00000000..4a9cf23c --- /dev/null +++ b/todo/phaser tests/cameras/camera texture.js @@ -0,0 +1,46 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('balls', 'assets/sprites/balls.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var miniCam; + var bigCam; + function create() { + //myGame.add.sprite('grid', 0, 0); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.setTexture('balls'); + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + //miniCam = myGame.add.camera(600, 32, 200, 200); + //miniCam.follow(car, Camera.STYLE_LOCKON); + //miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + //miniCam.showBorder = true; + //miniCam.scale.setTo(0.5, 0.5); + //bigCam = myGame.add.camera(32, 32, 200, 200); + //bigCam.follow(car, Camera.STYLE_LOCKON); + //bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + //bigCam.showBorder = true; + //bigCam.scale.setTo(2, 2); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/camera texture.ts b/todo/phaser tests/cameras/camera texture.ts new file mode 100644 index 00000000..aa5ba0ed --- /dev/null +++ b/todo/phaser tests/cameras/camera texture.ts @@ -0,0 +1,72 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('balls', 'assets/sprites/balls.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var miniCam: Phaser.Camera; + var bigCam: Phaser.Camera; + + function create() { + + //myGame.add.sprite('grid', 0, 0); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.setTexture('balls'); + myGame.camera.follow(car); + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + //miniCam = myGame.add.camera(600, 32, 200, 200); + //miniCam.follow(car, Camera.STYLE_LOCKON); + //miniCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + //miniCam.showBorder = true; + //miniCam.scale.setTo(0.5, 0.5); + + //bigCam = myGame.add.camera(32, 32, 200, 200); + //bigCam.follow(car, Camera.STYLE_LOCKON); + //bigCam.setBounds(0, 0, myGame.world.width, myGame.world.height); + //bigCam.showBorder = true; + //bigCam.scale.setTo(2, 2); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/focus on.js b/todo/phaser tests/cameras/focus on.js new file mode 100644 index 00000000..e2aa5810 --- /dev/null +++ b/todo/phaser tests/cameras/focus on.js @@ -0,0 +1,36 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1920, 1920); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + myGame.loader.load(); + } + var melon; + function create() { + // locked to the camera + myGame.add.sprite(0, 0, 'grid'); + melon = myGame.add.sprite(600, 650, 'melon'); + melon.scrollFactor.setTo(1.5, 1.5); + // scrolls x2 camera speed + for(var i = 0; i < 100; i++) { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'carrot'); + tempSprite.scrollFactor.setTo(2, 2); + } + } + function update() { + myGame.camera.renderDebugInfo(32, 32); + melon.renderDebugInfo(200, 32); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.focusOnXY(640, 640); + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.focusOnXY(1234, 800); + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.focusOnXY(50, 200); + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.focusOnXY(1700, 1700); + } + } +})(); diff --git a/todo/phaser tests/cameras/focus on.ts b/todo/phaser tests/cameras/focus on.ts new file mode 100644 index 00000000..6807a0b9 --- /dev/null +++ b/todo/phaser tests/cameras/focus on.ts @@ -0,0 +1,62 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1920, 1920); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + + myGame.loader.load(); + + } + + var melon: Phaser.Sprite; + + function create() { + + // locked to the camera + myGame.add.sprite(0, 0, 'grid'); + + melon = myGame.add.sprite(600, 650, 'melon'); + melon.scrollFactor.setTo(1.5, 1.5); + + // scrolls x2 camera speed + for (var i = 0; i < 100; i++) + { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'carrot'); + tempSprite.scrollFactor.setTo(2, 2); + } + + } + + function update() { + + myGame.camera.renderDebugInfo(32, 32); + melon.renderDebugInfo(200, 32); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.focusOnXY(640, 640); + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.focusOnXY(1234, 800); + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.focusOnXY(50, 200); + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.focusOnXY(1700, 1700); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/follow deadzone.js b/todo/phaser tests/cameras/follow deadzone.js new file mode 100644 index 00000000..3f748921 --- /dev/null +++ b/todo/phaser tests/cameras/follow deadzone.js @@ -0,0 +1,36 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car); + // Here we'll set our own custom deadzone which is 64px smaller than the stage size on all sides + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + } + function update() { + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/follow deadzone.ts b/todo/phaser tests/cameras/follow deadzone.ts new file mode 100644 index 00000000..6dca503c --- /dev/null +++ b/todo/phaser tests/cameras/follow deadzone.ts @@ -0,0 +1,61 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car); + // Here we'll set our own custom deadzone which is 64px smaller than the stage size on all sides + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + } + + function update() { + + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/follow sprite.js b/todo/phaser tests/cameras/follow sprite.js new file mode 100644 index 00000000..e0a1eaaa --- /dev/null +++ b/todo/phaser tests/cameras/follow sprite.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1920, 1920); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car); + myGame.onRenderCallback = render; + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + } +})(); diff --git a/todo/phaser tests/cameras/follow sprite.ts b/todo/phaser tests/cameras/follow sprite.ts new file mode 100644 index 00000000..3b3669c5 --- /dev/null +++ b/todo/phaser tests/cameras/follow sprite.ts @@ -0,0 +1,62 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1920, 1920); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car); + + myGame.onRenderCallback = render; + + } + + function update() { + + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render() { + + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + + } + +})(); diff --git a/todo/phaser tests/cameras/follow topdown.js b/todo/phaser tests/cameras/follow topdown.js new file mode 100644 index 00000000..e6cbe459 --- /dev/null +++ b/todo/phaser tests/cameras/follow topdown.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(2240, 2240); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + function create() { + myGame.add.sprite(0, 0, 'grid'); + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car, Phaser.Camera.STYLE_TOPDOWN); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + } + function update() { + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/cameras/follow topdown.ts b/todo/phaser tests/cameras/follow topdown.ts new file mode 100644 index 00000000..382187de --- /dev/null +++ b/todo/phaser tests/cameras/follow topdown.ts @@ -0,0 +1,59 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(2240, 2240); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car, Phaser.Camera.STYLE_TOPDOWN); + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + } + + function update() { + + myGame.camera.renderDebugInfo(32, 32); + car.renderDebugInfo(200, 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/cameras/multicam1.js b/todo/phaser tests/cameras/multicam1.js new file mode 100644 index 00000000..42bede02 --- /dev/null +++ b/todo/phaser tests/cameras/multicam1.js @@ -0,0 +1,44 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(3000, 3000); + myGame.loader.addImageFile('car', 'assets/sprites/car.png'); + myGame.loader.addImageFile('coin', 'assets/sprites/coin.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + var cam2; + function create() { + for(var i = 0; i < 1000; i++) { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + myGame.camera.setPosition(16, 80); + myGame.camera.setSize(320, 320); + myGame.camera.showBorder = true; + myGame.camera.borderColor = 'rgb(255,0,0)'; + cam2 = myGame.add.camera(380, 100, 400, 400); + //cam2.transparent = false; + //cam2.backgroundColor = 'rgb(20,20,20)'; + cam2.showBorder = true; + cam2.borderColor = 'rgb(255,255,0)'; + } + function update() { + myGame.camera.renderDebugInfo(16, 16); + cam2.renderDebugInfo(200, 16); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + cam2.scroll.x -= 2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + cam2.scroll.x += 2; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + cam2.scroll.y -= 2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + cam2.scroll.y += 2; + } + } +})(); diff --git a/todo/phaser tests/cameras/multicam1.ts b/todo/phaser tests/cameras/multicam1.ts new file mode 100644 index 00000000..b408b224 --- /dev/null +++ b/todo/phaser tests/cameras/multicam1.ts @@ -0,0 +1,70 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(3000, 3000); + + myGame.loader.addImageFile('car', 'assets/sprites/car.png'); + myGame.loader.addImageFile('coin', 'assets/sprites/coin.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + var cam2: Phaser.Camera; + + function create() { + + for (var i = 0; i < 1000; i++) + { + myGame.add.sprite(Math.random() * 3000, Math.random() * 3000, 'melon'); + } + + myGame.camera.setPosition(16, 80); + myGame.camera.setSize(320, 320); + myGame.camera.showBorder = true; + myGame.camera.borderColor = 'rgb(255,0,0)'; + + cam2 = myGame.add.camera(380, 100, 400, 400); + //cam2.transparent = false; + //cam2.backgroundColor = 'rgb(20,20,20)'; + cam2.showBorder = true; + cam2.borderColor = 'rgb(255,255,0)'; + + } + + function update() { + + myGame.camera.renderDebugInfo(16, 16); + cam2.renderDebugInfo(200, 16); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + cam2.scroll.x -= 2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + cam2.scroll.x += 2; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + cam2.scroll.y -= 2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + cam2.scroll.y += 2; + } + + } + +})(); diff --git a/todo/phaser tests/cameras/scroll factor.js b/todo/phaser tests/cameras/scroll factor.js new file mode 100644 index 00000000..7dbc31db --- /dev/null +++ b/todo/phaser tests/cameras/scroll factor.js @@ -0,0 +1,37 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1920, 1200); + myGame.loader.addImageFile('backdrop', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + myGame.loader.load(); + } + var melon; + function create() { + // scrolls in time with the camera + myGame.add.sprite(0, 0, 'backdrop'); + melon = myGame.add.sprite(600, 650, 'melon'); + melon.scrollFactor.setTo(1.5, 1.5); + // scrolls x2 camera speed + for(var i = 0; i < 100; i++) { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'carrot'); + tempSprite.scrollFactor.setTo(2, 2); + } + } + function update() { + myGame.camera.renderDebugInfo(32, 32); + melon.renderDebugInfo(200, 32); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 1; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 1; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 1; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 1; + } + } +})(); diff --git a/todo/phaser tests/cameras/scroll factor.ts b/todo/phaser tests/cameras/scroll factor.ts new file mode 100644 index 00000000..df4fc3b6 --- /dev/null +++ b/todo/phaser tests/cameras/scroll factor.ts @@ -0,0 +1,63 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1920, 1200); + + myGame.loader.addImageFile('backdrop', 'assets/pics/remember-me.jpg'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + + myGame.loader.load(); + + } + + var melon: Phaser.Sprite; + + function create() { + + // scrolls in time with the camera + myGame.add.sprite(0, 0, 'backdrop'); + + melon = myGame.add.sprite(600, 650, 'melon'); + melon.scrollFactor.setTo(1.5, 1.5); + + // scrolls x2 camera speed + for (var i = 0; i < 100; i++) + { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'carrot'); + tempSprite.scrollFactor.setTo(2, 2); + } + + } + + function update() { + + myGame.camera.renderDebugInfo(32, 32); + melon.renderDebugInfo(200, 32); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 1; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 1; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 1; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 1; + } + + } + +})(); diff --git a/todo/phaser tests/collision/collide sprites.js b/todo/phaser tests/collision/collide sprites.js new file mode 100644 index 00000000..fe82a9b6 --- /dev/null +++ b/todo/phaser tests/collision/collide sprites.js @@ -0,0 +1,35 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + var car; + var melon; + function create() { + car = myGame.add.sprite(100, 300, 'car'); + melon = myGame.add.sprite(200, 310, 'melon'); + car.name = 'car'; + melon.name = 'melon'; + } + function update() { + car.renderDebugInfo(16, 16); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 200)); + } + myGame.collide(car, melon, collides); + } + function collides(a, b) { + console.log('Collision!!!!!'); + } +})(); diff --git a/todo/phaser tests/collision/collide sprites.ts b/todo/phaser tests/collision/collide sprites.ts new file mode 100644 index 00000000..1bcf3622 --- /dev/null +++ b/todo/phaser tests/collision/collide sprites.ts @@ -0,0 +1,61 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var melon: Phaser.Sprite; + + function create() { + + car = myGame.add.sprite(100, 300, 'car'); + melon = myGame.add.sprite(200, 310, 'melon'); + + car.name = 'car'; + melon.name = 'melon'; + + } + + function update() { + + car.renderDebugInfo(16, 16); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 200)); + } + + myGame.collide(car, melon, collides); + + } + + function collides(a, b) { + + console.log('Collision!!!!!'); + + } + +})(); diff --git a/todo/phaser tests/collision/falling balls.js b/todo/phaser tests/collision/falling balls.js new file mode 100644 index 00000000..0f650c7c --- /dev/null +++ b/todo/phaser tests/collision/falling balls.js @@ -0,0 +1,45 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + var atari; + var balls; + function create() { + atari = myGame.add.sprite(300, 450, 'atari'); + atari.immovable = true; + balls = myGame.add.group(); + for(var i = 0; i < 100; i++) { + var tempBall = new Phaser.Sprite(myGame, Math.random() * myGame.stage.width, -32, 'ball' + Math.round(Math.random() * 5)); + tempBall.velocity.y = 100 + Math.random() * 150; + tempBall.elasticity = 0.9; + balls.add(tempBall); + } + } + function update() { + atari.velocity.x = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + atari.velocity.x = -400; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + atari.velocity.x = 400; + } + balls.forEach(checkOffScreen, false); + myGame.collide(atari, balls); + } + function checkOffScreen(ball) { + if(ball.y < -32 || ball.y > myGame.stage.height || ball.x < 0 || ball.x > myGame.stage.width) { + ball.x = Math.random() * myGame.stage.width; + ball.y = -32; + ball.velocity.x = 0; + ball.velocity.y = 100 + Math.random() * 150; + } + } +})(); diff --git a/todo/phaser tests/collision/falling balls.ts b/todo/phaser tests/collision/falling balls.ts new file mode 100644 index 00000000..90033cb3 --- /dev/null +++ b/todo/phaser tests/collision/falling balls.ts @@ -0,0 +1,72 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + + myGame.loader.load(); + + } + + var atari: Phaser.Sprite; + var balls: Phaser.Group; + + function create() { + + atari = myGame.add.sprite(300, 450, 'atari'); + atari.immovable = true; + + balls = myGame.add.group(); + + for (var i = 0; i < 100; i++) + { + var tempBall: Phaser.Sprite = new Phaser.Sprite(myGame, Math.random() * myGame.stage.width, -32, 'ball' + Math.round(Math.random() * 5)); + tempBall.velocity.y = 100 + Math.random() * 150; + tempBall.elasticity = 0.9; + balls.add(tempBall); + } + + } + + function update() { + + atari.velocity.x = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + atari.velocity.x = -400; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + atari.velocity.x = 400; + } + + balls.forEach(checkOffScreen, false); + + myGame.collide(atari, balls); + + } + + function checkOffScreen(ball:Phaser.Sprite) { + + if (ball.y < -32 || ball.y > myGame.stage.height || ball.x < 0 || ball.x > myGame.stage.width) + { + ball.x = Math.random() * myGame.stage.width; + ball.y = -32; + ball.velocity.x = 0; + ball.velocity.y = 100 + Math.random() * 150; + } + + } + +})(); diff --git a/todo/phaser tests/collision/mask animation 1.js b/todo/phaser tests/collision/mask animation 1.js new file mode 100644 index 00000000..d2643a36 --- /dev/null +++ b/todo/phaser tests/collision/mask animation 1.js @@ -0,0 +1,31 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.load(); + } + var card; + var bot; + function create() { + card = myGame.add.sprite(200, 220, 'card'); + bot = myGame.add.sprite(myGame.stage.width - 100, 300, 'bot'); + // The collision mask is much thinner than the animated sprite + bot.collisionMask.offset.x = 16; + bot.collisionMask.width = 32; + bot.renderDebug = true; + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -150; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + bot.velocity.x = -150; + card.x = 200; + card.velocity.x = 0; + } + myGame.collide(card, bot); + } +})(); diff --git a/todo/phaser tests/collision/mask animation 1.ts b/todo/phaser tests/collision/mask animation 1.ts new file mode 100644 index 00000000..2a7ea22b --- /dev/null +++ b/todo/phaser tests/collision/mask animation 1.ts @@ -0,0 +1,50 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.load(); + + } + + var card: Phaser.Sprite; + var bot: Phaser.Sprite; + + function create() { + + card = myGame.add.sprite(200, 220, 'card'); + + bot = myGame.add.sprite(myGame.stage.width - 100, 300, 'bot'); + + // The collision mask is much thinner than the animated sprite + bot.collisionMask.offset.x = 16; + bot.collisionMask.width = 32; + bot.renderDebug = true; + + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bot.velocity.x = -150; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + bot.velocity.x = -150; + card.x = 200; + card.velocity.x = 0; + } + + myGame.collide(card, bot); + + } + +})(); diff --git a/todo/phaser tests/collision/mask test 1.js b/todo/phaser tests/collision/mask test 1.js new file mode 100644 index 00000000..59b3738c --- /dev/null +++ b/todo/phaser tests/collision/mask test 1.js @@ -0,0 +1,38 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + myGame.loader.load(); + } + var atari1; + var atari2; + var atari3; + function create() { + atari1 = myGame.add.sprite(270, 100, 'atari1'); + atari2 = myGame.add.sprite(400, 400, 'atari2'); + atari3 = myGame.add.sprite(0, 440, 'atari1'); + atari1.collisionMask.height = 16; + atari3.collisionMask.width = 16; + atari1.elasticity = 0.5; + atari3.elasticity = 0.5; + atari2.immovable = true; + atari1.renderDebug = true; + atari2.renderDebug = true; + atari3.renderDebug = true; + myGame.input.onTap.addOnce(startDrop, this); + } + function startDrop() { + atari1.velocity.y = 100; + atari3.velocity.x = 100; + } + function update() { + myGame.collide(myGame.world.group); + } + function collides(a, b) { + console.log('Collision!!!!!'); + } + function render() { + } +})(); diff --git a/todo/phaser tests/collision/mask test 1.ts b/todo/phaser tests/collision/mask test 1.ts new file mode 100644 index 00000000..865c8a9d --- /dev/null +++ b/todo/phaser tests/collision/mask test 1.ts @@ -0,0 +1,64 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + + myGame.loader.load(); + + } + + var atari1: Phaser.Sprite; + var atari2: Phaser.Sprite; + var atari3: Phaser.Sprite; + + function create() { + + atari1 = myGame.add.sprite(270, 100, 'atari1'); + atari2 = myGame.add.sprite(400, 400, 'atari2'); + atari3 = myGame.add.sprite(0, 440, 'atari1'); + + atari1.collisionMask.height = 16; + atari3.collisionMask.width = 16; + + atari1.elasticity = 0.5; + atari3.elasticity = 0.5; + + atari2.immovable = true; + + atari1.renderDebug = true; + atari2.renderDebug = true; + atari3.renderDebug = true; + + myGame.input.onTap.addOnce(startDrop, this); + + } + + function startDrop() { + + atari1.velocity.y = 100; + atari3.velocity.x = 100; + + } + + function update() { + + myGame.collide(myGame.world.group); + + } + + function collides(a, b) { + + console.log('Collision!!!!!'); + + } + + function render() { + } + +})(); diff --git a/todo/phaser tests/collision/mask test 2.js b/todo/phaser tests/collision/mask test 2.js new file mode 100644 index 00000000..5f7627ce --- /dev/null +++ b/todo/phaser tests/collision/mask test 2.js @@ -0,0 +1,35 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + myGame.loader.load(); + } + var atari1; + var atari2; + function create() { + atari1 = myGame.add.sprite(400, 100, 'atari1'); + atari2 = myGame.add.sprite(400, 400, 'atari2'); + //atari1.collisionMask.createCircle(64); + //atari1.rotation = 45; + atari1.elasticity = 0.5; + //atari2.collisionMask.createCircle(64); + atari2.immovable = true; + atari1.renderDebug = true; + atari2.renderDebug = true; + myGame.input.onTap.addOnce(startDrop, this); + } + function startDrop() { + atari1.velocity.y = 100; + } + function update() { + myGame.collide(myGame.world.group); + } + function collides(a, b) { + console.log('Collision!!!!!'); + } + function render() { + //atari1.ren + } +})(); diff --git a/todo/phaser tests/collision/mask test 2.ts b/todo/phaser tests/collision/mask test 2.ts new file mode 100644 index 00000000..44f0da2c --- /dev/null +++ b/todo/phaser tests/collision/mask test 2.ts @@ -0,0 +1,62 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + + myGame.loader.load(); + + } + + var atari1: Phaser.Sprite; + var atari2: Phaser.Sprite; + + function create() { + + atari1 = myGame.add.sprite(400, 100, 'atari1'); + atari2 = myGame.add.sprite(400, 400, 'atari2'); + + //atari1.collisionMask.createCircle(64); + //atari1.rotation = 45; + atari1.elasticity = 0.5; + + //atari2.collisionMask.createCircle(64); + atari2.immovable = true; + + atari1.renderDebug = true; + atari2.renderDebug = true; + + myGame.input.onTap.addOnce(startDrop, this); + + } + + function startDrop() { + + atari1.velocity.y = 100; + + } + + function update() { + + myGame.collide(myGame.world.group); + + } + + function collides(a, b) { + + console.log('Collision!!!!!'); + + } + + function render() { + + //atari1.ren + + } + +})(); diff --git a/todo/phaser tests/geometry/circle.js b/todo/phaser tests/geometry/circle.js new file mode 100644 index 00000000..752987a5 --- /dev/null +++ b/todo/phaser tests/geometry/circle.js @@ -0,0 +1,19 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + var circle; + var floor; + function create() { + circle = myGame.add.geomSprite(200, 0); + circle.createCircle(64); + circle.acceleration.y = 100; + circle.elasticity = 0.8; + // A simple floor + floor = myGame.add.geomSprite(0, 550); + floor.createRectangle(800, 50); + floor.immovable = true; + } + function update() { + myGame.collide(circle, floor); + } +})(); diff --git a/todo/phaser tests/geometry/circle.ts b/todo/phaser tests/geometry/circle.ts new file mode 100644 index 00000000..9a23b64a --- /dev/null +++ b/todo/phaser tests/geometry/circle.ts @@ -0,0 +1,30 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + + var circle: Phaser.GeomSprite; + var floor: Phaser.GeomSprite; + + function create() { + + circle = myGame.add.geomSprite(200, 0); + circle.createCircle(64); + circle.acceleration.y = 100; + circle.elasticity = 0.8; + + // A simple floor + floor = myGame.add.geomSprite(0, 550); + floor.createRectangle(800, 50); + floor.immovable = true; + + } + + function update() { + + myGame.collide(circle, floor); + + } + +})(); diff --git a/todo/phaser tests/geometry/line.js b/todo/phaser tests/geometry/line.js new file mode 100644 index 00000000..c879fd00 --- /dev/null +++ b/todo/phaser tests/geometry/line.js @@ -0,0 +1,27 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + var line; + function create() { + line = myGame.add.geomSprite(200, 200); + line.createLine(400, 400); + } + function update() { + //box.velocity.x = 0; + //box.velocity.y = 0; + //box.angularVelocity = 0; + //box.angularAcceleration = 0; + //if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + //{ + // box.angularVelocity = -200; + //} + //else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + //{ + // box.angularVelocity = 200; + //} + //if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + //{ + // box.velocity.copyFrom(myGame.motion.velocityFromAngle(box.angle, 200)); + //} + } +})(); diff --git a/todo/phaser tests/geometry/line.ts b/todo/phaser tests/geometry/line.ts new file mode 100644 index 00000000..cf69a9a8 --- /dev/null +++ b/todo/phaser tests/geometry/line.ts @@ -0,0 +1,40 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + + var line: Phaser.GeomSprite; + + function create() { + + line = myGame.add.geomSprite(200, 200); + + line.createLine(400, 400); + + } + + function update() { + + //box.velocity.x = 0; + //box.velocity.y = 0; + //box.angularVelocity = 0; + //box.angularAcceleration = 0; + + //if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + //{ + // box.angularVelocity = -200; + //} + //else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + //{ + // box.angularVelocity = 200; + //} + + //if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + //{ + // box.velocity.copyFrom(myGame.motion.velocityFromAngle(box.angle, 200)); + //} + + } + +})(); diff --git a/todo/phaser tests/geometry/multi rotate.js b/todo/phaser tests/geometry/multi rotate.js new file mode 100644 index 00000000..f00d76c4 --- /dev/null +++ b/todo/phaser tests/geometry/multi rotate.js @@ -0,0 +1,31 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var p1; + var p2; + var p3; + var p4; + var d = 0; + function create() { + p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY); + p2 = new Phaser.Point(p1.x - 50, p1.y - 50); + p3 = new Phaser.Point(p1.x - 100, p1.y - 100); + p4 = new Phaser.Point(p1.x - 150, p1.y - 150); + } + function update() { + p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + p3.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + p4.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + d++; + } + function render() { + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(0,255,0)'; + myGame.stage.context.fillRect(p3.x, p3.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(255,0,255)'; + myGame.stage.context.fillRect(p4.x, p4.y, 4, 4); + } +})(); diff --git a/todo/phaser tests/geometry/multi rotate.ts b/todo/phaser tests/geometry/multi rotate.ts new file mode 100644 index 00000000..075f82ca --- /dev/null +++ b/todo/phaser tests/geometry/multi rotate.ts @@ -0,0 +1,49 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var p1:Phaser.Point; + var p2:Phaser.Point; + var p3:Phaser.Point; + var p4:Phaser.Point; + + var d: number = 0; + + function create() { + + p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY); + p2 = new Phaser.Point(p1.x - 50, p1.y - 50); + p3 = new Phaser.Point(p1.x - 100, p1.y - 100); + p4 = new Phaser.Point(p1.x - 150, p1.y - 150); + + } + + function update() { + + p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + p3.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + p4.rotate(p1.x, p1.y, myGame.math.wrapAngle(d), true); + + d++; + + } + + function render() { + + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(0,255,0)'; + myGame.stage.context.fillRect(p3.x, p3.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(255,0,255)'; + myGame.stage.context.fillRect(p4.x, p4.y, 4, 4); + + } + +})(); diff --git a/todo/phaser tests/geometry/point.js b/todo/phaser tests/geometry/point.js new file mode 100644 index 00000000..126f3f3b --- /dev/null +++ b/todo/phaser tests/geometry/point.js @@ -0,0 +1,21 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + var floor; + function create() { + for(var i = 0; i < 100; i++) { + var p = myGame.add.geomSprite(myGame.stage.randomX, Math.random() * 100); + p.createPoint(); + p.fillColor = 'rgb(255,255,255)'; + p.acceleration.y = 100 + Math.random() * 100; + p.elasticity = 0.8; + } + // A simple floor + floor = myGame.add.geomSprite(0, 550); + floor.createRectangle(800, 50); + floor.immovable = true; + } + function update() { + myGame.collide(myGame.world.group, floor); + } +})(); diff --git a/todo/phaser tests/geometry/point.ts b/todo/phaser tests/geometry/point.ts new file mode 100644 index 00000000..2ae39d03 --- /dev/null +++ b/todo/phaser tests/geometry/point.ts @@ -0,0 +1,33 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + + var floor: Phaser.GeomSprite; + + function create() { + + for (var i = 0; i < 100; i++) + { + var p:Phaser.GeomSprite = myGame.add.geomSprite(myGame.stage.randomX, Math.random() * 100); + p.createPoint(); + p.fillColor = 'rgb(255,255,255)'; + p.acceleration.y = 100 + Math.random() * 100; + p.elasticity = 0.8; + } + + // A simple floor + floor = myGame.add.geomSprite(0, 550); + floor.createRectangle(800, 50); + floor.immovable = true; + + } + + function update() { + + myGame.collide(myGame.world.group, floor); + + } + +})(); diff --git a/todo/phaser tests/geometry/rect vs rect.js b/todo/phaser tests/geometry/rect vs rect.js new file mode 100644 index 00000000..b89c54e7 --- /dev/null +++ b/todo/phaser tests/geometry/rect vs rect.js @@ -0,0 +1,22 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + var box1; + var box2; + function create() { + box2 = myGame.add.geomSprite(300, 300).createRectangle(128, 128); + box1 = myGame.add.geomSprite(320, 100).createRectangle(64, 64); + box1.velocity.y = 50; + } + function update() { + if(box1.collide(box2) == true) { + box1.fillColor = 'rgb(255,0,0)'; + } else { + box1.fillColor = 'rgb(0,255,0)'; + } + } + function checkPoints() { + if(box2.rect.containsPoint(box1.rect.topLeft)) { + } + } +})(); diff --git a/todo/phaser tests/geometry/rect vs rect.ts b/todo/phaser tests/geometry/rect vs rect.ts new file mode 100644 index 00000000..b527a9cf --- /dev/null +++ b/todo/phaser tests/geometry/rect vs rect.ts @@ -0,0 +1,41 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + + var box1: Phaser.GeomSprite; + var box2: Phaser.GeomSprite; + + function create() { + + box2 = myGame.add.geomSprite(300, 300).createRectangle(128, 128); + box1 = myGame.add.geomSprite(320, 100).createRectangle(64, 64); + + box1.velocity.y = 50; + + } + + function update() { + + if (box1.collide(box2) == true) + { + box1.fillColor = 'rgb(255,0,0)'; + } + else + { + box1.fillColor = 'rgb(0,255,0)'; + } + + } + + function checkPoints() { + + if (box2.rect.containsPoint(box1.rect.topLeft)) + { + + } + + } + +})(); diff --git a/todo/phaser tests/geometry/rectangle.js b/todo/phaser tests/geometry/rectangle.js new file mode 100644 index 00000000..e6508013 --- /dev/null +++ b/todo/phaser tests/geometry/rectangle.js @@ -0,0 +1,24 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + var box; + function create() { + box = myGame.add.geomSprite(0, 0); + box.createRectangle(64, 64); + box.renderOutline = false; + } + function update() { + box.velocity.x = 0; + box.velocity.y = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + box.velocity.x = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + box.velocity.x = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + box.velocity.y = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + box.velocity.y = 200; + } + } +})(); diff --git a/todo/phaser tests/geometry/rectangle.ts b/todo/phaser tests/geometry/rectangle.ts new file mode 100644 index 00000000..55bfc90d --- /dev/null +++ b/todo/phaser tests/geometry/rectangle.ts @@ -0,0 +1,43 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update); + + var box: Phaser.GeomSprite; + + function create() { + + box = myGame.add.geomSprite(0, 0); + + box.createRectangle(64, 64); + box.renderOutline = false; + + } + + function update() { + + box.velocity.x = 0; + box.velocity.y = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + box.velocity.x = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + box.velocity.x = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + box.velocity.y = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + box.velocity.y = 200; + } + + } + +})(); diff --git a/todo/phaser tests/geometry/rope bridge.js b/todo/phaser tests/geometry/rope bridge.js new file mode 100644 index 00000000..60a056d1 --- /dev/null +++ b/todo/phaser tests/geometry/rope bridge.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.load(); + } + var segment; + function create() { + myGame.verlet.friction = 1; + myGame.verlet.hideNearestEntityCircle = true; + var points = []; + var startX = 100; + var startY = 200; + var spacing = 20; + for(var i = 0; i < 30; i++) { + points.push(new Phaser.Vector2(startX + (i * spacing), startY)); + } + segment = myGame.verlet.createLineSegments(points, 0.5); + segment.loadGraphic('ball5'); + segment.hideConstraints = false; + segment.pin(0); + segment.pin(points.length - 1); + } + function update() { + } + function render() { + myGame.verlet.render(); + //myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + //myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + //myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + //myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + } +})(); diff --git a/todo/phaser tests/geometry/rope bridge.ts b/todo/phaser tests/geometry/rope bridge.ts new file mode 100644 index 00000000..1d1a437f --- /dev/null +++ b/todo/phaser tests/geometry/rope bridge.ts @@ -0,0 +1,55 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.load(); + + } + + var segment: Phaser.Verlet.Composite; + + function create() { + + myGame.verlet.friction = 1; + myGame.verlet.hideNearestEntityCircle = true; + + var points: Phaser.Vector2[] = []; + var startX: number = 100; + var startY: number = 200; + var spacing: number = 20; + + for (var i = 0; i < 30; i++) + { + points.push(new Phaser.Vector2(startX + (i * spacing), startY)); + } + + segment = myGame.verlet.createLineSegments(points, 0.5); + segment.loadGraphic('ball5'); + segment.hideConstraints = false; + + segment.pin(0); + segment.pin(points.length - 1); + + } + + function update() { + } + + function render() { + + myGame.verlet.render(); + + //myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + //myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + + //myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + //myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + + } + +})(); diff --git a/todo/phaser tests/geometry/rotate point 1.js b/todo/phaser tests/geometry/rotate point 1.js new file mode 100644 index 00000000..229ec21f --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 1.js @@ -0,0 +1,21 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var p1; + var p2; + var d = 0; + function create() { + p1 = new Phaser.Point(200, 300); + p2 = new Phaser.Point(300, 300); + } + function update() { + p1.rotate(p2.x, p2.y, myGame.math.wrapAngle(d), true); + d++; + } + function render() { + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + } +})(); diff --git a/todo/phaser tests/geometry/rotate point 1.ts b/todo/phaser tests/geometry/rotate point 1.ts new file mode 100644 index 00000000..c97610f8 --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 1.ts @@ -0,0 +1,36 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var p1:Phaser.Point; + var p2:Phaser.Point; + var d: number = 0; + + function create() { + + p1 = new Phaser.Point(200, 300); + p2 = new Phaser.Point(300, 300); + + } + + function update() { + + p1.rotate(p2.x, p2.y, myGame.math.wrapAngle(d), true); + + d++; + + } + + function render() { + + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + + } + +})(); diff --git a/todo/phaser tests/geometry/rotate point 2.js b/todo/phaser tests/geometry/rotate point 2.js new file mode 100644 index 00000000..7e1c1688 --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 2.js @@ -0,0 +1,43 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var p1; + var p2; + var p3; + var p4; + var d2 = 0; + var d3 = 0; + var d4 = 0; + function create() { + p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY); + p2 = new Phaser.Point(p1.x - 50, p1.y - 50); + p3 = new Phaser.Point(p2.x - 50, p2.y - 50); + p4 = new Phaser.Point(p3.x - 50, p3.y - 50); + } + function update() { + p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d2), true, 150); + p3.rotate(p2.x, p2.y, myGame.math.wrapAngle(d3), true, 50); + p4.rotate(p3.x, p3.y, myGame.math.wrapAngle(d4), true, 100); + d2 += 1; + d3 += 4; + d4 += 6; + } + function render() { + myGame.stage.context.strokeStyle = 'rgb(0,255,255)'; + myGame.stage.context.beginPath(); + myGame.stage.context.moveTo(p1.x, p1.y); + myGame.stage.context.lineTo(p2.x, p2.y); + myGame.stage.context.lineTo(p3.x, p3.y); + myGame.stage.context.lineTo(p4.x, p4.y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(0,255,0)'; + myGame.stage.context.fillRect(p3.x, p3.y, 4, 4); + myGame.stage.context.fillStyle = 'rgb(255,0,255)'; + myGame.stage.context.fillRect(p4.x, p4.y, 4, 4); + } +})(); diff --git a/todo/phaser tests/geometry/rotate point 2.ts b/todo/phaser tests/geometry/rotate point 2.ts new file mode 100644 index 00000000..92ce8034 --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 2.ts @@ -0,0 +1,62 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var p1:Phaser.Point; + var p2:Phaser.Point; + var p3:Phaser.Point; + var p4:Phaser.Point; + + var d2: number = 0; + var d3: number = 0; + var d4: number = 0; + + function create() { + + p1 = new Phaser.Point(myGame.stage.centerX, myGame.stage.centerY); + p2 = new Phaser.Point(p1.x - 50, p1.y - 50); + p3 = new Phaser.Point(p2.x - 50, p2.y - 50); + p4 = new Phaser.Point(p3.x - 50, p3.y - 50); + + } + + function update() { + + p2.rotate(p1.x, p1.y, myGame.math.wrapAngle(d2), true, 150); + p3.rotate(p2.x, p2.y, myGame.math.wrapAngle(d3), true, 50); + p4.rotate(p3.x, p3.y, myGame.math.wrapAngle(d4), true, 100); + + d2 += 1; + d3 += 4; + d4 += 6; + + } + + function render() { + + myGame.stage.context.strokeStyle = 'rgb(0,255,255)'; + myGame.stage.context.beginPath(); + myGame.stage.context.moveTo(p1.x, p1.y); + myGame.stage.context.lineTo(p2.x, p2.y); + myGame.stage.context.lineTo(p3.x, p3.y); + myGame.stage.context.lineTo(p4.x, p4.y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.fillRect(p1.x, p1.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.fillRect(p2.x, p2.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(0,255,0)'; + myGame.stage.context.fillRect(p3.x, p3.y, 4, 4); + + myGame.stage.context.fillStyle = 'rgb(255,0,255)'; + myGame.stage.context.fillRect(p4.x, p4.y, 4, 4); + + } + +})(); diff --git a/todo/phaser tests/geometry/rotate point 3.js b/todo/phaser tests/geometry/rotate point 3.js new file mode 100644 index 00000000..13169cb2 --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 3.js @@ -0,0 +1,71 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var origin; + var p1; + var p2; + var p3; + var p4; + var d = 0; + function create() { + // This creates a box made up of 4 edge-points and rotates it around the origin + origin = new Phaser.Point(400, 300); + p1 = new Phaser.Point()// top left + ; + p2 = new Phaser.Point()// top right + ; + p3 = new Phaser.Point()// bottom right + ; + p4 = new Phaser.Point()// bottom left + ; + } + function update() { + // top left (red) + p1.rotate(origin.x, origin.y, myGame.math.wrapAngle(d), true, 200); + // top right (yellow) + p2.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 90), true, 200); + // bottom right (aqua) + p3.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 180), true, 200); + // bottom left (blue) + p4.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 270), true, 200); + d++; + } + function render() { + // Render the shape + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgba(0,255,0,0.2)'; + myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + myGame.stage.context.lineWidth = 1; + myGame.stage.context.moveTo(p1.x, p1.y); + myGame.stage.context.lineTo(p2.x, p2.y); + myGame.stage.context.lineTo(p3.x, p3.y); + myGame.stage.context.lineTo(p4.x, p4.y); + myGame.stage.context.lineTo(p1.x, p1.y); + myGame.stage.context.fill(); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + // Render the points + myGame.stage.context.fillStyle = 'rgb(255,255,255)'; + myGame.stage.context.fillRect(origin.x, origin.y, 4, 4); + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.arc(p1.x, p1.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.arc(p2.x, p2.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(0,255,255)'; + myGame.stage.context.arc(p3.x, p3.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(0,0,255)'; + myGame.stage.context.arc(p4.x, p4.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + } +})(); diff --git a/todo/phaser tests/geometry/rotate point 3.ts b/todo/phaser tests/geometry/rotate point 3.ts new file mode 100644 index 00000000..ad0c8d76 --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 3.ts @@ -0,0 +1,95 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var origin:Phaser.Point; + + var p1:Phaser.Point; + var p2:Phaser.Point; + var p3:Phaser.Point; + var p4:Phaser.Point; + + var d: number = 0; + + function create() { + + // This creates a box made up of 4 edge-points and rotates it around the origin + + origin = new Phaser.Point(400, 300); + + p1 = new Phaser.Point(); // top left + p2 = new Phaser.Point(); // top right + p3 = new Phaser.Point(); // bottom right + p4 = new Phaser.Point(); // bottom left + + } + + function update() { + + // top left (red) + p1.rotate(origin.x, origin.y, myGame.math.wrapAngle(d), true, 200); + + // top right (yellow) + p2.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 90), true, 200); + + // bottom right (aqua) + p3.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 180), true, 200); + + // bottom left (blue) + p4.rotate(origin.x, origin.y, myGame.math.wrapAngle(d + 270), true, 200); + + d++; + + } + + function render() { + + // Render the shape + + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgba(0,255,0,0.2)'; + myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + myGame.stage.context.lineWidth = 1; + myGame.stage.context.moveTo(p1.x, p1.y); + myGame.stage.context.lineTo(p2.x, p2.y); + myGame.stage.context.lineTo(p3.x, p3.y); + myGame.stage.context.lineTo(p4.x, p4.y); + myGame.stage.context.lineTo(p1.x, p1.y); + myGame.stage.context.fill(); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + + // Render the points + + myGame.stage.context.fillStyle = 'rgb(255,255,255)'; + myGame.stage.context.fillRect(origin.x, origin.y, 4, 4); + + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.arc(p1.x, p1.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(255,255,0)'; + myGame.stage.context.arc(p2.x, p2.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(0,255,255)'; + myGame.stage.context.arc(p3.x, p3.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + + myGame.stage.context.beginPath(); + myGame.stage.context.fillStyle = 'rgb(0,0,255)'; + myGame.stage.context.arc(p4.x, p4.y, 4, 0, Math.PI * 2); + myGame.stage.context.fill(); + myGame.stage.context.closePath(); + + } + +})(); diff --git a/todo/phaser tests/geometry/rotate point 4.js b/todo/phaser tests/geometry/rotate point 4.js new file mode 100644 index 00000000..ea9ed35c --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 4.js @@ -0,0 +1,54 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var origin; + var origin2; + var points = []; + var points2 = []; + var d = 0; + var d2 = 0; + var m = 64; + function create() { + // Let's have some fun :) + origin = new Phaser.Point(300, 200); + origin2 = new Phaser.Point(600, 350); + for(var i = 0; i < m; i++) { + points.push(new Phaser.Point()); + points2.push(new Phaser.Point()); + } + } + function update() { + for(var i = 0; i < m; i++) { + points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360 / m))), true, i * 5); + //points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, i * 10); + //points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, 200); + points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360 / m))), true, 200); + } + d -= 2; + d2 += 2; + } + function render() { + // Render the shape + myGame.stage.context.save(); + //myGame.stage.context.globalCompositeOperation = 'xor'; + myGame.stage.context.globalCompositeOperation = 'lighter'; + myGame.stage.context.lineWidth = 20; + for(var i = 0; i < m; i++) { + myGame.stage.context.beginPath(); + myGame.stage.context.strokeStyle = 'rgba(255,' + Math.round(i * (255 / m)).toString() + ',0,1)'; + myGame.stage.context.moveTo(origin.x, origin.y); + myGame.stage.context.lineTo(points[i].x, points[i].y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + } + for(var i = 0; i < m; i++) { + myGame.stage.context.beginPath(); + myGame.stage.context.strokeStyle = 'rgba(0,' + Math.round(i * (255 / m)).toString() + ',255,1)'; + myGame.stage.context.moveTo(origin2.x, origin2.y); + myGame.stage.context.lineTo(points2[i].x, points2[i].y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + } + myGame.stage.context.restore(); + } +})(); diff --git a/todo/phaser tests/geometry/rotate point 4.ts b/todo/phaser tests/geometry/rotate point 4.ts new file mode 100644 index 00000000..020c268c --- /dev/null +++ b/todo/phaser tests/geometry/rotate point 4.ts @@ -0,0 +1,82 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var origin:Phaser.Point; + var origin2:Phaser.Point; + + var points:Phaser.Point[] = []; + var points2:Phaser.Point[] = []; + + var d: number = 0; + var d2: number = 0; + var m: number = 64; + + function create() { + + // Let's have some fun :) + + origin = new Phaser.Point(300, 200); + origin2 = new Phaser.Point(600, 350); + + for (var i = 0; i < m; i++) + { + points.push(new Phaser.Point()); + points2.push(new Phaser.Point()); + } + + } + + function update() { + + for (var i = 0; i < m; i++) + { + points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, i * 5); + //points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, i * 10); + + //points[i].rotate(origin.x, origin.y, myGame.math.wrapAngle(d + (i * (360/m))), true, 200); + points2[i].rotate(origin2.x, origin2.y, myGame.math.wrapAngle(d2 + (i * (360/m))), true, 200); + } + + d -= 2; + d2 += 2; + + } + + function render() { + + // Render the shape + + myGame.stage.context.save(); + //myGame.stage.context.globalCompositeOperation = 'xor'; + myGame.stage.context.globalCompositeOperation = 'lighter'; + myGame.stage.context.lineWidth = 20; + + for (var i = 0; i < m; i++) + { + myGame.stage.context.beginPath(); + myGame.stage.context.strokeStyle = 'rgba(255,' + Math.round(i * (255/m)).toString() + ',0,1)'; + myGame.stage.context.moveTo(origin.x, origin.y); + myGame.stage.context.lineTo(points[i].x, points[i].y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + } + + + for (var i = 0; i < m; i++) + { + myGame.stage.context.beginPath(); + myGame.stage.context.strokeStyle = 'rgba(0,' + Math.round(i * (255/m)).toString() + ',255,1)'; + myGame.stage.context.moveTo(origin2.x, origin2.y); + myGame.stage.context.lineTo(points2[i].x, points2[i].y); + myGame.stage.context.stroke(); + myGame.stage.context.closePath(); + } + + myGame.stage.context.restore(); + + } + +})(); diff --git a/todo/phaser tests/geometry/verlet 1.js b/todo/phaser tests/geometry/verlet 1.js new file mode 100644 index 00000000..ac4a3816 --- /dev/null +++ b/todo/phaser tests/geometry/verlet 1.js @@ -0,0 +1,26 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var segment; + function create() { + myGame.verlet.friction = 1; + segment = myGame.verlet.createLineSegments([ + new Phaser.Vector2(20, 10), + new Phaser.Vector2(40, 10), + new Phaser.Vector2(60, 10), + new Phaser.Vector2(80, 10), + new Phaser.Vector2(100, 10) + ], 0.02); + segment.pin(0); + segment.pin(4); + var wheel = myGame.verlet.createTire(new Phaser.Vector2(200, 50), 100, 30, 0.3, 0.9); + var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400, 50), 70, 7, 0.1, 0.2); + var cube = myGame.verlet.createTire(new Phaser.Vector2(600, 50), 70, 4, 1, 1); + var tri = myGame.verlet.createTire(new Phaser.Vector2(700, 50), 100, 3, 1, 1); + } + function update() { + } + function render() { + myGame.verlet.render(); + } +})(); diff --git a/todo/phaser tests/geometry/verlet 1.ts b/todo/phaser tests/geometry/verlet 1.ts new file mode 100644 index 00000000..d6265c00 --- /dev/null +++ b/todo/phaser tests/geometry/verlet 1.ts @@ -0,0 +1,33 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var segment: Phaser.Verlet.Composite; + + function create() { + + myGame.verlet.friction = 1; + + segment = myGame.verlet.createLineSegments([new Phaser.Vector2(20, 10), new Phaser.Vector2(40, 10), new Phaser.Vector2(60, 10), new Phaser.Vector2(80, 10), new Phaser.Vector2(100, 10)], 0.02); + segment.pin(0); + segment.pin(4); + + var wheel = myGame.verlet.createTire(new Phaser.Vector2(200,50), 100, 30, 0.3, 0.9); + var tire2 = myGame.verlet.createTire(new Phaser.Vector2(400,50), 70, 7, 0.1, 0.2); + var cube = myGame.verlet.createTire(new Phaser.Vector2(600,50), 70, 4, 1, 1); + var tri = myGame.verlet.createTire(new Phaser.Vector2(700,50), 100, 3, 1, 1); + + } + + function update() { + } + + function render() { + + myGame.verlet.render(); + + } + +})(); diff --git a/todo/phaser tests/geometry/verlet sprites.js b/todo/phaser tests/geometry/verlet sprites.js new file mode 100644 index 00000000..52f41605 --- /dev/null +++ b/todo/phaser tests/geometry/verlet sprites.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.load(); + } + var wheel; + var diamond; + var triangle; + var cube; + function create() { + myGame.verlet.friction = 1; + myGame.verlet.step = 32; + wheel = myGame.verlet.createTire(new Phaser.Vector2(200, 50), 100, 30, 0.3, 0.9); + wheel.loadGraphic('ball0'); + diamond = myGame.verlet.createTire(new Phaser.Vector2(400, 50), 70, 7, 0.1, 0.2); + diamond.loadGraphic('ball1'); + triangle = myGame.verlet.createTire(new Phaser.Vector2(600, 50), 100, 3, 1, 1); + triangle.loadGraphic('ball2'); + cube = myGame.verlet.createTire(new Phaser.Vector2(300, 50), 100, 4, 0.3, 0.9); + cube.loadGraphic('ball3'); + } + function update() { + } + function render() { + myGame.verlet.render(); + } +})(); diff --git a/todo/phaser tests/geometry/verlet sprites.ts b/todo/phaser tests/geometry/verlet sprites.ts new file mode 100644 index 00000000..3432b36b --- /dev/null +++ b/todo/phaser tests/geometry/verlet sprites.ts @@ -0,0 +1,53 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + + myGame.loader.load(); + + } + + var wheel: Phaser.Verlet.Composite; + var diamond: Phaser.Verlet.Composite; + var triangle: Phaser.Verlet.Composite; + var cube: Phaser.Verlet.Composite; + + function create() { + + myGame.verlet.friction = 1; + myGame.verlet.step = 32; + + wheel = myGame.verlet.createTire(new Phaser.Vector2(200,50), 100, 30, 0.3, 0.9); + wheel.loadGraphic('ball0'); + + diamond = myGame.verlet.createTire(new Phaser.Vector2(400,50), 70, 7, 0.1, 0.2); + diamond.loadGraphic('ball1'); + + triangle = myGame.verlet.createTire(new Phaser.Vector2(600,50), 100, 3, 1, 1); + triangle.loadGraphic('ball2'); + + cube = myGame.verlet.createTire(new Phaser.Vector2(300, 50), 100, 4, 0.3, 0.9); + cube.loadGraphic('ball3'); + + } + + function update() { + } + + function render() { + + myGame.verlet.render(); + + } + +})(); diff --git a/todo/phaser tests/groups/basic group.js b/todo/phaser tests/groups/basic group.js new file mode 100644 index 00000000..a0a1fae1 --- /dev/null +++ b/todo/phaser tests/groups/basic group.js @@ -0,0 +1,39 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.world.setSize(1920, 1920); + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + var car; + var melons; + function create() { + myGame.add.sprite(0, 0, 'grid'); + melons = myGame.add.group(); + for(var i = 0; i < 100; i++) { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'melon'); + tempSprite.scrollFactor.setTo(1.2, 1.2); + melons.add(tempSprite); + } + car = myGame.add.sprite(400, 300, 'car'); + myGame.camera.follow(car); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } +})(); diff --git a/todo/phaser tests/groups/basic group.ts b/todo/phaser tests/groups/basic group.ts new file mode 100644 index 00000000..344b6898 --- /dev/null +++ b/todo/phaser tests/groups/basic group.ts @@ -0,0 +1,66 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.world.setSize(1920, 1920); + + myGame.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var melons: Phaser.Group; + + function create() { + + myGame.add.sprite(0, 0, 'grid'); + + melons = myGame.add.group(); + + for (var i = 0; i < 100; i++) + { + var tempSprite = myGame.add.sprite(Math.random() * myGame.world.width, Math.random() * myGame.world.height, 'melon'); + tempSprite.scrollFactor.setTo(1.2, 1.2); + melons.add(tempSprite); + } + + car = myGame.add.sprite(400, 300, 'car'); + + myGame.camera.follow(car); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + +})(); diff --git a/todo/phaser tests/groups/display order.js b/todo/phaser tests/groups/display order.js new file mode 100644 index 00000000..089983b0 --- /dev/null +++ b/todo/phaser tests/groups/display order.js @@ -0,0 +1,32 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + myGame.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + myGame.loader.load(); + } + var items; + var card; + function create() { + items = myGame.add.group(); + // Items are rendered in the depth order in which they are added to the Group + items.add(myGame.add.sprite(64, 100, 'atari1')); + card = items.add(myGame.add.sprite(240, 80, 'card')); + items.add(myGame.add.sprite(280, 100, 'atari2')); + myGame.input.onTap.addOnce(removeCard, this); + } + function removeCard() { + // Now let's kill the card sprite + card.kill(); + myGame.input.onTap.addOnce(replaceCard, this); + } + function replaceCard() { + // And bring it back to life again - I assume it will render in the same place as before? + var bob = items.getFirstDead(); + bob.revive(); + } + function update() { + } +})(); diff --git a/todo/phaser tests/groups/display order.ts b/todo/phaser tests/groups/display order.ts new file mode 100644 index 00000000..38a12f5a --- /dev/null +++ b/todo/phaser tests/groups/display order.ts @@ -0,0 +1,56 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + myGame.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + + myGame.loader.load(); + + } + + var items: Phaser.Group; + var card: Phaser.Sprite; + + function create() { + + items = myGame.add.group(); + + // Items are rendered in the depth order in which they are added to the Group + + items.add(myGame.add.sprite(64, 100, 'atari1')); + card = items.add(myGame.add.sprite(240, 80, 'card')); + items.add(myGame.add.sprite(280, 100, 'atari2')); + + myGame.input.onTap.addOnce(removeCard, this); + + } + + function removeCard() { + + // Now let's kill the card sprite + card.kill(); + + myGame.input.onTap.addOnce(replaceCard, this); + + } + + function replaceCard() { + + // And bring it back to life again - I assume it will render in the same place as before? + var bob = items.getFirstDead(); + + bob.revive(); + + } + + function update() { + + } + +})(); diff --git a/todo/phaser tests/input/mouse scale.js b/todo/phaser tests/input/mouse scale.js new file mode 100644 index 00000000..9a4f2146 --- /dev/null +++ b/todo/phaser tests/input/mouse scale.js @@ -0,0 +1,35 @@ +/// +(function () { + // Here we create a quite tiny game (320x240 in size) + var myGame = new Phaser.Game(this, 'game', 320, 240, init, create, update, render); + function init() { + // This sets a limit on the up-scale + myGame.stage.scale.maxWidth = 640; + myGame.stage.scale.maxHeight = 480; + // Then we tell Phaser that we want it to scale up to whatever the browser can handle, but to do it proportionally + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + function create() { + myGame.world.setSize(2000, 2000); + for(var i = 0; i < 1000; i++) { + myGame.add.sprite(myGame.world.randomX, myGame.world.randomY, 'melon'); + } + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y += 4; + } + } + function render() { + myGame.input.renderDebugInfo(16, 16); + } +})(); diff --git a/todo/phaser tests/input/mouse scale.ts b/todo/phaser tests/input/mouse scale.ts new file mode 100644 index 00000000..6c6997cf --- /dev/null +++ b/todo/phaser tests/input/mouse scale.ts @@ -0,0 +1,62 @@ +/// + +(function () { + + // Here we create a quite tiny game (320x240 in size) + var myGame = new Phaser.Game(this, 'game', 320, 240, init, create, update, render); + + function init() { + + // This sets a limit on the up-scale + myGame.stage.scale.maxWidth = 640; + myGame.stage.scale.maxHeight = 480; + + // Then we tell Phaser that we want it to scale up to whatever the browser can handle, but to do it proportionally + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + function create() { + + myGame.world.setSize(2000, 2000); + + for (var i = 0; i < 1000; i++) + { + myGame.add.sprite(myGame.world.randomX, myGame.world.randomY, 'melon'); + } + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y += 4; + } + + } + + function render() { + + myGame.input.renderDebugInfo(16, 16); + + } + +})(); diff --git a/todo/phaser tests/input/multitouch.js b/todo/phaser tests/input/multitouch.js new file mode 100644 index 00000000..02a627a8 --- /dev/null +++ b/todo/phaser tests/input/multitouch.js @@ -0,0 +1,23 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('dragonsun', 'assets/pics/cougar_dragonsun.png'); + myGame.loader.load(); + } + function create() { + console.log('dragons 8'); + myGame.input.onDown.add(test1, this); + } + function test1() { + console.log('down'); + } + function update() { + } + function render() { + myGame.input.pointer1.renderDebug(); + myGame.input.pointer2.renderDebug(); + myGame.input.pointer3.renderDebug(); + myGame.input.pointer4.renderDebug(); + } +})(); diff --git a/todo/phaser tests/input/multitouch.ts b/todo/phaser tests/input/multitouch.ts new file mode 100644 index 00000000..b75d747f --- /dev/null +++ b/todo/phaser tests/input/multitouch.ts @@ -0,0 +1,40 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('dragonsun', 'assets/pics/cougar_dragonsun.png'); + + myGame.loader.load(); + + } + + function create() { + + console.log('dragons 8'); + + myGame.input.onDown.add(test1, this); + + } + + function test1() { + console.log('down'); + } + + function update() { + + } + + function render() { + + myGame.input.pointer1.renderDebug(); + myGame.input.pointer2.renderDebug(); + myGame.input.pointer3.renderDebug(); + myGame.input.pointer4.renderDebug(); + + } + +})(); diff --git a/todo/phaser tests/input/single tap.js b/todo/phaser tests/input/single tap.js new file mode 100644 index 00000000..efbae556 --- /dev/null +++ b/todo/phaser tests/input/single tap.js @@ -0,0 +1,39 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + myGame.loader.load(); + } + var balls; + function create() { + balls = myGame.add.group(); + myGame.input.onTap.add(tapped, this); + } + function tapped(pointer, doubleTap) { + if(balls.countDead() > 0) { + var tempBall = balls.getFirstDead(); + tempBall.revive(); + tempBall.x = pointer.x; + tempBall.y = pointer.y; + } else { + var tempBall = new Phaser.Sprite(myGame, pointer.x, pointer.y, 'ball' + Math.round(Math.random() * 5)); + tempBall.setBoundsFromWorld(Phaser.GameObject.OUT_OF_BOUNDS_KILL); + balls.add(tempBall); + } + tempBall.velocity.y = 150; + if(doubleTap) { + tempBall.scale.setTo(4, 4); + } + } + function update() { + } + function render() { + myGame.input.renderDebugInfo(16, 16); + } +})(); diff --git a/todo/phaser tests/input/single tap.ts b/todo/phaser tests/input/single tap.ts new file mode 100644 index 00000000..db3e525a --- /dev/null +++ b/todo/phaser tests/input/single tap.ts @@ -0,0 +1,64 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('ball0', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/green_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/purple_ball.png'); + + myGame.loader.load(); + + } + + var balls: Phaser.Group; + + function create() { + + balls = myGame.add.group(); + + myGame.input.onTap.add(tapped, this); + + } + + function tapped(pointer: Phaser.Pointer, doubleTap: bool) { + + if (balls.countDead() > 0) + { + var tempBall: Phaser.Sprite = balls.getFirstDead(); + tempBall.revive(); + tempBall.x = pointer.x; + tempBall.y = pointer.y; + } + else + { + var tempBall: Phaser.Sprite = new Phaser.Sprite(myGame, pointer.x, pointer.y, 'ball' + Math.round(Math.random() * 5)); + tempBall.setBoundsFromWorld(Phaser.GameObject.OUT_OF_BOUNDS_KILL); + balls.add(tempBall); + } + + tempBall.velocity.y = 150; + + if (doubleTap) + { + tempBall.scale.setTo(4, 4); + } + + } + + function update() { + } + + function render() { + + myGame.input.renderDebugInfo(16, 16); + + } + +})(); diff --git a/todo/phaser tests/input/single touch.js b/todo/phaser tests/input/single touch.js new file mode 100644 index 00000000..3dc654d4 --- /dev/null +++ b/todo/phaser tests/input/single touch.js @@ -0,0 +1,19 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + } + function create() { + // We lock the game to allowing only 1 Pointer active + // This means on multi-touch systems it will ignore any extra fingers placed down beyond the first + myGame.input.maxPointers = 1; + } + function update() { + } + function render() { + myGame.input.renderDebugInfo(16, 16); + myGame.input.pointer1.renderDebug(true); + myGame.input.pointer2.renderDebug(true); + myGame.input.pointer3.renderDebug(true); + } +})(); diff --git a/todo/phaser tests/input/single touch.ts b/todo/phaser tests/input/single touch.ts new file mode 100644 index 00000000..57570ddf --- /dev/null +++ b/todo/phaser tests/input/single touch.ts @@ -0,0 +1,31 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + } + + function create() { + + // We lock the game to allowing only 1 Pointer active + // This means on multi-touch systems it will ignore any extra fingers placed down beyond the first + myGame.input.maxPointers = 1; + + } + + function update() { + } + + function render() { + + myGame.input.renderDebugInfo(16, 16); + + myGame.input.pointer1.renderDebug(true); + myGame.input.pointer2.renderDebug(true); + myGame.input.pointer3.renderDebug(true); + + } + +})(); diff --git a/todo/phaser tests/mini games/formula 1.js b/todo/phaser tests/mini games/formula 1.js new file mode 100644 index 00000000..f58f4ac9 --- /dev/null +++ b/todo/phaser tests/mini games/formula 1.js @@ -0,0 +1,36 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 840, 400, init, create, update); + function init() { + myGame.loader.addImageFile('track', 'assets/games/f1/track.png'); + myGame.loader.addImageFile('car', 'assets/games/f1/car1.png'); + myGame.loader.load(); + } + var car; + var bigCam; + function create() { + myGame.camera.setBounds(0, 0, myGame.stage.width, myGame.stage.height); + myGame.add.sprite(0, 0, 'track'); + car = myGame.add.sprite(180, 298, 'car'); + car.rotation = 180; + car.maxVelocity.setTo(150, 150); + bigCam = myGame.add.camera(640, 0, 100, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.stage.width, myGame.stage.height); + bigCam.showBorder = true; + bigCam.borderColor = 'rgb(0,0,0)'; + bigCam.scale.setTo(2, 2); + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.rotation -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.rotation += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 150)); + } else { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 60)); + } + } +})(); diff --git a/todo/phaser tests/mini games/formula 1.ts b/todo/phaser tests/mini games/formula 1.ts new file mode 100644 index 00000000..035c7a34 --- /dev/null +++ b/todo/phaser tests/mini games/formula 1.ts @@ -0,0 +1,59 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 840, 400, init, create, update); + + function init() { + + myGame.loader.addImageFile('track', 'assets/games/f1/track.png'); + myGame.loader.addImageFile('car', 'assets/games/f1/car1.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var bigCam: Phaser.Camera; + + function create() { + + myGame.camera.setBounds(0, 0, myGame.stage.width, myGame.stage.height); + myGame.add.sprite(0, 0, 'track'); + + car = myGame.add.sprite(180, 298, 'car'); + car.rotation = 180; + car.maxVelocity.setTo(150, 150); + + bigCam = myGame.add.camera(640, 0, 100, 200); + bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + bigCam.setBounds(0, 0, myGame.stage.width, myGame.stage.height); + bigCam.showBorder = true; + bigCam.borderColor = 'rgb(0,0,0)'; + bigCam.scale.setTo(2, 2); + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.rotation -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.rotation += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 150)); + } + else + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 60)); + } + + } + +})(); diff --git a/todo/phaser tests/misc/bootscreen.js b/todo/phaser tests/misc/bootscreen.js new file mode 100644 index 00000000..57cb3bfa --- /dev/null +++ b/todo/phaser tests/misc/bootscreen.js @@ -0,0 +1,4 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600); +})(); diff --git a/todo/phaser tests/misc/bootscreen.ts b/todo/phaser tests/misc/bootscreen.ts new file mode 100644 index 00000000..f0a67343 --- /dev/null +++ b/todo/phaser tests/misc/bootscreen.ts @@ -0,0 +1,7 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600); + +})(); diff --git a/todo/phaser tests/misc/multi game.js b/todo/phaser tests/misc/multi game.js new file mode 100644 index 00000000..8e1944ed --- /dev/null +++ b/todo/phaser tests/misc/multi game.js @@ -0,0 +1,58 @@ +/// +(function () { + // Let's test having 2 totally separate games embedded on the same page + var myGame = new Phaser.Game(this, 'game', 400, 400, init, create, update); + // They can share the same parent div, they'll just butt-up next to each other + var myGame2 = new Phaser.Game(this, 'game', 400, 400, init2, create2, update2); + function init() { + myGame.world.setSize(3000, 3000); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.load(); + } + function create() { + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + for(var i = 0; i < 1000; i++) { + myGame.add.sprite(myGame.world.randomX, myGame.world.randomY, 'melon'); + } + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + myGame.camera.scroll.x -= 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + myGame.camera.scroll.x += 4; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + myGame.camera.scroll.y += 4; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + myGame.camera.scroll.y -= 4; + } + } + // And now for game 2, we're basically just duplicating the functions from above, but that's fine for this test + function init2() { + myGame2.world.setSize(1920, 1920); + myGame2.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame2.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame2.loader.load(); + } + var car; + function create2() { + myGame2.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + myGame2.add.sprite(0, 0, 'grid'); + car = myGame2.add.sprite(400, 300, 'car'); + myGame2.camera.follow(car); + } + function update2() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame2.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame2.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame2.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } +})(); diff --git a/todo/phaser tests/misc/multi game.ts b/todo/phaser tests/misc/multi game.ts new file mode 100644 index 00000000..2704c3ca --- /dev/null +++ b/todo/phaser tests/misc/multi game.ts @@ -0,0 +1,104 @@ +/// + +(function () { + + // Let's test having 2 totally separate games embedded on the same page + var myGame = new Phaser.Game(this, 'game', 400, 400, init, create, update); + + // They can share the same parent div, they'll just butt-up next to each other + var myGame2 = new Phaser.Game(this, 'game', 400, 400, init2, create2, update2); + + function init() { + + myGame.world.setSize(3000, 3000); + + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + + myGame.loader.load(); + + } + + function create() { + + myGame.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + for (var i = 0; i < 1000; i++) + { + myGame.add.sprite(myGame.world.randomX, myGame.world.randomY, 'melon'); + } + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + myGame.camera.scroll.x -= 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + myGame.camera.scroll.x += 4; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + myGame.camera.scroll.y += 4; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + myGame.camera.scroll.y -= 4; + } + + } + + // And now for game 2, we're basically just duplicating the functions from above, but that's fine for this test + + function init2() { + + myGame2.world.setSize(1920, 1920); + + myGame2.loader.addImageFile('grid', 'assets/tests/debug-grid-1920x1920.png'); + myGame2.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame2.loader.load(); + + } + + var car; + + function create2() { + + myGame2.camera.setBounds(0, 0, myGame.world.width, myGame.world.height); + + myGame2.add.sprite(0, 0, 'grid'); + + car = myGame2.add.sprite(400, 300, 'car'); + + myGame2.camera.follow(car); + + } + + function update2() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame2.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame2.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame2.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + +})(); diff --git a/todo/phaser tests/misc/starfield.js b/todo/phaser tests/misc/starfield.js new file mode 100644 index 00000000..55f3e468 --- /dev/null +++ b/todo/phaser tests/misc/starfield.js @@ -0,0 +1,46 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + var starfield; + var xx = []; + var yy = []; + var zz = []; + var xxx = 0; + var yyy = 0; + function create() { + // the width of the starfield + var star_w = 12000; + for(var i = 0; i < 800; i++) { + xx[i] = Math.floor(Math.random() * star_w * 2) - star_w; + yy[i] = Math.floor(Math.random() * star_w * 2) - star_w; + zz[i] = Math.floor(Math.random() * 160) + 1; + } + starfield = myGame.add.dynamicTexture(800, 600); + } + function update() { + starfield.clear(); + for(var i = 0; i < 800; i++) { + if(zz[i] == 1) { + zz[i] = 100; + } + xxx = (xx[i]) / (zz[i]); + yyy = (yy[i]) / (zz[i])--; + //var x: number = xxx + myGame.input.x; + //var y: number = yyy + myGame.input.y; + var x = xxx + 400; + var y = yyy + 300; + var c = '#ffffff'; + if(zz[i] > 80) { + c = '#666666'; + } else if(zz[i] > 60) { + c = '#888888'; + } else if(zz[i] > 40) { + c = '#aaaaaa'; + } + starfield.setPixel(x, y, c); + } + } + function render() { + starfield.render(); + } +})(); diff --git a/todo/phaser tests/misc/starfield.ts b/todo/phaser tests/misc/starfield.ts new file mode 100644 index 00000000..ae5e0814 --- /dev/null +++ b/todo/phaser tests/misc/starfield.ts @@ -0,0 +1,61 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create, update, render); + + var starfield: Phaser.DynamicTexture; + + var xx = []; + var yy = []; + var zz = []; + var xxx = 0; + var yyy = 0; + + function create() { + + // the width of the starfield + var star_w: number = 12000; + + for (var i: number = 0; i < 800; i++) + { + xx[i] = Math.floor(Math.random() * star_w * 2) - star_w + yy[i] = Math.floor(Math.random() * star_w * 2) - star_w + zz[i] = Math.floor(Math.random() * 160) + 1; + } + + starfield = myGame.add.dynamicTexture(800, 600); + + } + + function update() { + + starfield.clear(); + + for (var i: number = 0; i < 800; i++) + { + if (zz[i] == 1) zz[i] = 100; + xxx = (xx[i]) / (zz[i]); + yyy = (yy[i]) / (zz[i])--; + //var x: number = xxx + myGame.input.x; + //var y: number = yyy + myGame.input.y; + var x: number = xxx + 400; + var y: number = yyy + 300; + var c: string = '#ffffff'; + + if (zz[i] > 80) c = '#666666'; + else if (zz[i] > 60) c = '#888888' + else if (zz[i] > 40) c = '#aaaaaa'; + + starfield.setPixel(x, y, c); + } + + } + + function render() { + + starfield.render(); + + } + +})(); diff --git a/todo/phaser tests/misc/time.js b/todo/phaser tests/misc/time.js new file mode 100644 index 00000000..75fe1bf8 --- /dev/null +++ b/todo/phaser tests/misc/time.js @@ -0,0 +1,33 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('car', 'assets/sprites/asteroids_ship.png'); + myGame.loader.load(); + } + var car; + function create() { + car = myGame.add.sprite(200, 300, 'car'); + myGame.onRenderCallback = render; + myGame.stage.context.font = '16px Arial'; + myGame.stage.context.fillStyle = 'rgb(255,255,255)'; + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 200); + car.velocity.copyFrom(motion); + } + } + function render() { + myGame.stage.context.fillText(myGame.time.time.toString(), 32, 32); + } +})(); diff --git a/todo/phaser tests/misc/time.ts b/todo/phaser tests/misc/time.ts new file mode 100644 index 00000000..95872f1b --- /dev/null +++ b/todo/phaser tests/misc/time.ts @@ -0,0 +1,58 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('car', 'assets/sprites/asteroids_ship.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + + function create() { + + car = myGame.add.sprite(200, 300, 'car'); + + myGame.onRenderCallback = render; + + myGame.stage.context.font = '16px Arial'; + myGame.stage.context.fillStyle = 'rgb(255,255,255)'; + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 200); + car.velocity.copyFrom(motion); + } + + } + + function render() { + + myGame.stage.context.fillText(myGame.time.time.toString(), 32, 32); + + } + +})(); diff --git a/todo/phaser tests/mobile/bunny mobile.js b/todo/phaser tests/mobile/bunny mobile.js new file mode 100644 index 00000000..767cf72c --- /dev/null +++ b/todo/phaser tests/mobile/bunny mobile.js @@ -0,0 +1,58 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 320, 460, init, create, update, render); + function init() { + myGame.loader.addImageFile('bunny', 'assets/sprites/wabbit.png'); + myGame.loader.load(); + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + } + var maxX; + var maxY; + var minX; + var minY; + function create() { + minX = 0; + minY = 0; + maxX = myGame.stage.width - 26; + maxY = myGame.stage.height - 37; + myGame.input.onDown.add(addBunnies, this); + // This will really help on slow Android phones + myGame.framerate = 30; + // Make sure the camera doesn't clip anything + myGame.camera.disableClipping = true; + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.font = '20px Arial'; + addBunnies(); + } + function addBunnies() { + for(var i = 0; i < 10; i++) { + var tempSprite = myGame.add.sprite(myGame.stage.randomX, 0, 'bunny'); + tempSprite.velocity.x = -200 + (Math.random() * 400); + tempSprite.velocity.y = 100 + Math.random() * 200; + } + } + function update() { + myGame.world.group.forEach(checkWalls); + } + function render() { + // Note: Displaying canvas text causes a big performance hit on mobile + myGame.stage.context.fillText("fps: " + myGame.time.fps.toString(), 0, 32); + } + function checkWalls(bunny) { + if(bunny.x > maxX) { + bunny.velocity.x *= -1; + bunny.x = maxX; + } else if(bunny.x < minX) { + bunny.velocity.x *= -1; + bunny.x = minX; + } + if(bunny.y > maxY) { + bunny.velocity.y *= -0.8; + bunny.y = maxY; + } else if(bunny.y < minY) { + bunny.velocity.x = -200 + (Math.random() * 400); + bunny.velocity.y = 100 + Math.random() * 200; + bunny.y = minY; + } + } +})(); diff --git a/todo/phaser tests/mobile/bunny mobile.ts b/todo/phaser tests/mobile/bunny mobile.ts new file mode 100644 index 00000000..6cf9f940 --- /dev/null +++ b/todo/phaser tests/mobile/bunny mobile.ts @@ -0,0 +1,94 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 320, 460, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('bunny', 'assets/sprites/wabbit.png'); + + myGame.loader.load(); + + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + + } + + var maxX: number; + var maxY: number; + var minX: number; + var minY: number; + + function create() { + + minX = 0; + minY = 0; + maxX = myGame.stage.width - 26; + maxY = myGame.stage.height - 37; + + myGame.input.onDown.add(addBunnies, this); + + // This will really help on slow Android phones + myGame.framerate = 30; + // Make sure the camera doesn't clip anything + myGame.camera.disableClipping = true; + + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.font = '20px Arial'; + + addBunnies(); + + } + + function addBunnies() { + + for (var i = 0; i < 10; i++) + { + var tempSprite = myGame.add.sprite(myGame.stage.randomX, 0, 'bunny'); + tempSprite.velocity.x = -200 + (Math.random() * 400); + tempSprite.velocity.y = 100 + Math.random() * 200; + } + + } + + function update() { + + myGame.world.group.forEach(checkWalls); + + } + + function render() { + + // Note: Displaying canvas text causes a big performance hit on mobile + myGame.stage.context.fillText("fps: " + myGame.time.fps.toString(), 0, 32); + + } + + function checkWalls(bunny:Phaser.Sprite) { + + if (bunny.x > maxX) + { + bunny.velocity.x *= -1; + bunny.x = maxX; + } + else if (bunny.x < minX) + { + bunny.velocity.x *= -1; + bunny.x = minX; + } + + if (bunny.y > maxY) + { + bunny.velocity.y *= -0.8; + bunny.y = maxY; + } + else if (bunny.y < minY) + { + bunny.velocity.x = -200 + (Math.random() * 400); + bunny.velocity.y = 100 + Math.random() * 200; + bunny.y = minY; + } + + } + +})(); diff --git a/todo/phaser tests/mobile/fullscreen.html b/todo/phaser tests/mobile/fullscreen.html new file mode 100644 index 00000000..0c669898 --- /dev/null +++ b/todo/phaser tests/mobile/fullscreen.html @@ -0,0 +1,62 @@ + + + + + + Phaser Mobile Fullscreen Test + + + + + +
+ + + + + \ No newline at end of file diff --git a/todo/phaser tests/mobile/sprite test 1.js b/todo/phaser tests/mobile/sprite test 1.js new file mode 100644 index 00000000..717a958f --- /dev/null +++ b/todo/phaser tests/mobile/sprite test 1.js @@ -0,0 +1,46 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 320, 400, init, create); + var emitter; + function init() { + myGame.loader.addImageFile('backdrop1', 'assets/pics/atari_fujilogo.png'); + myGame.loader.addImageFile('backdrop2', 'assets/pics/acryl_bladerunner.png'); + myGame.loader.addImageFile('jet', 'assets/sprites/carrot.png'); + myGame.loader.load(); + // This can help a lot on crappy old Android phones :) + //myGame.framerate = 30; + myGame.stage.backgroundColor = 'rgb(50,50,50)'; + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + //myGame.stage.scaleMode = Phaser.StageScaleMode.EXACT_FIT; + //myGame.stage.scaleMode = Phaser.StageScaleMode.NO_SCALE; + } + var pic1; + var pic2; + function create() { + pic1 = myGame.add.sprite(0, 0, 'backdrop1'); + pic2 = myGame.add.sprite(0, 0, 'backdrop2'); + // Creates a basic emitter, bursting out 50 default sprites (i.e. 16x16 white boxes) + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 50, false, 0); + emitter.setRotation(0, 0); + emitter.start(false, 10, 0.1); + // Make sure the camera doesn't clip anything + myGame.camera.disableClipping = true; + myGame.stage.scale.enterLandscape.add(goneLandscape, this); + myGame.stage.scale.enterPortrait.add(gonePortrait, this); + myGame.onRenderCallback = render; + } + function goneLandscape() { + pic1.visible = true; + pic2.visible = false; + } + function gonePortrait() { + pic1.visible = false; + pic2.visible = true; + } + function render() { + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.font = '20px Arial'; + //myGame.stage.context.fillText("ttc: " + myGame._raf.timeToCall.toString(), 0, 64); + } +})(); diff --git a/todo/phaser tests/mobile/sprite test 1.ts b/todo/phaser tests/mobile/sprite test 1.ts new file mode 100644 index 00000000..fac3d440 --- /dev/null +++ b/todo/phaser tests/mobile/sprite test 1.ts @@ -0,0 +1,73 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 320, 400, init, create); + + var emitter: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('backdrop1', 'assets/pics/atari_fujilogo.png'); + myGame.loader.addImageFile('backdrop2', 'assets/pics/acryl_bladerunner.png'); + myGame.loader.addImageFile('jet', 'assets/sprites/carrot.png'); + + myGame.loader.load(); + + // This can help a lot on crappy old Android phones :) + //myGame.framerate = 30; + + myGame.stage.backgroundColor = 'rgb(50,50,50)'; + myGame.stage.scaleMode = Phaser.StageScaleMode.SHOW_ALL; + //myGame.stage.scaleMode = Phaser.StageScaleMode.EXACT_FIT; + //myGame.stage.scaleMode = Phaser.StageScaleMode.NO_SCALE; + + } + + var pic1; + var pic2; + + function create() { + + pic1 = myGame.add.sprite(0, 0, 'backdrop1'); + pic2 = myGame.add.sprite(0, 0, 'backdrop2'); + + // Creates a basic emitter, bursting out 50 default sprites (i.e. 16x16 white boxes) + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 50, false, 0); + emitter.setRotation(0, 0); + emitter.start(false, 10, 0.1); + + // Make sure the camera doesn't clip anything + myGame.camera.disableClipping = true; + + myGame.stage.scale.enterLandscape.add(goneLandscape, this); + myGame.stage.scale.enterPortrait.add(gonePortrait, this); + + myGame.onRenderCallback = render; + + } + + function goneLandscape() { + + pic1.visible = true; + pic2.visible = false; + + } + + function gonePortrait() { + + pic1.visible = false; + pic2.visible = true; + + } + + function render() { + + myGame.stage.context.fillStyle = 'rgb(255,0,0)'; + myGame.stage.context.font = '20px Arial'; + //myGame.stage.context.fillText("ttc: " + myGame._raf.timeToCall.toString(), 0, 64); + + } + +})(); diff --git a/todo/phaser tests/particles/basic emitter.js b/todo/phaser tests/particles/basic emitter.js new file mode 100644 index 00000000..e5fa1650 --- /dev/null +++ b/todo/phaser tests/particles/basic emitter.js @@ -0,0 +1,11 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create); + var emitter; + function create() { + // Creates a basic emitter, bursting out 50 default sprites (i.e. 16x16 white boxes) + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles(null, 50, false, 0); + emitter.start(true); + } +})(); diff --git a/todo/phaser tests/particles/basic emitter.ts b/todo/phaser tests/particles/basic emitter.ts new file mode 100644 index 00000000..1dcc6367 --- /dev/null +++ b/todo/phaser tests/particles/basic emitter.ts @@ -0,0 +1,18 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, null, create); + + var emitter: Phaser.Emitter; + + function create() { + + // Creates a basic emitter, bursting out 50 default sprites (i.e. 16x16 white boxes) + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles(null, 50, false, 0); + emitter.start(true); + + } + +})(); diff --git a/todo/phaser tests/particles/graphic emitter.js b/todo/phaser tests/particles/graphic emitter.js new file mode 100644 index 00000000..eabb6d54 --- /dev/null +++ b/todo/phaser tests/particles/graphic emitter.js @@ -0,0 +1,14 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + var emitter; + function init() { + myGame.loader.addImageFile('jet', 'assets/sprites/jets.png'); + myGame.loader.load(); + } + function create() { + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 50, false, 0); + emitter.start(false, 10, 0.1); + } +})(); diff --git a/todo/phaser tests/particles/graphic emitter.ts b/todo/phaser tests/particles/graphic emitter.ts new file mode 100644 index 00000000..eff00efd --- /dev/null +++ b/todo/phaser tests/particles/graphic emitter.ts @@ -0,0 +1,25 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + var emitter: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('jet', 'assets/sprites/jets.png'); + + myGame.loader.load(); + + } + + function create() { + + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 50, false, 0); + emitter.start(false, 10, 0.1); + + } + +})(); diff --git a/todo/phaser tests/particles/mousetrail.js b/todo/phaser tests/particles/mousetrail.js new file mode 100644 index 00000000..8b5ac2e8 --- /dev/null +++ b/todo/phaser tests/particles/mousetrail.js @@ -0,0 +1,23 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + var emitter; + function init() { + myGame.loader.addImageFile('jet', 'assets/sprites/particle1.png'); + myGame.loader.load(); + } + function create() { + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 100); + emitter.gravity = 200; + emitter.setXSpeed(-50, 50); + emitter.setYSpeed(-50, -100); + emitter.setRotation(0, 0); + emitter.start(false, 10, 0.05); + } + function update() { + emitter.x = myGame.input.x; + emitter.y = myGame.input.y; + //emitter.em + } +})(); diff --git a/todo/phaser tests/particles/mousetrail.ts b/todo/phaser tests/particles/mousetrail.ts new file mode 100644 index 00000000..b179dfb2 --- /dev/null +++ b/todo/phaser tests/particles/mousetrail.ts @@ -0,0 +1,38 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + var emitter: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('jet', 'assets/sprites/particle1.png'); + + myGame.loader.load(); + + } + + function create() { + + emitter = myGame.add.emitter(myGame.stage.centerX, myGame.stage.centerY); + emitter.makeParticles('jet', 100); + + emitter.gravity = 200; + emitter.setXSpeed(-50, 50); + emitter.setYSpeed(-50, -100); + emitter.setRotation(0, 0); + emitter.start(false, 10, 0.05); + + } + + function update() { + + emitter.x = myGame.input.x; + emitter.y = myGame.input.y; + //emitter.em + + } + +})(); diff --git a/todo/phaser tests/particles/multiple streams.js b/todo/phaser tests/particles/multiple streams.js new file mode 100644 index 00000000..ee0454ef --- /dev/null +++ b/todo/phaser tests/particles/multiple streams.js @@ -0,0 +1,49 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + var emitter1; + var emitter2; + var emitter3; + var emitter4; + var emitter5; + var emitter6; + function init() { + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/purple_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball6', 'assets/sprites/green_ball.png'); + myGame.loader.load(); + } + function makeEmitter(emitter, x, y, graphic) { + emitter = myGame.add.emitter(x, y); + emitter.gravity = 100; + emitter.bounce = 0.5; + if(x == 0) { + emitter.setXSpeed(200, 250); + } else { + emitter.setXSpeed(-200, -250); + } + emitter.setYSpeed(-50, -10); + emitter.makeParticles(graphic, 250, false, 0); + return emitter; + } + function create() { + emitter1 = makeEmitter(emitter1, 0, 50, 'ball1'); + emitter2 = makeEmitter(emitter2, 0, 250, 'ball2'); + emitter3 = makeEmitter(emitter3, 0, 450, 'ball3'); + emitter4 = makeEmitter(emitter4, myGame.stage.width, 50, 'ball4'); + emitter5 = makeEmitter(emitter5, myGame.stage.width, 250, 'ball5'); + emitter6 = makeEmitter(emitter6, myGame.stage.width, 450, 'ball6'); + emitter1.start(false, 50, 0.05); + emitter2.start(false, 50, 0.05); + emitter3.start(false, 50, 0.05); + emitter4.start(false, 50, 0.05); + emitter5.start(false, 50, 0.05); + emitter6.start(false, 50, 0.05); + } + function update() { + //myGame.collide(leftEmitter, rightEmitter); + } +})(); diff --git a/todo/phaser tests/particles/multiple streams.ts b/todo/phaser tests/particles/multiple streams.ts new file mode 100644 index 00000000..129e1d98 --- /dev/null +++ b/todo/phaser tests/particles/multiple streams.ts @@ -0,0 +1,73 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + var emitter1: Phaser.Emitter; + var emitter2: Phaser.Emitter; + var emitter3: Phaser.Emitter; + var emitter4: Phaser.Emitter; + var emitter5: Phaser.Emitter; + var emitter6: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/yellow_ball.png'); + myGame.loader.addImageFile('ball3', 'assets/sprites/red_ball.png'); + myGame.loader.addImageFile('ball4', 'assets/sprites/purple_ball.png'); + myGame.loader.addImageFile('ball5', 'assets/sprites/blue_ball.png'); + myGame.loader.addImageFile('ball6', 'assets/sprites/green_ball.png'); + + myGame.loader.load(); + + } + + function makeEmitter(emitter, x, y, graphic) { + + emitter = myGame.add.emitter(x, y); + emitter.gravity = 100; + emitter.bounce = 0.5; + + if (x == 0) + { + emitter.setXSpeed(200, 250); + } + else + { + emitter.setXSpeed(-200, -250); + } + + emitter.setYSpeed(-50, -10); + emitter.makeParticles(graphic, 250, false, 0); + + return emitter; + + } + + function create() { + + emitter1 = makeEmitter(emitter1, 0, 50, 'ball1'); + emitter2 = makeEmitter(emitter2, 0, 250, 'ball2'); + emitter3 = makeEmitter(emitter3, 0, 450, 'ball3'); + emitter4 = makeEmitter(emitter4, myGame.stage.width, 50, 'ball4'); + emitter5 = makeEmitter(emitter5, myGame.stage.width, 250, 'ball5'); + emitter6 = makeEmitter(emitter6, myGame.stage.width, 450, 'ball6'); + + emitter1.start(false, 50, 0.05); + emitter2.start(false, 50, 0.05); + emitter3.start(false, 50, 0.05); + emitter4.start(false, 50, 0.05); + emitter5.start(false, 50, 0.05); + emitter6.start(false, 50, 0.05); + + } + + function update() { + + //myGame.collide(leftEmitter, rightEmitter); + + } + +})(); diff --git a/todo/phaser tests/particles/sprite emitter.js b/todo/phaser tests/particles/sprite emitter.js new file mode 100644 index 00000000..c06c225c --- /dev/null +++ b/todo/phaser tests/particles/sprite emitter.js @@ -0,0 +1,46 @@ +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +/// +/// +/// +// Actually we could achieve the same result as this by using a sprite sheet and basic Particle +// but it still shows you how to use it properly from TypeScript, so it was worth making +var customParticle = (function (_super) { + __extends(customParticle, _super); + function customParticle(game) { + _super.call(this, game); + var s = [ + 'carrot', + 'melon', + 'eggplant', + 'mushroom', + 'pineapple' + ]; + this.loadGraphic(game.math.getRandom(s)); + } + return customParticle; +})(Phaser.Particle); +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + var emitter; + function init() { + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('eggplant', 'assets/sprites/eggplant.png'); + myGame.loader.addImageFile('mushroom', 'assets/sprites/mushroom.png'); + myGame.loader.addImageFile('pineapple', 'assets/sprites/pineapple.png'); + myGame.loader.load(); + } + function create() { + emitter = myGame.add.emitter(myGame.stage.centerX, 50); + emitter.gravity = 100; + // Here we tell the emitter to use our customParticle class + // The customParticle needs to extend Particle and must take game:Game as the first constructor parameter, otherwise it's free as a bird + emitter.particleClass = customParticle; + emitter.makeParticles(null, 500, false, 0); + emitter.start(false, 10, 0.05); + } +})(); diff --git a/todo/phaser tests/particles/sprite emitter.ts b/todo/phaser tests/particles/sprite emitter.ts new file mode 100644 index 00000000..7a4b4014 --- /dev/null +++ b/todo/phaser tests/particles/sprite emitter.ts @@ -0,0 +1,52 @@ +/// +/// +/// + +// Actually we could achieve the same result as this by using a sprite sheet and basic Particle +// but it still shows you how to use it properly from TypeScript, so it was worth making +class customParticle extends Phaser.Particle { + + constructor(game:Phaser.Game) { + + super(game); + + var s = ['carrot', 'melon', 'eggplant', 'mushroom', 'pineapple']; + + this.loadGraphic(game.math.getRandom(s)); + } + +} + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + var emitter: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('eggplant', 'assets/sprites/eggplant.png'); + myGame.loader.addImageFile('mushroom', 'assets/sprites/mushroom.png'); + myGame.loader.addImageFile('pineapple', 'assets/sprites/pineapple.png'); + + myGame.loader.load(); + + } + + function create() { + + emitter = myGame.add.emitter(myGame.stage.centerX, 50); + emitter.gravity = 100; + + // Here we tell the emitter to use our customParticle class + // The customParticle needs to extend Particle and must take game:Game as the first constructor parameter, otherwise it's free as a bird + emitter.particleClass = customParticle; + + emitter.makeParticles(null, 500, false, 0); + emitter.start(false, 10, 0.05); + + } + +})(); diff --git a/todo/phaser tests/particles/when particles collide.js b/todo/phaser tests/particles/when particles collide.js new file mode 100644 index 00000000..921c42bc --- /dev/null +++ b/todo/phaser tests/particles/when particles collide.js @@ -0,0 +1,30 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + var leftEmitter; + var rightEmitter; + function init() { + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/yellow_ball.png'); + myGame.loader.load(); + } + function create() { + leftEmitter = myGame.add.emitter(0, myGame.stage.centerY - 200); + leftEmitter.gravity = 100; + leftEmitter.bounce = 0.5; + leftEmitter.setXSpeed(100, 200); + leftEmitter.setYSpeed(-50, 50); + leftEmitter.makeParticles('ball1', 250, false, 1); + rightEmitter = myGame.add.emitter(myGame.stage.width, myGame.stage.centerY - 200); + rightEmitter.gravity = 100; + rightEmitter.bounce = 0.5; + rightEmitter.setXSpeed(-100, -200); + rightEmitter.setYSpeed(-50, 50); + rightEmitter.makeParticles('ball2', 250, false, 1); + leftEmitter.start(false, 50, 0.05); + rightEmitter.start(false, 50, 0.05); + } + function update() { + myGame.collide(leftEmitter, rightEmitter); + } +})(); diff --git a/todo/phaser tests/particles/when particles collide.ts b/todo/phaser tests/particles/when particles collide.ts new file mode 100644 index 00000000..7b4eca6c --- /dev/null +++ b/todo/phaser tests/particles/when particles collide.ts @@ -0,0 +1,47 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + var leftEmitter: Phaser.Emitter; + var rightEmitter: Phaser.Emitter; + + function init() { + + myGame.loader.addImageFile('ball1', 'assets/sprites/aqua_ball.png'); + myGame.loader.addImageFile('ball2', 'assets/sprites/yellow_ball.png'); + + myGame.loader.load(); + + } + + function create() { + + leftEmitter = myGame.add.emitter(0, myGame.stage.centerY - 200); + leftEmitter.gravity = 100; + leftEmitter.bounce = 0.5; + leftEmitter.setXSpeed(100, 200); + leftEmitter.setYSpeed(-50, 50); + leftEmitter.makeParticles('ball1', 250, false, 1); + + + rightEmitter = myGame.add.emitter(myGame.stage.width, myGame.stage.centerY - 200); + rightEmitter.gravity = 100; + rightEmitter.bounce = 0.5; + rightEmitter.setXSpeed(-100, -200); + rightEmitter.setYSpeed(-50, 50); + rightEmitter.makeParticles('ball2', 250, false, 1); + + leftEmitter.start(false, 50, 0.05); + rightEmitter.start(false, 50, 0.05); + + } + + function update() { + + myGame.collide(leftEmitter, rightEmitter); + + } + +})(); diff --git a/todo/phaser tests/physics/temp1.js b/todo/phaser tests/physics/temp1.js new file mode 100644 index 00000000..9b6e962b --- /dev/null +++ b/todo/phaser tests/physics/temp1.js @@ -0,0 +1,113 @@ +/// +var Physics = (function () { + function Physics() { + this.max_bodies = 512; + this.max_vertices = 1024; + this.max_edges = 1024; + this.max_body_vertices = 64; + this.max_body_edges = 64; + this.vertices = []; + this.edges = []; + this.bodies = []; + } + Physics.prototype.updateForces = // Sets the force on each vertex to the gravity force. You could of course apply other forces like magnetism etc. + function () { + for(var i = 0; i < this.vertexCount; i++) { + this.vertices[i].acceleration = this.gravity; + } + }; + Physics.prototype.updateVerlet = // Updates the vertex position + function () { + for(var i = 0; i < this.vertexCount; i++) { + var v = this.vertices[i]; + var temp = v.position; + //v.position.mutableAdd( + //v.position += v.position - v.oldPosition + v.acceleration * this.timestep * this.timestep; + } + }; + Physics.prototype.updateEdges = function () { + }; + Physics.prototype.iterateCollisions = function () { + }; + Physics.prototype.detectCollision = function (body1, body2) { + }; + Physics.prototype.processCollision = function () { + }; + Physics.prototype.intervalDistance = function (minA, maxA, minB, maxB) { + }; + Physics.prototype.bodiesOverlap = function (body1, body2) { + }; + Physics.prototype.update = // CollisionInfo + // depth, normal, edge, vertex + function () { + }; + Physics.prototype.render = function () { + }; + Physics.prototype.addBody = function (body) { + this.bodies.push(body); + this.bodyCount = this.bodies.length; + }; + Physics.prototype.addEdge = function (edge) { + this.edges.push(edge); + this.edgeCount = this.edges.length; + }; + Physics.prototype.addVertex = function (vertex) { + this.vertices.push(vertex); + this.vertexCount = this.vertices.length; + }; + Physics.prototype.findVertex = function (x, y) { + }; + return Physics; +})(); +var PhysicsBody = (function () { + function PhysicsBody() { + this.vertices = []; + this.edges = []; + } + PhysicsBody.prototype.addEdge = function (edge) { + }; + PhysicsBody.prototype.addVertex = function (vertex) { + }; + PhysicsBody.prototype.projectToAxis = function (axis, min, max) { + }; + PhysicsBody.prototype.calculateCenter = function () { + }; + PhysicsBody.prototype.createBox = function (x, y, width, height) { + }; + return PhysicsBody; +})(); +var Vertex = (function () { + function Vertex(body, posX, posY) { + } + return Vertex; +})(); +var Edge = (function () { + function Edge(body, pV1, pV2, pBoundary) { + } + return Edge; +})(); +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + function create() { + var p = new Physics(); + //p.max_bodies + } + function update() { + } + function render() { + //myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + //myGame.stage.context.beginPath(); + //myGame.stage.context.moveTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + //for (var i = 1; i < poly1.points.length; i++) + //{ + // myGame.stage.context.lineTo(poly1.points[i].x + poly1.pos.x, poly1.points[i].y + poly1.pos.y); + //} + //myGame.stage.context.lineTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + //myGame.stage.context.stroke(); + //myGame.stage.context.closePath(); + } +})(); diff --git a/todo/phaser tests/physics/temp1.ts b/todo/phaser tests/physics/temp1.ts new file mode 100644 index 00000000..bd379200 --- /dev/null +++ b/todo/phaser tests/physics/temp1.ts @@ -0,0 +1,200 @@ +/// + +class Physics { + + constructor() { + + this.vertices = []; + this.edges = []; + this.bodies = []; + + } + + public gravity: Phaser.Vector2; + + public max_bodies: number = 512; + public max_vertices: number = 1024; + public max_edges: number = 1024; + public max_body_vertices: number = 64; + public max_body_edges: number = 64; + + public bodyCount: number; + public vertexCount: number; + public edgeCount: number; + public timestep: number; + public iterations; + + public vertices: Vertex[]; + public edges: Edge[]; + public bodies: PhysicsBody[]; + + // Sets the force on each vertex to the gravity force. You could of course apply other forces like magnetism etc. + public updateForces() { + + for (var i:number = 0; i < this.vertexCount; i++) + { + this.vertices[i].acceleration = this.gravity; + } + } + + // Updates the vertex position + public updateVerlet() { + + for (var i:number = 0; i < this.vertexCount; i++) + { + var v:Vertex = this.vertices[i]; + var temp: Phaser.Vector2 = v.position; + //v.position.mutableAdd( + //v.position += v.position - v.oldPosition + v.acceleration * this.timestep * this.timestep; + } + + } + + public updateEdges() { + } + + public iterateCollisions() { + } + + public detectCollision(body1, body2) { + } + + public processCollision() { + } + + public intervalDistance(minA, maxA, minB, maxB) { + } + + public bodiesOverlap(body1, body2) { + } + + // CollisionInfo + // depth, normal, edge, vertex + + public update() { + } + + public render() { + } + + public addBody(body:PhysicsBody) { + this.bodies.push(body); + this.bodyCount = this.bodies.length; + } + + public addEdge(edge:Edge) { + this.edges.push(edge); + this.edgeCount = this.edges.length; + } + + public addVertex(vertex:Vertex) { + this.vertices.push(vertex); + this.vertexCount = this.vertices.length; + } + + public findVertex(x, y) { + } + +} + +class PhysicsBody { + + constructor() { + } + + center: Phaser.Vector2; + minX; + minY; + maxX; + maxY; + vertextCount; + edgeCount; + + vertices = []; + edges = []; + + public addEdge(edge) { + } + + public addVertex(vertex) { + + } + + public projectToAxis(axis, min, max) { + } + + public calculateCenter() { + } + + public createBox(x, y, width, height) { + } + +} + +class Vertex { + + constructor(body, posX, posY) { + } + + position: Phaser.Vector2; + oldPosition: Phaser.Vector2; + acceleration: Phaser.Vector2; + parent: PhysicsBody; +} + +class Edge { + + constructor(body, pV1, pV2, pBoundary) { + } + + v1: Vertex; + v2: Vertex; + length; + boundary; + parent: PhysicsBody; + +} + +(function () { + + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + + } + + function create() { + + var p = new Physics(); + //p.max_bodies + + } + + function update() { + + + } + + function render() { + + //myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + //myGame.stage.context.beginPath(); + //myGame.stage.context.moveTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + + //for (var i = 1; i < poly1.points.length; i++) + //{ + // myGame.stage.context.lineTo(poly1.points[i].x + poly1.pos.x, poly1.points[i].y + poly1.pos.y); + //} + + //myGame.stage.context.lineTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + + //myGame.stage.context.stroke(); + //myGame.stage.context.closePath(); + + } + +})(); diff --git a/todo/phaser tests/physics/temp2.js b/todo/phaser tests/physics/temp2.js new file mode 100644 index 00000000..ca7d6f1a --- /dev/null +++ b/todo/phaser tests/physics/temp2.js @@ -0,0 +1,1413 @@ +/// +var NPhysics = (function () { + function NPhysics() { + this.grav = 0.2; + this.drag = 1; + this.bounce = 0.3; + this.friction = 0.05; + this.min_f = 0; + this.max_f = 1; + this.min_b = 0; + this.max_b = 1; + this.min_g = 0; + this.max_g = 1; + this.xmin = 0; + this.xmax = 800; + this.ymin = 0; + this.ymax = 600; + this.objrad = 24; + this.tilerad = 24 * 2; + this.objspeed = 0.2; + this.maxspeed = 20; + } + NPhysics.prototype.update = function () { + // demoObj.Verlet(); + // demoObj.CollideVsWorldBounds(); + }; + return NPhysics; +})(); +var AABB = (function () { + function AABB(x, y, xw, yw) { + this.type = 0; + this.pos = new Phaser.Vector2(x, y); + this.oldpos = this.pos.clone(); + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + this.aabbTileProjections = { + }//hash object to hold tile-specific collision functions + ; + this.aabbTileProjections[TileMapCell.CTYPE_FULL] = this.ProjAABB_Full; + } + AABB.COL_NONE = 0; + AABB.COL_AXIS = 1; + AABB.COL_OTHER = 2; + AABB.prototype.IntegrateVerlet = function () { + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + var p = this.pos; + var o = this.oldpos; + var px, py; + var ox = o.x;//we can't swap buffers since mcs/sticks point directly to vector2s.. + + var oy = o.y; + o.x = px = p.x//get vector values + ; + o.y = py = p.y//p = position + ; + //o = oldposition + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + AABB.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + + var ty = vy - ny; + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + if(dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + ; + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + p.x += px//project object out of collision + ; + p.y += py; + o.x += px + bx + fx//apply bounce+friction impulses which alter velocity + ; + o.y += py + by + fy; + }; + AABB.prototype.CollideAABBVsWorldBounds = function () { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if(0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + xw) - XMAX; + if(0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if(0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + yw) - YMAX; + if(0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + AABB.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + /* + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + */ + }; + AABB.prototype.ResolveBoxTile = function (x, y, box, t) { + if(0 < t.ID) { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } else { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + }; + AABB.prototype.ProjAABB_Full = function (x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + return AABB.COL_AXIS; + }; + return AABB; +})(); +var TileMapCell = (function () { + function TileMapCell(x, y, xw, yw) { + this.ID = TileMapCell.TID_EMPTY//all tiles start empty + ; + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.pos = new Phaser.Vector2(x, y)//setup collision properties + ; + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + TileMapCell.TID_EMPTY = 0; + TileMapCell.TID_FULL = 1; + TileMapCell.TID_45DEGpn = 2; + TileMapCell.TID_45DEGnn = 3; + TileMapCell.TID_45DEGnp = 4; + TileMapCell.TID_45DEGpp = 5; + TileMapCell.TID_CONCAVEpn = 6; + TileMapCell.TID_CONCAVEnn = 7; + TileMapCell.TID_CONCAVEnp = 8; + TileMapCell.TID_CONCAVEpp = 9; + TileMapCell.TID_CONVEXpn = 10; + TileMapCell.TID_CONVEXnn = 11; + TileMapCell.TID_CONVEXnp = 12; + TileMapCell.TID_CONVEXpp = 13; + TileMapCell.TID_22DEGpnS = 14; + TileMapCell.TID_22DEGnnS = 15; + TileMapCell.TID_22DEGnpS = 16; + TileMapCell.TID_22DEGppS = 17; + TileMapCell.TID_22DEGpnB = 18; + TileMapCell.TID_22DEGnnB = 19; + TileMapCell.TID_22DEGnpB = 20; + TileMapCell.TID_22DEGppB = 21; + TileMapCell.TID_67DEGpnS = 22; + TileMapCell.TID_67DEGnnS = 23; + TileMapCell.TID_67DEGnpS = 24; + TileMapCell.TID_67DEGppS = 25; + TileMapCell.TID_67DEGpnB = 26; + TileMapCell.TID_67DEGnnB = 27; + TileMapCell.TID_67DEGnpB = 28; + TileMapCell.TID_67DEGppB = 29; + TileMapCell.TID_HALFd = 30; + TileMapCell.TID_HALFr = 31; + TileMapCell.TID_HALFu = 32; + TileMapCell.TID_HALFl = 33; + TileMapCell.CTYPE_EMPTY = 0; + TileMapCell.CTYPE_FULL = 1; + TileMapCell.CTYPE_45DEG = 2; + TileMapCell.CTYPE_CONCAVE = 6; + TileMapCell.CTYPE_CONVEX = 10; + TileMapCell.CTYPE_22DEGs = 14; + TileMapCell.CTYPE_22DEGb = 18; + TileMapCell.CTYPE_67DEGs = 22; + TileMapCell.CTYPE_67DEGb = 26; + TileMapCell.CTYPE_HALF = 30; + TileMapCell.prototype.SetState = //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + function (ID) { + if(ID == TileMapCell.TID_EMPTY) { + this.Clear(); + } else { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + }; + TileMapCell.prototype.Clear = function () { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY; + this.UpdateType(); + //this.Draw(); + }; + TileMapCell.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + }; + TileMapCell.prototype.UpdateType = //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + function () { + if(0 < this.ID) { + //tile is non-empty; collide + if(this.ID < TileMapCell.CTYPE_45DEG) { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } else if(this.ID < TileMapCell.CTYPE_CONCAVE) { + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if(this.ID == TileMapCell.TID_45DEGpn) { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2//get slope _unit_ normal + ; + this.sy = this.signy / Math.SQRT2//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + ; + } else if(this.ID == TileMapCell.TID_45DEGnn) { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2//get slope _unit_ normal + ; + this.sy = this.signy / Math.SQRT2//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + ; + } else if(this.ID == TileMapCell.TID_45DEGnp) { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2//get slope _unit_ normal + ; + this.sy = this.signy / Math.SQRT2//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + ; + } else if(this.ID == TileMapCell.TID_45DEGpp) { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2//get slope _unit_ normal + ; + this.sy = this.signy / Math.SQRT2//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + ; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_CONVEX) { + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if(this.ID == TileMapCell.TID_CONCAVEpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONCAVEnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONCAVEnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONCAVEpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_22DEGs) { + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if(this.ID == TileMapCell.TID_CONVEXpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONVEXnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONVEXnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if(this.ID == TileMapCell.TID_CONVEXpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_22DEGb) { + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if(this.ID == TileMapCell.TID_22DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_67DEGs) { + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if(this.ID == TileMapCell.TID_22DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if(this.ID == TileMapCell.TID_22DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_67DEGb) { + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if(this.ID == TileMapCell.TID_67DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if(this.ID < TileMapCell.CTYPE_HALF) { + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if(this.ID == TileMapCell.TID_67DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if(this.ID == TileMapCell.TID_67DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if(this.ID == TileMapCell.TID_HALFd) { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } else if(this.ID == TileMapCell.TID_HALFu) { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } else if(this.ID == TileMapCell.TID_HALFl) { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else if(this.ID == TileMapCell.TID_HALFr) { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + } else { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + }; + return TileMapCell; +})(); +var Circle = (function () { + function Circle(x, y, radius) { + this.type = 1; + this.pos = new Phaser.Vector2(x, y); + this.oldpos = this.pos.clone(); + this.radius = radius; + this.circleTileProjections = { + }//hash object to hold tile-specific collision functions + ; + this.circleTileProjections[TileMapCell.CTYPE_FULL] = this.ProjCircle_Full; + this.circleTileProjections[TileMapCell.CTYPE_45DEG] = this.ProjCircle_45Deg; + this.circleTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjCircle_Concave; + this.circleTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjCircle_Convex; + //Proj_CircleTile[CTYPE_22DEGs] = ProjCircle_22DegS; + //Proj_CircleTile[CTYPE_22DEGb] = ProjCircle_22DegB; + //Proj_CircleTile[CTYPE_67DEGs] = ProjCircle_67DegS; + //Proj_CircleTile[CTYPE_67DEGb] = ProjCircle_67DegB; + //Proj_CircleTile[CTYPE_HALF] = ProjCircle_Half; + } + Circle.COL_NONE = 0; + Circle.COL_AXIS = 1; + Circle.COL_OTHER = 2; + Circle.prototype.IntegrateVerlet = function () { + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + var p = this.pos; + var o = this.oldpos; + var px, py; + var ox = o.x;//we can't swap buffers since mcs/sticks point directly to vector2s.. + + var oy = o.y; + o.x = px = p.x//get vector values + ; + o.y = py = p.y//p = position + ; + //o = oldposition + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + Circle.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + + var ty = vy - ny; + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + if(dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + ; + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + p.x += px//project object out of collision + ; + p.y += py; + o.x += px + bx + fx//apply bounce+friction impulses which alter velocity + ; + o.y += py + by + fy; + }; + Circle.prototype.CollideCircleVsWorldBounds = function () { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + if(0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + r) - XMAX; + if(0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + if(0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + r) - YMAX; + if(0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + if(this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } else if(this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + if(this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } else if(this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + }; + Circle.prototype.CollideCircleVsTile = function (tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + var dx = pos.x - tx;//tile->obj delta + + var px = (txw + r) - Math.abs(dx);//penetration depth in x + + if(0 < px) { + var dy = pos.y - ty;//tile->obj delta + + var py = (tyw + r) - Math.abs(dy);//pen depth in y + + if(0 < py) { + //object may be colliding with tile + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if(dx < -txw) { + //circle is on left side of tile + this.oH = -1; + } else if(txw < dx) { + //circle is on right side of tile + this.oH = 1; + } + if(dy < -tyw) { + //circle is on top side of tile + this.oV = -1; + } else if(tyw < dy) { + //circle is on bottom side of tile + this.oV = 1; + } + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + } + } + }; + Circle.prototype.ResolveCircleTile = function (x, y, oH, oV, obj, t) { + if(0 < t.ID) { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } else { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + }; + Circle.prototype.ProjCircle_Full = function (x, y, oH, oV, obj, t) { + //if we're colliding vs. the current cell, we need to project along the + //smallest penetration vector. + //if we're colliding vs. horiz. or vert. neighb, we simply project horiz/vert + //if we're colliding diagonally, we need to collide vs. tile corner + if(oH == 0) { + if(oV == 0) { + //collision with current cell + if(x < y) { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x;//get sign for projection along x-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if(dx < 0) { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Circle.COL_AXIS; + } + } else { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y;//get sign for projection along y-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if(dy < 0) { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Circle.COL_AXIS; + } + } + } else { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + return Circle.COL_AXIS; + } + } else if(oV == 0) { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } else { + //diagonal collision + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + return Circle.COL_NONE; + }; + Circle.prototype.ProjCircle_45Deg = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + if(oH == 0) { + if(oV == 0) { + //colliding with current tile + var sx = t.sx; + var sy = t.sy; + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x;//this gives is the coordinates of the innermost + + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y;//point on the circle, relative to the tile center + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if(dp < 0) { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp//(sx,sy) is now the penetration vector + ; + sy *= -dp; + //find the smallest axial projection vector + if(x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + var lenN = Math.sqrt(sx * sx + sy * sy); + if(lenP < lenN) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + return Circle.COL_OTHER; + } + } + } else { + //colliding vertically + if((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost + + var oy = obj.pos.y - (t.pos.y + (oV * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if(0 < (perp * signx * signy)) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if(0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + + if(0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + return Circle.COL_OTHER; + } + } + } + } + } else if(oV == 0) { + //colliding horizontally + if((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost + + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if((perp * signx * signy) < 0) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if(0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + + if(0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + return Circle.COL_OTHER; + } + } + } + } else { + //colliding diagonally + if(0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } + return Circle.COL_NONE; + }; + Circle.prototype.ProjCircle_Concave = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + if(oH == 0) { + if(oV == 0) { + //colliding with current tile + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x;//(ox,oy) is the vector from the circle to + + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y;//tile-circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + if(0 < pen) { + //find the smallest axial projection vector + if(x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + if(lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + return Circle.COL_AXIS; + } else { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } else { + return Circle.COL_NONE; + } + } else { + //colliding vertically + if((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the vertical tip + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out vertically + dx = 0; + dy = oV; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } + } else if(oV == 0) { + //colliding horizontally + if((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the horizontal tip + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out horizontally + dx = oH; + dy = 0; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } else { + //colliding diagonally + if(0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } + return Circle.COL_NONE; + }; + Circle.prototype.ProjCircle_Convex = function (x, y, oH, oV, obj, t) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + if(oH == 0) { + if(oV == 0) { + //colliding with current tile + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + if(0 < pen) { + //find the smallest axial projection vector + if(x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + if(lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + return Circle.COL_AXIS; + } else { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } + } else { + //colliding vertically + if((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + return Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + if(0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } + } + } else if(oV == 0) { + //colliding horizontally + if((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + if(0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } + } else { + //colliding diagonally + if(0 < ((signx * oH) + (signy * oV))) { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + if(0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + return Circle.COL_OTHER; + } + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + var dx = obj.pos.x - vx;//calc vert->circle vector + + var dy = obj.pos.y - vy; + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if(0 < pen) { + //vertex is in the circle; project outward + if(len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } + return Circle.COL_NONE; + }; + return Circle; +})(); +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + var cells; + var physics; + var b; + var c; + var t; + function create() { + this.physics = new NPhysics(); + this.c = new Circle(200, 100, 25); + this.b = new AABB(200, 200, 50, 50); + // pos is center, not upper-left + this.cells = []; + var tid; + for(var i = 0; i < 10; i++) { + if(i % 2 == 0) { + console.log('pn'); + tid = TileMapCell.TID_CONCAVEpn; + } else { + console.log('nn'); + tid = TileMapCell.TID_CONCAVEnn; + } + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(tid)); + this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + } + //this.t = new TileMapCell(200, 500, 100, 100); + //this.t.SetState(TileMapCell.TID_FULL); + //this.t.SetState(TileMapCell.TID_45DEGpn); + //this.t.SetState(TileMapCell.TID_CONCAVEpn); + //this.t.SetState(TileMapCell.TID_CONVEXpn); + } + function update() { + var fx = 0; + var fy = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + fx -= 0.2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + fx += 0.2; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + fy -= 0.2 + 0.2; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + fy += 0.2; + } + // update circle + var p = this.c.pos; + var o = this.c.oldpos; + var vx = p.x - o.x; + var vy = p.y - o.y; + var newx = Math.min(20, Math.max(-20, vx + fx)); + var newy = Math.min(20, Math.max(-20, vy + fy)); + p.x = o.x + newx; + p.y = o.y + newy; + this.c.IntegrateVerlet(); + // update box + var p = this.b.pos; + var o = this.b.oldpos; + var vx = p.x - o.x; + var vy = p.y - o.y; + var newx = Math.min(20, Math.max(-20, vx + fx)); + var newy = Math.min(20, Math.max(-20, vy + fy)); + p.x = o.x + newx; + p.y = o.y + newy; + this.b.IntegrateVerlet(); + for(var i = 0; i < this.cells.length; i++) { + this.c.CollideCircleVsTile(this.cells[i]); + //this.cells[i].render(myGame.stage.context); + } + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + } + function render() { + this.c.render(myGame.stage.context); + this.b.render(myGame.stage.context); + for(var i = 0; i < this.cells.length; i++) { + this.cells[i].render(myGame.stage.context); + } + } +})(); diff --git a/todo/phaser tests/physics/temp2.ts b/todo/phaser tests/physics/temp2.ts new file mode 100644 index 00000000..8fdc1da2 --- /dev/null +++ b/todo/phaser tests/physics/temp2.ts @@ -0,0 +1,1934 @@ +/// + +class NPhysics { + + grav: number = 0.2; + drag: number = 1; + bounce: number = 0.3; + friction: number = 0.05; + + min_f: number = 0; + max_f: number = 1; + + min_b: number = 0; + max_b: number = 1; + + min_g: number = 0; + max_g = 1; + + xmin: number = 0; + xmax: number = 800; + ymin: number = 0; + ymax: number = 600; + + objrad: number = 24; + tilerad: number = 24*2; + objspeed: number = 0.2; + maxspeed: number = 20; + + public update() { + // demoObj.Verlet(); + // demoObj.CollideVsWorldBounds(); + } + +} + +class AABB { + + constructor(x: number, y: number, xw, yw) { + + this.pos = new Phaser.Vector2(x, y); + this.oldpos = this.pos.clone(); + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + this.aabbTileProjections = {};//hash object to hold tile-specific collision functions + this.aabbTileProjections[TileMapCell.CTYPE_FULL] = this.ProjAABB_Full; + } + + type:number = 0; + pos: Phaser.Vector2; + oldpos: Phaser.Vector2; + xw: number; + yw: number; + aabbTileProjections; + public oH: number; + public oV: number; + static COL_NONE = 0; + static COL_AXIS = 1; + static COL_OTHER = 2; + + public IntegrateVerlet() { + + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; //we can't swap buffers since mcs/sticks point directly to vector2s.. + var oy = o.y; + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + //o = oldposition + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px, py, dx, dy, obj: TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + public CollideAABBVsWorldBounds() { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context:CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + + /* + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + */ + + } + + public ResolveBoxTile(x, y, box, t) { + if (0 < t.ID) + { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } + else + { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + + public ProjAABB_Full(x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return AABB.COL_AXIS; + } + + + +} + +class TileMapCell { + + //TILETYPE ENUMERATION + static TID_EMPTY = 0; + static TID_FULL = 1;//fullAABB tile + static TID_45DEGpn = 2;//45-degree triangle, whose normal is (+ve,-ve) + static TID_45DEGnn = 3;//(+ve,+ve) + static TID_45DEGnp = 4;//(-ve,+ve) + static TID_45DEGpp = 5;//(-ve,-ve) + static TID_CONCAVEpn = 6;//1/4-circle cutout + static TID_CONCAVEnn = 7; + static TID_CONCAVEnp = 8; + static TID_CONCAVEpp = 9; + static TID_CONVEXpn = 10;//1/4/circle + static TID_CONVEXnn = 11; + static TID_CONVEXnp = 12; + static TID_CONVEXpp = 13; + static TID_22DEGpnS = 14;//22.5 degree slope + static TID_22DEGnnS = 15; + static TID_22DEGnpS = 16; + static TID_22DEGppS = 17; + static TID_22DEGpnB = 18; + static TID_22DEGnnB = 19; + static TID_22DEGnpB = 20; + static TID_22DEGppB = 21; + static TID_67DEGpnS = 22;//67.5 degree slope + static TID_67DEGnnS = 23; + static TID_67DEGnpS = 24; + static TID_67DEGppS = 25; + static TID_67DEGpnB = 26; + static TID_67DEGnnB = 27; + static TID_67DEGnpB = 28; + static TID_67DEGppB = 29; + static TID_HALFd = 30;//half-full tiles + static TID_HALFr = 31; + static TID_HALFu = 32; + static TID_HALFl = 33; + + //collision shape "types" + static CTYPE_EMPTY = 0; + static CTYPE_FULL = 1; + static CTYPE_45DEG = 2; + static CTYPE_CONCAVE = 6; + static CTYPE_CONVEX = 10; + static CTYPE_22DEGs = 14; + static CTYPE_22DEGb = 18; + static CTYPE_67DEGs = 22; + static CTYPE_67DEGb = 26; + static CTYPE_HALF = 30; + + ID; + CTYPE; + pos: Phaser.Vector2; + xw; + yw; + minx; + maxx; + miny; + maxy; + signx; + signy; + sx; + sy; + + constructor(x,y,xw,yw) { + + this.ID = TileMapCell.TID_EMPTY; //all tiles start empty + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vector2(x,y); //setup collision properties + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + SetState(ID) { + if (ID == TileMapCell.TID_EMPTY) + { + this.Clear(); + } + else + { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + } + + Clear() { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY + this.UpdateType(); + //this.Draw(); + } + + public render(context: CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + + } + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + UpdateType() { + if (0 < this.ID) + { + //tile is non-empty; collide + if (this.ID < TileMapCell.CTYPE_45DEG) + { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + } + else if (this.ID < TileMapCell.CTYPE_CONCAVE) + { + + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) + { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnn) + { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnp) + { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.ID == TileMapCell.TID_45DEGpp) + { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_CONVEX) + { + + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGs) + { + + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGb) + { + + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGs) + { + + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGb) + { + + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_HALF) + { + + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else + { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) + { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFu) + { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFl) + { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFr) + { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else + { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + + } + + } + else + { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + } + +} + +class Circle { + + constructor(x, y, radius) { + + this.pos = new Phaser.Vector2(x, y); + this.oldpos = this.pos.clone(); + this.radius = radius; + this.circleTileProjections = {};//hash object to hold tile-specific collision functions + this.circleTileProjections[TileMapCell.CTYPE_FULL] = this.ProjCircle_Full; + this.circleTileProjections[TileMapCell.CTYPE_45DEG] = this.ProjCircle_45Deg; + this.circleTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjCircle_Concave; + this.circleTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjCircle_Convex; + + //Proj_CircleTile[CTYPE_22DEGs] = ProjCircle_22DegS; + //Proj_CircleTile[CTYPE_22DEGb] = ProjCircle_22DegB; + //Proj_CircleTile[CTYPE_67DEGs] = ProjCircle_67DegS; + //Proj_CircleTile[CTYPE_67DEGb] = ProjCircle_67DegB; + //Proj_CircleTile[CTYPE_HALF] = ProjCircle_Half; + + } + + type:number = 1; + pos: Phaser.Vector2; + oldpos: Phaser.Vector2; + radius: number; + circleTileProjections; + public oH: number; + public oV: number; + static COL_NONE = 0; + static COL_AXIS = 1; + static COL_OTHER = 2; + + public IntegrateVerlet() { + + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; //we can't swap buffers since mcs/sticks point directly to vector2s.. + var oy = o.y; + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + //o = oldposition + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px, py, dx, dy, obj: TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + public CollideCircleVsWorldBounds() { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context:CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + } + + public CollideCircleVsTile(tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + r) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + r) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile + + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) + { + //circle is on left side of tile + this.oH = -1; + } + else if (txw < dx) + { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) + { + //circle is on top side of tile + this.oV = -1; + } + else if (tyw < dy) + { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + + } + } + } + + public ResolveCircleTile(x, y, oH, oV, obj, t) { + + if (0 < t.ID) + { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } + else + { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + } + + public ProjCircle_Full(x, y, oH, oV, obj:Circle, t:TileMapCell) { + + //if we're colliding vs. the current cell, we need to project along the + //smallest penetration vector. + //if we're colliding vs. horiz. or vert. neighb, we simply project horiz/vert + //if we're colliding diagonally, we need to collide vs. tile corner + + if (oH == 0) + { + if (oV == 0) + { + //collision with current cell + if (x < y) + { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x;//get sign for projection along x-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dx < 0) + { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Circle.COL_AXIS; + } + } + else + { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y;//get sign for projection along y-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dy < 0) + { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Circle.COL_AXIS; + } + } + } + else + { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + } + else if (oV == 0) + { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } + else + { + //diagonal collision + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + + return Circle.COL_NONE; + + } + + public ProjCircle_45Deg(x, y, oH, oV, obj: Circle, t: TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y;//point on the circle, relative to the tile center + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) + { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp;//(sx,sy) is now the penetration vector + sy *= -dp; + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Circle.COL_OTHER; + } + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (oV * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + } + + public ProjCircle_Concave(x, y, oH, oV, obj: Circle, t: TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x;//(ox,oy) is the vector from the circle to + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y;//tile-circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + return Circle.COL_NONE; + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the vertical tip + + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out vertically + dx = 0; + dy = oV; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the horizontal tip + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out horizontally + dx = oH; + dy = 0; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + + } + + public ProjCircle_Convex(x, y, oH, oV, obj: Circle, t: TileMapCell) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + + } + } + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + + } + +} + + + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + + } + + var cells; + var physics: NPhysics; + var b: Circle; + var c: Circle; + var t: TileMapCell; + + function create() { + + this.physics = new NPhysics(); + this.c = new Circle(200, 100, 25); + this.b = new AABB(200, 200, 50, 50); + + // pos is center, not upper-left + this.cells = []; + + var tid; + + for (var i = 0; i < 10; i++) + { + if (i % 2 == 0) + { + console.log('pn'); + tid = TileMapCell.TID_CONCAVEpn; + } + else + { + console.log('nn'); + tid = TileMapCell.TID_CONCAVEnn; + } + + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(tid)); + this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + } + + //this.t = new TileMapCell(200, 500, 100, 100); + //this.t.SetState(TileMapCell.TID_FULL); + //this.t.SetState(TileMapCell.TID_45DEGpn); + //this.t.SetState(TileMapCell.TID_CONCAVEpn); + //this.t.SetState(TileMapCell.TID_CONVEXpn); + + } + + function update() { + + var fx = 0; + var fy = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + fx -= 0.2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + fx += 0.2; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + fy -= 0.2 + 0.2; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + fy += 0.2; + } + + // update circle + var p = this.c.pos; + var o = this.c.oldpos; + var vx = p.x - o.x; + var vy = p.y - o.y; + var newx = Math.min(20, Math.max(-20, vx+fx)); + var newy = Math.min(20, Math.max(-20, vy+fy)); + p.x = o.x + newx; + p.y = o.y + newy; + this.c.IntegrateVerlet(); + + // update box + var p = this.b.pos; + var o = this.b.oldpos; + var vx = p.x - o.x; + var vy = p.y - o.y; + var newx = Math.min(20, Math.max(-20, vx+fx)); + var newy = Math.min(20, Math.max(-20, vy+fy)); + p.x = o.x + newx; + p.y = o.y + newy; + this.b.IntegrateVerlet(); + + + for (var i = 0; i < this.cells.length; i++) + { + this.c.CollideCircleVsTile(this.cells[i]); + //this.cells[i].render(myGame.stage.context); + } + + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + + } + + function render() { + + this.c.render(myGame.stage.context); + this.b.render(myGame.stage.context); + + for (var i = 0; i < this.cells.length; i++) + { + this.cells[i].render(myGame.stage.context); + } + + } + +})(); diff --git a/todo/phaser tests/scrollzones/ballscroller.js b/todo/phaser tests/scrollzones/ballscroller.js new file mode 100644 index 00000000..3934d00b --- /dev/null +++ b/todo/phaser tests/scrollzones/ballscroller.js @@ -0,0 +1,25 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('balls', 'assets/sprites/balls.png'); + myGame.loader.load(); + } + var scroller; + function create() { + // The source image (balls.png) is only 102x17 in size, but we want it to create a scroll the size of the whole game window. + // We can take advantage of the way a ScrollZone can create a seamless pattern for us automatically. + // If you create a ScrollRegion larger than the source texture, it'll create a DynamicTexture and perform a pattern fill on it and use that + // for rendering. + // We've rounded the height up to 612 because in order to have a seamless pattern it needs to be a multiple of 17 (the height of the source image) + scroller = myGame.add.scrollZone('balls', 0, 0, 800, 612); + // Some sin/cos data for the movement + myGame.math.sinCosGenerator(256, 4, 4, 2); + } + function update() { + // Cycle through the wave data and apply it to the scroll speed (causes the circular wave motion) + scroller.currentRegion.scrollSpeed.x = myGame.math.shiftSinTable(); + scroller.currentRegion.scrollSpeed.y = myGame.math.shiftCosTable(); + } +})(); diff --git a/todo/phaser tests/scrollzones/ballscroller.ts b/todo/phaser tests/scrollzones/ballscroller.ts new file mode 100644 index 00000000..753243c7 --- /dev/null +++ b/todo/phaser tests/scrollzones/ballscroller.ts @@ -0,0 +1,41 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('balls', 'assets/sprites/balls.png'); + + myGame.loader.load(); + + } + + var scroller: Phaser.ScrollZone; + + function create() { + + // The source image (balls.png) is only 102x17 in size, but we want it to create a scroll the size of the whole game window. + // We can take advantage of the way a ScrollZone can create a seamless pattern for us automatically. + // If you create a ScrollRegion larger than the source texture, it'll create a DynamicTexture and perform a pattern fill on it and use that + // for rendering. + + // We've rounded the height up to 612 because in order to have a seamless pattern it needs to be a multiple of 17 (the height of the source image) + scroller = myGame.add.scrollZone('balls', 0, 0, 800, 612); + + // Some sin/cos data for the movement + myGame.math.sinCosGenerator(256, 4, 4, 2); + + } + + function update() { + + // Cycle through the wave data and apply it to the scroll speed (causes the circular wave motion) + scroller.currentRegion.scrollSpeed.x = myGame.math.shiftSinTable(); + scroller.currentRegion.scrollSpeed.y = myGame.math.shiftCosTable(); + + } + +})(); diff --git a/todo/phaser tests/scrollzones/blasteroids.js b/todo/phaser tests/scrollzones/blasteroids.js new file mode 100644 index 00000000..422197ad --- /dev/null +++ b/todo/phaser tests/scrollzones/blasteroids.js @@ -0,0 +1,95 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('nashwan', 'assets/sprites/xenon2_ship.png'); + myGame.loader.addImageFile('starfield', 'assets/misc/starfield.jpg'); + myGame.loader.addImageFile('jet', 'assets/sprites/particle1.png'); + myGame.loader.addImageFile('bullet', 'assets/misc/bullet1.png'); + myGame.loader.load(); + } + var scroller; + var emitter; + var ship; + var bullets; + var speed = 0; + var fireRate = 0; + var shipMotion; + function create() { + scroller = myGame.add.scrollZone('starfield', 0, 0, 1024, 1024); + emitter = myGame.add.emitter(myGame.stage.centerX + 16, myGame.stage.centerY + 12); + emitter.makeParticles('jet', 250, false, 0); + emitter.setRotation(0, 0); + // Looks like a smoke trail! + //emitter.globalCompositeOperation = 'xor'; + // Looks way cool :) + emitter.globalCompositeOperation = 'lighter'; + bullets = myGame.add.group(50); + // Create our bullet pool + for(var i = 0; i < 50; i++) { + var tempBullet = new Phaser.Sprite(myGame, myGame.stage.centerX, myGame.stage.centerY, 'bullet'); + tempBullet.exists = false; + tempBullet.rotationOffset = 90; + tempBullet.setBounds(-100, -100, 900, 700); + tempBullet.outOfBoundsAction = Phaser.GameObject.OUT_OF_BOUNDS_KILL; + bullets.add(tempBullet); + } + ship = myGame.add.sprite(myGame.stage.centerX, myGame.stage.centerY, 'nashwan'); + // We do this because the ship was drawn facing up, but 0 degrees is pointing to the right + ship.rotationOffset = 90; + myGame.input.onDown.add(test, this); + } + function test(event) { + myGame.stage.scale.startFullScreen(); + } + function update() { + ship.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + ship.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + ship.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + speed += 0.1; + if(speed > 10) { + speed = 10; + } + } else { + speed -= 0.1; + if(speed < 0) { + speed = 0; + } + } + shipMotion = myGame.motion.velocityFromAngle(ship.angle, speed); + scroller.setSpeed(shipMotion.x, shipMotion.y); + // emit particles + if(speed > 2) { + // We use the opposite of the motion because the jets emit out the back of the ship + // The 20 and 30 values just keep them nice and fast + emitter.setXSpeed(-(shipMotion.x * 20), -(shipMotion.x * 30)); + emitter.setYSpeed(-(shipMotion.y * 20), -(shipMotion.y * 30)); + emitter.emitParticle(); + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR)) { + fire(); + } + } + function recycleBullet(bullet) { + if(bullet.exists && bullet.x < -40 || bullet.x > 840 || bullet.y < -40 || bullet.y > 640) { + bullet.exists = false; + } + } + function fire() { + if(myGame.time.now > fireRate) { + var b = bullets.getFirstAvailable(); + b.x = ship.x; + b.y = ship.y - 26; + var bulletMotion = myGame.motion.velocityFromAngle(ship.angle, 400); + b.revive(); + b.angle = ship.angle; + b.velocity.setTo(bulletMotion.x, bulletMotion.y); + fireRate = myGame.time.now + 100; + } + } +})(); diff --git a/todo/phaser tests/scrollzones/blasteroids.ts b/todo/phaser tests/scrollzones/blasteroids.ts new file mode 100644 index 00000000..c7cf776d --- /dev/null +++ b/todo/phaser tests/scrollzones/blasteroids.ts @@ -0,0 +1,151 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('nashwan', 'assets/sprites/xenon2_ship.png'); + myGame.loader.addImageFile('starfield', 'assets/misc/starfield.jpg'); + myGame.loader.addImageFile('jet', 'assets/sprites/particle1.png'); + myGame.loader.addImageFile('bullet', 'assets/misc/bullet1.png'); + + myGame.loader.load(); + + } + + var scroller: Phaser.ScrollZone; + var emitter: Phaser.Emitter; + var ship: Phaser.Sprite; + var bullets: Phaser.Group; + + var speed: number = 0; + var fireRate: number = 0; + var shipMotion: Phaser.Point; + + function create() { + + scroller = myGame.add.scrollZone('starfield', 0, 0, 1024, 1024); + + emitter = myGame.add.emitter(myGame.stage.centerX + 16, myGame.stage.centerY + 12); + emitter.makeParticles('jet', 250, false, 0); + emitter.setRotation(0, 0); + + // Looks like a smoke trail! + //emitter.globalCompositeOperation = 'xor'; + + // Looks way cool :) + emitter.globalCompositeOperation = 'lighter'; + + bullets = myGame.add.group(50); + + // Create our bullet pool + for (var i = 0; i < 50; i++) + { + var tempBullet = new Phaser.Sprite(myGame, myGame.stage.centerX, myGame.stage.centerY, 'bullet'); + tempBullet.exists = false; + tempBullet.rotationOffset = 90; + tempBullet.setBounds(-100, -100, 900, 700); + tempBullet.outOfBoundsAction = Phaser.GameObject.OUT_OF_BOUNDS_KILL; + bullets.add(tempBullet); + } + + ship = myGame.add.sprite(myGame.stage.centerX, myGame.stage.centerY, 'nashwan'); + + // We do this because the ship was drawn facing up, but 0 degrees is pointing to the right + ship.rotationOffset = 90; + + myGame.input.onDown.add(test, this); + + } + + function test(event) { + + myGame.stage.scale.startFullScreen(); + + } + + function update() { + + ship.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + ship.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + ship.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + speed += 0.1; + + if (speed > 10) + { + speed = 10; + } + } + else + { + speed -= 0.1; + + if (speed < 0) { + speed = 0; + } + } + + shipMotion = myGame.motion.velocityFromAngle(ship.angle, speed); + + scroller.setSpeed(shipMotion.x, shipMotion.y); + + // emit particles + if (speed > 2) + { + // We use the opposite of the motion because the jets emit out the back of the ship + // The 20 and 30 values just keep them nice and fast + emitter.setXSpeed(-(shipMotion.x * 20), -(shipMotion.x * 30)); + emitter.setYSpeed(-(shipMotion.y * 20), -(shipMotion.y * 30)); + emitter.emitParticle(); + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR)) + { + fire(); + } + + } + + function recycleBullet(bullet:Phaser.Sprite) { + + if (bullet.exists && bullet.x < -40 || bullet.x > 840 || bullet.y < -40 || bullet.y > 640) + { + bullet.exists = false; + } + + } + + function fire() { + + if (myGame.time.now > fireRate) + { + var b:Phaser.Sprite = bullets.getFirstAvailable(); + + b.x = ship.x; + b.y = ship.y - 26; + + var bulletMotion = myGame.motion.velocityFromAngle(ship.angle, 400); + + b.revive(); + b.angle = ship.angle; + b.velocity.setTo(bulletMotion.x, bulletMotion.y); + + fireRate = myGame.time.now + 100; + } + + } + +})(); diff --git a/todo/phaser tests/scrollzones/parallax.js b/todo/phaser tests/scrollzones/parallax.js new file mode 100644 index 00000000..d3e91cb4 --- /dev/null +++ b/todo/phaser tests/scrollzones/parallax.js @@ -0,0 +1,31 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('starray', 'assets/pics/auto_scroll_landscape.png'); + myGame.loader.load(); + } + function create() { + var zone = myGame.add.scrollZone('starray'); + // Hide the default region (the full image) + zone.currentRegion.visible = false; + var y = 0; + var speed = 16; + // The image consists of 10px high scrolling layers, this creates them quickly (top = fastest, getting slower as we move down) + for(var z = 0; z < 32; z++) { + zone.addRegion(0, y, 640, 10, speed); + if(z <= 15) { + speed -= 1; + } else { + speed += 1; + } + if(z == 15) { + y = 240; + speed += 1; + } else { + y += 10; + } + } + } +})(); diff --git a/todo/phaser tests/scrollzones/parallax.ts b/todo/phaser tests/scrollzones/parallax.ts new file mode 100644 index 00000000..5f74a294 --- /dev/null +++ b/todo/phaser tests/scrollzones/parallax.ts @@ -0,0 +1,53 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('starray', 'assets/pics/auto_scroll_landscape.png'); + + myGame.loader.load(); + + } + + function create() { + + var zone: Phaser.ScrollZone = myGame.add.scrollZone('starray'); + + // Hide the default region (the full image) + zone.currentRegion.visible = false; + + var y:number = 0; + var speed:number = 16; + + // The image consists of 10px high scrolling layers, this creates them quickly (top = fastest, getting slower as we move down) + for (var z:number = 0; z < 32; z++) + { + zone.addRegion(0, y, 640, 10, speed); + + if (z <= 15) + { + speed -= 1; + } + else + { + speed += 1; + } + + if (z == 15) + { + y = 240; + speed += 1; + } + else + { + y += 10; + } + } + + } + +})(); diff --git a/todo/phaser tests/scrollzones/region demo.js b/todo/phaser tests/scrollzones/region demo.js new file mode 100644 index 00000000..9a0f1d29 --- /dev/null +++ b/todo/phaser tests/scrollzones/region demo.js @@ -0,0 +1,22 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('angelDawn', 'assets/pics/game14_angel_dawn.png'); + myGame.loader.load(); + } + var scroller; + function create() { + // This creates our ScrollZone centered in the middle of the stage. + scroller = myGame.add.scrollZone('angelDawn', myGame.stage.centerX - 320, 100); + // By default we won't scroll the full image, but we will create 3 ScrollRegions within it: + // This creates a ScrollRegion which can be thought of as a rectangle within the ScrollZone that can be scrolled + // independantly - this one scrolls the image of the spacemans head + scroller.addRegion(32, 32, 352, 240, 0, 2); + // The head in the top right + scroller.addRegion(480, 30, 96, 96, 4, 0); + // The small piece of text + scroller.addRegion(466, 160, 122, 14, 0, -0.5); + } +})(); diff --git a/todo/phaser tests/scrollzones/region demo.ts b/todo/phaser tests/scrollzones/region demo.ts new file mode 100644 index 00000000..beabb6f0 --- /dev/null +++ b/todo/phaser tests/scrollzones/region demo.ts @@ -0,0 +1,37 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('angelDawn', 'assets/pics/game14_angel_dawn.png'); + + myGame.loader.load(); + + } + + var scroller: Phaser.ScrollZone; + + function create() { + + // This creates our ScrollZone centered in the middle of the stage. + scroller = myGame.add.scrollZone('angelDawn', myGame.stage.centerX - 320, 100); + + // By default we won't scroll the full image, but we will create 3 ScrollRegions within it: + + // This creates a ScrollRegion which can be thought of as a rectangle within the ScrollZone that can be scrolled + // independantly - this one scrolls the image of the spacemans head + scroller.addRegion(32, 32, 352, 240, 0, 2); + + // The head in the top right + scroller.addRegion(480, 30, 96, 96, 4, 0); + + // The small piece of text + scroller.addRegion(466, 160, 122, 14, 0, -0.5); + + } + +})(); diff --git a/todo/phaser tests/scrollzones/scroll window.js b/todo/phaser tests/scrollzones/scroll window.js new file mode 100644 index 00000000..835558c6 --- /dev/null +++ b/todo/phaser tests/scrollzones/scroll window.js @@ -0,0 +1,18 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('dragonsun', 'assets/pics/cougar_dragonsun.png'); + myGame.loader.addImageFile('overlay', 'assets/pics/scrollframe.png'); + myGame.loader.load(); + } + var scroller; + function create() { + // This creates our ScrollZone. It is positioned at x32 y32 (world coodinates) + // and is a size of 352x240 (which matches the window in our overlay image) + scroller = myGame.add.scrollZone('dragonsun', 32, 32, 352, 240); + scroller.setSpeed(2, 2); + myGame.add.sprite(0, 0, 'overlay'); + } +})(); diff --git a/todo/phaser tests/scrollzones/scroll window.ts b/todo/phaser tests/scrollzones/scroll window.ts new file mode 100644 index 00000000..52f1ebbb --- /dev/null +++ b/todo/phaser tests/scrollzones/scroll window.ts @@ -0,0 +1,31 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('dragonsun', 'assets/pics/cougar_dragonsun.png'); + myGame.loader.addImageFile('overlay', 'assets/pics/scrollframe.png'); + + myGame.loader.load(); + + } + + var scroller: Phaser.ScrollZone; + + function create() { + + // This creates our ScrollZone. It is positioned at x32 y32 (world coodinates) + // and is a size of 352x240 (which matches the window in our overlay image) + scroller = myGame.add.scrollZone('dragonsun', 32, 32, 352, 240); + + scroller.setSpeed(2, 2); + + myGame.add.sprite(0, 0, 'overlay'); + + } + +})(); diff --git a/todo/phaser tests/scrollzones/simple scrollzone.js b/todo/phaser tests/scrollzones/simple scrollzone.js new file mode 100644 index 00000000..4b2466c6 --- /dev/null +++ b/todo/phaser tests/scrollzones/simple scrollzone.js @@ -0,0 +1,16 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('crystal', 'assets/pics/jim_sachs_time_crystal.png'); + myGame.loader.load(); + } + function create() { + // This creates our ScrollZone. It is positioned at x0 y0 (world coodinates) by default and uses + // the 'crystal' image from the cache. + // The default is for the scroll zone to create 1 new scrolling region the size of the whole image you gave it. + // For this example we'll keep that, but look at the other tests to see reasons why you may not want to. + myGame.add.scrollZone('crystal').setSpeed(4, 2); + } +})(); diff --git a/todo/phaser tests/scrollzones/simple scrollzone.ts b/todo/phaser tests/scrollzones/simple scrollzone.ts new file mode 100644 index 00000000..31ecfd1d --- /dev/null +++ b/todo/phaser tests/scrollzones/simple scrollzone.ts @@ -0,0 +1,28 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('crystal', 'assets/pics/jim_sachs_time_crystal.png'); + + myGame.loader.load(); + + } + + function create() { + + // This creates our ScrollZone. It is positioned at x0 y0 (world coodinates) by default and uses + // the 'crystal' image from the cache. + + // The default is for the scroll zone to create 1 new scrolling region the size of the whole image you gave it. + // For this example we'll keep that, but look at the other tests to see reasons why you may not want to. + + myGame.add.scrollZone('crystal').setSpeed(4, 2); + + } + +})(); diff --git a/todo/phaser tests/sprites/align.js b/todo/phaser tests/sprites/align.js new file mode 100644 index 00000000..0bbd6f93 --- /dev/null +++ b/todo/phaser tests/sprites/align.js @@ -0,0 +1,27 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('teddy', 'assets/pics/profil-sad_plush.png'); + myGame.loader.load(); + } + var teddy; + function create() { + teddy = myGame.add.sprite(0, 0, 'teddy'); + teddy.x = myGame.stage.centerX - teddy.width / 2; + teddy.y = myGame.stage.centerY - teddy.height / 2; + myGame.input.onDown.add(click, this); + teddy.renderDebug = true; + } + function click() { + if(teddy.align == Phaser.GameObject.ALIGN_BOTTOM_RIGHT) { + teddy.align = Phaser.GameObject.ALIGN_TOP_LEFT; + } else { + teddy.align++; + } + } + function update() { + teddy.x = myGame.input.x; + teddy.y = myGame.input.y; + } +})(); diff --git a/todo/phaser tests/sprites/align.ts b/todo/phaser tests/sprites/align.ts new file mode 100644 index 00000000..be666803 --- /dev/null +++ b/todo/phaser tests/sprites/align.ts @@ -0,0 +1,50 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('teddy', 'assets/pics/profil-sad_plush.png'); + + myGame.loader.load(); + + } + + var teddy: Phaser.Sprite; + + function create() { + + teddy = myGame.add.sprite(0, 0, 'teddy'); + + teddy.x = myGame.stage.centerX - teddy.width / 2; + teddy.y = myGame.stage.centerY - teddy.height / 2; + + myGame.input.onDown.add(click, this); + + teddy.renderDebug = true; + + } + + function click() { + + if (teddy.align == Phaser.GameObject.ALIGN_BOTTOM_RIGHT) + { + teddy.align = Phaser.GameObject.ALIGN_TOP_LEFT; + } + else + { + teddy.align++; + } + + } + + function update() { + + teddy.x = myGame.input.x; + teddy.y = myGame.input.y; + + } + +})(); diff --git a/todo/phaser tests/sprites/animate by framename.js b/todo/phaser tests/sprites/animate by framename.js new file mode 100644 index 00000000..0aaa7c21 --- /dev/null +++ b/todo/phaser tests/sprites/animate by framename.js @@ -0,0 +1,34 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.load(); + } + var bot; + function create() { + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + // If you are using a Texture Atlas and want to specify the frames of an animation by their name rather than frame index + // then you can use this format: + bot.animations.add('run', [ + 'run00', + 'run01', + 'run02', + 'run03', + 'run04', + 'run05', + 'run06', + 'run07', + 'run08', + 'run09', + 'run10' + ], 10, true, false); + bot.animations.play('run'); + bot.velocity.x = -100; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + } + } +})(); diff --git a/todo/phaser tests/sprites/animate by framename.ts b/todo/phaser tests/sprites/animate by framename.ts new file mode 100644 index 00000000..6d253ca0 --- /dev/null +++ b/todo/phaser tests/sprites/animate by framename.ts @@ -0,0 +1,40 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + + myGame.loader.load(); + + } + + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + + // If you are using a Texture Atlas and want to specify the frames of an animation by their name rather than frame index + // then you can use this format: + bot.animations.add('run', ['run00', 'run01', 'run02', 'run03', 'run04', 'run05', 'run06', 'run07', 'run08', 'run09', 'run10'], 10, true, false); + + bot.animations.play('run'); + + bot.velocity.x = -100; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + } + + } + +})(); diff --git a/todo/phaser tests/sprites/animation 1.js b/todo/phaser tests/sprites/animation 1.js new file mode 100644 index 00000000..081eb211 --- /dev/null +++ b/todo/phaser tests/sprites/animation 1.js @@ -0,0 +1,32 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addSpriteSheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + //myGame.loader.addSpriteSheet('coin', 'assets/sprites/coin.png', 32, 32); + myGame.loader.addSpriteSheet('monster', 'assets/sprites/metalslug_monster39x40.png', 39, 40); + myGame.loader.load(); + } + var car; + function create() { + car = myGame.add.sprite(200, 300, 'monster'); + car.animations.add('spin', null, 30, true); + //car.animations.play('spin', 30, true); + car.animations.play('spin'); + } + function update() { + car.renderDebugInfo(16, 16); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 200)); + } + } +})(); diff --git a/todo/phaser tests/sprites/animation 1.ts b/todo/phaser tests/sprites/animation 1.ts new file mode 100644 index 00000000..81d580f6 --- /dev/null +++ b/todo/phaser tests/sprites/animation 1.ts @@ -0,0 +1,55 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addSpriteSheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + //myGame.loader.addSpriteSheet('coin', 'assets/sprites/coin.png', 32, 32); + myGame.loader.addSpriteSheet('monster', 'assets/sprites/metalslug_monster39x40.png', 39, 40); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + + function create() { + + car = myGame.add.sprite(200, 300, 'monster'); + + car.animations.add('spin', null, 30, true); + + //car.animations.play('spin', 30, true); + car.animations.play('spin'); + + } + + function update() { + + car.renderDebugInfo(16, 16); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 200)); + } + + } + +})(); diff --git a/todo/phaser tests/sprites/dynamic texture 1.js b/todo/phaser tests/sprites/dynamic texture 1.js new file mode 100644 index 00000000..89890eb9 --- /dev/null +++ b/todo/phaser tests/sprites/dynamic texture 1.js @@ -0,0 +1,59 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('ball', 'assets/sprites/shinyball.png'); + myGame.loader.load(); + } + var wobblyBall; + function create() { + // Create our DynamicTexture + wobblyBall = myGame.add.dynamicTexture(32, 64); + // And apply it to 100 randomly positioned sprites + for(var i = 0; i < 100; i++) { + var temp = myGame.add.sprite(myGame.world.randomX, myGame.world.randomY); + temp.width = 32; + temp.height = 64; + temp.loadDynamicTexture(wobblyBall); + } + // Populate the wave with some data + waveData = myGame.math.sinCosGenerator(32, 8, 8, 2); + } + function update() { + wobblyBall.clear(); + updateWobblyBall(); + } + // This creates a simple sine-wave effect running through our DynamicTexture. + // This is then duplicated across all sprites using it, meaning we only have to calculate it once. + var waveSize = 8; + var wavePixelChunk = 2; + var waveData; + var waveDataCounter; + function updateWobblyBall() { + var s = 0; + var copyRect = { + x: 0, + y: 0, + w: wavePixelChunk, + h: 32 + }; + var copyPoint = { + x: 0, + y: 0 + }; + for(var x = 0; x < 32; x += wavePixelChunk) { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData[s]; + wobblyBall.context.drawImage(myGame.cache.getImage('ball'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + copyRect.x += wavePixelChunk; + s++; + } + // Cycle through the wave data - this is what causes the image to "undulate" + var t = waveData.shift(); + waveData.push(t); + waveDataCounter++; + if(waveDataCounter == waveData.length) { + waveDataCounter = 0; + } + } +})(); diff --git a/todo/phaser tests/sprites/dynamic texture 1.ts b/todo/phaser tests/sprites/dynamic texture 1.ts new file mode 100644 index 00000000..88b75e3a --- /dev/null +++ b/todo/phaser tests/sprites/dynamic texture 1.ts @@ -0,0 +1,82 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('ball', 'assets/sprites/shinyball.png'); + + myGame.loader.load(); + + } + + var wobblyBall: Phaser.DynamicTexture; + + function create() { + + // Create our DynamicTexture + wobblyBall = myGame.add.dynamicTexture(32, 64); + + // And apply it to 100 randomly positioned sprites + for (var i = 0; i < 100; i++) + { + var temp = myGame.add.sprite(myGame.world.randomX, myGame.world.randomY); + temp.width = 32; + temp.height = 64; + temp.loadDynamicTexture(wobblyBall); + } + + // Populate the wave with some data + waveData = myGame.math.sinCosGenerator(32, 8, 8, 2); + + } + + function update() { + + wobblyBall.clear(); + + updateWobblyBall(); + + } + + // This creates a simple sine-wave effect running through our DynamicTexture. + // This is then duplicated across all sprites using it, meaning we only have to calculate it once. + + var waveSize = 8; + var wavePixelChunk = 2; + var waveData; + var waveDataCounter; + + function updateWobblyBall() + { + var s = 0; + var copyRect = { x: 0, y: 0, w: wavePixelChunk, h: 32 }; + var copyPoint = { x: 0, y: 0 }; + + for (var x = 0; x < 32; x += wavePixelChunk) + { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData[s]; + + wobblyBall.context.drawImage(myGame.cache.getImage('ball'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + + copyRect.x += wavePixelChunk; + + s++; + } + + // Cycle through the wave data - this is what causes the image to "undulate" + var t = waveData.shift(); + waveData.push(t); + + waveDataCounter++; + + if (waveDataCounter == waveData.length) + { + waveDataCounter = 0; + } + } + +})(); diff --git a/todo/phaser tests/sprites/dynamic texture 2.js b/todo/phaser tests/sprites/dynamic texture 2.js new file mode 100644 index 00000000..5494a16a --- /dev/null +++ b/todo/phaser tests/sprites/dynamic texture 2.js @@ -0,0 +1,78 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('slime', 'assets/sprites/slime.png'); + myGame.loader.addImageFile('eyes', 'assets/sprites/slimeeyes.png'); + myGame.loader.load(); + } + var slime; + var eyes; + var wobble; + function create() { + myGame.camera.backgroundColor = 'rgb(82,154,206)'; + // Create our DynamicTexture + wobble = myGame.add.dynamicTexture(48, 100); + slime = myGame.add.sprite(200, 300); + slime.width = 48; + slime.height = 100; + slime.loadDynamicTexture(wobble); + eyes = myGame.add.sprite(210, 326, 'eyes'); + // Populate the wave with some data + waveData = myGame.math.sinCosGenerator(32, 8, 8, 2); + } + function update() { + wobble.clear(); + updateWobble(); + slime.velocity.x = 0; + slime.velocity.y = 0; + slime.angularVelocity = 0; + eyes.velocity.x = 0; + eyes.velocity.y = 0; + eyes.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + slime.angularVelocity = -200; + eyes.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + slime.angularVelocity = 200; + eyes.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + slime.velocity.copyFrom(myGame.motion.velocityFromAngle(slime.angle, 200)); + eyes.velocity.copyFrom(myGame.motion.velocityFromAngle(slime.angle, 200)); + } + } + // This creates a simple sine-wave effect running through our DynamicTexture. + // This is then duplicated across all sprites using it, meaning we only have to calculate it once. + var waveSize = 8; + var wavePixelChunk = 2; + var waveData; + var waveDataCounter; + function updateWobble() { + var s = 0; + var copyRect = { + x: 0, + y: 0, + w: wavePixelChunk, + h: 52 + }; + var copyPoint = { + x: 0, + y: 0 + }; + for(var x = 0; x < 48; x += wavePixelChunk) { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData[s]; + wobble.context.drawImage(myGame.cache.getImage('slime'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + copyRect.x += wavePixelChunk; + s++; + } + // Cycle through the wave data - this is what causes the image to "undulate" + var t = waveData.shift(); + waveData.push(t); + waveDataCounter++; + if(waveDataCounter == waveData.length) { + waveDataCounter = 0; + } + } +})(); diff --git a/todo/phaser tests/sprites/dynamic texture 2.ts b/todo/phaser tests/sprites/dynamic texture 2.ts new file mode 100644 index 00000000..e5c83faa --- /dev/null +++ b/todo/phaser tests/sprites/dynamic texture 2.ts @@ -0,0 +1,109 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('slime', 'assets/sprites/slime.png'); + myGame.loader.addImageFile('eyes', 'assets/sprites/slimeeyes.png'); + + myGame.loader.load(); + + } + + var slime: Phaser.Sprite; + var eyes: Phaser.Sprite; + var wobble: Phaser.DynamicTexture; + + function create() { + + myGame.camera.backgroundColor = 'rgb(82,154,206)'; + + // Create our DynamicTexture + wobble = myGame.add.dynamicTexture(48, 100); + + slime = myGame.add.sprite(200, 300); + slime.width = 48; + slime.height = 100; + slime.loadDynamicTexture(wobble); + + eyes = myGame.add.sprite(210, 326, 'eyes'); + + // Populate the wave with some data + waveData = myGame.math.sinCosGenerator(32, 8, 8, 2); + + } + + function update() { + + wobble.clear(); + + updateWobble(); + + slime.velocity.x = 0; + slime.velocity.y = 0; + slime.angularVelocity = 0; + eyes.velocity.x = 0; + eyes.velocity.y = 0; + eyes.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + slime.angularVelocity = -200; + eyes.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + slime.angularVelocity = 200; + eyes.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + slime.velocity.copyFrom(myGame.motion.velocityFromAngle(slime.angle, 200)); + eyes.velocity.copyFrom(myGame.motion.velocityFromAngle(slime.angle, 200)); + } + + } + + // This creates a simple sine-wave effect running through our DynamicTexture. + // This is then duplicated across all sprites using it, meaning we only have to calculate it once. + + var waveSize = 8; + var wavePixelChunk = 2; + var waveData; + var waveDataCounter; + + function updateWobble() + { + var s = 0; + var copyRect = { x: 0, y: 0, w: wavePixelChunk, h: 52 }; + var copyPoint = { x: 0, y: 0 }; + + for (var x = 0; x < 48; x += wavePixelChunk) + { + copyPoint.x = x; + copyPoint.y = waveSize + (waveSize / 2) + waveData[s]; + + wobble.context.drawImage(myGame.cache.getImage('slime'), copyRect.x, copyRect.y, copyRect.w, copyRect.h, copyPoint.x, copyPoint.y, copyRect.w, copyRect.h); + + copyRect.x += wavePixelChunk; + + s++; + } + + // Cycle through the wave data - this is what causes the image to "undulate" + var t = waveData.shift(); + waveData.push(t); + + waveDataCounter++; + + if (waveDataCounter == waveData.length) + { + waveDataCounter = 0; + } + } + +})(); diff --git a/todo/phaser tests/sprites/flipped.js b/todo/phaser tests/sprites/flipped.js new file mode 100644 index 00000000..a176e5dd --- /dev/null +++ b/todo/phaser tests/sprites/flipped.js @@ -0,0 +1,42 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.addTextureAtlas('atlas', 'assets/pics/texturepacker_test.png', 'assets/pics/texturepacker_test.json'); + myGame.loader.load(); + } + var bot; + var bot2; + var car; + function create() { + // This bot will flip properly when he reaches the edge + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -200; + // This one won't + bot2 = myGame.add.sprite(myGame.stage.width, 200, 'bot'); + bot2.animations.add('run'); + bot2.animations.play('run', 10, true); + bot2.velocity.x = -150; + // Flip a static sprite (not an animation) + car = myGame.add.sprite(100, 400, 'atlas'); + car.frameName = 'supercars_parsec.png'; + car.flipped = true; + } + function update() { + if(bot.x < -bot.width) { + bot.flipped = true; + bot.velocity.x = 200; + } else if(bot.x > myGame.stage.width) { + bot.flipped = false; + bot.velocity.x = -200; + } + if(bot2.x < -bot2.width) { + bot2.velocity.x = 200; + } else if(bot2.x > myGame.stage.width) { + bot2.velocity.x = -200; + } + } +})(); diff --git a/todo/phaser tests/sprites/flipped.ts b/todo/phaser tests/sprites/flipped.ts new file mode 100644 index 00000000..19b6052c --- /dev/null +++ b/todo/phaser tests/sprites/flipped.ts @@ -0,0 +1,64 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.addTextureAtlas('atlas', 'assets/pics/texturepacker_test.png', 'assets/pics/texturepacker_test.json'); + myGame.loader.load(); + + } + + var bot: Phaser.Sprite; + var bot2: Phaser.Sprite; + var car: Phaser.Sprite; + + function create() { + + // This bot will flip properly when he reaches the edge + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -200; + + // This one won't + bot2 = myGame.add.sprite(myGame.stage.width, 200, 'bot'); + bot2.animations.add('run'); + bot2.animations.play('run', 10, true); + bot2.velocity.x = -150; + + // Flip a static sprite (not an animation) + car = myGame.add.sprite(100, 400, 'atlas'); + car.frameName = 'supercars_parsec.png'; + car.flipped = true; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.flipped = true; + bot.velocity.x = 200; + } + else if (bot.x > myGame.stage.width) + { + bot.flipped = false; + bot.velocity.x = -200; + } + + if (bot2.x < -bot2.width) + { + bot2.velocity.x = 200; + } + else if (bot2.x > myGame.stage.width) + { + bot2.velocity.x = -200; + } + + } + +})(); diff --git a/todo/phaser tests/sprites/mark of the bunny.js b/todo/phaser tests/sprites/mark of the bunny.js new file mode 100644 index 00000000..fcaa675a --- /dev/null +++ b/todo/phaser tests/sprites/mark of the bunny.js @@ -0,0 +1,56 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('bunny', 'assets/sprites/wabbit.png'); + myGame.loader.load(); + } + var maxX; + var maxY; + var minX; + var minY; + var fps; + function create() { + // FPS counter TextArea + fps = document.createElement('textarea'); + fps.style.width = '300px'; + fps.style.height = '100px'; + document.getElementById('game').appendChild(fps); + minX = 0; + minY = 0; + maxX = myGame.stage.width - 26; + maxY = myGame.stage.height - 37; + addBunnies(500); + } + function addBunnies(quantity) { + for(var i = 0; i < quantity; i++) { + var tempSprite = myGame.add.sprite(myGame.stage.randomX, 0, 'bunny'); + tempSprite.velocity.x = -200 + (Math.random() * 400); + tempSprite.velocity.y = 100 + Math.random() * 200; + } + } + function update() { + fps.textContent = 'Press Up to add more\n\nBunnies: ' + myGame.world.group.length + '\nFPS: ' + myGame.time.fps + ' (' + myGame.time.fpsMin + '-' + myGame.time.fpsMax + ')'; + myGame.world.group.forEach(checkWalls); + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + addBunnies(10); + } + } + function checkWalls(bunny) { + if(bunny.x > maxX) { + bunny.velocity.x *= -1; + bunny.x = maxX; + } else if(bunny.x < minX) { + bunny.velocity.x *= -1; + bunny.x = minX; + } + if(bunny.y > maxY) { + bunny.velocity.y *= -0.8; + bunny.y = maxY; + } else if(bunny.y < minY) { + bunny.velocity.x = -200 + (Math.random() * 400); + bunny.velocity.y = 100 + Math.random() * 200; + bunny.y = minY; + } + } +})(); diff --git a/todo/phaser tests/sprites/mark of the bunny.ts b/todo/phaser tests/sprites/mark of the bunny.ts new file mode 100644 index 00000000..cd8af7d8 --- /dev/null +++ b/todo/phaser tests/sprites/mark of the bunny.ts @@ -0,0 +1,89 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('bunny', 'assets/sprites/wabbit.png'); + + myGame.loader.load(); + + } + + var maxX: number; + var maxY: number; + var minX: number; + var minY: number; + var fps: HTMLTextAreaElement; + + function create() { + + // FPS counter TextArea + fps = document.createElement('textarea'); + fps.style.width = '300px'; + fps.style.height = '100px'; + document.getElementById('game').appendChild(fps); + + minX = 0; + minY = 0; + maxX = myGame.stage.width - 26; + maxY = myGame.stage.height - 37; + + addBunnies(500); + + } + + function addBunnies(quantity) { + + for (var i = 0; i < quantity; i++) + { + var tempSprite = myGame.add.sprite(myGame.stage.randomX, 0, 'bunny'); + tempSprite.velocity.x = -200 + (Math.random() * 400); + tempSprite.velocity.y = 100 + Math.random() * 200; + } + + } + + function update() { + + fps.textContent = 'Press Up to add more\n\nBunnies: ' + myGame.world.group.length + '\nFPS: ' + myGame.time.fps + ' (' + myGame.time.fpsMin + '-' + myGame.time.fpsMax + ')'; + + myGame.world.group.forEach(checkWalls); + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + addBunnies(10); + } + + } + + function checkWalls(bunny:Phaser.Sprite) { + + if (bunny.x > maxX) + { + bunny.velocity.x *= -1; + bunny.x = maxX; + } + else if (bunny.x < minX) + { + bunny.velocity.x *= -1; + bunny.x = minX; + } + + if (bunny.y > maxY) + { + bunny.velocity.y *= -0.8; + bunny.y = maxY; + } + else if (bunny.y < minY) + { + bunny.velocity.x = -200 + (Math.random() * 400); + bunny.velocity.y = 100 + Math.random() * 200; + bunny.y = minY; + } + + } + +})(); diff --git a/todo/phaser tests/sprites/rotation.js b/todo/phaser tests/sprites/rotation.js new file mode 100644 index 00000000..a338099a --- /dev/null +++ b/todo/phaser tests/sprites/rotation.js @@ -0,0 +1,22 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('teddy', 'assets/pics/profil-sad_plush.png'); + myGame.loader.load(); + } + var teddy; + function create() { + teddy = myGame.add.sprite(0, 0, 'teddy'); + teddy.x = myGame.stage.centerX - teddy.width / 2; + teddy.y = myGame.stage.centerY - teddy.height / 2; + teddy.renderDebug = true; + } + function update() { + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + teddy.angularAcceleration = -40; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + teddy.angularAcceleration = 40; + } + } +})(); diff --git a/todo/phaser tests/sprites/rotation.ts b/todo/phaser tests/sprites/rotation.ts new file mode 100644 index 00000000..52e629ab --- /dev/null +++ b/todo/phaser tests/sprites/rotation.ts @@ -0,0 +1,39 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('teddy', 'assets/pics/profil-sad_plush.png'); + + myGame.loader.load(); + + } + + var teddy: Phaser.Sprite; + + function create() { + + teddy = myGame.add.sprite(0, 0, 'teddy'); + teddy.x = myGame.stage.centerX - teddy.width / 2; + teddy.y = myGame.stage.centerY - teddy.height / 2; + teddy.renderDebug = true; + + } + + function update() { + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + teddy.angularAcceleration = -40; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + teddy.angularAcceleration = 40; + } + + } + +})(); diff --git a/todo/phaser tests/sprites/starling texture atlas 1.js b/todo/phaser tests/sprites/starling texture atlas 1.js new file mode 100644 index 00000000..fc7cdeee --- /dev/null +++ b/todo/phaser tests/sprites/starling texture atlas 1.js @@ -0,0 +1,32 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Starling/Sparrow XML Texture Atlas Method 1 + // + // In this example we assume that the XML data is stored in an external file + myGame.loader.addTextureAtlas('bits', 'assets/sprites/shoebox.png', 'assets/sprites/shoebox.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/shoebot.png', 'assets/sprites/shoebot.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.load(); + } + var bits; + var bot; + function create() { + bot = myGame.add.sprite(800, 200, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bits = myGame.add.sprite(200, 200, 'bits'); + bits.frame = 0; + bot.velocity.x = -300; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + bits.frame++; + console.log(bits.frame, bits.animations.frameTotal); + if(bits.frame == bits.animations.frameTotal - 1) { + bits.frame = 0; + } + } + } +})(); diff --git a/todo/phaser tests/sprites/starling texture atlas 1.ts b/todo/phaser tests/sprites/starling texture atlas 1.ts new file mode 100644 index 00000000..c6dc0d8f --- /dev/null +++ b/todo/phaser tests/sprites/starling texture atlas 1.ts @@ -0,0 +1,52 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Starling/Sparrow XML Texture Atlas Method 1 + // + // In this example we assume that the XML data is stored in an external file + myGame.loader.addTextureAtlas('bits', 'assets/sprites/shoebox.png', 'assets/sprites/shoebox.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/shoebot.png', 'assets/sprites/shoebot.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + + myGame.loader.load(); + + } + + var bits: Phaser.Sprite; + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(800, 200, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bits = myGame.add.sprite(200, 200, 'bits'); + bits.frame = 0; + + bot.velocity.x = -300; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + + bits.frame++; + console.log(bits.frame, bits.animations.frameTotal); + + if (bits.frame == bits.animations.frameTotal - 1) + { + bits.frame = 0; + } + } + + } + +})(); diff --git a/todo/phaser tests/sprites/texture atlas 2.js b/todo/phaser tests/sprites/texture atlas 2.js new file mode 100644 index 00000000..1fb6d2a8 --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 2.js @@ -0,0 +1,25 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Texture Atlas Method 2 + // + // In this example we assume that the TexturePacker JSON data is a string of json data stored as a var + // (in this case botData) + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', null, botData); + myGame.loader.load(); + } + var bot; + function create() { + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -100; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + } + } + var botData = '{"frames": [{"filename": "running bot.swf/0000","frame": { "x": 34, "y": 128, "w": 56, "h": 60 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 2, "w": 56, "h": 60 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0001","frame": { "x": 54, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0002","frame": { "x": 54, "y": 58, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0003","frame": { "x": 0, "y": 192, "w": 34, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0004","frame": { "x": 0, "y": 64, "w": 54, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0005","frame": { "x": 196, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0006","frame": { "x": 0, "y": 0, "w": 54, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0007","frame": { "x": 140, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0008","frame": { "x": 34, "y": 188, "w": 50, "h": 60 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 3, "y": 2, "w": 50, "h": 60 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0009","frame": { "x": 0, "y": 128, "w": 34, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0010","frame": { "x": 84, "y": 188, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }}]}'; +})(); diff --git a/todo/phaser tests/sprites/texture atlas 2.ts b/todo/phaser tests/sprites/texture atlas 2.ts new file mode 100644 index 00000000..7ae3264b --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 2.ts @@ -0,0 +1,43 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Texture Atlas Method 2 + // + // In this example we assume that the TexturePacker JSON data is a string of json data stored as a var + // (in this case botData) + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', null, botData); + + myGame.loader.load(); + + } + + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bot.velocity.x = -100; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + } + + } + + var botData = '{"frames": [{"filename": "running bot.swf/0000","frame": { "x": 34, "y": 128, "w": 56, "h": 60 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 2, "w": 56, "h": 60 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0001","frame": { "x": 54, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0002","frame": { "x": 54, "y": 58, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0003","frame": { "x": 0, "y": 192, "w": 34, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0004","frame": { "x": 0, "y": 64, "w": 54, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0005","frame": { "x": 196, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0006","frame": { "x": 0, "y": 0, "w": 54, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0007","frame": { "x": 140, "y": 0, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0008","frame": { "x": 34, "y": 188, "w": 50, "h": 60 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 3, "y": 2, "w": 50, "h": 60 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0009","frame": { "x": 0, "y": 128, "w": 34, "h": 64 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 },"sourceSize": { "w": 56, "h": 64 }},{"filename": "running bot.swf/0010","frame": { "x": 84, "y": 188, "w": 56, "h": 58 },"rotated": false,"trimmed": true,"spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 },"sourceSize": { "w": 56, "h": 64 }}]}'; + +})(); diff --git a/todo/phaser tests/sprites/texture atlas 3.js b/todo/phaser tests/sprites/texture atlas 3.js new file mode 100644 index 00000000..b430c9fd --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 3.js @@ -0,0 +1,23 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Texture Atlas Method 3 + // + // In this example we assume that the TexturePacker JSON data is stored in an external file + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + myGame.loader.load(); + } + var bot; + function create() { + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -100; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + } + } +})(); diff --git a/todo/phaser tests/sprites/texture atlas 3.ts b/todo/phaser tests/sprites/texture atlas 3.ts new file mode 100644 index 00000000..49fec4fa --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 3.ts @@ -0,0 +1,40 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Texture Atlas Method 3 + // + // In this example we assume that the TexturePacker JSON data is stored in an external file + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', 'assets/sprites/running_bot.json'); + + myGame.loader.load(); + + } + + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bot.velocity.x = -100; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + } + + } + +})(); diff --git a/todo/phaser tests/sprites/texture atlas 4.js b/todo/phaser tests/sprites/texture atlas 4.js new file mode 100644 index 00000000..a5e7b5d1 --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 4.js @@ -0,0 +1,32 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + // Texture Atlas Method 4 + // + // We load a TexturePacker JSON file and image and show you how to make several unique sprites from the same file + myGame.loader.addTextureAtlas('atlas', 'assets/pics/texturepacker_test.png', 'assets/pics/texturepacker_test.json'); + myGame.loader.load(); + } + var chick; + var car; + var mech; + var robot; + var cop; + function create() { + myGame.camera.backgroundColor = 'rgb(40, 40, 40)'; + chick = myGame.add.sprite(64, 64, 'atlas'); + // You can set the frame based on the frame name (which TexturePacker usually sets to be the filename of the image itself) + chick.frameName = 'budbrain_chick.png'; + // Or by setting the frame index + //chick.frame = 0; + cop = myGame.add.sprite(600, 64, 'atlas'); + cop.frameName = 'ladycop.png'; + robot = myGame.add.sprite(50, 300, 'atlas'); + robot.frameName = 'robot.png'; + car = myGame.add.sprite(100, 400, 'atlas'); + car.frameName = 'supercars_parsec.png'; + mech = myGame.add.sprite(250, 100, 'atlas'); + mech.frameName = 'titan_mech.png'; + } +})(); diff --git a/todo/phaser tests/sprites/texture atlas 4.ts b/todo/phaser tests/sprites/texture atlas 4.ts new file mode 100644 index 00000000..faf74f46 --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas 4.ts @@ -0,0 +1,50 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + // Texture Atlas Method 4 + // + // We load a TexturePacker JSON file and image and show you how to make several unique sprites from the same file + myGame.loader.addTextureAtlas('atlas', 'assets/pics/texturepacker_test.png', 'assets/pics/texturepacker_test.json'); + + myGame.loader.load(); + + } + + var chick: Phaser.Sprite; + var car: Phaser.Sprite; + var mech: Phaser.Sprite; + var robot: Phaser.Sprite; + var cop: Phaser.Sprite; + + function create() { + + myGame.camera.backgroundColor = 'rgb(40, 40, 40)'; + + chick = myGame.add.sprite(64, 64, 'atlas'); + + // You can set the frame based on the frame name (which TexturePacker usually sets to be the filename of the image itself) + chick.frameName = 'budbrain_chick.png'; + + // Or by setting the frame index + //chick.frame = 0; + + cop = myGame.add.sprite(600, 64, 'atlas'); + cop.frameName = 'ladycop.png'; + + robot = myGame.add.sprite(50, 300, 'atlas'); + robot.frameName = 'robot.png'; + + car = myGame.add.sprite(100, 400, 'atlas'); + car.frameName = 'supercars_parsec.png'; + + mech = myGame.add.sprite(250, 100, 'atlas'); + mech.frameName = 'titan_mech.png'; + + } + +})(); diff --git a/todo/phaser tests/sprites/texture atlas.js b/todo/phaser tests/sprites/texture atlas.js new file mode 100644 index 00000000..ce5837bf --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas.js @@ -0,0 +1,271 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Texture Atlas Method 1 + // + // In this example we assume that the TexturePacker JSON data is a real json object stored as a var + // (in this case botData) + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', null, botData); + myGame.loader.load(); + } + var bot; + function create() { + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bot.velocity.x = -100; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + } + } + var botData = { + "frames": [ + { + "filename": "running bot.swf/0000", + "frame": { + "x": 34, + "y": 128, + "w": 56, + "h": 60 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 2, + "w": 56, + "h": 60 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0001", + "frame": { + "x": 54, + "y": 0, + "w": 56, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 56, + "h": 58 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0002", + "frame": { + "x": 54, + "y": 58, + "w": 56, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 56, + "h": 58 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0003", + "frame": { + "x": 0, + "y": 192, + "w": 34, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 11, + "y": 0, + "w": 34, + "h": 64 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0004", + "frame": { + "x": 0, + "y": 64, + "w": 54, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 54, + "h": 64 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0005", + "frame": { + "x": 196, + "y": 0, + "w": 56, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 56, + "h": 58 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0006", + "frame": { + "x": 0, + "y": 0, + "w": 54, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 1, + "y": 0, + "w": 54, + "h": 64 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0007", + "frame": { + "x": 140, + "y": 0, + "w": 56, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 56, + "h": 58 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0008", + "frame": { + "x": 34, + "y": 188, + "w": 50, + "h": 60 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 3, + "y": 2, + "w": 50, + "h": 60 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0009", + "frame": { + "x": 0, + "y": 128, + "w": 34, + "h": 64 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 11, + "y": 0, + "w": 34, + "h": 64 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + }, + { + "filename": "running bot.swf/0010", + "frame": { + "x": 84, + "y": 188, + "w": 56, + "h": 58 + }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { + "x": 0, + "y": 3, + "w": 56, + "h": 58 + }, + "sourceSize": { + "w": 56, + "h": 64 + } + } + ], + "meta": { + "app": "http://www.texturepacker.com", + "version": "1.0", + "image": "running_bot.png", + "format": "RGBA8888", + "size": { + "w": 252, + "h": 256 + }, + "scale": "0.2", + "smartupdate": "$TexturePacker:SmartUpdate:fb56f261b1eb04e3215824426595f64c$" + } + }; +})(); diff --git a/todo/phaser tests/sprites/texture atlas.ts b/todo/phaser tests/sprites/texture atlas.ts new file mode 100644 index 00000000..1aa46bff --- /dev/null +++ b/todo/phaser tests/sprites/texture atlas.ts @@ -0,0 +1,143 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Texture Atlas Method 1 + // + // In this example we assume that the TexturePacker JSON data is a real json object stored as a var + // (in this case botData) + myGame.loader.addTextureAtlas('bot', 'assets/sprites/running_bot.png', null, botData); + + myGame.loader.load(); + + } + + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(myGame.stage.width, 300, 'bot'); + + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bot.velocity.x = -100; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + } + + } + + var botData = { + "frames": [ + + { + "filename": "running bot.swf/0000", + "frame": { "x": 34, "y": 128, "w": 56, "h": 60 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 2, "w": 56, "h": 60 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0001", + "frame": { "x": 54, "y": 0, "w": 56, "h": 58 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0002", + "frame": { "x": 54, "y": 58, "w": 56, "h": 58 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0003", + "frame": { "x": 0, "y": 192, "w": 34, "h": 64 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0004", + "frame": { "x": 0, "y": 64, "w": 54, "h": 64 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0005", + "frame": { "x": 196, "y": 0, "w": 56, "h": 58 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0006", + "frame": { "x": 0, "y": 0, "w": 54, "h": 64 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 1, "y": 0, "w": 54, "h": 64 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0007", + "frame": { "x": 140, "y": 0, "w": 56, "h": 58 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0008", + "frame": { "x": 34, "y": 188, "w": 50, "h": 60 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 3, "y": 2, "w": 50, "h": 60 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0009", + "frame": { "x": 0, "y": 128, "w": 34, "h": 64 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 0, "w": 34, "h": 64 }, + "sourceSize": { "w": 56, "h": 64 } + }, + { + "filename": "running bot.swf/0010", + "frame": { "x": 84, "y": 188, "w": 56, "h": 58 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 3, "w": 56, "h": 58 }, + "sourceSize": { "w": 56, "h": 64 } + }], + "meta": { + "app": "http://www.texturepacker.com", + "version": "1.0", + "image": "running_bot.png", + "format": "RGBA8888", + "size": { "w": 252, "h": 256 }, + "scale": "0.2", + "smartupdate": "$TexturePacker:SmartUpdate:fb56f261b1eb04e3215824426595f64c$" + } + }; + +})(); diff --git a/todo/phaser tests/sprites/velocity.js b/todo/phaser tests/sprites/velocity.js new file mode 100644 index 00000000..6731ad5e --- /dev/null +++ b/todo/phaser tests/sprites/velocity.js @@ -0,0 +1,33 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addImageFile('car', 'assets/sprites/asteroids_ship.png'); + myGame.loader.load(); + } + var car; + function create() { + car = myGame.add.sprite(200, 300, 'car'); + } + function update() { + car.renderDebugInfo(16, 16); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 200); + // instant + car.velocity.copyFrom(motion); + // acceleration + //car.acceleration.copyFrom(motion); + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + //car.velocity.y = 200; + } + } +})(); diff --git a/todo/phaser tests/sprites/velocity.ts b/todo/phaser tests/sprites/velocity.ts new file mode 100644 index 00000000..50d75682 --- /dev/null +++ b/todo/phaser tests/sprites/velocity.ts @@ -0,0 +1,58 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addImageFile('car', 'assets/sprites/asteroids_ship.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + + function create() { + + car = myGame.add.sprite(200, 300, 'car'); + + } + + function update() { + + car.renderDebugInfo(16, 16); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 200); + + // instant + car.velocity.copyFrom(motion); + + // acceleration + //car.acceleration.copyFrom(motion); + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + //car.velocity.y = 200; + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/basic tilemap.js b/todo/phaser tests/tilemap/basic tilemap.js new file mode 100644 index 00000000..2b52968e --- /dev/null +++ b/todo/phaser tests/tilemap/basic tilemap.js @@ -0,0 +1,50 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Tiled JSON Test + //myGame.loader.addTextFile('jsontest', 'assets/maps/test.json'); + myGame.loader.addTextFile('jsontest', 'assets/maps/multi-layer-test.json'); + myGame.loader.addImageFile('jsontiles', 'assets/tiles/platformer_tiles.png'); + // CSV Test + myGame.loader.addTextFile('csvtest', 'assets/maps/catastrophi_level2.csv'); + myGame.loader.addImageFile('csvtiles', 'assets/tiles/catastrophi_tiles_16.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var map; + var bigCam; + function create() { + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + //bigCam = myGame.add.camera(30, 30, 200, 200); + //bigCam.showBorder = true; + //bigCam.scale.setTo(1.5, 1.5); + //map = myGame.add.tilemap('jsontiles', 'jsontest', Phaser.Tilemap.FORMAT_TILED_JSON); + map = myGame.add.tilemap('csvtiles', 'csvtest', Phaser.Tilemap.FORMAT_CSV, true, 16, 16); + car = myGame.add.sprite(300, 100, 'car'); + myGame.camera.follow(car); + //bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + myGame.onRenderCallback = render; + } + function update() { + //bigCam.rotation += 0.5; + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + var motion = myGame.motion.velocityFromAngle(car.angle, 300); + car.velocity.copyFrom(motion); + } + } + function render() { + //map.renderDebugInfo(400, 16); + } +})(); diff --git a/todo/phaser tests/tilemap/basic tilemap.ts b/todo/phaser tests/tilemap/basic tilemap.ts new file mode 100644 index 00000000..2bdcb3b6 --- /dev/null +++ b/todo/phaser tests/tilemap/basic tilemap.ts @@ -0,0 +1,82 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Tiled JSON Test + //myGame.loader.addTextFile('jsontest', 'assets/maps/test.json'); + myGame.loader.addTextFile('jsontest', 'assets/maps/multi-layer-test.json'); + myGame.loader.addImageFile('jsontiles', 'assets/tiles/platformer_tiles.png'); + + // CSV Test + myGame.loader.addTextFile('csvtest', 'assets/maps/catastrophi_level2.csv'); + myGame.loader.addImageFile('csvtiles', 'assets/tiles/catastrophi_tiles_16.png'); + + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var map: Phaser.Tilemap; + var bigCam: Phaser.Camera; + + function create() { + + myGame.camera.deadzone = new Phaser.Rectangle(64, 64, myGame.stage.width - 128, myGame.stage.height - 128); + + //bigCam = myGame.add.camera(30, 30, 200, 200); + //bigCam.showBorder = true; + //bigCam.scale.setTo(1.5, 1.5); + + //map = myGame.add.tilemap('jsontiles', 'jsontest', Phaser.Tilemap.FORMAT_TILED_JSON); + map = myGame.add.tilemap('csvtiles', 'csvtest', Phaser.Tilemap.FORMAT_CSV, true, 16, 16); + + car = myGame.add.sprite(300, 100, 'car'); + + myGame.camera.follow(car); + //bigCam.follow(car, Phaser.Camera.STYLE_LOCKON); + + myGame.onRenderCallback = render; + + } + + function update() { + + //bigCam.rotation += 0.5; + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + car.angularAcceleration = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + var motion:Phaser.Point = myGame.motion.velocityFromAngle(car.angle, 300); + + car.velocity.copyFrom(motion); + } + + } + + function render { + + //map.renderDebugInfo(400, 16); + + } + +})(); diff --git a/todo/phaser tests/tilemap/collide with tile.js b/todo/phaser tests/tilemap/collide with tile.js new file mode 100644 index 00000000..d6ad11de --- /dev/null +++ b/todo/phaser tests/tilemap/collide with tile.js @@ -0,0 +1,66 @@ +/// +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var CACTUS = 31; + var SIGN_POST = 46; + var map; + var car; + var tile; + var flash; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + // When the car collides with the cactus tile we'll flash the screen red briefly, + // but it won't stop the car (the separateX/Y values are set to false) + map.setCollisionByIndex([ + CACTUS + ], Phaser.Collision.ANY, true, false, false); + // When the car collides with the sign post tile we'll stop the car moving (separation is set to true) + map.setCollisionByIndex([ + SIGN_POST + ], Phaser.Collision.ANY, true, true, true); + // This is the callback that will be called every time map.collide() returns true + map.collisionCallback = collide; + // This is the context in which the callback is called (usually 'this' if you want to be able to access local vars) + map.collisionCallbackContext = this; + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + myGame.camera.follow(car); + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + } + function update() { + // Collide the car object with the tilemap + // It's important to do this BEFORE you adjust the object velocity (below) otherwise it can jitter around a lot + map.collide(car); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function collide(object, collisionData) { + // collisionData is an array containing all of the tiles the object overlapped with (can be more than 1) + for(var i = 0; i < collisionData.length; i++) { + if(collisionData[i].tile.index == CACTUS) { + console.log('you hit a cactus!'); + flash.start(0xff0000, 1); + } else if(collisionData[i].tile.index == SIGN_POST) { + console.log('you hit a sign post!'); + } + } + } +})(); diff --git a/todo/phaser tests/tilemap/collide with tile.ts b/todo/phaser tests/tilemap/collide with tile.ts new file mode 100644 index 00000000..9f9fa08b --- /dev/null +++ b/todo/phaser tests/tilemap/collide with tile.ts @@ -0,0 +1,96 @@ +/// +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + var CACTUS = 31; + var SIGN_POST = 46; + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var tile: Phaser.Tile; + var flash: Phaser.FX.Camera.Flash; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + // When the car collides with the cactus tile we'll flash the screen red briefly, + // but it won't stop the car (the separateX/Y values are set to false) + map.setCollisionByIndex([CACTUS], Phaser.Collision.ANY, true, false, false); + + // When the car collides with the sign post tile we'll stop the car moving (separation is set to true) + map.setCollisionByIndex([SIGN_POST], Phaser.Collision.ANY, true, true, true); + + // This is the callback that will be called every time map.collide() returns true + map.collisionCallback = collide; + + // This is the context in which the callback is called (usually 'this' if you want to be able to access local vars) + map.collisionCallbackContext = this; + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + myGame.camera.follow(car); + + flash = myGame.camera.fx.add(Phaser.FX.Camera.Flash); + + } + + function update() { + + // Collide the car object with the tilemap + // It's important to do this BEFORE you adjust the object velocity (below) otherwise it can jitter around a lot + map.collide(car); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function collide(object, collisionData) { + + // collisionData is an array containing all of the tiles the object overlapped with (can be more than 1) + for (var i = 0; i < collisionData.length; i++) + { + if (collisionData[i].tile.index == CACTUS) + { + console.log('you hit a cactus!'); + flash.start(0xff0000, 1); + } + else if (collisionData[i].tile.index == SIGN_POST) + { + console.log('you hit a sign post!'); + } + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/collision.js b/todo/phaser tests/tilemap/collision.js new file mode 100644 index 00000000..0f41a8c0 --- /dev/null +++ b/todo/phaser tests/tilemap/collision.js @@ -0,0 +1,63 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('platform', 'assets/maps/platform-test-1.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('ufo', 'assets/sprites/ufo.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('chunk', 'assets/sprites/chunk.png'); + myGame.loader.load(); + } + var map; + var ufo; + var tile; + var emitter; + var test; + function create() { + map = myGame.add.tilemap('tiles', 'platform', Phaser.Tilemap.FORMAT_TILED_JSON); + map.setCollisionRange(21, 53); + map.setCollisionRange(105, 109); + myGame.camera.opaque = true; + myGame.camera.backgroundColor = 'rgb(47,154,204)'; + myGame.input.keyboard.addKeyCapture([ + Phaser.Keyboard.LEFT, + Phaser.Keyboard.RIGHT, + Phaser.Keyboard.UP, + Phaser.Keyboard.DOWN + ]); + //emitter = myGame.add.emitter(32, 80); + //emitter.width = 700; + //emitter.makeParticles('chunk', 100, false, 1); + //emitter.gravity = 200; + //emitter.bounce = 0.8; + //emitter.start(false, 10, 0.05); + ufo = myGame.add.sprite(250, 64, 'ufo'); + ufo.renderDebug = true; + ufo.renderRotation = false; + test = myGame.add.sprite(200, 64, 'ufo'); + test.elasticity = 1; + test.velocity.x = 50; + test.velocity.y = 100; + ufo.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + } + function update() { + // Collide everything with the map + map.collide(); + // And collide everything in the game :) + myGame.collide(); + ufo.velocity.x = 0; + ufo.velocity.y = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + ufo.velocity.x = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + ufo.velocity.x = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + ufo.velocity.y = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + ufo.velocity.y = 200; + } + } +})(); diff --git a/todo/phaser tests/tilemap/collision.ts b/todo/phaser tests/tilemap/collision.ts new file mode 100644 index 00000000..d3520434 --- /dev/null +++ b/todo/phaser tests/tilemap/collision.ts @@ -0,0 +1,88 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('platform', 'assets/maps/platform-test-1.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('ufo', 'assets/sprites/ufo.png'); + myGame.loader.addImageFile('melon', 'assets/sprites/melon.png'); + myGame.loader.addImageFile('chunk', 'assets/sprites/chunk.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var ufo: Phaser.Sprite; + var tile: Phaser.Tile; + var emitter: Phaser.Emitter; + var test: Phaser.Sprite; + + function create() { + + map = myGame.add.tilemap('tiles', 'platform', Phaser.Tilemap.FORMAT_TILED_JSON); + map.setCollisionRange(21,53); + map.setCollisionRange(105,109); + + myGame.camera.opaque = true; + myGame.camera.backgroundColor = 'rgb(47,154,204)'; + + myGame.input.keyboard.addKeyCapture([Phaser.Keyboard.LEFT, Phaser.Keyboard.RIGHT, Phaser.Keyboard.UP, Phaser.Keyboard.DOWN]); + + //emitter = myGame.add.emitter(32, 80); + //emitter.width = 700; + //emitter.makeParticles('chunk', 100, false, 1); + //emitter.gravity = 200; + //emitter.bounce = 0.8; + //emitter.start(false, 10, 0.05); + + ufo = myGame.add.sprite(250, 64, 'ufo'); + ufo.renderDebug = true; + ufo.renderRotation = false; + + test = myGame.add.sprite(200, 64, 'ufo'); + test.elasticity = 1; + test.velocity.x = 50; + test.velocity.y = 100; + + ufo.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + } + + function update() { + + // Collide everything with the map + map.collide(); + + // And collide everything in the game :) + myGame.collide(); + + ufo.velocity.x = 0; + ufo.velocity.y = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + ufo.velocity.x = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + ufo.velocity.x = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + ufo.velocity.y = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + ufo.velocity.y = 200; + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/fill tiles.js b/todo/phaser tests/tilemap/fill tiles.js new file mode 100644 index 00000000..bba31d50 --- /dev/null +++ b/todo/phaser tests/tilemap/fill tiles.js @@ -0,0 +1,54 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + myGame.input.onDown.add(fillTiles); + } + function fillTiles() { + // Fills the given region of the map (2,2,10,20) with tile index 15 + map.currentLayer.fillTile(15, 2, 2, 10, 20); + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/fill tiles.ts b/todo/phaser tests/tilemap/fill tiles.ts new file mode 100644 index 00000000..defbfb40 --- /dev/null +++ b/todo/phaser tests/tilemap/fill tiles.ts @@ -0,0 +1,86 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + + myGame.input.onDown.add(fillTiles); + } + + function fillTiles() { + + // Fills the given region of the map (2,2,10,20) with tile index 15 + map.currentLayer.fillTile(15, 2, 2, 10, 20); + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tilemap/get tile.js b/todo/phaser tests/tilemap/get tile.js new file mode 100644 index 00000000..4b9978dc --- /dev/null +++ b/todo/phaser tests/tilemap/get tile.js @@ -0,0 +1,49 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/get tile.ts b/todo/phaser tests/tilemap/get tile.ts new file mode 100644 index 00000000..e7fe5887 --- /dev/null +++ b/todo/phaser tests/tilemap/get tile.ts @@ -0,0 +1,78 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tilemap/map draw.js b/todo/phaser tests/tilemap/map draw.js new file mode 100644 index 00000000..ef99703a --- /dev/null +++ b/todo/phaser tests/tilemap/map draw.js @@ -0,0 +1,39 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('platform', 'assets/maps/mapdraw.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + myGame.loader.load(); + } + var map; + var emitter; + var marker; + function create() { + map = myGame.add.tilemap('tiles', 'platform', Phaser.Tilemap.FORMAT_TILED_JSON); + map.setCollisionRange(21, 53); + map.setCollisionRange(105, 109); + myGame.camera.backgroundColor = 'rgb(47,154,204)'; + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(16, 16); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + emitter = myGame.add.emitter(32, 80); + emitter.width = 700; + emitter.makeParticles('carrot', 100, false, 1); + emitter.gravity = 150; + emitter.bounce = 0.8; + emitter.start(false, 20, 0.05); + } + function update() { + // Collide everything with the map + map.collide(); + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 16); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 16); + if(myGame.input.mousePointer.isDown) { + map.putTile(marker.x, marker.y, 32); + } + } +})(); diff --git a/todo/phaser tests/tilemap/map draw.ts b/todo/phaser tests/tilemap/map draw.ts new file mode 100644 index 00000000..5e2c994e --- /dev/null +++ b/todo/phaser tests/tilemap/map draw.ts @@ -0,0 +1,58 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('platform', 'assets/maps/mapdraw.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('carrot', 'assets/sprites/carrot.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var emitter: Phaser.Emitter; + var marker: Phaser.GeomSprite; + + function create() { + + map = myGame.add.tilemap('tiles', 'platform', Phaser.Tilemap.FORMAT_TILED_JSON); + map.setCollisionRange(21,53); + map.setCollisionRange(105,109); + + myGame.camera.backgroundColor = 'rgb(47,154,204)'; + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(16, 16); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + emitter = myGame.add.emitter(32, 80); + emitter.width = 700; + emitter.makeParticles('carrot', 100, false, 1); + emitter.gravity = 150; + emitter.bounce = 0.8; + emitter.start(false, 20, 0.05); + } + + function update() { + + // Collide everything with the map + map.collide(); + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 16); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 16); + + if (myGame.input.mousePointer.isDown) + { + map.putTile(marker.x, marker.y, 32); + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/put tile.js b/todo/phaser tests/tilemap/put tile.js new file mode 100644 index 00000000..b6ae38a9 --- /dev/null +++ b/todo/phaser tests/tilemap/put tile.js @@ -0,0 +1,52 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + } + function paintTile() { + map.putTile(marker.x, marker.y, 32); + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/put tile.ts b/todo/phaser tests/tilemap/put tile.ts new file mode 100644 index 00000000..ce2397b9 --- /dev/null +++ b/todo/phaser tests/tilemap/put tile.ts @@ -0,0 +1,83 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + } + + function paintTile() { + + map.putTile(marker.x, marker.y, 32); + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tilemap/random tiles.js b/todo/phaser tests/tilemap/random tiles.js new file mode 100644 index 00000000..3da7e378 --- /dev/null +++ b/todo/phaser tests/tilemap/random tiles.js @@ -0,0 +1,60 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + myGame.input.onDown.add(randomTiles); + } + function randomTiles() { + map.currentLayer.randomiseTiles([ + 30, + 31, + 32, + 38, + 39, + 40 + ]); + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/random tiles.ts b/todo/phaser tests/tilemap/random tiles.ts new file mode 100644 index 00000000..019fa284 --- /dev/null +++ b/todo/phaser tests/tilemap/random tiles.ts @@ -0,0 +1,85 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + + myGame.input.onDown.add(randomTiles); + } + + function randomTiles() { + + map.currentLayer.randomiseTiles([30, 31, 32, 38, 39, 40]); + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tilemap/replace tiles.js b/todo/phaser tests/tilemap/replace tiles.js new file mode 100644 index 00000000..9242fbe9 --- /dev/null +++ b/todo/phaser tests/tilemap/replace tiles.js @@ -0,0 +1,53 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + myGame.input.onDown.add(swapTiles); + } + function swapTiles() { + map.currentLayer.replaceTile(30, 31); + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/replace tiles.ts b/todo/phaser tests/tilemap/replace tiles.ts new file mode 100644 index 00000000..ff808e33 --- /dev/null +++ b/todo/phaser tests/tilemap/replace tiles.ts @@ -0,0 +1,85 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + + myGame.input.onDown.add(swapTiles); + } + + function swapTiles() { + + map.currentLayer.replaceTile(30, 31); + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tilemap/small map.js b/todo/phaser tests/tilemap/small map.js new file mode 100644 index 00000000..bf3b85ba --- /dev/null +++ b/todo/phaser tests/tilemap/small map.js @@ -0,0 +1,42 @@ +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('jsontest', 'assets/maps/multi-layer-test.json'); + myGame.loader.addImageFile('jsontiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('overlay', 'assets/pics/scrollframe.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var car; + var map; + var overlay; + var smallCam; + function create() { + map = myGame.add.tilemap('jsontiles', 'jsontest', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(300, 100, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + // Hide the tilemap and car sprite from the main camera (it will still be seen by the smallCam) + map.hideFromCamera(myGame.camera); + car.hideFromCamera(myGame.camera); + smallCam = myGame.add.camera(32, 32, 352, 240); + smallCam.setBounds(0, 0, map.widthInPixels, map.heightInPixels); + smallCam.follow(car); + overlay = myGame.add.sprite(0, 0, 'overlay'); + overlay.hideFromCamera(smallCam); + } + function update() { + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } +})(); diff --git a/todo/phaser tests/tilemap/small map.ts b/todo/phaser tests/tilemap/small map.ts new file mode 100644 index 00000000..0014c0c0 --- /dev/null +++ b/todo/phaser tests/tilemap/small map.ts @@ -0,0 +1,66 @@ +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('jsontest', 'assets/maps/multi-layer-test.json'); + myGame.loader.addImageFile('jsontiles', 'assets/tiles/platformer_tiles.png'); + myGame.loader.addImageFile('overlay', 'assets/pics/scrollframe.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var car: Phaser.Sprite; + var map: Phaser.Tilemap; + var overlay: Phaser.Sprite; + var smallCam: Phaser.Camera; + + function create() { + + map = myGame.add.tilemap('jsontiles', 'jsontest', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(300, 100, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + // Hide the tilemap and car sprite from the main camera (it will still be seen by the smallCam) + map.hideFromCamera(myGame.camera); + car.hideFromCamera(myGame.camera); + + smallCam = myGame.add.camera(32, 32, 352, 240); + smallCam.setBounds(0, 0, map.widthInPixels, map.heightInPixels); + smallCam.follow(car); + + overlay = myGame.add.sprite(0, 0, 'overlay'); + overlay.hideFromCamera(smallCam); + + } + + function update() { + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/sprite draw tiles.js b/todo/phaser tests/tilemap/sprite draw tiles.js new file mode 100644 index 00000000..301a20c1 --- /dev/null +++ b/todo/phaser tests/tilemap/sprite draw tiles.js @@ -0,0 +1,36 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + // Fills the whole map to one tile + map.currentLayer.fillTile(30); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + myGame.camera.follow(car); + } + function update() { + map.putTile(car.x + 16, car.y + 16, 34); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } +})(); diff --git a/todo/phaser tests/tilemap/sprite draw tiles.ts b/todo/phaser tests/tilemap/sprite draw tiles.ts new file mode 100644 index 00000000..19c4c3d3 --- /dev/null +++ b/todo/phaser tests/tilemap/sprite draw tiles.ts @@ -0,0 +1,59 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + // Fills the whole map to one tile + map.currentLayer.fillTile(30); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + myGame.camera.follow(car); + } + + function update() { + + map.putTile(car.x + 16, car.y + 16, 34); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + +})(); diff --git a/todo/phaser tests/tilemap/swap tiles.js b/todo/phaser tests/tilemap/swap tiles.js new file mode 100644 index 00000000..17f3ca60 --- /dev/null +++ b/todo/phaser tests/tilemap/swap tiles.js @@ -0,0 +1,53 @@ +/// +/// +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + myGame.loader.load(); + } + var map; + var car; + var marker; + var tile; + function create() { + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + myGame.camera.follow(car); + myGame.onRenderCallback = render; + myGame.input.onDown.add(swapTiles); + } + function swapTiles() { + map.currentLayer.swapTile(30, 31); + } + function update() { + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + if(myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + car.angularVelocity = -200; + } else if(myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + car.angularVelocity = 200; + } + if(myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + } + function render() { + tile = map.getTileFromInputXY(); + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + } +})(); diff --git a/todo/phaser tests/tilemap/swap tiles.ts b/todo/phaser tests/tilemap/swap tiles.ts new file mode 100644 index 00000000..e6802552 --- /dev/null +++ b/todo/phaser tests/tilemap/swap tiles.ts @@ -0,0 +1,85 @@ +/// +/// +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + myGame.loader.addTextFile('desert', 'assets/maps/desert.json'); + myGame.loader.addImageFile('tiles', 'assets/tiles/tmw_desert_spacing.png'); + myGame.loader.addImageFile('car', 'assets/sprites/car90.png'); + + myGame.loader.load(); + + } + + var map: Phaser.Tilemap; + var car: Phaser.Sprite; + var marker: Phaser.GeomSprite; + var tile: Phaser.Tile; + + function create() { + + map = myGame.add.tilemap('tiles', 'desert', Phaser.Tilemap.FORMAT_TILED_JSON); + + car = myGame.add.sprite(250, 200, 'car'); + car.setBounds(0, 0, map.widthInPixels - 32, map.heightInPixels - 32); + + marker = myGame.add.geomSprite(0, 0); + marker.createRectangle(32, 32); + marker.renderFill = false; + marker.lineColor = 'rgb(0,0,0)'; + + myGame.camera.follow(car); + myGame.onRenderCallback = render; + + myGame.input.onDown.add(swapTiles); + } + + function swapTiles() { + + map.currentLayer.swapTile(30, 31); + + } + + function update() { + + marker.x = myGame.math.snapToFloor(myGame.input.getWorldX(), 32); + marker.y = myGame.math.snapToFloor(myGame.input.getWorldY(), 32); + + car.velocity.x = 0; + car.velocity.y = 0; + car.angularVelocity = 0; + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + car.angularVelocity = -200; + } + else if (myGame.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + car.angularVelocity = 200; + } + + if (myGame.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + car.velocity.copyFrom(myGame.motion.velocityFromAngle(car.angle, 300)); + } + + } + + function render { + + tile = map.getTileFromInputXY(); + + myGame.stage.context.font = '18px Arial'; + myGame.stage.context.fillStyle = 'rgb(0,0,0)'; + myGame.stage.context.fillText(tile.toString(), 32, 32); + + myGame.input.renderDebugInfo(32, 64, 'rgb(0,0,0)'); + + } + +})(); diff --git a/todo/phaser tests/tweens/bounce.js b/todo/phaser tests/tweens/bounce.js new file mode 100644 index 00000000..a50fd04b --- /dev/null +++ b/todo/phaser tests/tweens/bounce.js @@ -0,0 +1,22 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + var atari; + function create() { + atari = myGame.add.sprite(300, 0, 'atari'); + startBounceTween(); + } + function startBounceTween() { + atari.y = 0; + var bounce = myGame.add.tween(atari); + bounce.to({ + y: 500 + }, 1000 + Math.random() * 3000, Phaser.Easing.Bounce.Out); + bounce.onComplete.add(startBounceTween, this); + bounce.start(); + } +})(); diff --git a/todo/phaser tests/tweens/bounce.ts b/todo/phaser tests/tweens/bounce.ts new file mode 100644 index 00000000..575cb950 --- /dev/null +++ b/todo/phaser tests/tweens/bounce.ts @@ -0,0 +1,36 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + + myGame.loader.load(); + + } + + var atari: Phaser.Sprite; + + function create() { + + atari = myGame.add.sprite(300, 0, 'atari'); + + startBounceTween(); + } + + function startBounceTween() { + + atari.y = 0; + + var bounce: Phaser.Tween = myGame.add.tween(atari); + + bounce.to({ y: 500 }, 1000 + Math.random() * 3000, Phaser.Easing.Bounce.Out); + bounce.onComplete.add(startBounceTween, this); + bounce.start(); + + } + +})(); diff --git a/todo/phaser tests/tweens/elastic.js b/todo/phaser tests/tweens/elastic.js new file mode 100644 index 00000000..5d793f57 --- /dev/null +++ b/todo/phaser tests/tweens/elastic.js @@ -0,0 +1,15 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + function create() { + var atari = myGame.add.sprite(300, 0, 'atari'); + // Here is the short-hand way of creating a tween, by chaining the call to it: + myGame.add.tween(atari).to({ + y: 400 + }, 5000, Phaser.Easing.Elastic.Out, true); + } +})(); diff --git a/todo/phaser tests/tweens/elastic.ts b/todo/phaser tests/tweens/elastic.ts new file mode 100644 index 00000000..6d60f5b7 --- /dev/null +++ b/todo/phaser tests/tweens/elastic.ts @@ -0,0 +1,24 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + myGame.loader.addImageFile('atari', 'assets/sprites/atari130xe.png'); + + myGame.loader.load(); + + } + + function create() { + + var atari = myGame.add.sprite(300, 0, 'atari'); + + // Here is the short-hand way of creating a tween, by chaining the call to it: + myGame.add.tween(atari).to({ y: 400 }, 5000, Phaser.Easing.Elastic.Out, true); + + } + +})();