Added Touch and Full Screen support for mobile devices. Also added Random Data Generator and Device classes and organised Point, Rect and the new Circle class into Geom.

This commit is contained in:
Richard Davey 2013-04-13 04:25:03 +01:00
parent 92cf118a06
commit 5450202cde
29 changed files with 4923 additions and 71 deletions

View file

@ -1,7 +1,7 @@
/// <reference path="Game.ts" />
/// <reference path="GameMath.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="geom/Point.ts" />
/// <reference path="system/Camera.ts" />
// TODO: If the Camera is larger than the Stage size then the rotation offset isn't correct

View file

@ -1,6 +1,6 @@
/// <reference path="Group.ts" />
/// <reference path="Particle.ts" />
/// <reference path="Point.ts" />
/// <reference path="geom/Point.ts" />
/**
* <code>Emitter</code> is a lightweight particle emitter.

View file

@ -11,11 +11,13 @@
/// <reference path="World.ts" />
/// <reference path="system/input/Input.ts" />
/// <reference path="system/RequestAnimationFrame.ts" />
/// <reference path="system/RandomDataGenerator.ts" />
/// <reference path="system/Device.ts" />
/**
* Phaser
*
* v0.5 - April 12th 2013
* v0.6 - April 13th 2013
*
* A small and feature-packed 2D canvas game framework born from the firey pits of Flixel and Kiwi.
*
@ -48,6 +50,8 @@ class Game {
}
public static VERSION: string = 'Phaser version 0.6';
private _raf: RequestAnimationFrame;
private _maxAccumulation: number = 32;
private _accumulator: number = 0;
@ -56,8 +60,6 @@ class Game {
private _paused: bool = false;
private _pendingState = null;
public static VERSION: string = 'Phaser version 0.5';
// Event callbacks
public callbackContext;
public onInitCallback = null;
@ -75,6 +77,8 @@ class Game {
public time: Time;
public math: GameMath;
public world: World;
public rnd: RandomDataGenerator;
public device: Device;
public isBooted: bool = false;
@ -86,6 +90,7 @@ class Game {
}
else
{
this.device = new Device();
this.stage = new Stage(this, parent, width, height);
this.world = new World(this, width, height);
this.sound = new SoundManager(this);
@ -94,6 +99,7 @@ class Game {
this.time = new Time(this);
this.input = new Input(this);
this.math = new GameMath(this);
this.rnd = new RandomDataGenerator([(Date.now() * Math.random()).toString()]);
this.framerate = 60;

View file

@ -1,3 +1,5 @@
/// <reference path="Game.ts" />
/**
* Phaser - GameMath
*

View file

@ -1,8 +1,8 @@
/// <reference path="Basic.ts" />
/// <reference path="Game.ts" />
/// <reference path="GameMath.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="geom/Point.ts" />
class GameObject extends Basic {

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -66,8 +66,8 @@
<TypeScriptCompile Include="Group.ts" />
<TypeScriptCompile Include="Loader.ts" />
<TypeScriptCompile Include="Particle.ts" />
<TypeScriptCompile Include="Point.ts" />
<TypeScriptCompile Include="Rectangle.ts" />
<TypeScriptCompile Include="geom\Point.ts" />
<TypeScriptCompile Include="geom\Rectangle.ts" />
<TypeScriptCompile Include="Sound.ts" />
<TypeScriptCompile Include="Sprite.ts" />
<TypeScriptCompile Include="Stage.ts" />
@ -89,6 +89,19 @@
<TypeScriptCompile Include="Time.ts" />
<TypeScriptCompile Include="World.ts" />
</ItemGroup>
<ItemGroup>
<Content Include="geom\Circle.ts" />
<Content Include="Signal.ts" />
<Content Include="SignalBinding.ts" />
<Content Include="system\Device.ts" />
<Content Include="system\FullScreen.js">
<DependentUpon>FullScreen.ts</DependentUpon>
</Content>
<TypeScriptCompile Include="system\FullScreen.ts" />
<Content Include="system\input\Finger.ts" />
<Content Include="system\input\Touch.ts" />
<Content Include="system\RandomDataGenerator.ts" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TypeScript\Microsoft.TypeScript.targets" />
<PropertyGroup>
<PostBuildEvent>cd $(ProjectDir)

336
Phaser/Signal.ts Normal file
View file

@ -0,0 +1,336 @@
/// <reference path="SignalBinding.ts" />
/*
* Signal
*
* @desc A TypeScript conversion of JS Signals by Miller Medeiros
* Released under the MIT license
* http://millermedeiros.github.com/js-signals/
*
* @version 1. - 7th March 2013
*
* @author Richard Davey, TypeScript conversion
* @author Miller Medeiros, JS Signals
*
*/
/**
* Custom event broadcaster
* <br />- inspired by Robert Penner's AS3 Signals.
* @name Signal
* @author Miller Medeiros
* @constructor
*/
class Signal {
/**
*
* @property _bindings
* @type Array
* @private
*/
private _bindings: SignalBinding[] = [];
/**
*
* @property _prevParams
* @type Any
* @private
*/
private _prevParams = null;
/**
* Signals Version Number
* @property VERSION
* @type String
* @const
*/
public static VERSION: string = '1.0.0';
/**
* If Signal should keep record of previously dispatched parameters and
* automatically execute listener during `add()`/`addOnce()` if Signal was
* already dispatched before.
* @type boolean
*/
public memorize: bool = false;
/**
* @type boolean
* @private
*/
private _shouldPropagate: bool = true;
/**
* If Signal is active and should broadcast events.
* <p><strong>IMPORTANT:</strong> Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.</p>
* @type boolean
*/
public active: bool = true;
/**
*
* @method validateListener
* @param {Any} listener
* @param {Any} fnName
*/
public validateListener(listener, fnName) {
if (typeof listener !== 'function')
{
throw new Error('listener is a required param of {fn}() and should be a Function.'.replace('{fn}', fnName));
}
}
/**
* @param {Function} listener
* @param {boolean} isOnce
* @param {Object} [listenerContext]
* @param {Number} [priority]
* @return {SignalBinding}
* @private
*/
private _registerListener(listener, isOnce: bool, listenerContext, priority: number): SignalBinding {
var prevIndex: number = this._indexOfListener(listener, listenerContext);
var binding: SignalBinding;
if (prevIndex !== -1)
{
binding = this._bindings[prevIndex];
if (binding.isOnce() !== isOnce)
{
throw new Error('You cannot add' + (isOnce ? '' : 'Once') + '() then add' + (!isOnce ? '' : 'Once') + '() the same listener without removing the relationship first.');
}
}
else
{
binding = new SignalBinding(this, listener, isOnce, listenerContext, priority);
this._addBinding(binding);
}
if (this.memorize && this._prevParams)
{
binding.execute(this._prevParams);
}
return binding;
}
/**
*
* @method _addBinding
* @param {SignalBinding} binding
* @private
*/
private _addBinding(binding: SignalBinding) {
//simplified insertion sort
var n: number = this._bindings.length;
do { --n; } while (this._bindings[n] && binding.priority <= this._bindings[n].priority);
this._bindings.splice(n + 1, 0, binding);
}
/**
*
* @method _indexOfListener
* @param {Function} listener
* @return {number}
* @private
*/
private _indexOfListener(listener, context): number {
var n: number = this._bindings.length;
var cur: SignalBinding;
while (n--)
{
cur = this._bindings[n];
if (cur.getListener() === listener && cur.context === context)
{
return n;
}
}
return -1;
}
/**
* Check if listener was attached to Signal.
* @param {Function} listener
* @param {Object} [context]
* @return {boolean} if Signal has the specified listener.
*/
public has(listener, context?: any = null): bool {
return this._indexOfListener(listener, context) !== -1;
}
/**
* Add a listener to the signal.
* @param {Function} listener Signal handler function.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
* @return {SignalBinding} An Object representing the binding between the Signal and listener.
*/
public add(listener, listenerContext?: any = null, priority?: number = 0): SignalBinding {
this.validateListener(listener, 'add');
return this._registerListener(listener, false, listenerContext, priority);
}
/**
* Add listener to the signal that should be removed after first execution (will be executed only once).
* @param {Function} listener Signal handler function.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
* @return {SignalBinding} An Object representing the binding between the Signal and listener.
*/
public addOnce(listener, listenerContext?: any = null, priority?: number = 0): SignalBinding {
this.validateListener(listener, 'addOnce');
return this._registerListener(listener, true, listenerContext, priority);
}
/**
* Remove a single listener from the dispatch queue.
* @param {Function} listener Handler function that should be removed.
* @param {Object} [context] Execution context (since you can add the same handler multiple times if executing in a different context).
* @return {Function} Listener handler function.
*/
public remove(listener, context?: any = null) {
this.validateListener(listener, 'remove');
var i: number = this._indexOfListener(listener, context);
if (i !== -1)
{
this._bindings[i]._destroy(); //no reason to a SignalBinding exist if it isn't attached to a signal
this._bindings.splice(i, 1);
}
return listener;
}
/**
* Remove all listeners from the Signal.
*/
public removeAll() {
var n: number = this._bindings.length;
while (n--)
{
this._bindings[n]._destroy();
}
this._bindings.length = 0;
}
/**
* @return {number} Number of listeners attached to the Signal.
*/
public getNumListeners(): number {
return this._bindings.length;
}
/**
* Stop propagation of the event, blocking the dispatch to next listeners on the queue.
* <p><strong>IMPORTANT:</strong> should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.</p>
* @see Signal.prototype.disable
*/
public halt() {
this._shouldPropagate = false;
}
/**
* Dispatch/Broadcast Signal to all listeners added to the queue.
* @param {...*} [params] Parameters that should be passed to each handler.
*/
public dispatch(...paramsArr: any[]) {
if (!this.active)
{
return;
}
var n: number = this._bindings.length;
var bindings: SignalBinding[];
if (this.memorize)
{
this._prevParams = paramsArr;
}
if (!n)
{
//should come after memorize
return;
}
bindings = this._bindings.slice(0); //clone array in case add/remove items during dispatch
this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch.
//execute all callbacks until end of the list or until a callback returns `false` or stops propagation
//reverse loop since listeners with higher priority will be added at the end of the list
do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false);
}
/**
* Forget memorized arguments.
* @see Signal.memorize
*/
public forget() {
this._prevParams = null;
}
/**
* Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).
* <p><strong>IMPORTANT:</strong> calling any method on the signal instance after calling dispose will throw errors.</p>
*/
public dispose() {
this.removeAll();
delete this._bindings;
delete this._prevParams;
}
/**
* @return {string} String representation of the object.
*/
public toString(): string {
return '[Signal active:' + this.active + ' numListeners:' + this.getNumListeners() + ']';
}
}

