7 Star 6 Fork 0

Gitee 极速下载 / GameQuery

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/onaluf/gameQuery
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
jquery.gamequery.js 84.05 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852
/*
* gameQuery rev. $Revision$
*
* Copyright (c) 2012 Selim Arsever (http://gamequeryjs.com)
* licensed under the MIT-License
*/
// This allows use of the convenient $ notation in a plugin
(function($) {
// CSS Feature detection from: Craig Buckler (http://www.sitepoint.com/detect-css3-property-browser-support/)
var cssTransform = false;
var detectElement = document.createElement("detect"),
CSSprefix = "Webkit,Moz,O,ms,Khtml".split(","),
All = ("transform," + CSSprefix.join("Transform,") + "Transform").split(",");
for (var i = 0, l = All.length; i < l; i++) {
if (detectElement.style[All[i]] === "") {
cssTransform = All[i];
}
}
// This prefix can be use whenever needed to namespace CSS classes, .data() inputs aso.
var gQprefix = "gQ_";
// Those are the possible states of the engine
var STATE_NEW = 0; // Not yet started for the first time
var STATE_RUNNING = 1; // Started and running
var STATE_PAUSED = 2; // Paused
/**
* Utility function that returns the radius for a geometry.
*
* @param {object} elem DOM element
* @param {float} angle the angle in degrees
* @return {object} .x, .y radius of geometry
*/
var proj = function (elem, angle) {
switch (elem.geometry){
case $.gameQuery.GEOMETRY_RECTANGLE :
var b = angle*Math.PI*2/360;
var Rx = Math.abs(Math.cos(b)*elem.width/2*elem.factor)+Math.abs(Math.sin(b)*elem.height/2*elem.factor);
var Ry = Math.abs(Math.cos(b)*elem.height/2*elem.factor)+Math.abs(Math.sin(b)*elem.width/2*elem.factor);
return {x: Rx, y: Ry};
}
};
/**
* Utility function that checks for collision between two elements.
*
* @param {object} elem1 DOM for the first element
* @param {float} offset1 offset of the first element
* @param {object} elem2 DOM for the second element
* @param {float} offset2 offset of the second element
* @return {boolean} if the two elements collide or not
*/
var collide = function(elem1, offset1, elem2, offset2) {
// test real collision (only for two rectangles...)
if((elem1.geometry == $.gameQuery.GEOMETRY_RECTANGLE && elem2.geometry == $.gameQuery.GEOMETRY_RECTANGLE)){
var dx = offset2.x + elem2.boundingCircle.x - elem1.boundingCircle.x - offset1.x;
var dy = offset2.y + elem2.boundingCircle.y - elem1.boundingCircle.y - offset1.y;
var a = Math.atan(dy/dx);
var Dx = Math.abs(Math.cos(a-elem1.angle*Math.PI*2/360)/Math.cos(a)*dx);
var Dy = Math.abs(Math.sin(a-elem1.angle*Math.PI*2/360)/Math.sin(a)*dy);
var R = proj(elem2, elem2.angle-elem1.angle);
if((elem1.width/2*elem1.factor+R.x <= Dx) || (elem1.height/2*elem1.factor+R.y <= Dy)) {
return false;
} else {
var Dx = Math.abs(Math.cos(a-elem2.angle*Math.PI*2/360)/Math.cos(a)*-dx);
var Dy = Math.abs(Math.sin(a-elem2.angle*Math.PI*2/360)/Math.sin(a)*-dy);
var R = proj(elem1, elem1.angle-elem2.angle);
if((elem2.width/2*elem2.factor+R.x <= Dx) || (elem2.height/2*elem2.factor+R.y <= Dy)) {
return false;
} else {
return true;
}
}
} else {
return false;
}
};
/**
* Utility function computes the offset relative to the playground of a gameQuery element without using DOM's position.
* This should be faster than the standand .offset() function.
*
* Warning: No non-gameQuery elements should be present between this element and the playground div!
*
* @param {jQuery} element the jQuery wrapped DOM element representing the gameQuery object.
* @return {object} an object {x:, y: } containing the x and y offset. (Not top and left like jQuery's .offset())
*/
var offset = function(element) {
// Get the tileSet offset (relative to the playground)
var offset = {x: 0, y: 0};
var parent = element[0];
while(parent !== $.gameQuery.playground[0] && parent.gameQuery !== undefined) {
offset.x += parent.gameQuery.posx;
offset.y += parent.gameQuery.posy;
parent = parent.parentNode;
}
return offset
}
/**
* Utility function computes the index range of the tiles for a tilemap.
*
* @param {jQuery} element the jQuery wrapped DOM element representing the tilemap.
* @param {object} offset an object holding the x and y offset of the tilemap, this is optional and will be computed if not provided.
* @return {object} an object {firstColumn: , lastColumn: , fristRow: , lastRow: }
*/
var visibleTilemapIndexes = function (element, elementOffset) {
if (elementOffset === undefined) {
elementOffset = offset(element);
}
var gameQuery = element[0].gameQuery;
// Activate the visible tiles
return {
firstRow: Math.max(Math.min(Math.floor(-elementOffset.y/gameQuery.height), gameQuery.sizey), 0),
lastRow: Math.max(Math.min(Math.ceil(($.gameQuery.playground[0].height-elementOffset.y)/gameQuery.height), gameQuery.sizey), 0),
firstColumn: Math.max(Math.min(Math.floor(-elementOffset.x/gameQuery.width), gameQuery.sizex), 0),
lastColumn: Math.max(Math.min(Math.ceil(($.gameQuery.playground[0].width-elementOffset.x)/gameQuery.width), gameQuery.sizex), 0)
}
}
/**
* Utility function thast computes the buffered zone of a tilemap
*
* @param {jQuery} element the jQuery wrapped DOM element representing the tilemap.
* @param {object} visible an object describing the visible zone
* @return {object} an object {firstColumn: , lastColumn: , fristRow: , lastRow: }
*/
var bufferedTilemapIndexes = function (element, visible) {
var gameQuery = element[0].gameQuery;
return {
firstRow: Math.max(Math.min(visible.firstRow - gameQuery.buffer, gameQuery.sizey), 0),
lastRow: Math.max(Math.min(visible.lastRow + gameQuery.buffer, gameQuery.sizey), 0),
firstColumn: Math.max(Math.min(visible.firstColumn - gameQuery.buffer, gameQuery.sizex), 0),
lastColumn: Math.max(Math.min(visible.lastColumn + gameQuery.buffer, gameQuery.sizex), 0)
}
}
/**
* Utility function that creates a tile in the given tilemap
*
* @param {jQuery} tileSet the jQuery element representing the tile map
* @param {integer} row the row index of the tile in the tile map
* @param {integer} column the column index of the tile in the tile map
*/
var addTile = function(tileSet, row, column) {
var gameQuery = tileSet[0].gameQuery;
var name = tileSet.attr("id");
var tileDescription;
if(gameQuery.func) {
tileDescription = gameQuery.tiles(row,column)-1;
} else {
tileDescription = gameQuery.tiles[row][column]-1;
}
var animation;
if(gameQuery.multi) {
animation = gameQuery.animations;
} else {
animation = gameQuery.animations[tileDescription];
}
if(tileDescription >= 0){
tileSet.addSprite($.gameQuery.tileIdPrefix+name+"_"+row+"_"+column,
{width: gameQuery.width,
height: gameQuery.height,
posx: column*gameQuery.width,
posy: row*gameQuery.height,
animation: animation});
var newTile = tileSet.find("#"+$.gameQuery.tileIdPrefix+name+"_"+row+"_"+column);
if (gameQuery.multi) {
newTile.setAnimation(tileDescription);
} else {
newTile[0].gameQuery.animationNumber = tileDescription;
}
newTile.removeClass($.gameQuery.spriteCssClass);
newTile.addClass($.gameQuery.tileCssClass);
newTile.addClass($.gameQuery.tileTypePrefix+tileDescription);
}
}
// Define the list of object/function accessible through $.
$.extend({ gameQuery: {
/**
* This is the Animation Object
*/
Animation: function (options, imediateCallback) {
// private default values
var defaults = {
imageURL: "",
numberOfFrame: 1,
delta: 0,
rate: 30,
type: 0,
distance: 0,
offsetx: 0,
offsety: 0
};
// options extends defaults
options = $.extend(defaults, options);
// "public" attributes:
this.imageURL = options.imageURL; // The url of the image to be used as an animation or sprite
this.numberOfFrame = options.numberOfFrame; // The number of frames to be displayed when playing the animation
this.delta = options.delta; // The distance in pixels between two frames
this.rate = options.rate; // The rate at which the frames change in miliseconds
this.type = options.type; // The type of the animation.This is a bitwise OR of the properties.
this.distance = options.distance; // The distance in pixels between two animations
this.offsetx = options.offsetx; // The x coordinate where the first sprite begins
this.offsety = options.offsety; // The y coordinate where the first sprite begins
// Whenever a new animation is created we add it to the ResourceManager animation list
$.gameQuery.resourceManager.addAnimation(this, imediateCallback);
return true;
},
/**
* "constants" for the different types of an animation
*/
ANIMATION_VERTICAL: 1, // Generated by a vertical offset of the background
ANIMATION_HORIZONTAL: 2, // Generated by a horizontal offset of the background
ANIMATION_ONCE: 4, // Played only once (else looping indefinitely)
ANIMATION_CALLBACK: 8, // A callback is exectued at the end of a cycle
ANIMATION_MULTI: 16, // The image file contains many animations
ANIMATION_PINGPONG: 32, // At the last frame of the animation it reverses (if used in conjunction with ONCE it will have no effect)
// "constants" for the different type of geometry for a sprite
GEOMETRY_RECTANGLE: 1,
GEOMETRY_DISC: 2,
// basic values
refreshRate: 30,
/**
* An object to manage resource loading
*/
resourceManager: {
animations: [], // List of animations / images used in the game
sounds: [], // List of sounds used in the game
callbacks: [], // List of the functions called at each refresh
loadedAnimationsPointer: 0, // Keep track of the last loaded animation
loadedSoundsPointer: 0, // Keep track of the last loaded sound
/**
* Load resources before starting the game.
*/
preload: function() {
// Start loading the images
for (var i = this.animations.length-1 ; i >= this.loadedAnimationsPointer; i --){
this.animations[i].domO = new Image();
this.animations[i].domO.src = this.animations[i].imageURL;
}
// Start loading the sounds
for (var i = this.sounds.length-1 ; i >= this.loadedSoundsPointer; i --){
this.sounds[i].load();
}
$.gameQuery.resourceManager.waitForResources();
},
/**
* Wait for all the resources called for in preload() to finish loading.
*/
waitForResources: function() {
// Check the images
var imageCount = 0;
for(var i=this.loadedAnimationsPointer; i < this.animations.length; i++){
if(this.animations[i].domO.complete){
imageCount++;
}
}
// Check the sounds
var soundCount = 0;
for(var i=this.loadedSoundsPointer; i < this.sounds.length; i++){
var temp = this.sounds[i].ready();
if(temp){
soundCount++;
}
}
// Call the load callback with the current progress
if($.gameQuery.resourceManager.loadCallback){
var percent = (imageCount + soundCount)/(this.animations.length + this.sounds.length - this.loadedAnimationsPointer - this.loadedSoundsPointer)*100;
$.gameQuery.resourceManager.loadCallback(percent);
}
if(imageCount + soundCount < (this.animations.length + this.sounds.length - this.loadedAnimationsPointer - this.loadedSoundsPointer)){
imgWait=setTimeout(function () {
$.gameQuery.resourceManager.waitForResources();
}, 100);
} else {
this.loadedAnimationsPointer = this.animations.length;
this.loadedSoundsPointer = this.sounds.length;
// All the resources are loaded! We can now associate the animation's images to their corresponding sprites
$.gameQuery.scenegraph.children().each(function(){
// recursive call on the children:
$(this).children().each(arguments.callee);
// add the image as a background
if(this.gameQuery && this.gameQuery.animation){
$(this).css("background-image", "url("+this.gameQuery.animation.imageURL+")");
// we set the correct kind of repeat
if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
$(this).css("background-repeat", "repeat-x");
} else if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
$(this).css("background-repeat", "repeat-y");
} else {
$(this).css("background-repeat", "no-repeat");
}
}
});
// Launch the refresh loop
if($.gameQuery.state === STATE_NEW){
setInterval(function () {
$.gameQuery.resourceManager.refresh();
},($.gameQuery.refreshRate));
}
$.gameQuery.state = STATE_RUNNING;
if($.gameQuery.startCallback){
$.gameQuery.startCallback();
}
// Make the scenegraph visible
$.gameQuery.scenegraph.css("visibility","visible");
}
},
/**
* This function refresh a unique sprite here 'this' represent a dom object
*/
refreshSprite: function() {
// Check if 'this' is a gameQuery element
if(this.gameQuery != undefined){
var gameQuery = this.gameQuery;
// Does 'this' has an animation ?
if(gameQuery.animation){
// Do we have anything to do?
if ( (gameQuery.idleCounter == gameQuery.animation.rate-1) && gameQuery.playing){
// Does 'this' loops?
if(gameQuery.animation.type & $.gameQuery.ANIMATION_ONCE){
if(gameQuery.currentFrame < gameQuery.animation.numberOfFrame-1){
gameQuery.currentFrame += gameQuery.frameIncrement;
} else if(gameQuery.currentFrame == gameQuery.animation.numberOfFrame-1) {
// Does 'this' has a callback ?
if(gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){
if($.isFunction(gameQuery.callback)){
gameQuery.callback(this);
gameQuery.callback = false;
}
}
}
} else {
if(gameQuery.animation.type & $.gameQuery.ANIMATION_PINGPONG){
if(gameQuery.currentFrame == gameQuery.animation.numberOfFrame-1 && gameQuery.frameIncrement == 1) {
gameQuery.frameIncrement = -1;
} else if (gameQuery.currentFrame == 0 && gameQuery.frameIncrement == -1) {
gameQuery.frameIncrement = 1;
}
}
gameQuery.currentFrame = (gameQuery.currentFrame+gameQuery.frameIncrement)%gameQuery.animation.numberOfFrame;
if(gameQuery.currentFrame == 0){
// Does 'this' has a callback ?
if(gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){
if($.isFunction(gameQuery.callback)){
gameQuery.callback(this);
}
}
}
}
// Update the background
if((gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animation.numberOfFrame > 1)){
if(gameQuery.multi){
$(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.multi)+"px "+(-gameQuery.animation.offsety-gameQuery.animation.delta*gameQuery.currentFrame)+"px");
} else {
$(this).css("background-position",""+(-gameQuery.animation.offsetx)+"px "+(-gameQuery.animation.offsety-gameQuery.animation.delta*gameQuery.currentFrame)+"px");
}
} else if((gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animation.numberOfFrame > 1)) {
if(gameQuery.multi){
$(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.animation.delta*gameQuery.currentFrame)+"px "+(-gameQuery.animation.offsety-gameQuery.multi)+"px");
} else {
$(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.animation.delta*gameQuery.currentFrame)+"px "+(-gameQuery.animation.offsety)+"px");
}
}
}
gameQuery.idleCounter = (gameQuery.idleCounter+1)%gameQuery.animation.rate;
}
}
return true;
},
/**
* This function refresh a unique tile-map, here 'this' represent a dom object
*/
refreshTilemap: function() {
// Check if 'this' is a gameQuery element
if(this.gameQuery != undefined){
var gameQuery = this.gameQuery;
if($.isArray(gameQuery.frameTracker)){
for(var i=0; i<gameQuery.frameTracker.length; i++){
// Do we have anything to do?
if(gameQuery.idleCounter[i] == gameQuery.animations[i].rate-1){
// Does 'this' loops?
if(gameQuery.animations[i].type & $.gameQuery.ANIMATION_ONCE){
if(gameQuery.frameTracker[i] < gameQuery.animations[i].numberOfFrame-1){
gameQuery.frameTracker[i] += gameQuery.frameIncrement[i];
}
} else {
if(gameQuery.animations[i].type & $.gameQuery.ANIMATION_PINGPONG){
if(gameQuery.frameTracker[i] == gameQuery.animations[i].numberOfFrame-1 && gameQuery.frameIncrement[i] == 1) {
gameQuery.frameIncrement[i] = -1;
} else if (gameQuery.frameTracker[i] == 0 && gameQuery.frameIncrement[i] == -1) {
gameQuery.frameIncrement[i] = 1;
}
}
gameQuery.frameTracker[i] = (gameQuery.frameTracker[i]+gameQuery.frameIncrement[i])%gameQuery.animations[i].numberOfFrame;
}
}
gameQuery.idleCounter[i] = (gameQuery.idleCounter[i]+1)%gameQuery.animations[i].rate;
}
} else {
// Do we have anything to do?
if(gameQuery.idleCounter == gameQuery.animations.rate-1){
// Does 'this' loops?
if(gameQuery.animations.type & $.gameQuery.ANIMATION_ONCE){
if(gameQuery.frameTracker < gameQuery.animations.numberOfFrame-1){
gameQuery.frameTracker += gameQuery.frameIncrement;
}
} else {
if(gameQuery.animations.type & $.gameQuery.ANIMATION_PINGPONG){
if(gameQuery.frameTracker == gameQuery.animations.numberOfFrame-1 && gameQuery.frameIncrement == 1) {
gameQuery.frameIncrement = -1;
} else if (gameQuery.frameTracker == 0 && gameQuery.frameIncrement == -1) {
gameQuery.frameIncrement = 1;
}
}
gameQuery.frameTracker = (gameQuery.frameTracker+gameQuery.frameIncrement)%gameQuery.animations.numberOfFrame;
}
}
gameQuery.idleCounter = (gameQuery.idleCounter+1)%gameQuery.animations.rate;
}
// Update the background of all active tiles
$(this).find("."+$.gameQuery.tileCssClass).each(function(){
if($.isArray(gameQuery.frameTracker)){
var animationNumber = this.gameQuery.animationNumber
if((gameQuery.animations[animationNumber].type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animations[animationNumber].numberOfFrame > 1)){
$(this).css("background-position",""+(-gameQuery.animations[animationNumber].offsetx)+"px "+(-gameQuery.animations[animationNumber].offsety-gameQuery.animations[animationNumber].delta*gameQuery.frameTracker[animationNumber])+"px");
} else if((gameQuery.animations[animationNumber].type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animations[animationNumber].numberOfFrame > 1)) {
$(this).css("background-position",""+(-gameQuery.animations[animationNumber].offsetx-gameQuery.animations[animationNumber].delta*gameQuery.frameTracker[animationNumber])+"px "+(-gameQuery.animations[animationNumber].offsety)+"px");
}
} else {
if((gameQuery.animations.type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animations.numberOfFrame > 1)){
$(this).css("background-position",""+(-gameQuery.animations.offsetx-this.gameQuery.multi)+"px "+(-gameQuery.animations.offsety-gameQuery.animations.delta*gameQuery.frameTracker)+"px");
} else if((gameQuery.animations.type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animations.numberOfFrame > 1)) {
$(this).css("background-position",""+(-gameQuery.animations.offsetx-gameQuery.animations.delta*gameQuery.frameTracker)+"px "+(-gameQuery.animations.offsety-this.gameQuery.multi)+"px");
}
}
});
}
return true;
},
/**
* Called periodically to refresh the state of the game.
*/
refresh: function() {
if($.gameQuery.state === STATE_RUNNING) {
$.gameQuery.playground.find("."+$.gameQuery.spriteCssClass).each(this.refreshSprite);
$.gameQuery.playground.find("."+$.gameQuery.tilemapCssClass).each(this.refreshTilemap);
var deadCallback= new Array();
for (var i = this.callbacks.length-1; i >= 0; i--){
if(this.callbacks[i].idleCounter == this.callbacks[i].rate-1){
var returnedValue = this.callbacks[i].fn();
if(typeof returnedValue == 'boolean'){
// If we have a boolean: 'true' means 'no more execution', 'false' means 'keep on executing'
if(returnedValue){
deadCallback.push(i);
}
} else if(typeof returnedValue == 'number') {
// If we have a number it re-defines the time to the next call
this.callbacks[i].rate = Math.round(returnedValue/$.gameQuery.refreshRate);
this.callbacks[i].idleCounter = 0;
}
}
this.callbacks[i].idleCounter = (this.callbacks[i].idleCounter+1)%this.callbacks[i].rate;
}
for(var i = deadCallback.length-1; i >= 0; i--){
this.callbacks.splice(deadCallback[i],1);
}
}
},
/**
* Add an animation to the resource Manager
*/
addAnimation: function(animation, callback) {
if($.inArray(animation,this.animations)<0){
//normalize the animation rate:
animation.rate = Math.round(animation.rate/$.gameQuery.refreshRate);
if(animation.rate==0){
animation.rate = 1;
}
this.animations.push(animation);
switch ($.gameQuery.state){
case STATE_NEW:
case STATE_PAUSED:
// Nothing to do for now
break;
case STATE_RUNNING:
// immediatly load the animation and call the callback if any
this.animations[this.loadedAnimationsPointer].domO = new Image();
this.animations[this.loadedAnimationsPointer].domO.src = animation.imageURL;
if (callback !== undefined){
this.animations[this.loadedAnimationsPointer].domO.onload = callback;
}
this.loadedAnimationsPointer++;
break;
}
}
},
/**
* Add a sound to the resource Manager
*/
addSound: function(sound, callback){
if($.inArray(sound,this.sounds)<0){
this.sounds.push(sound);
switch ($.gameQuery.state){
case STATE_NEW:
case STATE_PAUSED:
// Nothing to do for now
break;
case STATE_RUNNING:
// immediatly load the sound and call the callback if any
sound.load();
// TODO callback....
this.loadedSoundsPointer++;
break;
}
}
},
/**
* Register a callback
*
* @param {function} fn the callback
* @param {integer} rate the rate in ms at which the callback should be called (should be a multiple of the playground rate or will be rounded)
*/
registerCallback: function(fn, rate){
rate = Math.round(rate/$.gameQuery.refreshRate);
if(rate==0){
rate = 1;
}
this.callbacks.push({fn: fn, rate: rate, idleCounter: 0});
},
/**
* Clear the animations and sounds
*/
clear: function(callbacksToo){
this.animations = [];
this.loadedAnimationsPointer = 0;
this.sounds = [];
this.loadedSoundsPointer = 0;
if(callbacksToo) {
this.callbacks = [];
}
}
},
/**
* This is a single place to update the underlying data of sprites/groups/tiles after a position or dimesion modification.
*/
update: function(descriptor, transformation) {
// Did we really receive a descriptor or a jQuery object instead?
if(!$.isPlainObject(descriptor)){
// Then we must get real descriptor
if(descriptor.length > 0){
var gameQuery = descriptor[0].gameQuery;
} else {
var gameQuery = descriptor.gameQuery;
}
} else {
var gameQuery = descriptor;
}
// If we couldn't find one we return
if(!gameQuery) return;
if(gameQuery.tileSet === true){
// We have a tilemap
var visible = visibleTilemapIndexes(descriptor);
var buffered = gameQuery.buffered;
// Test what kind of transformation we have and react accordingly
for(property in transformation){
switch(property){
case "x":
if(visible.lastColumn > buffered.lastColumn) {
// Detach the tilemap
var parent = descriptor[0].parentNode;
var tilemap = descriptor.detach();
var newBuffered = bufferedTilemapIndexes(descriptor, visible);
for(var i = gameQuery.buffered.firstRow; i < gameQuery.buffered.lastRow; i++){
// Remove the newly invisible tiles
for(var j = gameQuery.buffered.firstColumn; j < Math.min(newBuffered.firstColumn, gameQuery.buffered.lastColumn); j++) {
tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
}
// And add the newly visible tiles
for(var j = Math.max(gameQuery.buffered.lastColumn,newBuffered.firstColumn); j < newBuffered.lastColumn ; j++) {
addTile(tilemap,i,j);
}
}
gameQuery.buffered.firstColumn = newBuffered.firstColumn;
gameQuery.buffered.lastColumn = newBuffered.lastColumn;
// Attach the tilemap back
tilemap.appendTo(parent);
}
if(visible.firstColumn < buffered.firstColumn) {
// Detach the tilemap
var parent = descriptor[0].parentNode;
var tilemap = descriptor.detach();
var newBuffered = bufferedTilemapIndexes(descriptor, visible);
for(var i = gameQuery.buffered.firstRow; i < gameQuery.buffered.lastRow; i++){
// Remove the newly invisible tiles
for(var j = Math.max(newBuffered.lastColumn,gameQuery.buffered.firstColumn); j < gameQuery.buffered.lastColumn ; j++) {
tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
}
// And add the newly visible tiles
for(var j = newBuffered.firstColumn; j < Math.min(gameQuery.buffered.firstColumn,newBuffered.lastColumn); j++) {
addTile(tilemap,i,j);
}
}
gameQuery.buffered.firstColumn = newBuffered.firstColumn;
gameQuery.buffered.lastColumn = newBuffered.lastColumn;
// Attach the tilemap back
tilemap.appendTo(parent);
}
break;
case "y":
if(visible.lastRow > buffered.lastRow) {
// Detach the tilemap
var parent = descriptor[0].parentNode;
var tilemap = descriptor.detach();
var newBuffered = bufferedTilemapIndexes(descriptor, visible);
for(var j = gameQuery.buffered.firstColumn; j < gameQuery.buffered.lastColumn ; j++) {
// Remove the newly invisible tiles
for(var i = gameQuery.buffered.firstRow; i < Math.min(newBuffered.firstRow, gameQuery.buffered.lastRow); i++){
tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
}
// And add the newly visible tiles
for(var i = Math.max(gameQuery.buffered.lastRow, newBuffered.firstRow); i < newBuffered.lastRow; i++){
addTile(tilemap,i,j);
}
}
gameQuery.buffered.firstRow = newBuffered.firstRow;
gameQuery.buffered.lastRow = newBuffered.lastRow;
// Attach the tilemap back
tilemap.appendTo(parent);
}
if(visible.firstRow < buffered.firstRow) {
// Detach the tilemap
var parent = descriptor[0].parentNode;
var tilemap = descriptor.detach();
var newBuffered = bufferedTilemapIndexes(descriptor, visible);
for(var j = gameQuery.buffered.firstColumn; j < gameQuery.buffered.lastColumn ; j++) {
// Remove the newly invisible tiles
for(var i = Math.max(newBuffered.lastRow, gameQuery.buffered.firstRow); i < gameQuery.buffered.lastRow; i++){
tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
}
// And add the newly visible tiles
for(var i = newBuffered.firstRow; i < Math.min(gameQuery.buffered.firstRow, newBuffered.lastRow); i++){
addTile(tilemap,i,j);
}
}
gameQuery.buffered.firstRow = newBuffered.firstRow;
gameQuery.buffered.lastRow = newBuffered.lastRow;
// Attach the tilemap back
tilemap.appendTo(parent);
}
break;
case "angle":
//TODO
break;
case "factor":
//TODO
break;
}
}
} else {
var refreshBoundingCircle = $.gameQuery.playground && !$.gameQuery.playground.disableCollision;
// Update the descriptor
for(property in transformation){
switch(property){
case "x":
if(refreshBoundingCircle){
gameQuery.boundingCircle.x = gameQuery.posx+gameQuery.width/2;
}
break;
case "y":
if(refreshBoundingCircle){
gameQuery.boundingCircle.y = gameQuery.posy+gameQuery.height/2;
}
break;
case "w":
case "h":
gameQuery.boundingCircle.originalRadius = Math.sqrt(Math.pow(gameQuery.width,2) + Math.pow(gameQuery.height,2))/2
gameQuery.boundingCircle.radius = gameQuery.factor*gameQuery.boundingCircle.originalRadius;
break;
case "angle": //(in degrees)
gameQuery.angle = parseFloat(transformation.angle);
break;
case "factor":
gameQuery.factor = parseFloat(transformation.factor);
if(refreshBoundingCircle){
gameQuery.boundingCircle.radius = gameQuery.factor*gameQuery.boundingCircle.originalRadius;
}
break;
}
}
}
},
// State of the engine
state: STATE_NEW,
// CSS classes used to mark game element
spriteCssClass: gQprefix + "sprite",
groupCssClass: gQprefix + "group",
tilemapCssClass: gQprefix + "tilemap",
tileCssClass: gQprefix + "tile",
// Prefix for CSS Ids or Classes
tileTypePrefix: gQprefix + "tileType_",
tileIdPrefix: gQprefix + "tile_"
},
/**
* Mute (or unmute) all the sounds.
*/
muteSound: function(muted){
for (var i = $.gameQuery.resourceManager.sounds.length-1 ; i >= 0; i --) {
$.gameQuery.resourceManager.sounds[i].muted(muted);
}
},
/**
* Accessor for the currently defined playground as a jQuery object
*/
playground: function() {
return $.gameQuery.playground
},
/**
* Define a callback called during the loading of the game's resources.
*
* The function will recieve as unique parameter
* a number representing the progess percentage.
*/
loadCallback: function(callback){
$.gameQuery.resourceManager.loadCallback = callback;
}
}); // end of the extensio of $
// fragments used to create DOM element
var spriteFragment = $("<div class='"+$.gameQuery.spriteCssClass+"' style='position: absolute; display: block; overflow: hidden' />");
var groupFragment = $("<div class='"+$.gameQuery.groupCssClass+"' style='position: absolute; display: block; overflow: hidden' />");
var tilemapFragment = $("<div class='"+$.gameQuery.tilemapCssClass+"' style='position: absolute; display: block; overflow: hidden;' />");
// Define the list of object/function accessible through $("selector").
$.fn.extend({
/**
* Defines the currently selected div to which contains the game and initialize it.
*
* This is a non-destructive call
*/
playground: function(options) {
if(this.length == 1){
if(this[0] == document){
// Old usage detected, this is not supported anymore
throw "Old playground usage, use $.playground() to retreive the playground and $('mydiv').playground(options) to set the div!";
}
options = $.extend({
height: 320,
width: 480,
refreshRate: 30,
position: "absolute",
keyTracker: false,
mouseTracker: false,
disableCollision: false
}, options);
// We save the playground node and set some variable for this node:
$.gameQuery.playground = this;
$.gameQuery.refreshRate = options.refreshRate;
$.gameQuery.playground[0].height = options.height;
$.gameQuery.playground[0].width = options.width;
// We initialize the display of the div
$.gameQuery.playground.css({
position: options.position,
display: "block",
overflow: "hidden",
height: options.height+"px",
width: options.width+"px"
})
.append("<div id='"+gQprefix+"scenegraph' style='visibility: hidden'/>");
$.gameQuery.scenegraph = $("#"+gQprefix+"scenegraph");
// Add the keyTracker to the gameQuery object:
$.gameQuery.keyTracker = {};
// We only enable the real tracking if the users wants it
if(options.keyTracker){
$(document).keydown(function(event){
$.gameQuery.keyTracker[event.keyCode] = true;
});
$(document).keyup(function(event){
$.gameQuery.keyTracker[event.keyCode] = false;
});
}
// Add the mouseTracker to the gameQuery object:
$.gameQuery.mouseTracker = {
x: 0,
y: 0};
// We only enable the real tracking if the users wants it
var scenegraphOffset = $.gameQuery.playground.offset();
if(options.mouseTracker){
$($.gameQuery.playground).mousemove(function(event){
$.gameQuery.mouseTracker.x = event.pageX - scenegraphOffset.left;
$.gameQuery.mouseTracker.y = event.pageY - scenegraphOffset.top;
});
$(document).mousedown(function(event){
$.gameQuery.mouseTracker[event.which] = true;
});
$(document).mouseup(function(event){
$.gameQuery.mouseTracker[event.which] = false;
});
}
}
return this;
},
/**
* Starts the game.
*
* Resources from the resource manager are preloaded if necesary
* Works only for the playground node.
*
* This is a non-destructive call
*/
startGame: function(callback) {
$.gameQuery.startCallback = callback;
$.gameQuery.resourceManager.preload();
return this;
},
/**
* TODO
*/
pauseGame: function() {
$.gameQuery.state = STATE_PAUSED;
$.gameQuery.scenegraph.css("visibility","hidden");
return this;
},
/**
* Resume the game if it was paused and call the callback passed in argument once the newly added ressources are loaded.
*/
resumeGame: function(callback) {
if($.gameQuery.state === STATE_PAUSED){
$.gameQuery.startCallback = callback;
$.gameQuery.resourceManager.preload();
}
return this;
},
/**
* Removes all the sprites, groups and tilemaps present in the scenegraph
*/
clearScenegraph: function() {
$.gameQuery.scenegraph.empty()
return this;
},
/**
* Removes all the sprites, groups and tilemaps present in the scenegraph as well as all loaded animations and sounds
*/
clearAll: function(callbackToo) {
$.gameQuery.scenegraph.empty();
$.gameQuery.resourceManager.clear(callbackToo)
return this;
},
/**
* Add a group to the scene graph. Works only on the scenegraph root or on another group
*
* This IS a destructive call and should be terminated with end()
* to go back one level up in the chaining
*/
addGroup: function(group, options) {
options = $.extend({
width: 32,
height: 32,
posx: 0,
posy: 0,
posz: 0,
posOffsetX: 0,
posOffsetY: 0,
overflow: "visible",
geometry: $.gameQuery.GEOMETRY_RECTANGLE,
angle: 0,
factor: 1,
factorh: 1,
factorv: 1
}, options);
var newGroupElement = groupFragment.clone().attr("id",group).css({
overflow: options.overflow,
height: options.height,
width: options.width
});
if(this == $.gameQuery.playground){
$.gameQuery.scenegraph.append(newGroupElement);
} else if ((this == $.gameQuery.scenegraph)||(this.hasClass($.gameQuery.groupCssClass))){
this.append(newGroupElement);
}
newGroupElement[0].gameQuery = options;
newGroupElement[0].gameQuery.boundingCircle = {x: options.posx + options.width/2,
y: options.posy + options.height/0,
originalRadius: Math.sqrt(Math.pow(options.width,2) + Math.pow(options.height,2))/2};
newGroupElement[0].gameQuery.boundingCircle.radius = newGroupElement[0].gameQuery.boundingCircle.originalRadius;
newGroupElement[0].gameQuery.group = true;
newGroupElement.transform();
return this.pushStack(newGroupElement);
},
/**
* Add a sprite to the current node. Works only on the playground or any of its sub-nodes
*
* This is a non-destructive call
*/
addSprite: function(sprite, options) {
options = $.extend({
width: 32,
height: 32,
posx: 0,
posy: 0,
posz: 0,
posOffsetX: 0,
posOffsetY: 0,
idleCounter: 0,
currentFrame: 0,
frameIncrement: 1,
geometry: $.gameQuery.GEOMETRY_RECTANGLE,
angle: 0,
factor: 1,
playing: true,
factorh: 1,
factorv: 1
}, options);
var newSpriteElem = spriteFragment.clone().attr("id",sprite).css({
height: options.height,
width: options.width,
backgroundPosition: ((options.animation)? -options.animation.offsetx : 0)+"px "+((options.animation)? -options.animation.offsety : 0)+"px"
});
if(this == $.gameQuery.playground){
$.gameQuery.scenegraph.append(newSpriteElem);
} else {
this.append(newSpriteElem);
}
// If the game has already started we want to add the animation's image as a background now
if(options.animation){
// The second test is a fix for default background (https://github.com/onaluf/gameQuery/issues/3)
if($.gameQuery.state === STATE_RUNNING && options.animation.imageURL !== ''){
newSpriteElem.css("background-image", "url("+options.animation.imageURL+")");
}
if(options.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
newSpriteElem.css("background-repeat", "repeat-x");
} else if(options.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
newSpriteElem.css("background-repeat", "repeat-y");
} else {
newSpriteElem.css("background-repeat", "no-repeat");
}
}
var spriteDOMObject = newSpriteElem[0];
if(spriteDOMObject != undefined){
spriteDOMObject.gameQuery = options;
// Compute bounding Circle
spriteDOMObject.gameQuery.boundingCircle = {x: options.posx + options.width/2,
y: options.posy + options.height/2,
originalRadius: Math.sqrt(Math.pow(options.width,2) + Math.pow(options.height,2))/2};
spriteDOMObject.gameQuery.boundingCircle.radius = spriteDOMObject.gameQuery.boundingCircle.originalRadius;
}
newSpriteElem.transform();
return this;
},
/**
* Add a Tile Map to the selected element.
*
* This is a non-destructive call. The added sprite is NOT selected after a call to this function!
*/
addTilemap: function(name, tileDescription, animationList, options){
options = $.extend({
width: 32,
height: 32,
sizex: 32,
sizey: 32,
posx: 0,
posy: 0,
posz: 0,
posOffsetX: 0,
posOffsetY: 0,
angle: 0,
factor: 1,
factorh: 1,
factorv: 1,
buffer: 1
}, options);
var tileSet = tilemapFragment.clone().attr("id",name).css({
height: options.height*options.sizey,
width: options.width*options.sizex
});
if(this == $.gameQuery.playground){
$.gameQuery.scenegraph.append(tileSet);
} else {
this.append(tileSet);
}
tileSet[0].gameQuery = options;
var gameQuery = tileSet[0].gameQuery;
gameQuery.tileSet = true;
gameQuery.tiles = tileDescription;
gameQuery.func = (typeof tileDescription === "function");
if($.isArray(animationList)){
var frameTracker = [];
var idleCounter = [];
var frameIncrement = [];
for(var i=0; i<animationList.length; i++){
frameTracker[i] = 0;
idleCounter[i] = 0;
frameIncrement[i] = 1;
}
gameQuery.frameTracker = frameTracker;
gameQuery.animations = animationList;
gameQuery.idleCounter = idleCounter;
gameQuery.frameIncrement = frameIncrement;
gameQuery.multi = false;
} else {
gameQuery.frameTracker = 0;
gameQuery.frameIncrement = 1;
gameQuery.animations = animationList;
gameQuery.idleCounter = 0;
gameQuery.multi = true;
}
// Get the tileSet offset (relative to the playground)
var visible = visibleTilemapIndexes(tileSet);
var buffered = bufferedTilemapIndexes(tileSet, visible);
gameQuery.buffered = buffered;
// For many simple animation
for(var i = buffered.firstRow; i < buffered.lastRow; i++){
for(var j = buffered.firstColumn; j < buffered.lastColumn ; j++) {
addTile(tileSet, i, j);
}
}
tileSet.transform()
return this.pushStack(tileSet);
},
/**
* This function imports a JSON file generated by Tiled (http://www.mapeditor.org/).
* All the created tilemaps will be directly under the currently selected element.
* Their name will be made of the provided prefix followed by a number starting at 0.
*
* Only layer of type "tilelayer" are supported for now. Only one single tileset
* per layer is supported.
*
* After the call to this function the second argument will hold two new arrays:
* - tiles: an arrays of tilemaps wraped in jQuery.
* - animations: an arrays of animations
*
* This is a non-destructive call
*/
importTilemaps: function(url, prefix, generatedElements){
var animations = [];
var tilemaps = [];
var that = this;
var tilemapJsonLoaded = function(json){
var tilesetGID = [];
for (var i = 0; i < json.tilesets.length; i++) {
tilesetGID[i] = json.tilesets[i].firstgid;
}
var getTilesetIndex = function(index){
var i = 0;
while(index >= tilesetGID[i] && i < tilesetGID.length){
i++;
}
return i-1;
}
var height = json.height;
var width = json.width;
var tileHeight = json.tileheight;
var tileWidth = json.tilewidth;
var layers = json.layers;
var usedTiles = [];
var animationCounter = 0;
var tilemapArrays = [];
// Detect which animations we need to generate
// and convert the tiles array indexes to the new ones
for (var i=0; i < layers.length; i++){
if(layers[i].type === "tilelayer"){
var tilemapArray = new Array(height);
for (var j=0; j<height; j++){
tilemapArray[j] = new Array(width);
}
for (var j=0; j < layers[i].data.length; j++){
var tile = layers[i].data[j];
if(tile === 0){
tilemapArray[Math.floor(j / width)][j % width] = 0;
} else {
if(!usedTiles[tile]){
animationCounter++;
usedTiles[tile] = animationCounter;
animations.push(new $.gameQuery.Animation({
imageURL: json.tilesets[getTilesetIndex(tile)].image,
offsetx: ((tile-1) % Math.floor(json.tilesets[getTilesetIndex(tile)].imagewidth / tileWidth)) * tileWidth,
offsety: Math.floor((tile-1) / Math.floor(json.tilesets[getTilesetIndex(tile)].imagewidth / tileWidth)) * tileHeight
}));
}
tilemapArray[Math.floor(j / width)][j % width] = usedTiles[tile];
}
}
tilemapArrays.push(tilemapArray);
}
}
// adding the tilemaps
for (var i=0; i<tilemapArrays.length; i++){
tilemaps.push(that.addTilemap(
prefix+i,
tilemapArrays[i],
animations,
{
sizex: width,
sizey: height,
width: tileWidth,
height: tileHeight
}));
}
};
$.ajax({
url: url,
async: false,
dataType: 'json',
success: tilemapJsonLoaded
});
if(generatedElements !== undefined){
generatedElements.animations = animations;
generatedElements.tilemaps = tilemaps;
}
return this;
},
/**
* Stop the animation at the current frame
*
* This is a non-destructive call.
*/
pauseAnimation: function() {
this[0].gameQuery.playing = false;
return this;
},
/**
* Resume the animation (if paused)
*
* This is a non-destructive call.
*/
resumeAnimation: function() {
this[0].gameQuery.playing = true;
return this;
},
/**
* Changes the animation associated with a sprite.
*
* WARNING: no checks are made to ensure that the object is really a sprite
*
* This is a non-destructive call.
*/
setAnimation: function(animation, callback) {
var gameQuery = this[0].gameQuery;
if(typeof animation == "number"){
if(gameQuery.animation.type & $.gameQuery.ANIMATION_MULTI){
var distance = gameQuery.animation.distance * animation;
gameQuery.multi = distance;
gameQuery.frameIncrement = 1;
gameQuery.currentFrame = 0;
if(gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
this.css("background-position",""+(-distance-gameQuery.animation.offsetx)+"px "+(-gameQuery.animation.offsety)+"px");
} else if(gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
this.css("background-position",""+(-gameQuery.animation.offsetx)+"px "+(-distance-gameQuery.animation.offsety)+"px");
}
}
} else {
if(animation){
gameQuery.animation = animation;
gameQuery.currentFrame = 0;
gameQuery.frameIncrement = 1;
if (animation.imageURL !== '') {this.css("backgroundImage", "url('"+animation.imageURL+"')");}
this.css({"background-position": ""+(-animation.offsetx)+"px "+(-animation.offsety)+"px"});
if(gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
this.css("background-repeat", "repeat-x");
} else if(gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
this.css("background-repeat", "repeat-y");
} else {
this.css("background-repeat", "no-repeat");
}
} else {
this.css("background-image", "");
}
}
if(callback != undefined){
this[0].gameQuery.callback = callback;
}
return this;
},
/**
* Register a callback funnction
*
* This is a non-destructive call
*
* @param {Function} fn the callback function.
* @param {Number} rate time in milliseconds between calls.
*/
registerCallback: function(fn, rate) {
$.gameQuery.resourceManager.registerCallback(fn, rate);
return this;
},
/**
* Retrieve a list of objects in collision with the subject.
*
* If 'this' is a sprite or a group, the function will retrieve the list of sprites (not groups!!!) that touch it. For now all abject are considered to be boxes.
*
* This IS a destructive call and should be terminated with end() to go back one level up in the chaining.
*/
collision: function(arg1, arg2){
var filter, override;
if ($.isPlainObject(arg1)){
override = arg1;
} else if (typeof arg1 === "string") {
filter = arg1;
}
if ($.isPlainObject(arg2)){
override = arg2;
} else if (typeof arg2 === "string") {
filter = arg2;
}
var resultList = [];
// Retrieve 'this' offset by looking at the parents
var itsParent = this[0].parentNode, offsetX = 0, offsetY = 0;
while (itsParent != $.gameQuery.playground[0]){
if(itsParent.gameQuery){
offsetX += itsParent.gameQuery.posx;
offsetY += itsParent.gameQuery.posy;
}
itsParent = itsParent.parentNode;
}
// Retrieve the playground's absolute position and size information
var pgdGeom = {top: 0, left: 0, bottom: $.playground().height(), right: $.playground().width()};
// Retrieve the gameQuery object and correct it with the override
var gameQuery = jQuery.extend(true, {}, this[0].gameQuery);
// Retrieve the BoundingCircle and correct it with the override
var boundingCircle = jQuery.extend(true, {}, gameQuery.boundingCircle);
if(override && override.w){
gameQuery.width = override.w;
}
if(override && override.h){
gameQuery.height = override.h;
}
boundingCircle.originalRadius = Math.sqrt(Math.pow(gameQuery.width,2) + Math.pow(gameQuery.height,2))/2
boundingCircle.radius = gameQuery.factor*boundingCircle.originalRadius;
if(override && override.x){
boundingCircle.x = override.x + gameQuery.width/2.0;
}
if(override && override.y){
boundingCircle.y = override.y + gameQuery.height/2.0;
}
gameQuery.boundingCircle = boundingCircle;
// Is 'this' inside the playground ?
if( (gameQuery.boundingCircle.y + gameQuery.boundingCircle.radius + offsetY < pgdGeom.top) ||
(gameQuery.boundingCircle.x + gameQuery.boundingCircle.radius + offsetX < pgdGeom.left) ||
(gameQuery.boundingCircle.y - gameQuery.boundingCircle.radius + offsetY > pgdGeom.bottom) ||
(gameQuery.boundingCircle.x - gameQuery.boundingCircle.radius + offsetX > pgdGeom.right)){
return this.pushStack(new $([]));
}
if(this !== $.gameQuery.playground){
// We must find all the elements that touche 'this'
var elementsToCheck = new Array();
elementsToCheck.push($.gameQuery.scenegraph.children(filter).get());
elementsToCheck[0].offsetX = 0;
elementsToCheck[0].offsetY = 0;
for(var i = 0, len = elementsToCheck.length; i < len; i++) {
var subLen = elementsToCheck[i].length;
while(subLen--){
var elementToCheck = elementsToCheck[i][subLen];
// Is it a gameQuery generated element?
if(elementToCheck.gameQuery){
// We don't want to check groups
if(!elementToCheck.gameQuery.group && !elementToCheck.gameQuery.tileSet){
// Does it touche the selection?
if(this[0]!=elementToCheck){
// Check bounding circle collision
var distance = Math.sqrt(Math.pow(offsetY + gameQuery.boundingCircle.y - elementsToCheck[i].offsetY - elementToCheck.gameQuery.boundingCircle.y, 2) + Math.pow(offsetX + gameQuery.boundingCircle.x - elementsToCheck[i].offsetX - elementToCheck.gameQuery.boundingCircle.x, 2));
if(distance - gameQuery.boundingCircle.radius - elementToCheck.gameQuery.boundingCircle.radius <= 0){
// Check real collision
if(collide(gameQuery, {x: offsetX, y: offsetY}, elementToCheck.gameQuery, {x: elementsToCheck[i].offsetX, y: elementsToCheck[i].offsetY})) {
// Add to the result list if collision detected
resultList.push(elementsToCheck[i][subLen]);
}
}
}
}
// Add the children nodes to the list
var eleChildren = $(elementToCheck).children(filter);
if(eleChildren.length){
elementsToCheck.push(eleChildren.get());
elementsToCheck[len].offsetX = elementToCheck.gameQuery.posx + elementsToCheck[i].offsetX;
elementsToCheck[len].offsetY = elementToCheck.gameQuery.posy + elementsToCheck[i].offsetY;
len++;
}
}
}
}
return this.pushStack($(resultList));
}
},
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/** -- Sound related functions ------------------------------------------------------------------------------------------------------------------ **/
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/**
* Add the sound to the resourceManager for later use and
* associates it to the selected dom element(s).
*
* This is a non-destructive call
*/
addSound: function(sound, add) {
// Does a SoundWrapper exist?
if($.gameQuery.SoundWrapper) {
var gameQuery = this[0].gameQuery;
// Should we add to existing sounds?
if(add) {
// Do we have some sound associated with 'this'?
var sounds = gameQuery.sounds;
if(sounds) {
// Yes, we add it
sounds.push(sound);
} else {
// No, we create a new sound array
gameQuery.sounds = [sound];
}
} else {
// No, we replace all sounds with this one
gameQuery.sounds = [sound];
}
}
return this;
},
/**
* Play the sound(s) associated with the selected dom element(s).
*
* This is a non-destructive call.
*/
playSound: function() {
$(this).each(function(){
var gameQuery = this.gameQuery;
if(gameQuery.sounds) {
for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
gameQuery.sounds[i].play();
}
}
});
return this;
},
/**
* Stops the sound(s) associated with the selected dom element(s) and rewinds them.
*
* This is a non-destructive call.
*/
stopSound: function() {
$(this).each(function(){
var gameQuery = this.gameQuery;
if(gameQuery.sounds) {
for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
gameQuery.sounds[i].stop();
}
}
});
return this;
},
/**
* Pauses the sound(s) associated with the selected dom element(s).
*
* This is a non-destructive call.
*/
pauseSound: function() {
$(this).each(function(){
var gameQuery = this.gameQuery;
if(gameQuery.sounds) {
for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
gameQuery.sounds[i].pause();
}
}
});
return this;
},
/**
* Mute or unmute the selected sound or all the sounds if none is specified.
*
* This is a non-destructive call.
*/
muteSound: function(muted) {
$(this).each(function(){
var gameQuery = this.gameQuery;
if(gameQuery.sounds) {
for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
gameQuery.sounds[i].muted(muted);
}
}
});
return this;
},
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/** -- Transformation functions ----------------------------------------------------------------------------------------------------------------- **/
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/**
* Internal function doing the combined actions of rotate and scale.
*
* Please use .rotate() or .scale() instead since they are part of the supported API!
*
* This is a non-destructive call.
*/
transform: function() {
var gameQuery = this[0].gameQuery;
if(cssTransform){
var transform = "translate("+gameQuery.posx+"px, "+gameQuery.posy+"px) rotate("+gameQuery.angle+"deg) scale("+(gameQuery.factor*gameQuery.factorh)+","+(gameQuery.factor*gameQuery.factorv)+")";
this.css(cssTransform,transform);
} else {
// Only apply filter if really necessary (break PNG alpha channel and is very slow)
if (gameQuery.angle !== 0 || gameQuery.factor !== 1 || gameQuery.factorh !== 1 || gameQuery.factorv !== 1) {
var angle_rad = Math.PI * 2 / 360 * gameQuery.angle;
// try filter for IE
// For ie from 5.5
var cos = Math.cos(angle_rad) * gameQuery.factor;
var sin = Math.sin(angle_rad) * gameQuery.factor;
this.css("filter","progid:DXImageTransform.Microsoft.Matrix(M11="+(cos*gameQuery.factorh)+",M12="+(-sin*gameQuery.factorv)+",M21="+(sin*gameQuery.factorh)+",M22="+(cos*gameQuery.factorv)+",SizingMethod='auto expand',FilterType='nearest neighbor')");
}
var newWidth = this.width();
var newHeight = this.height();
gameQuery.posOffsetX = (newWidth-gameQuery.width)/2;
gameQuery.posOffsetY = (newHeight-gameQuery.height)/2;
this.css("left", ""+(gameQuery.posx-gameQuery.posOffsetX)+"px");
this.css("top", ""+(gameQuery.posy-gameQuery.posOffsetY)+"px");
}
return this;
},
/**
* Rotate the element(s) clock-wise.
*
* @param {Number} angle the angle in degrees
* @param {Boolean} relative or not
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current rotation angle!
*/
rotate: function(angle, relative){
var gameQuery = this[0].gameQuery;
if(angle !== undefined) {
if(relative === true){
angle += gameQuery.angle;
angle %= 360;
}
$.gameQuery.update(gameQuery,{angle: angle});
return this.transform();
} else {
var ang = gameQuery.angle;
return ang;
}
},
/**
* Change the scale of the selected element(s). The passed argument is a ratio:
*
* @param {Number} factor a ratio: 1.0 = original size, 0.5 = half the original size etc.
* @param {Boolean} relative or not
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current scale factor!
*/
scale: function(factor, relative){
var gameQuery = this[0].gameQuery;
if(factor !== undefined) {
if(relative === true){
factor *= gameQuery.factor;
}
$.gameQuery.update(gameQuery,{factor: factor});
return this.transform();
} else {
var fac = gameQuery.factor;
return fac;
}
},
/**
* Flips the element(s) horizontally.
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current horizontal flipping status!
*/
fliph: function(flip){
var gameQuery = this[0].gameQuery;
if (flip === undefined) {
return (gameQuery.factorh !== undefined) ? (gameQuery.factorh === -1) : false;
} else if (flip) {
gameQuery.factorh = -1;
} else {
gameQuery.factorh = 1;
}
return this.transform();
},
/**
* Flips the element(s) vertically.
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current vertical flipping status!
*/
flipv: function(flip){
var gameQuery = this[0].gameQuery;
if (flip === undefined) {
return (gameQuery.factorv !== undefined) ? (gameQuery.factorv === -1) : false;;
} else if (flip) {
gameQuery.factorv = -1;
} else {
gameQuery.factorv = 1;
}
return this.transform();
},
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/** -- Position getter/setter functions --------------------------------------------------------------------------------------------------------- **/
/** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
/**
* Main function to change the sprite/group/tilemap position on screen.
* The three first agruments are the coordiate (double) and the last one is a flag
* to specify if the coordinate given are absolute or relative.
*
* If no argument is specified then the functions act as a getter and return a
* object {x,y,z}
*
* Please note that the z coordinate is just the z-index.
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
*/
xyz: function(x, y, z, relative) {
if (x === undefined) {
return this.getxyz();
} else {
return this.setxyz({x: x, y: y, z: z}, relative);
}
},
/**
* The following functions are all all shortcuts for the .xyz(...) function.
*
* @see xyz for detailed documentation.
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
*/
x: function(value, relative) {
if (value === undefined) {
return this.getxyz().x;
} else {
return this.setxyz({x: value}, relative);
}
},
y: function(value, relative) {
if (value === undefined) {
return this.getxyz().y;
} else {
return this.setxyz({y: value}, relative);
}
},
z: function(value, relative) {
if (value === undefined) {
return this.getxyz().z;
} else {
return this.setxyz({z: value}, relative);
}
},
xy: function(x, y, relative) {
if (x === undefined) {
// we return the z too since it doesn't cost anything
return this.getxyz();
} else {
return this.setxyz({x: x, y: y}, relative);
}
},
/**
* Main function to change the sprite/group/tilemap dimension on screen.
* The two first arguments are the width and height (double) and the last one is a
* flag to specify if the dimensions given are absolute or relative.
*
* If no argument is specified then the functions act as a getter and
*
* return an object {w,h}
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
*/
wh: function(w, h, relative) {
if (w === undefined) {
return this.getwh();
} else {
return this.setwh({w: w, h: h}, relative);
}
},
/**
* The following functions are all all shortcuts for the .wh(...) function.
*
* @see wh for detailed documentation.
*
* This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
*/
w: function(value, relative) {
if (value === undefined) {
return this.getwh().w;
} else {
return this.setwh({w: value}, relative);
}
},
h: function(value, relative) {
if (value === undefined) {
return this.getwh().h;
} else {
return this.setwh({h: value}, relative);
}
},
/**
* The following four functions are 'private', and are not supposed to
* be used outside of the library.
* They are NOT part of the API and so are not guaranteed to remain unchanged.
* You should really use .xyz() and .wh() instead.
*/
getxyz: function() {
var gameQuery = this[0].gameQuery;
return {x: gameQuery.posx, y: gameQuery.posy, z: gameQuery.posz};
},
setxyz: function(option, relative) {
var gameQuery = this[0].gameQuery;
for (coordinate in option) {
// Update the gameQuery object
switch (coordinate) {
case 'x':
if(relative) {
option.x += gameQuery.posx;
}
gameQuery.posx = option.x;
this.transform();
//update the sub tile maps (if any), this forces to recompute which tiles are visible
this.find("."+$.gameQuery.tilemapCssClass).each(function(){
$(this).x(0, true);
});
break;
case 'y':
if(relative) {
option.y += gameQuery.posy;
}
gameQuery.posy = option.y;
this.transform();
//update the sub tile maps (if any), this forces to recompute which tiles are visible
this.find("."+$.gameQuery.tilemapCssClass).each(function(){
$(this).y(0, true);
});
break;
case 'z':
if(relative) {
option.z += gameQuery.posz;
}
gameQuery.posz = option.z;
this.css("z-index",gameQuery.posz);
break;
}
}
$.gameQuery.update(this, option);
return this;
},
getwh: function() {
var gameQuery = this[0].gameQuery;
return {w: gameQuery.width, h: gameQuery.height};
},
setwh: function(option, relative) {
var gameQuery = this[0].gameQuery;
for (coordinate in option) {
// Update the gameQuery object
switch (coordinate) {
case 'w':
if(relative) {
option.w += gameQuery.width;
}
gameQuery.width = option.w;
this.css("width","" + gameQuery.width + "px");
break;
case 'h':
if(relative) {
option.h += gameQuery.height;
}
gameQuery.height = option.h;
this.css("height","" + gameQuery.height + "px");
break;
}
}
$.gameQuery.update(this, option);
return this;
}
}); // end of the extensio of $.fn
// alias gameQuery to gQ for easier access
$.extend({ gQ: $.gameQuery});
})(jQuery);
JavaScript
1
https://gitee.com/mirrors/gamequery.git
git@gitee.com:mirrors/gamequery.git
mirrors
gamequery
GameQuery
master

搜索帮助