Merge branch 'photonstorm/097'

This commit is contained in:
Sean 2013-07-18 19:25:32 +08:00
commit 727a06cd9b
258 changed files with 23278 additions and 662 deletions

View file

@ -14,6 +14,7 @@
/// <reference path="cameras/CameraManager.ts" />
/// <reference path="gameobjects/GameObjectFactory.ts" />
/// <reference path="sound/SoundManager.ts" />
/// <reference path="sound/Sound.ts" />
/// <reference path="Stage.ts" />
/// <reference path="Time.ts" />
/// <reference path="tweens/TweenManager.ts" />
@ -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)
{

View file

@ -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;
}
}
}

View file

@ -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}

View file

@ -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;
}

View file

@ -19,9 +19,14 @@ module Phaser {
* Create a new <code>Button</code> 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) {

View file

@ -94,6 +94,10 @@ module Phaser {
return <Sprite> this._world.group.add(new Sprite(this._game, x, y, key, frame, bodyType));
}
public audio(key: string, volume?: number = 1, loop?: bool = false) {
return <Sound> 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.

View file

@ -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)
{

View file

@ -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}
*/

View file

@ -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

View file

@ -79,9 +79,7 @@ module Phaser {
* @param {Any} event
**/
private consumeTouchMove(event) {
event.preventDefault();
}
/**

View file

@ -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.

View file

@ -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':

View file

@ -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

View file

@ -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);
}
}
}
}

View file

@ -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;
}
}

View file

@ -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 = <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) { }

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -56,6 +56,14 @@
<TypeScriptCompile Include="cameras\basic camera 1.ts" />
<TypeScriptCompile Include="buttons\basic button.ts" />
<TypeScriptCompile Include="buttons\basic button 2.ts" />
<TypeScriptCompile Include="audio\play sound 1.ts" />
<TypeScriptCompile Include="audio\audio sprites 1.ts" />
<Content Include="audio\audio sprites 1.js">
<DependentUpon>audio sprites 1.ts</DependentUpon>
</Content>
<Content Include="audio\play sound 1.js">
<DependentUpon>play sound 1.ts</DependentUpon>
</Content>
<Content Include="buttons\basic button 2.js">
<DependentUpon>basic button 2.ts</DependentUpon>
</Content>

Binary file not shown.

View file

@ -0,0 +1,84 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmpDM="http://ns.adobe.com/xmp/1.0/DynamicMedia/">
<xmpDM:Tracks>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<xmpDM:trackName>CuePoint Markers</xmpDM:trackName>
<xmpDM:trackType>Cue</xmpDM:trackType>
<xmpDM:frameRate>f44100</xmpDM:frameRate>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<xmpDM:trackName>Subclip Markers</xmpDM:trackName>
<xmpDM:trackType>InOut</xmpDM:trackType>
<xmpDM:frameRate>f44100</xmpDM:frameRate>
</rdf:li>
</rdf:Bag>
</xmpDM:Tracks>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<xmp:MetadataDate>2013-07-16T02:39:59+01:00</xmp:MetadataDate>
<xmp:ModifyDate>2013-07-16T02:39:59+01:00</xmp:ModifyDate>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#">
<xmpMM:InstanceID>xmp.iid:CF0377343CEDE211B46AA47009D9EF1D</xmpMM:InstanceID>
<xmpMM:DocumentID>xmp.did:CF0377343CEDE211B46AA47009D9EF1D</xmpMM:DocumentID>
<xmpMM:OriginalDocumentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</xmpMM:OriginalDocumentID>
<xmpMM:History>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:CE0377343CEDE211B46AA47009D9EF1D</stEvt:instanceID>
<stEvt:when>2013-07-16T02:39:59+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Audition CS6 (Windows)</stEvt:softwareAgent>
<stEvt:changed>/metadata</stEvt:changed>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:CF0377343CEDE211B46AA47009D9EF1D</stEvt:instanceID>
<stEvt:when>2013-07-16T02:39:59+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Audition CS6 (Windows)</stEvt:softwareAgent>
<stEvt:changed>/</stEvt:changed>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
<xmpMM:DerivedFrom rdf:parseType="Resource">
<stRef:instanceID>xmp.iid:CE0377343CEDE211B46AA47009D9EF1D</stRef:instanceID>
<stRef:documentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</stRef:documentID>
<stRef:originalDocumentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</stRef:originalDocumentID>
</xmpMM:DerivedFrom>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:format>audio/ogg; codec="vorbis"</dc:format>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>

View file

@ -0,0 +1,44 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//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();
}
}
})();

View file

@ -0,0 +1,58 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//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();
}
}
})();

View file

@ -0,0 +1,55 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//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();
}
})();

View file

@ -0,0 +1,70 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//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();
}
})();

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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() {

View file

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

249
build/phaser.d.ts vendored
View file

@ -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 <code>Button</code> 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 <code>SoundManager</code>.
*/
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}
*/

File diff suppressed because it is too large Load diff

BIN
todo/docChanges.zip Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -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 = {}));

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,365 @@
/// <reference path="../Game.ts" />
/**
* Phaser - CollisionMask
*/
var Phaser;
(function (Phaser) {
var CollisionMask = (function () {
/**
* CollisionMask constructor. Creates a new <code>CollisionMask</code> 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 = {}));

View file

@ -0,0 +1,501 @@
/// <reference path="../Game.ts" />
/**
* Phaser - CollisionMask
*/
module Phaser {
export class CollisionMask {
/**
* CollisionMask constructor. Creates a new <code>CollisionMask</code> 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;
}
}
}

View file

@ -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();

View file

@ -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);
}
*/
}
}

View file

@ -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 = {}));

View file

@ -0,0 +1,454 @@
/// <reference path="../Game.ts" />
/// <reference path="../Group.ts" />
/**
* 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 <code>Emitter</code> 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 <code>acceleration.y</code> 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 <code>Particle</code>.
*/
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 <code>Object</code>.
*
* @param Object {object} The <code>Object</code> 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);
}
}
}