186
Phaser/SignalBinding.ts Normal file
View file

@ -0,0 +1,186 @@
/// <reference path="Signal.ts" />
/*
* SignalBinding
*
* @desc An object that represents a binding between a Signal and a listener function.
* Released under the MIT license
* http://millermedeiros.github.com/js-signals/
*
* @version 1. - 7th March 2013
*
* @author Richard Davey, TypeScript conversion
* @author Miller Medeiros, JS Signals
*
*/
class SignalBinding {
/**
* Object that represents a binding between a Signal and a listener function.
* <br />- <strong>This is an internal constructor and shouldn't be called by regular users.</strong>
* <br />- inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.
* @author Miller Medeiros
* @constructor
* @internal
* @name SignalBinding
* @param {Signal} signal Reference to Signal object that listener is currently bound to.
* @param {Function} listener Handler function bound to the signal.
* @param {boolean} isOnce If binding should be executed just once.
* @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {Number} [priority] The priority level of the event listener. (default = 0).
*/
constructor(signal: Signal, listener, isOnce: bool, listenerContext, priority?: number = 0) {
this._listener = listener;
this._isOnce = isOnce;
this.context = listenerContext;
this._signal = signal;
this.priority = priority || 0;
}
/**
* Handler function bound to the signal.
* @type Function
* @private
*/
private _listener;
/**
* If binding should be executed just once.
* @type boolean
* @private
*/
private _isOnce: bool;
/**
* Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @memberOf SignalBinding.prototype
* @name context
* @type Object|undefined|null
*/
public context;
/**
* Reference to Signal object that listener is currently bound to.
* @type Signal
* @private
*/
private _signal: Signal;
/**
* Listener priority
* @type Number
*/
public priority: number;
/**
* If binding is active and should be executed.
* @type boolean
*/
public active: bool = true;
/**
* Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute`. (curried parameters)
* @type Array|null
*/
public params = null;
/**
* Call listener passing arbitrary parameters.
* <p>If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.</p>
* @param {Array} [paramsArr] Array of parameters that should be passed to the listener
* @return {*} Value returned by the listener.
*/
public execute(paramsArr?: any[]) {
var handlerReturn;
var params;
if (this.active && !!this._listener)
{
params = this.params ? this.params.concat(paramsArr) : paramsArr;
handlerReturn = this._listener.apply(this.context, params);
if (this._isOnce)
{
this.detach();
}
}
return handlerReturn;
}
/**
* Detach binding from signal.
* - alias to: mySignal.remove(myBinding.getListener());
* @return {Function|null} Handler function bound to the signal or `null` if binding was previously detached.
*/
public detach() {
return this.isBound() ? this._signal.remove(this._listener, this.context) : null;
}
/**
* @return {Boolean} `true` if binding is still bound to the signal and have a listener.
*/
public isBound(): bool {
return (!!this._signal && !!this._listener);
}
/**
* @return {boolean} If SignalBinding will only be executed once.
*/
public isOnce(): bool {
return this._isOnce;
}
/**
* @return {Function} Handler function bound to the signal.
*/
public getListener() {
return this._listener;
}
/**
* @return {Signal} Signal that listener is currently bound to.
*/
public getSignal() {
return this._signal;
}
/**
* Delete instance properties
* @private
*/
public _destroy() {
delete this._signal;
delete this._listener;
delete this.context;
}
/**
* @return {string} String representation of the object.
*/
public toString(): string {
return '[SignalBinding isOnce:' + this._isOnce + ', isBound:' + this.isBound() + ', active:' + this.active + ']';
}
}

View file

@ -4,21 +4,24 @@ class SoundManager {
this._game = game;
if (!!window['AudioContext'])
{
this._context = new window['AudioContext']();
}
else if (!!window['webkitAudioContext'])
{
this._context = new window['webkitAudioContext']();
}
if (game.device.webaudio == true)
{
if (!!window['AudioContext'])
{
this._context = new window['AudioContext']();
}
else if (!!window['webkitAudioContext'])
{
this._context = new window['webkitAudioContext']();
}
if (this._context !== null)
{
this._gainNode = this._context.createGainNode();
this._gainNode.connect(this._context.destination);
this._volume = 1;
}
if (this._context !== null)
{
this._gainNode = this._context.createGainNode();
this._gainNode.connect(this._context.destination);
this._volume = 1;
}
}
}

View file

