mirror of
https://github.com/photonstorm/phaser
synced 2024-11-27 15:12:18 +00:00
1316 lines
40 KiB
JavaScript
1316 lines
40 KiB
JavaScript
|
/* HTML5 Virtual Game Controller
|
||
|
* Courtesy of Austin Hallock
|
||
|
* https://github.com/austinhallock/html5-virtual-game-controller/
|
||
|
* Copyright (c) 2013 Clay.io
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Helpers
|
||
|
*/
|
||
|
( function(exports) {
|
||
|
var __slice = [].slice;
|
||
|
var __hasProp = {}.hasOwnProperty;
|
||
|
var __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||
|
/* $.extend functionality */
|
||
|
function extend( target, src )
|
||
|
{
|
||
|
var options, name, copy, copyIsArray, clone,
|
||
|
i = 1,
|
||
|
length = 2,
|
||
|
deep = true;
|
||
|
|
||
|
// Handle a deep copy situation
|
||
|
if( typeof target === "boolean" )
|
||
|
{
|
||
|
deep = target;
|
||
|
// skip the boolean and the target
|
||
|
i = 2;
|
||
|
}
|
||
|
|
||
|
// Handle case when target is a string or something( possible in deep copy )
|
||
|
if( typeof target !== "object" && !typeof target === 'function' )
|
||
|
{
|
||
|
target = {};
|
||
|
}
|
||
|
// Only deal with non-null/undefined values
|
||
|
if( options = src )
|
||
|
{
|
||
|
// Extend the base object
|
||
|
for( name in options )
|
||
|
{
|
||
|
src = target[name];
|
||
|
copy = options[name];
|
||
|
|
||
|
// Prevent never-ending loop
|
||
|
if( target === copy )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
// Recurse if we're merging plain objects or arrays
|
||
|
if( deep &&( typeof copy == 'object' ||( copyIsArray = Object.prototype.toString.call( copy ) === '[object Array]' ) ) )
|
||
|
{
|
||
|
if( copyIsArray )
|
||
|
{
|
||
|
copyIsArray = false;
|
||
|
clone = src && Object.prototype.toString.call( src ) === '[object Array]' ? src : [];
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
clone = src && typeof src == 'object' ? src : {};
|
||
|
}
|
||
|
// Never move original objects, clone them
|
||
|
target[name] = extend( clone, copy );
|
||
|
|
||
|
// Don't bring in undefined values
|
||
|
}
|
||
|
else if( typeof copy !== 'undefined' )
|
||
|
{
|
||
|
target[name] = copy;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return target;
|
||
|
}
|
||
|
|
||
|
// Make available to window
|
||
|
exports.GameController = {
|
||
|
|
||
|
// Default options,
|
||
|
options: {
|
||
|
left: {
|
||
|
type: 'dpad',
|
||
|
position: { left: '13%', bottom: '22%' },
|
||
|
dpad: {
|
||
|
up: {
|
||
|
width: '7%',
|
||
|
height: '15%',
|
||
|
stroke: 2,
|
||
|
touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 38 );
|
||
|
GameController.simulateKeyEvent( 'down', 38 );
|
||
|
},
|
||
|
touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 38 );
|
||
|
}
|
||
|
},
|
||
|
left: {
|
||
|
width: '15%',
|
||
|
height: '7%',
|
||
|
stroke: 2,
|
||
|
touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 37 );
|
||
|
GameController.simulateKeyEvent( 'down', 37 );
|
||
|
},
|
||
|
touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 37 );
|
||
|
}
|
||
|
},
|
||
|
down: {
|
||
|
width: '7%',
|
||
|
height: '15%',
|
||
|
stroke: 2,
|
||
|
touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 40 );
|
||
|
GameController.simulateKeyEvent( 'down', 40 );
|
||
|
},
|
||
|
touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 40 );
|
||
|
}
|
||
|
},
|
||
|
right: {
|
||
|
width: '15%',
|
||
|
height: '7%',
|
||
|
stroke: 2,
|
||
|
touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 39 );
|
||
|
GameController.simulateKeyEvent( 'down', 39 );
|
||
|
},
|
||
|
touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 39 );
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
joystick: {
|
||
|
radius: 60,
|
||
|
touchMove: function( e ) {
|
||
|
console.log( e );
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
right: {
|
||
|
type: 'buttons',
|
||
|
position: { right: '17%', bottom: '28%' },
|
||
|
buttons: [
|
||
|
{ offset: { x: '-13%', y: 0 }, label: 'X', radius: '7%', stroke: 2, backgroundColor: 'blue', fontColor: '#fff', touchStart: function() {
|
||
|
// Blue is currently mapped to up button
|
||
|
GameController.simulateKeyEvent( 'press', 88 ); // x key
|
||
|
GameController.simulateKeyEvent( 'down', 88 );
|
||
|
}, touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 88 );
|
||
|
} },
|
||
|
{ offset: { x: 0, y: '-11%' }, label: 'Y', radius: '7%', stroke: 2, backgroundColor: 'yellow', fontColor: '#fff', touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 70 ); // f key
|
||
|
GameController.simulateKeyEvent( 'down', 70 );
|
||
|
}, touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 70 );
|
||
|
} },
|
||
|
{ offset: { x: '13%', y: 0 }, label: 'B', radius: '7%', stroke: 2, backgroundColor: 'red', fontColor: '#fff', touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 90 ); // z key
|
||
|
GameController.simulateKeyEvent( 'down', 90 );
|
||
|
}, touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 90 );
|
||
|
} },
|
||
|
{ offset: { x: 0, y: '11%' }, label: 'A', radius: '7%', stroke: 2, backgroundColor: 'green', fontColor: '#fff', touchStart: function() {
|
||
|
GameController.simulateKeyEvent( 'press', 67 ); // a key
|
||
|
GameController.simulateKeyEvent( 'down', 67 );
|
||
|
}, touchEnd: function() {
|
||
|
GameController.simulateKeyEvent( 'up', 67 );
|
||
|
} }
|
||
|
],
|
||
|
dpad: {
|
||
|
up: {
|
||
|
width: '7%',
|
||
|
height: '15%',
|
||
|
stroke: 2
|
||
|
},
|
||
|
left: {
|
||
|
width: '15%',
|
||
|
height: '7%',
|
||
|
stroke: 2
|
||
|
},
|
||
|
down: {
|
||
|
width: '7%',
|
||
|
height: '15%',
|
||
|
stroke: 2
|
||
|
},
|
||
|
right: {
|
||
|
width: '15%',
|
||
|
height: '7%',
|
||
|
stroke: 2
|
||
|
}
|
||
|
},
|
||
|
joystick: {
|
||
|
radius: 60,
|
||
|
touchMove: function( e ) {
|
||
|
console.log( e );
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
touchRadius: 45
|
||
|
},
|
||
|
|
||
|
// Areas (objects) on the screen that can be touched
|
||
|
touchableAreas: [],
|
||
|
touchableAreasCount: 0,
|
||
|
|
||
|
// Multi-touch
|
||
|
touches: [],
|
||
|
|
||
|
// Canvas offset on page (for coverting touch coordinates)
|
||
|
offsetX: 0,
|
||
|
offsetY: 0,
|
||
|
|
||
|
// Bounding box - used for clearRect - first we determine which areas of the canvas are actually drawn to
|
||
|
bound: {
|
||
|
left: false,
|
||
|
right: false,
|
||
|
top: false,
|
||
|
bottom: false
|
||
|
},
|
||
|
|
||
|
// Heavy sprites (with gradients) are cached as a canvas to improve performance
|
||
|
cachedSprites: {},
|
||
|
|
||
|
paused: false,
|
||
|
|
||
|
init: function( options ) {
|
||
|
|
||
|
// Don't do anything if there's no touch support
|
||
|
if( ! 'ontouchstart' in document.documentElement )
|
||
|
return;
|
||
|
|
||
|
|
||
|
// Merge default options and specified options
|
||
|
options = options || {};
|
||
|
extend( this.options, options );
|
||
|
|
||
|
var userAgent = navigator.userAgent.toLowerCase();
|
||
|
// See if we should run the performanceFriendly version (for slower CPUs)
|
||
|
this.performanceFriendly = ( userAgent.indexOf( 'iphone' ) !== -1 || userAgent.indexOf( 'android' ) !== -1 || this.options.forcePerformanceFriendly );
|
||
|
|
||
|
// Grab the canvas if one wasn't passed
|
||
|
var ele;
|
||
|
if( !this.options.canvas || !( ele = document.getElementById( this.options.canvas ) ) )
|
||
|
{
|
||
|
this.options.canvas = document.getElementsByTagName( 'canvas' )[0];
|
||
|
}
|
||
|
else if( ele )
|
||
|
{
|
||
|
this.options.canvas = ele;
|
||
|
}
|
||
|
|
||
|
this.options.ctx = this.options.canvas.getContext( '2d' );
|
||
|
|
||
|
// Create a canvas that goes directly on top of the game canvas
|
||
|
this.createOverlayCanvas();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Finds the actual 4 corners of canvas that are being used (so we don't have to clear the entire canvas each render)
|
||
|
* Called when each new touchableArea is added in
|
||
|
* @param {object} options - x, y, width, height
|
||
|
*/
|
||
|
boundingSet: function( options ) {
|
||
|
var directions = ['left', 'right'];
|
||
|
|
||
|
// Square - pivot is top left
|
||
|
if( options.width )
|
||
|
{
|
||
|
var width = this.getPixels( options.width );
|
||
|
var height = this.getPixels( options.height );
|
||
|
var left = this.getPixels( options.x );
|
||
|
var top = this.getPixels( options.y );
|
||
|
}
|
||
|
// Circle - pivot is center
|
||
|
else
|
||
|
{
|
||
|
if( this.options.touchRadius )
|
||
|
var radius = this.getPixels( options.radius ) * 2 + ( this.getPixels( this.options.touchRadius ) / 2 ); // size of the box the joystick can go to
|
||
|
else
|
||
|
var radius = options.radius;
|
||
|
var width = height = ( radius + this.getPixels( options.stroke ) ) * 2;
|
||
|
var left = this.getPixels( options.x ) - ( width / 2 );
|
||
|
var top = this.getPixels( options.y ) - ( height / 2 );
|
||
|
}
|
||
|
var right = left + width;
|
||
|
var bottom = top + height;
|
||
|
|
||
|
if( this.bound.left === false || left < this.bound.left )
|
||
|
this.bound.left = left;
|
||
|
if( this.bound.right === false || right > this.bound.right )
|
||
|
this.bound.right = right;
|
||
|
if( this.bound.top === false || top < this.bound.top )
|
||
|
this.bound.top = top;
|
||
|
if( this.bound.bottom === false || bottom > this.bound.bottom )
|
||
|
this.bound.bottom = bottom;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Creates the canvas that sits on top of the game's canvas and holds game controls
|
||
|
*/
|
||
|
createOverlayCanvas: function() {
|
||
|
this.canvas = document.createElement( 'canvas' );
|
||
|
|
||
|
// Scale to same size as original canvas
|
||
|
this.resize( true );
|
||
|
|
||
|
document.getElementsByTagName( 'body' )[0].appendChild( this.canvas );
|
||
|
this.ctx = this.canvas.getContext( '2d' );
|
||
|
|
||
|
var _this = this;
|
||
|
window.addEventListener( 'resize', function() {
|
||
|
// Wait for any other events to finish
|
||
|
setTimeout( function() { GameController.resize.call( _this ); }, 1 );
|
||
|
} );
|
||
|
|
||
|
|
||
|
// Set the touch events for this new canvas
|
||
|
this.setTouchEvents();
|
||
|
|
||
|
// Load in the initial UI elements
|
||
|
this.loadSide( 'left' );
|
||
|
this.loadSide( 'right' );
|
||
|
|
||
|
// Starts up the rendering / drawing
|
||
|
this.render();
|
||
|
|
||
|
if( ! this.touches || this.touches.length == 0 )
|
||
|
this.paused = true; // pause until a touch event
|
||
|
},
|
||
|
|
||
|
pixelRatio: 1,
|
||
|
resize: function( firstTime ) {
|
||
|
// Scale to same size as original canvas
|
||
|
this.canvas.width = this.options.canvas.width;
|
||
|
this.canvas.height = this.options.canvas.height;
|
||
|
|
||
|
this.offsetX = GameController.options.canvas.offsetLeft + document.body.scrollLeft;
|
||
|
this.offsetY = GameController.options.canvas.offsetTop + document.body.scrollTop;
|
||
|
|
||
|
// Get in on this retina action
|
||
|
if( this.options.canvas.style.width && this.options.canvas.style.height && this.options.canvas.style.height.indexOf( 'px' ) !== -1 )
|
||
|
{
|
||
|
this.canvas.style.width = this.options.canvas.style.width;
|
||
|
this.canvas.style.height = this.options.canvas.style.height;
|
||
|
this.pixelRatio = this.canvas.width / parseInt( this.canvas.style.width );
|
||
|
}
|
||
|
|
||
|
this.canvas.style.position = 'absolute';
|
||
|
this.canvas.style.zIndex = '5';
|
||
|
this.canvas.style.left = this.options.canvas.offsetLeft + 'px';
|
||
|
this.canvas.style.top = this.options.canvas.offsetTop + 'px';
|
||
|
this.canvas.setAttribute( 'style', this.canvas.getAttribute( 'style' ) +' -ms-touch-action: none;' );
|
||
|
|
||
|
if( !firstTime )
|
||
|
{
|
||
|
// Remove all current buttons
|
||
|
this.touchableAreas = [];
|
||
|
// Clear out the cached sprites
|
||
|
this.cachedSprites = [];
|
||
|
// Reload in the initial UI elements
|
||
|
this.reloadSide( 'left' );
|
||
|
this.reloadSide( 'right' );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Returns the scaled pixels. Given the value passed
|
||
|
* @param {int/string} value - either an integer for # of pixels, or 'x%' for relative
|
||
|
* @param {char} axis - x, y
|
||
|
*/
|
||
|
getPixels: function( value, axis )
|
||
|
{
|
||
|
if( typeof value === 'undefined' )
|
||
|
return 0
|
||
|
else if( typeof value === 'number' )
|
||
|
return value;
|
||
|
else // a percentage
|
||
|
{
|
||
|
if( axis == 'x' )
|
||
|
return ( parseInt( value ) / 100 ) * this.canvas.width;
|
||
|
else
|
||
|
return ( parseInt( value ) / 100 ) * this.canvas.height;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Simulates a key press
|
||
|
* @param {string} eventName - 'down', 'up'
|
||
|
* @param {char} character
|
||
|
*/
|
||
|
simulateKeyEvent: function( eventName, keyCode ) {
|
||
|
if( typeof window.onkeydown === 'undefined' ) // No keyboard, can't simulate...
|
||
|
return false;
|
||
|
|
||
|
/* If they have jQuery, use it because it works better for mobile safari */
|
||
|
if( typeof jQuery !== 'undefined' )
|
||
|
{
|
||
|
var press = jQuery.Event( 'key' + eventName );
|
||
|
press.ctrlKey = false;
|
||
|
press.which = keyCode;
|
||
|
press.keyCode = keyCode;
|
||
|
// Keypress on just the canvas instead of the document
|
||
|
$( this.options.canvas ).trigger( press );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var oEvent = document.createEvent( 'KeyboardEvent' );
|
||
|
|
||
|
// Chromium Hack
|
||
|
if( navigator.userAgent.toLowerCase().indexOf( 'chrome' ) !== -1 )
|
||
|
{
|
||
|
Object.defineProperty( oEvent, 'keyCode', {
|
||
|
get : function() {
|
||
|
return this.keyCodeVal;
|
||
|
}
|
||
|
} );
|
||
|
Object.defineProperty( oEvent, 'which', {
|
||
|
get : function() {
|
||
|
return this.keyCodeVal;
|
||
|
}
|
||
|
} );
|
||
|
}
|
||
|
|
||
|
if( oEvent.initKeyboardEvent )
|
||
|
{
|
||
|
oEvent.initKeyboardEvent( 'key' + eventName, true, true, document.defaultView, false, false, false, false, keyCode, keyCode );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
oEvent.initKeyEvent( 'key' + eventName, true, true, document.defaultView, false, false, false, false, keyCode, keyCode );
|
||
|
}
|
||
|
|
||
|
oEvent.keyCodeVal = keyCode;
|
||
|
|
||
|
},
|
||
|
|
||
|
setTouchEvents: function() {
|
||
|
var _this = this;
|
||
|
var touchStart = function( e ) {
|
||
|
if( _this.paused )
|
||
|
{
|
||
|
_this.paused = false;
|
||
|
}
|
||
|
|
||
|
e.preventDefault();
|
||
|
|
||
|
// Microsoft always has to have their own stuff...
|
||
|
if( window.navigator.msPointerEnabled && e.clientX && e.pointerType == e.MSPOINTER_TYPE_TOUCH )
|
||
|
{
|
||
|
_this.touches[ e.pointerId ] = { clientX: e.clientX, clientY: e.clientY };
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_this.touches = e.touches || [];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
this.canvas.addEventListener( 'touchstart', touchStart, false );
|
||
|
|
||
|
var touchEnd = function( e ) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
if( window.navigator.msPointerEnabled && e.pointerType == e.MSPOINTER_TYPE_TOUCH )
|
||
|
{
|
||
|
delete _this.touches[ e.pointerId ];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_this.touches = e.touches || [];
|
||
|
}
|
||
|
|
||
|
if( !e.touches || e.touches.length == 0 )
|
||
|
{
|
||
|
// Draw once more to remove the touch area
|
||
|
_this.render();
|
||
|
_this.paused = true;
|
||
|
}
|
||
|
};
|
||
|
this.canvas.addEventListener( 'touchend', touchEnd );
|
||
|
|
||
|
var touchMove = function( e ) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
if( window.navigator.msPointerEnabled && e.clientX && e.pointerType == e.MSPOINTER_TYPE_TOUCH )
|
||
|
{
|
||
|
_this.touches[ e.pointerId ] = { clientX: e.clientX, clientY: e.clientY };
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_this.touches = e.touches || [];
|
||
|
}
|
||
|
};
|
||
|
this.canvas.addEventListener( 'touchmove', touchMove );
|
||
|
|
||
|
if( window.navigator.msPointerEnabled )
|
||
|
{
|
||
|
this.canvas.addEventListener( 'MSPointerDown', touchStart );
|
||
|
this.canvas.addEventListener( 'MSPointerUp', touchEnd );
|
||
|
this.canvas.addEventListener( 'MSPointerMove', touchMove );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Adds the area to a list of touchable areas, draws
|
||
|
* @param {object} options with properties: x, y, width, height, touchStart, touchEnd, touchMove
|
||
|
*/
|
||
|
addTouchableDirection: function( options ) {
|
||
|
|
||
|
var direction = new TouchableDirection( options );
|
||
|
|
||
|
direction.id = this.touchableAreas.push( direction );
|
||
|
this.touchableAreasCount++;
|
||
|
|
||
|
this.boundingSet( options );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Adds the circular area to a list of touchable areas, draws
|
||
|
* @param {object} options with properties: x, y, width, height, touchStart, touchEnd, touchMove
|
||
|
*/
|
||
|
addJoystick: function( options ) { //x, y, radius, backgroundColor, touchStart, touchEnd ) {
|
||
|
|
||
|
var joystick = new TouchableJoystick( options );
|
||
|
|
||
|
joystick.id = this.touchableAreas.push( joystick );
|
||
|
this.touchableAreasCount++;
|
||
|
|
||
|
this.boundingSet( options );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Adds the circular area to a list of touchable areas, draws
|
||
|
* @param {object} options with properties: x, y, width, height, touchStart, touchEnd, touchMove
|
||
|
*/
|
||
|
addButton: function( options ) { //x, y, radius, backgroundColor, touchStart, touchEnd ) {
|
||
|
|
||
|
var button = new TouchableButton( options );
|
||
|
|
||
|
button.id = this.touchableAreas.push( button );
|
||
|
this.touchableAreasCount++;
|
||
|
|
||
|
this.boundingSet( options );
|
||
|
},
|
||
|
|
||
|
addTouchableArea: function( check, callback ) {
|
||
|
},
|
||
|
|
||
|
loadButtons: function( side ) {
|
||
|
var buttons = this.options[ side ].buttons;
|
||
|
var _this = this;
|
||
|
for( var i = 0, j = buttons.length; i < j; i++ )
|
||
|
{
|
||
|
if( typeof buttons[i] === 'undefined' || typeof buttons[i].offset === 'undefined' )
|
||
|
continue;
|
||
|
|
||
|
var posX = this.getPositionX( side );
|
||
|
var posY = this.getPositionY( side );
|
||
|
|
||
|
buttons[i].x = posX + this.getPixels( buttons[i].offset.x, 'y' );
|
||
|
buttons[i].y = posY + this.getPixels( buttons[i].offset.y, 'y' );
|
||
|
|
||
|
this.addButton( buttons[i] );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
loadDPad: function( side ) {
|
||
|
var dpad = this.options[ side ].dpad || {};
|
||
|
|
||
|
// Centered value is at this.options[ side ].position
|
||
|
|
||
|
var _this = this;
|
||
|
|
||
|
var posX = this.getPositionX( side );
|
||
|
var posY = this.getPositionY( side );
|
||
|
|
||
|
|
||
|
// If they have all 4 directions, add a circle to the center for looks
|
||
|
if( dpad.up && dpad.left && dpad.down && dpad.right )
|
||
|
{
|
||
|
var options = {
|
||
|
x: posX,
|
||
|
y: posY,
|
||
|
radius: dpad.right.height
|
||
|
}
|
||
|
var center = new TouchableCircle( options );
|
||
|
this.touchableAreas.push( center );
|
||
|
this.touchableAreasCount++;
|
||
|
}
|
||
|
|
||
|
// Up arrow
|
||
|
if( dpad.up !== false )
|
||
|
{
|
||
|
dpad.up.x = posX - this.getPixels( dpad.up.width, 'y' ) / 2;
|
||
|
dpad.up.y = posY - ( this.getPixels( dpad.up.height, 'y' ) + this.getPixels( dpad.left.height, 'y' ) / 2 );
|
||
|
dpad.up.direction = 'up';
|
||
|
this.addTouchableDirection( dpad.up );
|
||
|
}
|
||
|
|
||
|
// Left arrow
|
||
|
if( dpad.left !== false )
|
||
|
{
|
||
|
dpad.left.x = posX - ( this.getPixels( dpad.left.width, 'y' ) + this.getPixels( dpad.up.width, 'y' ) / 2 );
|
||
|
dpad.left.y = posY - ( this.getPixels( dpad.left.height, 'y' ) / 2 );
|
||
|
dpad.left.direction = 'left';
|
||
|
this.addTouchableDirection( dpad.left );
|
||
|
}
|
||
|
|
||
|
// Down arrow
|
||
|
if( dpad.down !== false )
|
||
|
{
|
||
|
dpad.down.x = posX - this.getPixels( dpad.down.width, 'y' ) / 2;
|
||
|
dpad.down.y = posY + ( this.getPixels( dpad.left.height, 'y' ) / 2 );
|
||
|
dpad.down.direction = 'down';
|
||
|
this.addTouchableDirection( dpad.down );
|
||
|
}
|
||
|
|
||
|
// Right arrow
|
||
|
if( dpad.right !== false )
|
||
|
{
|
||
|
dpad.right.x = posX + ( this.getPixels( dpad.up.width, 'y' ) / 2 );
|
||
|
dpad.right.y = posY - this.getPixels( dpad.right.height, 'y' ) / 2;
|
||
|
dpad.right.direction = 'right';
|
||
|
this.addTouchableDirection( dpad.right );
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
loadJoystick: function( side ) {
|
||
|
var joystick = this.options[ side ].joystick;
|
||
|
joystick.x = this.getPositionX( side );
|
||
|
joystick.y = this.getPositionY( side );
|
||
|
|
||
|
this.addJoystick( joystick );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Used for resizing. Currently is just an alias for loadSide
|
||
|
*/
|
||
|
reloadSide: function( side ) {
|
||
|
// Load in new ones
|
||
|
this.loadSide( side );
|
||
|
},
|
||
|
|
||
|
loadSide: function( side ) {
|
||
|
if( this.options[ side ].type === 'dpad' )
|
||
|
{
|
||
|
this.loadDPad( side );
|
||
|
}
|
||
|
else if( this.options[ side ].type === 'joystick' )
|
||
|
{
|
||
|
this.loadJoystick( side );
|
||
|
}
|
||
|
else if( this.options[ side ].type === 'buttons' )
|
||
|
{
|
||
|
this.loadButtons( side );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Normalize touch positions by the left and top offsets
|
||
|
* @param {int} x
|
||
|
*/
|
||
|
normalizeTouchPositionX: function( x )
|
||
|
{
|
||
|
return ( x - this.offsetX ) * ( this.pixelRatio );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Normalize touch positions by the left and top offsets
|
||
|
* @param {int} y
|
||
|
*/
|
||
|
normalizeTouchPositionY: function( y )
|
||
|
{
|
||
|
return ( y - this.offsetY ) * ( this.pixelRatio );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Returns the x position when given # of pixels from right (based on canvas size)
|
||
|
* @param {int} right
|
||
|
*/
|
||
|
getXFromRight: function( right ) {
|
||
|
return this.canvas.width - right;
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the y position when given # of pixels from bottom (based on canvas size)
|
||
|
* @param {int} right
|
||
|
*/
|
||
|
getYFromBottom: function( bottom ) {
|
||
|
return this.canvas.height - bottom;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Grabs the x position of either the left or right side/controls
|
||
|
* @param {string} side - 'left', 'right'
|
||
|
*/
|
||
|
getPositionX: function( side ) {
|
||
|
if( typeof this.options[ side ].position.left !== 'undefined' )
|
||
|
return this.getPixels( this.options[ side ].position.left, 'x' );
|
||
|
else
|
||
|
return this.getXFromRight( this.getPixels( this.options[ side ].position.right, 'x' ) );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Grabs the y position of either the left or right side/controls
|
||
|
* @param {string} side - 'left', 'right'
|
||
|
*/
|
||
|
getPositionY: function( side ) {
|
||
|
if( typeof this.options[ side ].position.top !== 'undefined' )
|
||
|
return this.getPixels( this.options[ side ].position.top, 'y' );
|
||
|
else
|
||
|
return this.getYFromBottom( this.getPixels( this.options[ side ].position.bottom, 'y' ) );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Processes the info for each touchableArea
|
||
|
*/
|
||
|
renderAreas: function() {
|
||
|
for( var i = 0, j = this.touchableAreasCount; i < j; i++ )
|
||
|
{
|
||
|
var area = this.touchableAreas[ i ];
|
||
|
|
||
|
if( typeof area === 'undefined' )
|
||
|
continue;
|
||
|
|
||
|
area.draw();
|
||
|
|
||
|
// Go through all touches to see if any hit this area
|
||
|
var touched = false;
|
||
|
for( var k = 0, l = this.touches.length; k < l; k++ )
|
||
|
{
|
||
|
var touch = this.touches[ k ];
|
||
|
if( typeof touch === 'undefined' )
|
||
|
continue;
|
||
|
|
||
|
var x = this.normalizeTouchPositionX( touch.clientX ), y = this.normalizeTouchPositionY( touch.clientY );
|
||
|
|
||
|
// Check that it's in the bounding box/circle
|
||
|
if( ( area.check( x, y ) ) !== false )
|
||
|
{
|
||
|
if( !touched )
|
||
|
touched = this.touches[ k ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( touched )
|
||
|
{
|
||
|
if( !area.active )
|
||
|
area.touchStartWrapper( touched );
|
||
|
area.touchMoveWrapper( touched );
|
||
|
}
|
||
|
else if( area.active )
|
||
|
{
|
||
|
area.touchEndWrapper( touched );
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
render: function() {
|
||
|
if( ! this.paused || ! this.performanceFriendly )
|
||
|
this.ctx.clearRect( this.bound.left, this.bound.top, this.bound.right - this.bound.left, this.bound.bottom - this.bound.top );
|
||
|
|
||
|
// Draw feedback for when screen is being touched
|
||
|
// When no touch events are happening, this enables 'paused' mode, which skips running this
|
||
|
// This isn't run at all in performanceFriendly mode
|
||
|
if( ! this.paused && ! this.performanceFriendly )
|
||
|
{
|
||
|
var cacheId = 'touch-circle';
|
||
|
var cached = this.cachedSprites[ cacheId ];
|
||
|
|
||
|
if( ! cached && this.options.touchRadius )
|
||
|
{
|
||
|
var subCanvas = document.createElement( 'canvas' );
|
||
|
var ctx = subCanvas.getContext( '2d' );
|
||
|
subCanvas.width = 2 * this.options.touchRadius;
|
||
|
subCanvas.height = 2 * this.options.touchRadius;
|
||
|
|
||
|
var center = this.options.touchRadius;
|
||
|
var gradient = ctx.createRadialGradient( center, center, 1, center, center, this.options.touchRadius ); // 10 = end radius
|
||
|
gradient.addColorStop( 0, 'rgba( 200, 200, 200, 1 )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 200, 200, 200, 0 )' );
|
||
|
ctx.beginPath();
|
||
|
ctx.fillStyle = gradient;
|
||
|
ctx.arc( center, center, this.options.touchRadius, 0 , 2 * Math.PI, false );
|
||
|
ctx.fill();
|
||
|
|
||
|
cached = GameController.cachedSprites[ cacheId ] = subCanvas;
|
||
|
}
|
||
|
// Draw the current touch positions if any
|
||
|
for( var i = 0, j = this.touches.length; i < j; i++ )
|
||
|
{
|
||
|
var touch = this.touches[ i ];
|
||
|
if( typeof touch === 'undefined' )
|
||
|
continue;
|
||
|
var x = this.normalizeTouchPositionX( touch.clientX ), y = this.normalizeTouchPositionY( touch.clientY );
|
||
|
if( x - this.options.touchRadius > this.bound.left && x + this.options.touchRadius < this.bound.right &&
|
||
|
y - this.options.touchRadius > this.bound.top && y + this.options.touchRadius < this.bound.bottom )
|
||
|
this.ctx.drawImage( cached, x - this.options.touchRadius, y - this.options.touchRadius );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Render if the game isn't paused, or we're not in performanceFriendly mode (running when not paused keeps the semi-transparent gradients looking better for some reason)
|
||
|
if( ! this.paused || ! this.performanceFriendly )
|
||
|
{
|
||
|
// Process all the info for each touchable area
|
||
|
this.renderAreas();
|
||
|
}
|
||
|
|
||
|
window.requestAnimationFrame( this.renderWrapper );
|
||
|
},
|
||
|
/**
|
||
|
* So we can keep scope, and don't have to create a new obj every requestAnimationFrame (bad for garbage collection)
|
||
|
*/
|
||
|
renderWrapper: function() {
|
||
|
GameController.render();
|
||
|
},
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Superclass for touchable stuff
|
||
|
*/
|
||
|
var TouchableArea = ( function() {
|
||
|
|
||
|
function TouchableArea()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Called when this direction is being touched
|
||
|
TouchableArea.prototype.touchStart = null;
|
||
|
|
||
|
// Called when this direction is being moved
|
||
|
TouchableArea.prototype.touchMove = null;
|
||
|
|
||
|
// Called when this direction is no longer being touched
|
||
|
TouchableArea.prototype.touchEnd = null;
|
||
|
|
||
|
TouchableArea.prototype.type = 'area';
|
||
|
TouchableArea.prototype.id = false;
|
||
|
TouchableArea.prototype.active = false;
|
||
|
|
||
|
/**
|
||
|
* Sets the user-specified callback for this direction being touched
|
||
|
* @param {function} callback
|
||
|
*/
|
||
|
TouchableArea.prototype.setTouchStart = function( callback ) {
|
||
|
this.touchStart = callback;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Called when this direction is no longer touched
|
||
|
*/
|
||
|
TouchableArea.prototype.touchStartWrapper = function( e ) {
|
||
|
// Fire the user specified callback
|
||
|
if( this.touchStart )
|
||
|
this.touchStart();
|
||
|
|
||
|
// Mark this direction as active
|
||
|
this.active = true;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Sets the user-specified callback for this direction no longer being touched
|
||
|
* @param {function} callback
|
||
|
*/
|
||
|
TouchableArea.prototype.setTouchMove = function( callback ) {
|
||
|
this.touchMove = callback;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Called when this direction is moved. Make sure it's actually changed before passing to developer
|
||
|
*/
|
||
|
TouchableArea.prototype.lastPosX = 0;
|
||
|
TouchableArea.prototype.lastPosY = 0;
|
||
|
TouchableArea.prototype.touchMoveWrapper = function( e ) {
|
||
|
// Fire the user specified callback
|
||
|
if( this.touchMove && ( e.clientX != TouchableArea.prototype.lastPosX || e.clientY != TouchableArea.prototype.lastPosY ) )
|
||
|
{
|
||
|
this.touchMove();
|
||
|
this.lastPosX = e.clientX;
|
||
|
this.lastPosY = e.clientY;
|
||
|
}
|
||
|
// Mark this direction as active
|
||
|
this.active = true;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Sets the user-specified callback for this direction no longer being touched
|
||
|
* @param {function} callback
|
||
|
*/
|
||
|
TouchableArea.prototype.setTouchEnd = function( callback ) {
|
||
|
this.touchEnd = callback;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Called when this direction is first touched
|
||
|
*/
|
||
|
TouchableArea.prototype.touchEndWrapper = function( e ) {
|
||
|
// Fire the user specified callback
|
||
|
if( this.touchEnd )
|
||
|
this.touchEnd();
|
||
|
|
||
|
// Mark this direction as inactive
|
||
|
this.active = false;
|
||
|
|
||
|
GameController.render();
|
||
|
};
|
||
|
|
||
|
return TouchableArea;
|
||
|
|
||
|
} )();
|
||
|
|
||
|
var TouchableDirection = ( function( __super ) {
|
||
|
__extends( TouchableDirection, __super );
|
||
|
|
||
|
function TouchableDirection( options )
|
||
|
{
|
||
|
for( var i in options )
|
||
|
{
|
||
|
if( i == 'x' )
|
||
|
this[i] = GameController.getPixels( options[i], 'x' );
|
||
|
else if( i == 'y' || i == 'height' || i == 'width' )
|
||
|
this[i] = GameController.getPixels( options[i], 'y' );
|
||
|
else
|
||
|
this[i] = options[i];
|
||
|
}
|
||
|
|
||
|
this.draw();
|
||
|
}
|
||
|
|
||
|
TouchableDirection.prototype.type = 'direction';
|
||
|
|
||
|
/**
|
||
|
* Checks if the touch is within the bounds of this direction
|
||
|
*/
|
||
|
TouchableDirection.prototype.check = function( touchX, touchY ) {
|
||
|
var distanceX, distanceY;
|
||
|
if( ( Math.abs( touchX - this.x ) < ( GameController.options.touchRadius / 2 ) || ( touchX > this.x ) ) && // left
|
||
|
( Math.abs( touchX - ( this.x + this.width ) ) < ( GameController.options.touchRadius / 2 ) || ( touchX < this.x + this.width ) ) && // right
|
||
|
( Math.abs( touchY - this.y ) < ( GameController.options.touchRadius / 2 ) || ( touchY > this.y ) ) && // top
|
||
|
( Math.abs( touchY - ( this.y + this.height ) ) < ( GameController.options.touchRadius / 2 ) || ( touchY < this.y + this.height ) ) // bottom
|
||
|
)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
TouchableDirection.prototype.draw = function() {
|
||
|
var cacheId = this.type + '' + this.id + '' + this.active;
|
||
|
var cached = GameController.cachedSprites[ cacheId ];
|
||
|
if( ! cached )
|
||
|
{
|
||
|
var subCanvas = document.createElement( 'canvas' );
|
||
|
var ctx = subCanvas.getContext( '2d' );
|
||
|
subCanvas.width = this.width + 2 * this.stroke;
|
||
|
subCanvas.height = this.height + 2 * this.stroke;
|
||
|
|
||
|
var opacity = this.opacity || 0.9;
|
||
|
|
||
|
if( ! this.active ) // Direction currently being touched
|
||
|
opacity *= 0.5;
|
||
|
|
||
|
switch( this.direction )
|
||
|
{
|
||
|
case 'up':
|
||
|
var gradient = ctx.createLinearGradient( 0, 0, 0, this.height );
|
||
|
gradient.addColorStop( 0, 'rgba( 0, 0, 0, ' + ( opacity * 0.5 ) + ' )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 0, 0, 0, ' + opacity + ' )' );
|
||
|
break;
|
||
|
case 'left':
|
||
|
var gradient = ctx.createLinearGradient( 0, 0, this.width, 0 );
|
||
|
gradient.addColorStop( 0, 'rgba( 0, 0, 0, ' + ( opacity * 0.5 ) + ' )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 0, 0, 0, ' + opacity + ' )' );
|
||
|
break;
|
||
|
case 'right':
|
||
|
var gradient = ctx.createLinearGradient( 0, 0, this.width, 0 );
|
||
|
gradient.addColorStop( 0, 'rgba( 0, 0, 0, ' + opacity + ' )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 0, 0, 0, ' + ( opacity * 0.5 ) + ' )' );
|
||
|
break;
|
||
|
case 'down':
|
||
|
default:
|
||
|
var gradient = ctx.createLinearGradient( 0, 0, 0, this.height );
|
||
|
gradient.addColorStop( 0, 'rgba( 0, 0, 0, ' + opacity + ' )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 0, 0, 0, ' + ( opacity * 0.5 ) + ' )' );
|
||
|
}
|
||
|
ctx.fillStyle = gradient;
|
||
|
|
||
|
ctx.fillRect( 0, 0, this.width, this.height );
|
||
|
ctx.lineWidth = this.stroke;
|
||
|
ctx.strokeStyle = 'rgba( 255, 255, 255, 0.1 )';
|
||
|
ctx.strokeRect( 0, 0, this.width, this.height );
|
||
|
|
||
|
cached = GameController.cachedSprites[ cacheId ] = subCanvas;
|
||
|
}
|
||
|
|
||
|
GameController.ctx.drawImage( cached, this.x, this.y );
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
return TouchableDirection;
|
||
|
} )( TouchableArea );
|
||
|
|
||
|
var TouchableButton = ( function( __super ) {
|
||
|
__extends( TouchableButton, __super );
|
||
|
|
||
|
function TouchableButton( options ) //x, y, radius, backgroundColor )
|
||
|
{
|
||
|
for( var i in options )
|
||
|
{
|
||
|
if( i == 'x' )
|
||
|
this[i] = GameController.getPixels( options[i], 'x' );
|
||
|
else if( i == 'x' || i == 'radius' )
|
||
|
this[i] = GameController.getPixels( options[i], 'y' );
|
||
|
else
|
||
|
this[i] = options[i];
|
||
|
}
|
||
|
|
||
|
this.draw();
|
||
|
}
|
||
|
|
||
|
TouchableButton.prototype.type = 'button';
|
||
|
|
||
|
/**
|
||
|
* Checks if the touch is within the bounds of this direction
|
||
|
*/
|
||
|
TouchableButton.prototype.check = function( touchX, touchY ) {
|
||
|
if(
|
||
|
( Math.abs( touchX - this.x ) < this.radius + ( GameController.options.touchRadius / 2 ) ) &&
|
||
|
( Math.abs( touchY - this.y ) < this.radius + ( GameController.options.touchRadius / 2 ) )
|
||
|
)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
TouchableButton.prototype.draw = function() {
|
||
|
var cacheId = this.type + '' + this.id + '' + this.active;
|
||
|
var cached = GameController.cachedSprites[ cacheId ];
|
||
|
if( ! cached )
|
||
|
{
|
||
|
var subCanvas = document.createElement( 'canvas' );
|
||
|
var ctx = subCanvas.getContext( '2d' );
|
||
|
ctx.lineWidth = this.stroke;
|
||
|
subCanvas.width = subCanvas.height = 2 * ( this.radius + ctx.lineWidth );
|
||
|
|
||
|
|
||
|
var gradient = ctx.createRadialGradient( this.radius, this.radius, 1, this.radius, this.radius, this.radius );
|
||
|
var textShadowColor;
|
||
|
switch( this.backgroundColor )
|
||
|
{
|
||
|
case 'blue':
|
||
|
gradient.addColorStop( 0, 'rgba(123, 181, 197, 0.6)' );
|
||
|
gradient.addColorStop( 1, '#105a78' );
|
||
|
textShadowColor = '#0A4861';
|
||
|
break;
|
||
|
case 'green':
|
||
|
gradient.addColorStop( 0, 'rgba(29, 201, 36, 0.6)' );
|
||
|
gradient.addColorStop( 1, '#107814' );
|
||
|
textShadowColor = '#085C0B';
|
||
|
break;
|
||
|
case 'red':
|
||
|
gradient.addColorStop( 0, 'rgba(165, 34, 34, 0.6)' );
|
||
|
gradient.addColorStop( 1, '#520101' );
|
||
|
textShadowColor = '#330000';
|
||
|
break;
|
||
|
case 'yellow':
|
||
|
gradient.addColorStop( 0, 'rgba(219, 217, 59, 0.6)' );
|
||
|
gradient.addColorStop( 1, '#E8E10E' );
|
||
|
textShadowColor = '#BDB600';
|
||
|
break;
|
||
|
case 'white':
|
||
|
default:
|
||
|
gradient.addColorStop( 0, 'rgba( 255,255,255,.3 )' );
|
||
|
gradient.addColorStop( 1, '#eee' );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( this.active )
|
||
|
ctx.fillStyle = textShadowColor;
|
||
|
else
|
||
|
ctx.fillStyle = gradient;
|
||
|
|
||
|
ctx.strokeStyle = textShadowColor;
|
||
|
|
||
|
ctx.beginPath();
|
||
|
//ctx.arc( this.x, this.y, this.radius, 0 , 2 * Math.PI, false );
|
||
|
ctx.arc( subCanvas.width / 2, subCanvas.width / 2, this.radius, 0 , 2 * Math.PI, false );
|
||
|
ctx.fill();
|
||
|
ctx.stroke();
|
||
|
|
||
|
if( this.label )
|
||
|
{
|
||
|
// Text Shadow
|
||
|
ctx.fillStyle = textShadowColor;
|
||
|
ctx.font = 'bold ' + ( this.fontSize || subCanvas.height * 0.35 ) + 'px Verdana';
|
||
|
ctx.textAlign = 'center';
|
||
|
ctx.textBaseline = 'middle';
|
||
|
ctx.fillText( this.label, subCanvas.height / 2 + 2, subCanvas.height / 2 + 2 );
|
||
|
|
||
|
|
||
|
ctx.fillStyle = this.fontColor;
|
||
|
ctx.font = 'bold ' + ( this.fontSize || subCanvas.height * 0.35 ) + 'px Verdana';
|
||
|
ctx.textAlign = 'center';
|
||
|
ctx.textBaseline = 'middle';
|
||
|
ctx.fillText( this.label, subCanvas.height / 2, subCanvas.height / 2 );
|
||
|
}
|
||
|
|
||
|
cached = GameController.cachedSprites[ cacheId ] = subCanvas;
|
||
|
}
|
||
|
|
||
|
GameController.ctx.drawImage( cached, this.x, this.y );
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
return TouchableButton;
|
||
|
} )( TouchableArea );
|
||
|
|
||
|
var TouchableJoystick = ( function( __super ) {
|
||
|
__extends( TouchableJoystick, __super );
|
||
|
|
||
|
function TouchableJoystick( options ) //x, y, radius, backgroundColor )
|
||
|
{
|
||
|
for( var i in options )
|
||
|
this[i] = options[i];
|
||
|
|
||
|
this.currentX = this.currentX || this.x;
|
||
|
this.currentY = this.currentY || this.y;
|
||
|
}
|
||
|
|
||
|
TouchableJoystick.prototype.type = 'joystick';
|
||
|
|
||
|
/**
|
||
|
* Checks if the touch is within the bounds of this direction
|
||
|
*/
|
||
|
TouchableJoystick.prototype.check = function( touchX, touchY ) {
|
||
|
if(
|
||
|
( Math.abs( touchX - this.x ) < this.radius + ( GameController.getPixels( GameController.options.touchRadius ) / 2 ) ) &&
|
||
|
( Math.abs( touchY - this.y ) < this.radius + ( GameController.getPixels( GameController.options.touchRadius ) / 2 ) )
|
||
|
)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* details for the joystick move event, stored here so we're not constantly creating new objs for garbage. The object has params:
|
||
|
* dx - the number of pixels the current joystick center is from the base center in x direction
|
||
|
* dy - the number of pixels the current joystick center is from the base center in y direction
|
||
|
* max - the maximum number of pixels dx or dy can be
|
||
|
* normalizedX - a number between -1 and 1 relating to how far left or right the joystick is
|
||
|
* normalizedY - a number between -1 and 1 relating to how far up or down the joystick is
|
||
|
*/
|
||
|
TouchableJoystick.prototype.moveDetails = {};
|
||
|
|
||
|
/**
|
||
|
* Called when this joystick is moved
|
||
|
*/
|
||
|
TouchableJoystick.prototype.touchMoveWrapper = function( e ) {
|
||
|
this.currentX = GameController.normalizeTouchPositionX( e.clientX );
|
||
|
this.currentY = GameController.normalizeTouchPositionY( e.clientY );
|
||
|
|
||
|
// Fire the user specified callback
|
||
|
if( this.touchMove )
|
||
|
{
|
||
|
if( this.moveDetails.dx != this.currentX - this.x && this.moveDetails.dy != this.y - this.currentY )
|
||
|
{
|
||
|
this.moveDetails.dx = this.currentX - this.x; // reverse so right is positive
|
||
|
this.moveDetails.dy = this.y - this.currentY;
|
||
|
this.moveDetails.max = this.radius + ( GameController.options.touchRadius / 2 );
|
||
|
this.moveDetails.normalizedX = this.moveDetails.dx / this.moveDetails.max;
|
||
|
this.moveDetails.normalizedY = this.moveDetails.dy / this.moveDetails.max;
|
||
|
|
||
|
this.touchMove( this.moveDetails );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Mark this direction as inactive
|
||
|
this.active = true;
|
||
|
};
|
||
|
|
||
|
TouchableJoystick.prototype.draw = function() {
|
||
|
if( ! this.id ) // wait until id is set
|
||
|
return false;
|
||
|
|
||
|
var cacheId = this.type + '' + this.id + '' + this.active;
|
||
|
var cached = GameController.cachedSprites[ cacheId ];
|
||
|
if( ! cached )
|
||
|
{
|
||
|
var subCanvas = document.createElement( 'canvas' );
|
||
|
this.stroke = this.stroke || 2;
|
||
|
subCanvas.width = subCanvas.height = 2 * ( this.radius + ( GameController.options.touchRadius ) + this.stroke );
|
||
|
|
||
|
var ctx = subCanvas.getContext( '2d' );
|
||
|
ctx.lineWidth = this.stroke;
|
||
|
if( this.active ) // Direction currently being touched
|
||
|
{
|
||
|
var gradient = ctx.createRadialGradient( 0, 0, 1, 0, 0, this.radius );
|
||
|
gradient.addColorStop( 0, 'rgba( 200,200,200,.5 )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 200,200,200,.9 )' );
|
||
|
ctx.strokeStyle = '#000';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// STYLING FOR BUTTONS
|
||
|
var gradient = ctx.createRadialGradient( 0, 0, 1, 0, 0, this.radius );
|
||
|
gradient.addColorStop( 0, 'rgba( 200,200,200,.2 )' );
|
||
|
gradient.addColorStop( 1, 'rgba( 200,200,200,.4 )' );
|
||
|
ctx.strokeStyle = 'rgba( 0,0,0,.4 )';
|
||
|
}
|
||
|
ctx.fillStyle = gradient;
|
||
|
// Actual joystick part that is being moved
|
||
|
ctx.beginPath();
|
||
|
ctx.arc( this.radius, this.radius, this.radius, 0 , 2 * Math.PI, false );
|
||
|
ctx.fill();
|
||
|
ctx.stroke();
|
||
|
|
||
|
cached = GameController.cachedSprites[ cacheId ] = subCanvas;
|
||
|
}
|
||
|
|
||
|
// Draw the base that stays static
|
||
|
GameController.ctx.fillStyle = '#444';
|
||
|
GameController.ctx.beginPath();
|
||
|
GameController.ctx.arc( this.x, this.y, this.radius * 0.7, 0 , 2 * Math.PI, false );
|
||
|
GameController.ctx.fill();
|
||
|
GameController.ctx.stroke();
|
||
|
|
||
|
GameController.ctx.drawImage( cached, this.currentX - this.radius, this.currentY - this.radius );
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
return TouchableJoystick;
|
||
|
} )( TouchableArea );
|
||
|
|
||
|
|
||
|
var TouchableCircle = ( function( __super ) {
|
||
|
__extends( TouchableCircle, __super );
|
||
|
|
||
|
function TouchableCircle( options )
|
||
|
{
|
||
|
for( var i in options )
|
||
|
{
|
||
|
if( i == 'x' )
|
||
|
this[i] = GameController.getPixels( options[i], 'x' );
|
||
|
else if( i == 'x' || i == 'radius' )
|
||
|
this[i] = GameController.getPixels( options[i], 'y' );
|
||
|
else
|
||
|
this[i] = options[i];
|
||
|
}
|
||
|
|
||
|
this.draw();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* No touch for this fella
|
||
|
*/
|
||
|
TouchableCircle.prototype.check = function( touchX, touchY ) {
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
TouchableCircle.prototype.draw = function() {
|
||
|
|
||
|
// STYLING FOR BUTTONS
|
||
|
GameController.ctx.fillStyle = 'rgba( 0, 0, 0, 0.5 )';
|
||
|
|
||
|
// Actual joystick part that is being moved
|
||
|
GameController.ctx.beginPath();
|
||
|
GameController.ctx.arc( this.x, this.y, this.radius, 0 , 2 * Math.PI, false );
|
||
|
GameController.ctx.fill();
|
||
|
|
||
|
};
|
||
|
|
||
|
return TouchableCircle;
|
||
|
} )( TouchableArea );
|
||
|
|
||
|
/**
|
||
|
* Shim for requestAnimationFrame
|
||
|
*/
|
||
|
( function() {
|
||
|
if (typeof module !== "undefined") return
|
||
|
var lastTime = 0;
|
||
|
var vendors = ['ms', 'moz', 'webkit', 'o'];
|
||
|
for( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x )
|
||
|
{
|
||
|
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
|
||
|
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|
||
|
|| window[vendors[x]+'CancelRequestAnimationFrame'];
|
||
|
}
|
||
|
|
||
|
if ( !window.requestAnimationFrame )
|
||
|
window.requestAnimationFrame = function( callback, element ) {
|
||
|
var currTime = new Date().getTime();
|
||
|
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
|
||
|
var id = window.setTimeout( function() { callback(currTime + timeToCall); },
|
||
|
timeToCall );
|
||
|
lastTime = currTime + timeToCall;
|
||
|
return id;
|
||
|
};
|
||
|
|
||
|
if ( !window.cancelAnimationFrame )
|
||
|
window.cancelAnimationFrame = function( id ) {
|
||
|
clearTimeout( id );
|
||
|
};
|
||
|
}() );
|
||
|
} )(typeof module !== "undefined" ? module.exports : window)
|