View file

@ -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 = {}));

View file

@ -0,0 +1,645 @@
/// <reference path="../Game.ts" />
/// <reference path="../geom/Polygon.ts" />
/// <reference path="../utils/RectangleUtils.ts" />
/**
* 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 <code>GeomSprite</code>.
*
* @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;
}
}
}

View file

@ -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();

View file

@ -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() {
}
}
}

View file

@ -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 = {}));

View file

@ -0,0 +1,409 @@
/// <reference path="Game.ts" />
/// <reference path="gameobjects/GameObject.ts" />
/**
* 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)<br>
* 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.<br>
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.<br>
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.<br>
* 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)<br>
* You must give a maximum speed value, beyond which the Sprite won't go any faster.<br>
* 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.<br>
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.<br>
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.<br>
*
* @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)<br>
* You must give a maximum speed value, beyond which the Sprite won't go any faster.<br>
* 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)<br>
* 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.<br>
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.<br>
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.<br>
*
* @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)<br>
* You must give a maximum speed value, beyond which the Sprite won't go any faster.<br>
* 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);
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,355 @@
/// <reference path="../Game.ts" />
/// <reference path="../AnimationManager.ts" />
/// <reference path="GameObject.ts" />
/// <reference path="../system/Camera.ts" />
/**
* 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 <code>Sprite</code>.
*
* @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 <code>update()</code> is automatically called by State/Group.
*/
public active: bool;
/**
* Controls whether <code>draw()</code> 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 <code>Object.reset()</code>.
*/
public revive() {
this.alive = true;
this.exists = true;
}
/**
* Convert object to readable string name. Useful for debugging, save games, etc.
*/
public toString(): string {
return "";
}
}
}

View file

@ -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();

View file

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

Binary file not shown.

View file

@ -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 = {}));

View file

@ -0,0 +1,434 @@
/// <reference path="../Game.ts" />
/// <reference path="../components/TilemapLayer.ts" />
/// <reference path="../components/Tile.ts" />
/**
* 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 <code>Tilemap</code>.
*
* @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
}
}

View file

@ -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)
<short demo of N, just watch a mainmenu replay>**
-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.
<demo ragdoll>**
[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)
<demo: level 0-0, ninja skidding around and rotating to match the surface of things>**
<demo: level 0-0, ninja falling to death>**
[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" <todo: refer to ginoVDB and geomtools books>
-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
<diagram: show seperated and overlapping cases>
[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
<diagram: as before, but point out/focus on normal directions>
-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 <diagram each step>
-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
<point to diagram of this>
[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 delta<dot>axis
[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)
<demo: use tutorialA demo: circle falling/sliding off the top of a square>**
<demo: as above, but use an AABB falling/sliding off the top of a square>**
-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
<demonstrate the above values/etc. with the diagram>
-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
<explain the following in english while using diagram to illustrate>
-regular grid:
-every object knows which grid cell(s) it touches
-every grid cell knows which object(s) are touching it
<refer to diagram>
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
<refer to diagram>
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?

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

View file

@ -0,0 +1,46 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,77 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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 = <Phaser.FX.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;
}
}
})();

View file

@ -0,0 +1,46 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,77 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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 = <Phaser.FX.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;
}
}
})();

View file

@ -0,0 +1,41 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,68 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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 = <Phaser.FX.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;
}
}
})();

View file

@ -0,0 +1,32 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,57 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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 = <Phaser.FX.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;
}
}
})();

View file

@ -0,0 +1,50 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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);
}
})();

View file

@ -0,0 +1,85 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../build/phaser-fx.d.ts" />
(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 = <Phaser.FX.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);
}
})();

View file

@ -0,0 +1,38 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,63 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,33 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
})();

View file

@ -0,0 +1,60 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
})();

View file

@ -0,0 +1,40 @@
/// <reference path="../../Phaser/Game.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,66 @@
/// <reference path="../../Phaser/Game.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,34 @@
/// <reference path="../../Phaser/Game.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,61 @@
/// <reference path="../../Phaser/Game.ts" />
(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;
}
}
})();

View file

@ -0,0 +1,45 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,71 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,47 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,73 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,46 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,72 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

View file

@ -0,0 +1,36 @@
/// <reference path="../../Phaser/Game.ts" />
(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);
}
}
})();

Some files were not shown because too many files have changed in this diff Show more