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);
+
+ }
+
+})();