@ -2,8 +2,8 @@
/// <reference path="GameObject.ts" />
/// <reference path="Game.ts" />
/// <reference path="GameMath.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="geom/Point.ts" />
class Sprite extends GameObject {

View file

@ -1,6 +1,7 @@
/// <reference path="Game.ts" />
/// <reference path="Point.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="geom/Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="system/Fullscreen.ts" />
class Stage {
@ -15,18 +16,20 @@ class Stage {
if (document.getElementById(parent))
{
document.getElementById(parent).appendChild(this.canvas);
document.getElementById(parent).style.overflow = 'hidden';
}
else
{
document.body.appendChild(this.canvas);
}
var offset:Point = this.getOffset(this.canvas);
this.bounds = new Rectangle(offset.x, offset.y, width, height);
this.context = this.canvas.getContext('2d');
this.offset = this.getOffset(this.canvas);
this.bounds = new Rectangle(this.offset.x, this.offset.y, width, height);
this.fullscreen = new FullScreen(this._game);
//document.addEventListener('visibilitychange', (event) => this.visibilityChange(event), false);
//document.addEventListener('webkitvisibilitychange', (event) => this.visibilityChange(event), false);
window.onblur = (event) => this.visibilityChange(event);
@ -37,13 +40,20 @@ class Stage {
private _game: Game;
private _bgColor: string;
public static ORIENTATION_LANDSCAPE:number = 0;
public static ORIENTATION_PORTRAIT:number = 1;
public bounds: Rectangle;
public clear: bool = true;
public canvas: HTMLCanvasElement;
public context: CanvasRenderingContext2D;
public offset: Point;
public fullscreen: FullScreen;
public update() {
this.fullscreen.update();
if (this.clear)
{
// implement dirty rect? could take up more cpu time than it saves. needs benching.

View file

@ -1,7 +1,7 @@
/// <reference path="Game.ts" />
/// <reference path="GameObject.ts" />
/// <reference path="Point.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="geom/Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="system/TilemapBuffer.ts" />
class Tilemap extends GameObject {

View file

@ -1,3 +1,5 @@
/// <reference path="Game.ts" />
class Time {
constructor(game: Game) {

View file

@ -2,8 +2,8 @@
/// <reference path="Game.ts" />
/// <reference path="GameMath.ts" />
/// <reference path="Group.ts" />
/// <reference path="Rectangle.ts" />
/// <reference path="Point.ts" />
/// <reference path="geom/Rectangle.ts" />
/// <reference path="geom/Point.ts" />
/// <reference path="Sprite.ts" />
/// <reference path="Tilemap.ts" />
/// <reference path="system/Camera.ts" />

474
Phaser/geom/Circle.ts Normal file
View file

@ -0,0 +1,474 @@
/// <reference path="Point.ts" />
/**
* Geom - Circle
*
* @desc A Circle object is an area defined by its position, as indicated by its center point (x,y) and diameter.
*
* @version 1.2 - 27th February 2013
* @author Richard Davey
* @author Ross Kettle
*
* @todo Intersections
*/
class Circle {
/**
* Creates a new Circle object with the center coordinate specified by the x and y parameters and the diameter specified by the diameter parameter. If you call this function without parameters, a circle with x, y, diameter and radius properties set to 0 is created.
* @class Circle
* @constructor
* @param {Number} x The x coordinate of the center of the circle.
* @param {Number} y The y coordinate of the center of the circle.
* @return {Circle} This circle object
**/
constructor(x: number = 0, y: number = 0, diameter: number = 0) {
this.setTo(x, y, diameter);
}
/**
* The diameter of the circle
* @property _diameter
* @type Number
**/
private _diameter: number = 0;
/**
* The radius of the circle
* @property _radius
* @type Number
**/
private _radius: number = 0;
/**
* The x coordinate of the center of the circle
* @property x
* @type Number
**/
public x: number = 0;
/**
* The y coordinate of the center of the circle
* @property y
* @type Number
**/
public y: number = 0;
/**
* The diameter of the circle. The largest distance between any two points on the circle. The same as the radius * 2.
* @method diameter
* @param {Number} The diameter of the circle.
* @return {Number}
**/
public diameter(value?: number): number {
if (value && value > 0)
{
this._diameter = value;
this._radius = value * 0.5;
}
return this._diameter;
}
/**
* The radius of the circle. The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
* @method radius
* @param {Number} The radius of the circle.
**/
public radius(value?: number) {
if (value && value > 0)
{
this._radius = value;
this._diameter = value * 2;
}
return this._radius;
}
/**
* The circumference of the circle.
* @method circumference
* @return {Number}
**/
public circumference(): number {
return 2 * (Math.PI * this._radius);
}
/**
* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @param {Number} The value to adjust the height of the circle by.
**/
public bottom(value?: number) {
if (value && !isNaN(value))
{
if (value < this.y)
{
this._radius = 0;
this._diameter = 0;
}
else
{
this.radius(value - this.y);
}
}
return this.y + this._radius;
}
/**
* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method left
* @param {Number} The value to adjust the position of the leftmost point of the circle by.
**/
public left(value?: number) {
if (value && !isNaN(value))
{
if (value < this.x)
{
this.radius(this.x - value);
}
else
{
this._radius = 0;
this._diameter = 0;
}
}
return this.x - this._radius;
}
/**
* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
* @method right
* @param {Number} The amount to adjust the diameter of the circle by.
**/
public right(value?: number) {
if (value && !isNaN(value))
{
if (value > this.x)
{
this.radius(value - this.x);
}
else
{
this._radius = 0;
this._diameter = 0;
}
}
return this.x + this._radius;
}
/**
* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
* @method bottom
* @param {Number} The amount to adjust the height of the circle by.
**/
public top(value?: number) {
if (value && !isNaN(value))
{
if (value > this.y)
{
this._radius = 0;
this._diameter = 0;
}
else
{
this.radius(this.y - value);
}
}
return this.y - this._radius;
}
/**
* Gets the area of this Circle.
* @method area
* @return {Number} This area of this circle.
**/
public area(): number {
if (this._radius > 0)
{
return Math.PI * this._radius * this._radius;
}
else
{
return 0;
}
}
/**
* Determines whether or not this Circle object is empty.
* @method isEmpty
* @return {Boolean} A value of true if the Circle objects diameter is less than or equal to 0; otherwise false.
**/
public isEmpty(): bool {
if (this._diameter < 1)
{
return true;
}
return false;
}
/**
* Whether the circle intersects with a line. Checks against infinite line defined by the two points on the line, not the line segment.
* If you need details about the intersection then use Kiwi.Geom.Intersect.lineToCircle instead.
* @method intersectCircleLine
* @param {Object} the line object to check.
* @return {Boolean}
**/
/*
public intersectCircleLine(line: Line): bool {
return Intersect.lineToCircle(line, this).result;
}
*/
/**
* Returns a new Circle object with the same values for the x, y, width, and height properties as the original Circle object.
* @method clone
* @param {Circle} output Optional Circle object. If given the values will be set into the object, otherwise a brand new Circle object will be created and returned.
* @return {Kiwi.Geom.Circle}
**/
public clone(output?: Circle = new Circle): Circle {
return output.setTo(this.x, this.y, this._diameter);
}
/**
* Return true if the given x/y coordinates are within this Circle object.
* If you need details about the intersection then use Kiwi.Geom.Intersect.circleContainsPoint instead.
* @method contains
* @param {Number} The X value of the coordinate to test.
* @param {Number} The Y value of the coordinate to test.
* @return {Boolean} True if the coordinates are within this circle, otherwise false.
**/
/*
public contains(x: number, y: number): bool {
return Intersect.circleContainsPoint(this, <Point> { x: x, y: y }).result;
}
*/
/**
* Return true if the coordinates of the given Point object are within this Circle object.
* If you need details about the intersection then use Kiwi.Geom.Intersect.circleContainsPoint instead.
* @method containsPoint
* @param {Kiwi.Geom.Point} The Point object to test.
* @return {Boolean} True if the coordinates are within this circle, otherwise false.
**/
/*
public containsPoint(point:Point): bool {
return Intersect.circleContainsPoint(this, point).result;
}
*/
/**
* Return true if the given Circle is contained entirely within this Circle object.
* If you need details about the intersection then use Kiwi.Geom.Intersect.circleToCircle instead.
* @method containsCircle
* @param {Kiwi.Geom.Circle} The Circle object to test.
* @return {Boolean} True if the coordinates are within this circle, otherwise false.
**/
/*
public containsCircle(circle:Circle): bool {
return Intersect.circleToCircle(this, circle).result;
}
*/
/**
* Copies all of circle data from the source Circle object into the calling Circle object.
* @method copyFrom
* @param {Circle} rect The source circle object to copy from
* @return {Circle} This circle object
**/
public copyFrom(source: Circle): Circle {
return this.setTo(source.x, source.y, source.diameter());
}
/**
* Copies all of circle data from this Circle object into the destination Circle object.
* @method copyTo
* @param {Circle} circle The destination circle object to copy in to
* @return {Circle} The destination circle object
**/
public copyTo(target: Circle) {
return target.copyFrom(this);
}
/**
* Returns the distance from the center of this Circle object to the given object (can be Circle, Point or anything with x/y values)
* @method distanceFrom
* @param {Circle/Point} target - The destination Point object.
* @param {Boolean} round - Round the distance to the nearest integer (default false)
* @return {Number} The distance between this Point object and the destination Point object.
**/
public distanceTo(target: any, round?: bool = false): number {
var dx = this.x - target.x;
var dy = this.y - target.y;
if (round === true)
{
return Math.round(Math.sqrt(dx * dx + dy * dy));
}
else
{
return Math.sqrt(dx * dx + dy * dy);
}
}
/**
* Determines whether the object specified in the toCompare parameter is equal to this Circle object. This method compares the x, y and diameter properties of an object against the same properties of this Circle object.
* @method equals
* @param {Circle} toCompare The circle to compare to this Circle object.
* @return {Boolean} A value of true if the object has exactly the same values for the x, y and diameter properties as this Circle object; otherwise false.
**/
public equals(toCompare: Circle): bool {
if (this.x === toCompare.x && this.y === toCompare.y && this.diameter() === toCompare.diameter())
{
return true;
}
return false;
}
/**
* Determines whether the Circle object specified in the toIntersect parameter intersects with this Circle object. This method checks the radius distances between the two Circle objects to see if they intersect.
* @method intersects
* @param {Circle} toIntersect The Circle object to compare against to see if it intersects with this Circle object.
* @return {Boolean} A value of true if the specified object intersects with this Circle object; otherwise false.
**/
public intersects(toIntersect: Circle): bool {
if (this.distanceTo(toIntersect, false) < (this._radius + toIntersect._radius))
{
return true;
}
return false;
}
/**
* Returns a Point object containing the coordinates of a point on the circumference of this Circle based on the given angle.
* @method circumferencePoint
* @param {Number} The angle in radians (unless asDegrees is true) to return the point from.
* @param {Boolean} Is the given angle in radians (false) or degrees (true)?
* @param {Kiwi.Geom.Point} An optional Point object to put the result in to. If none specified a new Point object will be created.
* @return {Kiwi.Geom.Point} The Point object holding the result.
**/
public circumferencePoint(angle: number, asDegrees: bool = false, output?: Point = new Point): Point {
if (asDegrees === true)
{
//angle = angle * (Math.PI / 180); // Degrees to Radians
angle = angle * (180 / Math.PI); // Radians to Degrees
}
output.x = this.x + this._radius * Math.cos(angle);
output.y = this.y + this._radius * Math.sin(angle);
return output;
}
/**
* Adjusts the location of the Circle object, as determined by its center coordinate, by the specified amounts.
* @method offset
* @param {Number} dx Moves the x value of the Circle object by this amount.
* @param {Number} dy Moves the y value of the Circle object by this amount.
* @return {Circle} This Circle object.
**/
public offset(dx: number, dy: number): Circle {
if (!isNaN(dx) && !isNaN(dy))
{
this.x += dx;
this.y += dy;
}
return this;
}
/**
* Adjusts the location of the Circle object using a Point object as a parameter. This method is similar to the Circle.offset() method, except that it takes a Point object as a parameter.
* @method offsetPoint
* @param {Point} point A Point object to use to offset this Circle object.
* @return {Circle} This Circle object.
**/
public offsetPoint(point: Point): Circle {
return this.offset(point.x, point.y);
}
/**
* Sets the members of Circle to the specified values.
* @method setTo
* @param {Number} x The x coordinate of the center of the circle.
* @param {Number} y The y coordinate of the center of the circle.
* @param {Number} diameter The diameter of the circle in pixels.
* @return {Circle} This circle object
**/
public setTo(x: number, y: number, diameter: number): Circle {
this.x = x;
this.y = y;
this._diameter = diameter;
this._radius = diameter * 0.5;
return this;
}
/**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
**/
public toString(): string {
return "[{Circle (x=" + this.x + " y=" + this.y + " diameter=" + this.diameter() + " radius=" + this.radius() + ")}]";
}
}

View file

@ -1,7 +1,7 @@
/// <reference path="../Game.ts" />
/// <reference path="../GameMath.ts" />
/// <reference path="../Rectangle.ts" />
/// <reference path="../Point.ts" />
/// <reference path="../geom/Rectangle.ts" />
/// <reference path="../geom/Point.ts" />
class Camera {

562
Phaser/system/Device.ts Normal file
View file

@ -0,0 +1,562 @@
/// <reference path="../Game.ts" />
/**
* Device
*
* @desc Detects device support capabilities. Using some elements from System.js by MrDoob and Modernizr
* https://github.com/Modernizr/Modernizr/blob/master/feature-detects/audio.js
*
* @version 1.0 - March 5th 2013
* @author Richard Davey
* @author mrdoob
* @author Modernizr team
*/
class Device {
/**
*
* @constructor
* @return {Device} This Object
*/
constructor() {
this._checkAudio();
this._checkBrowser();
this._checkCSS3D();
this._checkDevice();
this._checkFeatures();
this._checkOS();
}
// Operating System
public desktop: bool = false;
/**
*
* @property iOS
* @type Boolean
*/
public iOS: bool = false;
/**
*
* @property android
* @type Boolean
*/
public android: bool = false;
/**
*
* @property chromeOS
* @type Boolean
*/
public chromeOS: bool = false;
/**
*
* @property linux
* @type Boolean
*/
public linux: bool = false;
/**
*
* @property maxOS
* @type Boolean
*/
public macOS: bool = false;
/**
*
* @property windows
* @type Boolean
*/
public windows: bool = false;
// Features
/**
*
* @property canvas
* @type Boolean
*/
public canvas: bool = false;
/**
*
* @property file
* @type Boolean
*/
public file: bool = false;
/**
*
* @property fileSystem
* @type Boolean
*/
public fileSystem: bool = false;
/**
*
* @property localStorage
* @type Boolean
*/
public localStorage: bool = false;
/**
*
* @property webGL
* @type Boolean
*/
public webGL: bool = false;
/**
*
* @property worker
* @type Boolean
*/
public worker: bool = false;
/**
*
* @property touch
* @type Boolean
*/
public touch: bool = false;
/**
*
* @property css3D
* @type Boolean
*/
public css3D: bool = false;
// Browser
/**
*
* @property arora
* @type Boolean
*/
public arora: bool = false;
/**
*
* @property chrome
* @type Boolean
*/
public chrome: bool = false;
/**
*
* @property epiphany
* @type Boolean
*/
public epiphany: bool = false;
/**
*
* @property firefox
* @type Boolean
*/
public firefox: bool = false;
/**
*
* @property ie
* @type Boolean
*/
public ie: bool = false;
/**
*
* @property ieVersion
* @type Number
*/
public ieVersion: number = 0;
/**
*
* @property mobileSafari
* @type Boolean
*/
public mobileSafari: bool = false;
/**
*
* @property midori
* @type Boolean
*/
public midori: bool = false;
/**
*
* @property opera
* @type Boolean
*/
public opera: bool = false;
/**
*
* @property safari
* @type Boolean
*/
public safari: bool = false;
public webApp: bool = false;
// Audio
/**
*
* @property audioData
* @type Boolean
*/
public audioData: bool = false;
/**
*
* @property webaudio
* @type Boolean
*/
public webaudio: bool = false;
/**
*
* @property ogg
* @type Boolean
*/
public ogg: bool = false;
/**
*
* @property mp3
* @type Boolean
*/
public mp3: bool = false;
/**
*
* @property wav
* @type Boolean
*/
public wav: bool = false;
/**
*
* @property m4a
* @type Boolean
*/
public m4a: bool = false;
// Device
/**
*
* @property iPhone
* @type Boolean
*/
public iPhone: bool = false;
/**
*
* @property iPhone4
* @type Boolean
*/
public iPhone4: bool = false;
/**
*
* @property iPad
* @type Boolean
*/
public iPad: bool = false;
/**
*
* @property pixelRatio
* @type Number
*/
public pixelRatio: number = 0;
/**
*
* @method _checkOS
* @private
*/
private _checkOS() {
var ua = navigator.userAgent;
if (/Android/.test(ua))
{
this.android = true;
}
else if (/CrOS/.test(ua))
{
this.chromeOS = true;
}
else if (/iP[ao]d|iPhone/i.test(ua))
{
this.iOS = true;
}
else if (/Linux/.test(ua))
{
this.linux = true;
}
else if (/Mac OS/.test(ua))
{
this.macOS = true;
}
else if (/Windows/.test(ua))
{
this.windows = true;
}
if (this.windows || this.macOS || this.linux)
{
this.desktop = true;
}
}
/**
*
* @method _checkFeatures
* @private
*/
private _checkFeatures() {
this.canvas = !!window['CanvasRenderingContext2D'];
try
{
this.localStorage = !!localStorage.getItem;
}
catch (error)
{
this.localStorage = false;
}
this.file = !!window['File'] && !!window['FileReader'] && !!window['FileList'] && !!window['Blob'];
this.fileSystem = !!window['requestFileSystem'];
this.webGL = !!window['WebGLRenderingContext'];
this.worker = !!window['Worker'];
if ('ontouchstart' in document.documentElement || window.navigator.msPointerEnabled)
{
this.touch = true;
}
}
/**
*
* @method _checkBrowser
* @private
*/
private _checkBrowser() {
var ua = navigator.userAgent;
if (/Arora/.test(ua))
{
this.arora = true;
}
else if (/Chrome/.test(ua))
{
this.chrome = true;
}
else if (/Epiphany/.test(ua))
{
this.epiphany = true;
}
else if (/Firefox/.test(ua))
{
this.firefox = true;
}
else if (/Mobile Safari/.test(ua))
{
this.mobileSafari = true;
}
else if (/MSIE (\d+\.\d+);/.test(ua))
{
this.ie = true;
this.ieVersion = parseInt(RegExp.$1);
}
else if (/Midori/.test(ua))
{
this.midori = true;
}
else if (/Opera/.test(ua))
{
this.opera = true;
}
else if (/Safari/.test(ua))
{
this.safari = true;
}
// WebApp mode in iOS
if (navigator['standalone'])
{
this.webApp = true;
}
}
/**
*
* @method _checkAudio
* @private
*/
private _checkAudio() {
this.audioData = !!(window['Audio']);
this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']);
var audioElement: HTMLAudioElement = <HTMLAudioElement> document.createElement('audio');
var result = false;
try
{
if (result = !!audioElement.canPlayType)
{
if (audioElement.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''))
{
this.ogg = true;
}
if (audioElement.canPlayType('audio/mpeg;').replace(/^no$/, ''))
{
this.mp3 = true;
}
// Mimetypes accepted:
// developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
// bit.ly/iphoneoscodecs
if (audioElement.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''))
{
this.wav = true;
}
if (audioElement.canPlayType('audio/x-m4a;') || audioElement.canPlayType('audio/aac;').replace(/^no$/, ''))
{
this.m4a = true;
}
}
} catch (e) { }
}
/**
*
* @method _checkDevice
* @private
*/
private _checkDevice() {
this.pixelRatio = window['devicePixelRatio'] || 1;
this.iPhone = navigator.userAgent.toLowerCase().indexOf('iphone') != -1;
this.iPhone4 = (this.pixelRatio == 2 && this.iPhone);
this.iPad = navigator.userAgent.toLowerCase().indexOf('ipad') != -1;
}
/**
*
* @method _checkCSS3D
* @private
*/
private _checkCSS3D() {
var el = document.createElement('p');
var has3d;
var transforms = {
'webkitTransform': '-webkit-transform',
'OTransform': '-o-transform',
'msTransform': '-ms-transform',
'MozTransform': '-moz-transform',
'transform': 'transform'
};
// Add it to the body to get the computed style.
document.body.insertBefore(el, null);
for (var t in transforms)
{
if (el.style[t] !== undefined)
{
el.style[t] = "translate3d(1px,1px,1px)";
has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
}
}
document.body.removeChild(el);
this.css3D = (has3d !== undefined && has3d.length > 0 && has3d !== "none");
}
/**
*
* @method getAll
* @return {String}
*/
public getAll(): string {
var output: string = '';
output = output.concat('Device\n');
output = output.concat('iPhone : ' + this.iPhone + '\n');
output = output.concat('iPhone4 : ' + this.iPhone4 + '\n');
output = output.concat('iPad : ' + this.iPad + '\n');
output = output.concat('\n');
output = output.concat('Operating System\n');
output = output.concat('iOS: ' + this.iOS + '\n');
output = output.concat('Android: ' + this.android + '\n');
output = output.concat('ChromeOS: ' + this.chromeOS + '\n');
output = output.concat('Linux: ' + this.linux + '\n');
output = output.concat('MacOS: ' + this.macOS + '\n');
output = output.concat('Windows: ' + this.windows + '\n');
output = output.concat('\n');
output = output.concat('Browser\n');
output = output.concat('Arora: ' + this.arora + '\n');
output = output.concat('Chrome: ' + this.chrome + '\n');
output = output.concat('Epiphany: ' + this.epiphany + '\n');
output = output.concat('Firefox: ' + this.firefox + '\n');
output = output.concat('Internet Explorer: ' + this.ie + ' (' + this.ieVersion + ')\n');
output = output.concat('Mobile Safari: ' + this.mobileSafari + '\n');
output = output.concat('Midori: ' + this.midori + '\n');
output = output.concat('Opera: ' + this.opera + '\n');
output = output.concat('Safari: ' + this.safari + '\n');
output = output.concat('\n');
output = output.concat('Features\n');
output = output.concat('Canvas: ' + this.canvas + '\n');
output = output.concat('File: ' + this.file + '\n');
output = output.concat('FileSystem: ' + this.fileSystem + '\n');
output = output.concat('LocalStorage: ' + this.localStorage + '\n');
output = output.concat('WebGL: ' + this.webGL + '\n');
output = output.concat('Worker: ' + this.worker + '\n');
output = output.concat('Touch: ' + this.touch + '\n');
output = output.concat('CSS 3D: ' + this.css3D + '\n');
output = output.concat('\n');
output = output.concat('Audio\n');
output = output.concat('Audio Data: ' + this.canvas + '\n');
output = output.concat('Web Audio: ' + this.canvas + '\n');
output = output.concat('Can play OGG: ' + this.canvas + '\n');
output = output.concat('Can play MP3: ' + this.canvas + '\n');
output = output.concat('Can play M4A: ' + this.canvas + '\n');
output = output.concat('Can play WAV: ' + this.canvas + '\n');
return output;
}
}

125
Phaser/system/FullScreen.ts Normal file
View file

@ -0,0 +1,125 @@
/// <reference path="../Game.ts" />
/*
* Based on code from Viewporter v2.0
* http://github.com/zynga/viewporter
*
* Copyright 2011, Zynga Inc.
* Licensed under the MIT License.
* https://raw.github.com/zynga/viewporter/master/MIT-LICENSE.txt
*/
class FullScreen {
constructor(game: Game) {
this._game = game;
this.orientation = window['orientation'];
window.addEventListener('orientationchange', (event) => this.checkOrientation(event), false);
this.width = window.innerWidth;
this.height = window.innerHeight;
}
private _game: Game;
private _startHeight: number;
private _iterations: number;
private _check;
public width: number;
public height: number;
public orientation;
public go() {
this.refresh();
}
public update() {
if (window.innerWidth !== this.width || window.innerHeight !== this.height)
{
this.refresh();
}
}
public get isLandscape(): bool {
return window['orientation'] === 90 || window['orientation'] === -90;
}
private checkOrientation(event) {
if (window['orientation'] !== this.orientation)
{
this.refresh();
this.orientation = window['orientation'];
}
}
private refresh() {
// We can't do anything about the status bars in iPads, web apps or desktops
if (this._game.device.iPad == false && this._game.device.webApp == false && this._game.device.desktop == false)
{
document.documentElement.style.minHeight = '5000px';
this._startHeight = window.innerHeight;
if (this._game.device.android && this._game.device.chrome == false)
{
window.scrollTo(0, 1);
}
else
{
window.scrollTo(0, 0);
}
}
if (this._check == null)
{
this._iterations = 40;
this._check = window.setInterval(() => this.retryFullScreen(), 10);
}
}
private retryFullScreen() {
if (this._game.device.android && this._game.device.chrome == false)
{
window.scrollTo(0, 1);
}
else
{
window.scrollTo(0, 0);
}
this._iterations--;
if (window.innerHeight > this._startHeight || this._iterations < 0)
{
// set minimum height of content to new window height
document.documentElement.style.minHeight = window.innerHeight + 'px';
this._game.stage.canvas.style.width = window.innerWidth + 'px';
this._game.stage.canvas.style.height = window.innerHeight + 'px';
this.width = window.innerWidth;
this.height = window.innerHeight;
clearInterval(this._check);
this._check = null;
}
}
}

View file

@ -1,4 +1,4 @@
/// <reference path="../Rectangle.ts" />
/// <reference path="../geom/Rectangle.ts" />
/// <reference path="../Basic.ts" />
/// <reference path="LinkedList.ts" />

View file

@ -0,0 +1,282 @@
/**
* Repeatable Random Data Generator
*
* @desc Manages the creation of unique internal game IDs
* Based on Nonsense by Josh Faul https://github.com/jocafa/Nonsense
* Random number generator from http://baagoe.org/en/wiki/Better_random_numbers_for_javascript
*
* @version 1.1 - 1st March 2013
* @author Josh Faul
* @author Richard Davey, TypeScript conversion and additional methods
*/
class RandomDataGenerator {
/**
* @constructor
* @param {Array} seeds
* @return {Kiwi.Utils.RandomDataGenerator}
*/
constructor(seeds?: string[] = []) {
this.sow(seeds);
}
/**
* @property s0
* @type Any
* @private
*/
private s0;
/**
* @property s1
* @type Any
* @private
*/
private s1;
/**
* @property s2
* @type Any
* @private
*/
private s2;
/**
* @property c
* @type Number
* @private
*/
private c: number = 1;
/**
* @method uint32
* @private
*/
private uint32() {
return this.rnd.apply(this) * 0x100000000; // 2^32
}
/**
* @method fract32
* @private
*/
private fract32() {
return this.rnd.apply(this) + (this.rnd.apply(this) * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
}
// private random helper
/**
* @method rnd
* @private
*/
private rnd() {
var t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32
this.c = t | 0;
this.s0 = this.s1;
this.s1 = this.s2;
this.s2 = t - this.c;
return this.s2;
}
/**
* @method hash
* @param {Any} data
* @private
*/
private hash(data) {
var h, i, n;
n = 0xefc8249d;
data = data.toString();
for (i = 0; i < data.length; i++)
{
n += data.charCodeAt(i);
h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
}
/**
* Reset the seed of the random data generator
* @method sow
* @param {Array} seeds
*/
public sow(seeds?: string[] = []) {
this.s0 = this.hash(' ');
this.s1 = this.hash(this.s0);
this.s2 = this.hash(this.s1);
var seed;
for (var i = 0; seed = seeds[i++];)
{
this.s0 -= this.hash(seed);
this.s0 += ~~(this.s0 < 0);
this.s1 -= this.hash(seed);
this.s1 += ~~(this.s1 < 0);
this.s2 -= this.hash(seed);
this.s2 += ~~(this.s2 < 0);
}
}
/**
* Returns a random integer between 0 and 2^32
* @method integer
* @return {Number}
*/
public get integer(): number {
return this.uint32();
}
/**
* Returns a random real number between 0 and 1
* @method frac
* @return {Number}
*/
public get frac(): number {
return this.fract32();
}
/**
* Returns a random real number between 0 and 2^32
* @method real
* @return {Number}
*/
public get real(): number {
return this.uint32() + this.fract32();
}
/**
* Returns a random integer between min and max
* @method integerInRange
* @param {Number} min
* @param {Number} max
* @return {Number}
*/
public integerInRange(min: number, max: number): number {
return Math.floor(this.realInRange(min, max));
}
/**
* Returns a random real number between min and max
* @method realInRange
* @param {Number} min
* @param {Number} max
* @return {Number}
*/
public realInRange(min: number, max: number): number {
min = min || 0;
max = max || 0;
return this.frac * (max - min) + min;
}
/**
* Returns a random real number between -1 and 1
* @method normal
* @return {Number}
*/
public get normal(): number {
return 1 - 2 * this.frac;
}
/**
* Returns a valid v4 UUID hex string (from https://gist.github.com/1308368)
* @method uuid
* @return {String}
*/
public get uuid(): string {
var a, b;
for (
b = a = '';
a++ < 36;
b += ~a % 5 | a * 3 & 4 ? (a ^ 15 ? 8 ^ this.frac * (a ^ 20 ? 16 : 4) : 4).toString(16) : '-'
);
return b;
}
/**
* Returns a random member of `array`
* @method pick
* @param {Any} array
*/
public pick(array) {
return array[this.integerInRange(0, array.length)];
}
/**
* Returns a random member of `array`, favoring the earlier entries
* @method weightedPick
* @param {Any} array
*/
public weightedPick(array) {
return array[~~(Math.pow(this.frac, 2) * array.length)];
}
/**
* Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified
* @method timestamp
* @param {Number} min
* @param {Number} max
*/
public timestamp(min?: number = 946684800000, max?: number = 1577862000000):number {
return this.realInRange(min, max);
}
/**
* Returns a random angle between -180 and 180
* @method angle
*/
public get angle():number {
return this.integerInRange(-180, 180);
}
}

View file

@ -1,7 +1,7 @@
/// <reference path="../Game.ts" />
/// <reference path="../GameObject.ts" />
/// <reference path="../Tilemap.ts" />
/// <reference path="../Rectangle.ts" />
/// <reference path="../geom/Rectangle.ts" />
/// <reference path="Camera.ts" />
/**

View file

@ -0,0 +1,323 @@
/// <reference path="../../Game.ts" />
/// <reference path="../../geom/Point.ts" />
/// <reference path="../../geom/Circle.ts" />
/**
* Input - Finger
*
* @desc A Finger object used by the Touch manager
*
* @version 1.1 - 27th February 2013
* @author Richard Davey
*
* @todo Lots
*/
class Finger {
/**
* Constructor
* @param {Kiwi.Game} game.
* @return {Kiwi.Input.Finger} This object.
*/
constructor(game: Game) {
this._game = game;
this.active = false;
}
/**
*
* @property _game
* @type Kiwi.Game
* @private
**/
private _game: Game;
/**
* An identification number for each touch point. When a touch point becomes active, it must be assigned an identifier that is distinct from any other active touch point. While the touch point remains active, all events that refer to it must assign it the same identifier.
* @property identifier
* @type Number
*/
public identifier: number;
/**
*
* @property active
* @type Boolean
*/
public active: bool;
/**
*
* @property point
* @type Point
**/
public point: Point = null;
/**
*
* @property circle
* @type Circle
**/
public circle: Circle = null;
/**
*
* @property withinGame
* @type Boolean
*/
public withinGame: bool = false;
/**
* The horizontal coordinate of point relative to the viewport in pixels, excluding any scroll offset
* @property clientX
* @type Number
*/
public clientX: number = -1;
//
/**
* The vertical coordinate of point relative to the viewport in pixels, excluding any scroll offset
* @property clientY
* @type Number
*/
public clientY: number = -1;
//
/**
* The horizontal coordinate of point relative to the viewport in pixels, including any scroll offset
* @property pageX
* @type Number
*/
public pageX: number = -1;
/**
* The vertical coordinate of point relative to the viewport in pixels, including any scroll offset
* @property pageY
* @type Number
*/
public pageY: number = -1;
/**
* The horizontal coordinate of point relative to the screen in pixels
* @property screenX
* @type Number
*/
public screenX: number = -1;
/**
* The vertical coordinate of point relative to the screen in pixels
* @property screenY
* @type Number
*/
public screenY: number = -1;
/**
* The horizontal coordinate of point relative to the game element
* @property x
* @type Number
*/
public x: number = -1;
/**
* The vertical coordinate of point relative to the game element
* @property y
* @type Number
*/
public y: number = -1;
/**
* The Element on which the touch point started when it was first placed on the surface, even if the touch point has since moved outside the interactive area of that element.
* @property target
* @type Any
*/
public target;
/**
*
* @property isDown
* @type Boolean
**/
public isDown: bool = false;
/**
*
* @property isUp
* @type Boolean
**/
public isUp: bool = false;
/**
*
* @property timeDown
* @type Number
**/
public timeDown: number = 0;
/**
*
* @property duration
* @type Number
**/
public duration: number = 0;
/**
*
* @property timeUp
* @type Number
**/
public timeUp: number = 0;
/**
*
* @property justPressedRate
* @type Number
**/
public justPressedRate: number = 200;
/**
*
* @property justReleasedRate
* @type Number
**/
public justReleasedRate: number = 200;
/**
*
* @method start
* @param {Any} event
*/
public start(event) {
this.identifier = event.identifier;
this.target = event.target;
// populate geom objects
if (this.point === null)
{
this.point = new Point();
}
if (this.circle === null)
{
this.circle = new Circle(0, 0, 44);
}
this.move(event);
this.active = true;
this.withinGame = true;
this.isDown = true;
this.isUp = false;
this.timeDown = this._game.time.now;
}
/**
*
* @method move
* @param {Any} event
*/
public move(event) {
this.clientX = event.clientX;
this.clientY = event.clientY;
this.pageX = event.pageX;
this.pageY = event.pageY;
this.screenX = event.screenX;
this.screenY = event.screenY;
this.x = this.pageX - this._game.stage.offset.x;
this.y = this.pageY - this._game.stage.offset.y;
this.point.setTo(this.x, this.y);
this.circle.setTo(this.x, this.y, 44);
// Droppings history (used for gestures and motion tracking)
this.duration = this._game.time.now - this.timeDown;
}
/**
*
* @method leave
* @param {Any} event
*/
public leave(event) {
this.withinGame = false;
this.move(event);
}
/**
*
* @method stop
* @param {Any} event
*/
public stop(event) {
this.active = false;
this.withinGame = false;
this.isDown = false;
this.isUp = true;
this.timeUp = this._game.time.now;
this.duration = this.timeUp - this.timeDown;
}
/**
*
* @method justPressed
* @param {Number} [duration].
* @return {Boolean}
*/
public justPressed(duration?: number = this.justPressedRate): bool {
if (this.isDown === true && (this.timeDown + duration) > this._game.time.now)
{
return true;
}
else
{
return false;
}
}
/**
*
* @method justReleased
* @param {Number} [duration].
* @return {Boolean}
*/
public justReleased(duration?: number = this.justReleasedRate): bool {
if (this.isUp === true && (this.timeUp + duration) > this._game.time.now)
{
return true;
}
else
{
return false;
}
}
/**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
**/
public toString(): string {
return "[{Finger (identifer=" + this.identifier + " active=" + this.active + " duration=" + this.duration + " withinGame=" + this.withinGame + " x=" + this.x + " y=" + this.y + " clientX=" + this.clientX + " clientY=" + this.clientY + " screenX=" + this.screenX + " screenY=" + this.screenY + " pageX=" + this.pageX + " pageY=" + this.pageY + ")}]";
}
}

View file

@ -1,6 +1,7 @@
/// <reference path="../../Game.ts" />
/// <reference path="Mouse.ts" />
/// <reference path="Keyboard.ts" />
/// <reference path="Touch.ts" />
class Input {
@ -10,6 +11,7 @@ class Input {
this.mouse = new Mouse(this._game);
this.keyboard = new Keyboard(this._game);
this.touch = new Touch(this._game);
}
@ -17,6 +19,7 @@ class Input {
public mouse: Mouse;
public keyboard: Keyboard;
public touch: Touch;
public x: number;
public y: number;
@ -24,6 +27,7 @@ class Input {
public update() {
this.mouse.update();
this.touch.update();
}
@ -31,6 +35,7 @@ class Input {
this.mouse.reset();
this.keyboard.reset();
this.touch.reset();
}

View file

@ -0,0 +1,445 @@
/// <reference path="../../Game.ts" />
/// <reference path="../../Signal.ts" />
/// <reference path="Finger.ts" />
/**
* Input - Touch
*
* @desc http://www.w3.org/TR/touch-events/
* https://developer.mozilla.org/en-US/docs/DOM/TouchList
* http://www.html5rocks.com/en/mobile/touchandmouse/
* Android 2.x only supports 1 touch event at once, no multi-touch
*
* @version 1.1 - 27th February 2013
* @author Richard Davey
*
* @todo Try and resolve update lag in Chrome/Android
* Gestures (pinch, zoom, swipe)
* GameObject Touch
* Touch point within GameObject
* Input Zones (mouse and touch) - lock entities within them + axis aligned drags
*/
class Touch {
/**
* Constructor
* @param {Game} game.
* @return {Touch} This object.
*/
constructor(game: Game) {
this._game = game;
this.finger1 = new Finger(this._game);
this.finger2 = new Finger(this._game);
this.finger3 = new Finger(this._game);
this.finger4 = new Finger(this._game);
this.finger5 = new Finger(this._game);
this.finger6 = new Finger(this._game);
this.finger7 = new Finger(this._game);
this.finger8 = new Finger(this._game);
this.finger9 = new Finger(this._game);
this.finger10 = new Finger(this._game);
this._fingers = [this.finger1, this.finger2, this.finger3, this.finger4, this.finger5, this.finger6, this.finger7, this.finger8, this.finger9, this.finger10];
this.touchDown = new Signal();
this.touchUp = new Signal();
this.start();
}
/**
*
* @property _game
* @type Game
* @private
**/
private _game: Game;
/**
*
* @property x
* @type Number
**/
public x: number;
/**
*
* @property y
* @type Number
**/
public y: number;
/**
*
* @property _fingers
* @type Array
* @private
**/
private _fingers: Finger[];
/**
*
* @property finger1
* @type Finger
**/
public finger1: Finger;
/**
*
* @property finger2
* @type Finger
**/
public finger2: Finger;
/**
*
* @property finger3
* @type Finger
**/
public finger3: Finger;
/**
*
* @property finger4
* @type Finger
**/
public finger4: Finger;
/**
*
* @property finger5
* @type Finger
**/
public finger5: Finger;
/**
*
* @property finger6
* @type Finger
**/
public finger6: Finger;
/**
*
* @property finger7
* @type Finger
**/
public finger7: Finger;
/**
*
* @property finger8
* @type Finger
**/
public finger8: Finger;
/**
*
* @property finger9
* @type Finger
**/
public finger9: Finger;
/**
*
* @property finger10
* @type Finger
**/
public finger10: Finger;
/**
*
* @property latestFinger
* @type Finger
**/
public latestFinger: Finger;
/**
*
* @property isDown
* @type Boolean
**/
public isDown: bool = false;
/**
*
* @property isUp
* @type Boolean
**/
public isUp: bool = true;
public touchDown: Signal;
public touchUp: Signal;
/**
*
* @method start
*/
public start() {
this._game.stage.canvas.addEventListener('touchstart', (event) => this.onTouchStart(event), false);
this._game.stage.canvas.addEventListener('touchmove', (event) => this.onTouchMove(event), false);
this._game.stage.canvas.addEventListener('touchend', (event) => this.onTouchEnd(event), false);
this._game.stage.canvas.addEventListener('touchenter', (event) => this.onTouchEnter(event), false);
this._game.stage.canvas.addEventListener('touchleave', (event) => this.onTouchLeave(event), false);
this._game.stage.canvas.addEventListener('touchcancel', (event) => this.onTouchCancel(event), false);
document.addEventListener('touchmove', (event) => this.consumeTouchMove(event), false);
}
/**
* Prevent iOS bounce-back (doesn't work?)
* @method consumeTouchMove
* @param {Any} event
**/
private consumeTouchMove(event) {
event.preventDefault();
}
/**
*
* @method onTouchStart
* @param {Any} event
**/
private onTouchStart(event) {
event.preventDefault();
// A list of all the touch points that BECAME active with the current event
// https://developer.mozilla.org/en-US/docs/DOM/TouchList
// event.targetTouches = list of all touches on the TARGET ELEMENT (i.e. game dom element)
// event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].active === false)
{
this._fingers[f].start(event.changedTouches[i]);
this.x = this._fingers[f].x;
this.y = this._fingers[f].y;
this.touchDown.dispatch(this._fingers[f].x, this._fingers[f].y, this._fingers[f].timeDown, this._fingers[f].timeUp, this._fingers[f].duration);
this.isDown = true;
this.isUp = false;
break;
}
}
}
}
/**
* Doesn't appear to be supported by most browsers yet
* @method onTouchCancel
* @param {Any} event
**/
private onTouchCancel(event) {
event.preventDefault();
// Touch cancel - touches that were disrupted (perhaps by moving into a plugin or browser chrome)
// http://www.w3.org/TR/touch-events/#dfn-touchcancel
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].identifier === event.changedTouches[i].identifier)
{
this._fingers[f].stop(event.changedTouches[i]);
break;
}
}
}
}
/**
* Doesn't appear to be supported by most browsers yet
* @method onTouchEnter
* @param {Any} event
**/
private onTouchEnter(event) {
event.preventDefault();
// For touch enter and leave its a list of the touch points that have entered or left the target
// event.targetTouches = list of all touches on the TARGET ELEMENT (i.e. game dom element)
// event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].active === false)
{
this._fingers[f].start(event.changedTouches[i]);
break;
}
}
}
}
/**
* Doesn't appear to be supported by most browsers yet
* @method onTouchLeave
* @param {Any} event
**/
private onTouchLeave(event) {
event.preventDefault();
// For touch enter and leave its a list of the touch points that have entered or left the target
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].identifier === event.changedTouches[i].identifier)
{
this._fingers[f].leave(event.changedTouches[i]);
break;
}
}
}
}
/**
*
* @method onTouchMove
* @param {Any} event
**/
private onTouchMove(event) {
event.preventDefault();
// event.targetTouches = list of all touches on the TARGET ELEMENT (i.e. game dom element)
// event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].identifier === event.changedTouches[i].identifier)
{
this._fingers[f].move(event.changedTouches[i]);
this.x = this._fingers[f].x;
this.y = this._fingers[f].y;
break;
}
}
}
}
/**
*
* @method onTouchEnd
* @param {Any} event
**/
private onTouchEnd(event) {
event.preventDefault();
// For touch end its a list of the touch points that have been removed from the surface
// https://developer.mozilla.org/en-US/docs/DOM/TouchList
// event.changedTouches = the touches that CHANGED in this event, not the total number of them
for (var i = 0; i < event.changedTouches.length; i++)
{
for (var f = 0; f < this._fingers.length; f++)
{
if (this._fingers[f].identifier === event.changedTouches[i].identifier)
{
this._fingers[f].stop(event.changedTouches[i]);
this.x = this._fingers[f].x;
this.y = this._fingers[f].y;
this.touchUp.dispatch(this._fingers[f].x, this._fingers[f].y, this._fingers[f].timeDown, this._fingers[f].timeUp, this._fingers[f].duration);
this.isDown = false;
this.isUp = true;
break;
}
}
}
}
/**
*
* @method calculateDistance
* @param {Finger} finger1
* @param {Finger} finger2
**/
public calculateDistance(finger1: Finger, finger2: Finger) {
}
/**
*
* @method calculateAngle
* @param {Finger} finger1
* @param {Finger} finger2
**/
public calculateAngle(finger1: Finger, finger2: Finger) {
}
/**
*
* @method checkOverlap
* @param {Finger} finger1
* @param {Finger} finger2
**/
public checkOverlap(finger1: Finger, finger2: Finger) {
}
/**
*
* @method update
*/
public update() {
this._game.input.x = this.x;
this._game.input.y = this.y;
}
/**
*
* @method stop
*/
public stop() {
//this._domElement.addEventListener('touchstart', (event) => this.onTouchStart(event), false);
//this._domElement.addEventListener('touchmove', (event) => this.onTouchMove(event), false);
//this._domElement.addEventListener('touchend', (event) => this.onTouchEnd(event), false);
//this._domElement.addEventListener('touchenter', (event) => this.onTouchEnter(event), false);
//this._domElement.addEventListener('touchleave', (event) => this.onTouchLeave(event), false);
//this._domElement.addEventListener('touchcancel', (event) => this.onTouchCancel(event), false);
}
/**
*
* @method reset
**/
public reset() {
this.isDown = false;
this.isUp = false;
}
}

View file

@ -89,5 +89,8 @@
<TypeScriptCompile Include="sprites\velocity.ts" />
<TypeScriptCompile Include="tilemap\basic tilemap.ts" />
</ItemGroup>
<ItemGroup>
<Folder Include="mobile\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TypeScript\Microsoft.TypeScript.targets" />
</Project>

View file

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1 maximum-scale=1 user-scalable=0" />
<title>Phaser Mobile Fullscreen Test</title>
<style>
body {
padding: 0;
margin: 0;
}
</style>
<script src="../phaser.js"></script>
</head>
<body>
<div id="game"></div>
<script type="text/javascript">
var myGame = new Game(this, 'game', 320, 200, init, create, update);
function init() {
myGame.stage.fullscreen.go();
myGame.loader.addImageFile('backdrop1', '../assets/pics/atari_fujilogo.png');
myGame.loader.addImageFile('backdrop2', '../assets/pics/acryl_bladerunner.png');
myGame.loader.load();
}
var pic1;
var pic2;
function create() {
pic1 = myGame.createSprite(0, 0, 'backdrop1');
pic2 = myGame.createSprite(0, 0, 'backdrop2');
}
function update() {
if (myGame.stage.fullscreen.isLandscape == true)
{
pic1.visible = true;
pic2.visible = false;
}
else
{
pic2.visible = true;
pic1.visible = false;
}
}
</script>
</body>
</html>

File diff suppressed because it is too large Load diff