From: dsc Date: Wed, 24 Nov 2010 09:25:27 +0000 (-0800) Subject: Improves dodging and blocking AI. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=431a9fc956d0cb3773d69e365b59b0dbc0dbe779;p=tanks.git Improves dodging and blocking AI. --- diff --git a/src/portal/math/line.js b/src/portal/math/line.js index 237aaac..3224e41 100644 --- a/src/portal/math/line.js +++ b/src/portal/math/line.js @@ -66,8 +66,8 @@ math.Line = math.Vec.subclass('Line', { pointAtX : function pointAtX(x){ return new math.Vec(x, this.calcY(x)); }, pointAtY : function pointAtY(y){ return new math.Vec(this.calcX(y), y); }, - calcY : function calcY(x){ return (x === this.xint ? 0 : x*this.slope + this.yint); }, - calcX : function calcX(y){ return (y === this.yint ? 0 : y/this.slope + this.xint); }, + calcY : function calcY(x){ return (x !== this.xint ? x*this.slope + this.yint : 0); }, + calcX : function calcX(y){ return (y !== this.yint ? y/this.slope + this.xint : 0); }, near : function near(x,y){ if ( !isFinite(this.slope) ) diff --git a/src/tanks/config.js b/src/tanks/config.js index fc710fd..6386908 100644 --- a/src/tanks/config.js +++ b/src/tanks/config.js @@ -2,11 +2,11 @@ tanks.config = { ui : { showGridCoords : false, - showCountdown : true + showCountdown : false }, pathing : { + overlayAIPaths : true, overlayPathmap : false, - overlayAIPaths : false, traceTrajectories : false } }; diff --git a/src/tanks/map/level.js b/src/tanks/map/level.js index 17308f9..a3c6d2b 100644 --- a/src/tanks/map/level.js +++ b/src/tanks/map/level.js @@ -5,9 +5,10 @@ Level = Rect.subclass('Level', { Rect.init.call(this, w,h); this.canvas.remove(); - this.game = game; - this.walls = Y([]); - this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY); + this.game = game; + this.walls = Y([]); + this.allWalls = Y([]); + this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY); game.addEventListener('ready', this.setup.bind(this)); }, @@ -28,8 +29,8 @@ Level = Rect.subclass('Level', { E = game.addUnit(new Tank(2), 0,1); - game.addUnit(new Tank(2), 1,0); - game.addUnit(new Tank(2), 8,1); + // game.addUnit(new Tank(2), 1,0); + // game.addUnit(new Tank(2), 8,1); }, addWall : function addWall(x,y, w,h, isBoundary){ @@ -37,8 +38,9 @@ Level = Rect.subclass('Level', { this.pathmap.addBlocker(wall); this.append( wall.render(this).shape ); - if (!isBoundary) - this.walls.push(wall); + this.allWalls.push(wall); + if (!isBoundary) this.walls.push(wall); + return wall; }, diff --git a/src/tanks/map/pathmap.js b/src/tanks/map/pathmap.js index 5ab4c17..5cdf075 100644 --- a/src/tanks/map/pathmap.js +++ b/src/tanks/map/pathmap.js @@ -10,9 +10,11 @@ PathMap = QuadTree.subclass('PathMap', { init : function init(level, x1,y1, x2,y2, capacity) { x1 -= 1; y1 -= 1; x2 += 1; y2 += 1; QuadTree.init.call(this, x1,y1, x2,y2, capacity); - this.level = level; - this.game = level.game; - this.walls = level.walls; + + this.level = level; + this.game = level.game; + this.walls = level.walls; + this.allWalls = level.allWalls; this.game.addEventListener('ready', this.setup.bind(this, x1,y1, x2,y2)); }, diff --git a/src/tanks/map/trajectory.js b/src/tanks/map/trajectory.js index 038864c..bc39f3d 100644 --- a/src/tanks/map/trajectory.js +++ b/src/tanks/map/trajectory.js @@ -5,7 +5,7 @@ Trajectory = math.Line.subclass('Trajectory', { init : function initTrajectory(owner, x1,y1, x2,y2, tdist){ - Y.bindAll(this, 'cmp', 'closer'); + Y.bindAll(this, 'compare', 'closer', 'intersects'); this.owner = owner; this.game = owner.game; @@ -52,13 +52,26 @@ Trajectory = math.Line.subclass('Trajectory', { return this; }, + + intersects : function intersects(x,y){ + var o = x; + if (o instanceof Thing) + return o.boundingBox.intersects(this); + + if (o instanceof Loc.Rect) + return o.intersects(this); + + return math.Line.prototype.intersects.call(this, x,y); + }, + + /** * Compares how distant in the future two objects are on this trajectory. * Objects that have been passed are always further away than those in the future, * but otherwise the comparison is performed by absolute distance. * @returns -1 if a closer b, 1 if a further b, 0 if a same as b */ - cmp : function cmp(a, b){ + compare : function compare(a, b){ if (a instanceof Thing) a = a.midpoint; if (b instanceof Thing) b = b.midpoint; @@ -90,11 +103,11 @@ Trajectory = math.Line.subclass('Trajectory', { }, closer : function closer(o1, o2){ - return this.cmp(o1,o2) === 1 ? o2 : o1; + return this.compare(o1,o2) === 1 ? o2 : o1; }, closest : function closest(o1, o2){ - return new Y(arguments).sort( this.cmp ).shift(); + return new Y(arguments).sort( this.compare ).shift(); }, comesWithin : function comesWithin(pt, w,h){ @@ -121,10 +134,11 @@ Trajectory = math.Line.subclass('Trajectory', { pathBlocked : function pathBlocked(obj, ignore){ var blockers = - this.pathmap.walls - .concat( this.game.units ) - .apply('remove', Y(ignore || []).concat([this.owner]).end()) - .sort( this.cmp ) + this.pathmap.walls + .concat( this.game.units ) + .apply('remove', Y(ignore || []).concat([this.owner]).end()) + .filter( this.intersects ) + .sort( this.compare ) , blocker = blockers.shift() ; diff --git a/src/tanks/thing/tank.js b/src/tanks/thing/tank.js index 42e7570..543d456 100644 --- a/src/tanks/thing/tank.js +++ b/src/tanks/thing/tank.js @@ -1,3 +1,9 @@ +(function(){ + +var BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000 +, isBullet = Y.is(Bullet) +; + Tank = Thing.subclass('Tank', { projectile : Bullet, @@ -46,7 +52,7 @@ Tank = Thing.subclass('Tank', { - act : function act(){ + act : function act(allotment){ // Check to see if we should obey our last decision, and not recalc if ( this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > NOW ) { @@ -56,42 +62,33 @@ Tank = Thing.subclass('Tank', { this.forceCurrentMove = false; - // Are we ready to fire? + // Try to shoot down nearby bullets if ( this.ableToShoot() ) { - - // Try to shoot down nearby bullets - var bs = this.willCollide( this.findNearLike(16, Y.is(Bullet)) ); + var bs = this.willCollide( this.findNearLike(25, isBullet) ); + // console.log('['+TICKS+':'+this.id, this, '] Shoot down bullets?', bs.size() && bs); if ( bs.size() ) { var b = this.closestOf(bs); - - // console.log('Incoming! Shoot it down!', b); + // console.log(' --> Incoming! Shoot it down!', b); this.shoot(b.loc.x, b.loc.y); return this; } - - // Dodge incoming bullet - var bs = this.willCollide( this.findNearLike(50, Y.is(Bullet)), 10 ); - if ( bs.size() ) { - var b = this.closestOf(bs), bs = [b] - , mid = this.midpoint - , trj = b.trajectory.tangent(mid) - - , lvl = this.game.level, w = lvl.width, h = lvl.height - , x = (mid.x > w/2 ? w : 0) - , y = (mid.y > h/2 ? h : 0) - , to = this.currentMove = trj.near(x,y); - - this.forceCurrentMove = this.willCollide.bind(this, bs, 10); - this.currentMoveLimit = NOW + 750; - - this.move(to.x, to.y); - return this; - } - - // Try to blow up nearby tanks - var t = this.findNearEnemies(66, true).shift(); + } + + // Dodge incoming bullet + var bs = this.willCollide(this.findNearLike(71, isBullet), 5); + // console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs); + if ( bs.size() ) { + var bullet = this.closestOf(bs); + this.moveAwayFrom(bullet); + return this; + } + + // Try to blow up nearby tanks + if ( this.ableToShoot() ) { + var t = this.findNearEnemies(71, true).shift(); + // console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t); if (t) { - // console.log('I gotcha!', t); + // console.log(' --> I gotcha!', t); this.shoot(t.loc.x, t.loc.y); return this; } @@ -99,100 +96,32 @@ Tank = Thing.subclass('Tank', { // Nothing to shoot at? Move toward something this.continueMove(); - return this; }, - ableToShoot : function ableToShoot(){ - return this.nShots < this.stats.shots && this.cooldowns.attack.ready; - }, - closestOf : function closestOf(agents){ - if ( !(agents && agents.size()) ) - return null; - - var manhattan = math.Vec.manhattan - , bb = this.boundingBox, mid = this.midpoint ; + /** + * TODO: Get this.currentMove etc out of the calc methods. + */ + moveAwayFrom : function moveAwayFrom(agent){ + var mid = this.midpoint + , trj = agent.trajectory.tangent(mid) - agents.sort(function(a,b){ - return Y.op.cmp( - manhattan(a.loc,mid), - manhattan(b.loc,mid) ); - }); + , wall = this.closestOf(this.game.pathmap.allWalls) + , wmid = wall.midpoint - return agents.attr(0); - }, - - willCollide : function willCollide(bullets, wiggle){ - wiggle = wiggle || 0; - var tank = this, bb = this.boundingBox - , w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2 - ; - return bullets.filter(function(b){ - var trj = b.trajectory; - return ( !b.dead - && trj.comesWithin(tank, w,h) - && !trj.pathBlocked(tank) ); - }); - }, - - continueMove : function continueMove(){ - if ( !this.currentMove || this.currentMoveLimit <= NOW ){ - var t = this.findNearEnemies(10000).shift(); - if (t) this.calculatePath(t.midpoint); - } + , lvl = this.game.level.boundingBox, w = lvl.width, h = lvl.height + , x = ((mid.x - wmid.x) > 0 ? w : 0) + , y = ((mid.y - wmid.y) > 0 ? h : 0) + , to = this.currentMove = trj.near(x,y); - var to = this.currentMove; - if (!to) return; + this.forceCurrentMove = this.willCollide.bind(this, [agent], 5); + this.currentMoveLimit = NOW + 1000; this.move(to.x, to.y); - if ( this.midpoint.equals(to) ) { - this.currentMove = null; - this.forceCurrentMove = false; - this.currentMoveLimit = -1; - } - }, - - calculatePath : function calculatePath(end){ - if (!end) return; - - var pm = this.game.pathmap; - - // this.forceCurrentMove = Y.op.K(true); - // this.currentMoveLimit = NOW + 750; - this.forceCurrentMove = false; - this.currentMoveLimit = -1; - - // console.log(this, 'moving toward', t); - var bb = this.boundingBox - , mid = this.midpoint - , start = mid - - , path = this.lastPath = pm.path(start, end, this.id) - , to = this.currentMove = path[0] - ; - - // VVV We only really need this code if we're going to recalculate before we reach the currentMove - - // we may need to move closer to start if we occupy multiple grid-squares - // var tosq = pm.vec2Square(to), manhattan = pm.manhattan - // , tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1) - // , bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2) - // , straddles = [tl, tr, bl, br] - // ; - // - // - // if ( !tl.equals(br) ) { - // tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1); - // bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y); - // straddles.sort(function(a,b){ - // return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq)); - // }); - // to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt); - // path.unshift(to); - // } - // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to); + // console.log(' --> Dodge', agent, 'away from', wall, 'to', to); + return to; }, find : function find(x1,y1, x2,y2){ @@ -201,27 +130,66 @@ Tank = Thing.subclass('Tank', { findNearLike : function findNearLike(ticks, fn){ fn = (fn || Y.op.K(true)).toFunction(); - var bMovePerTick = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000 - , within = bMovePerTick*ticks + + var within = BULLET_MOVE_PER_FRAME*ticks , bb = this.boundingBox , x1 = bb.x1 - within, y1 = bb.y1 - within , x2 = bb.x2 + within, y2 = bb.y2 + within ; + return this.game.pathmap.get(x1,y1, x2,y2).filter(fn, this); }, - findNearEnemies : function findNearEnemies(ticks, wallObs){ - var bb = this.boundingBox - , mid = this.midpoint - , pm = this.game.pathmap ; + findNearEnemies : function findNearEnemies(ticks, needLineOfSight){ return this.findNearLike(ticks, function(agent){ var am = agent.midpoint; return ( agent.align !== this.align && Y.is(Tank, agent) - && !(wallObs && new Trajectory(this,mid,am).pathBlocked(agent)) ); + && !(needLineOfSight + && new Trajectory(this,this.midpoint,am).pathBlocked(agent)) + ); + }); + }, + + closestOf : function closestOf(agents){ + if ( !(agents && agents.size()) ) + return null; + + var manhattan = math.Vec.manhattan + , bb = this.boundingBox, mid = this.midpoint ; + + agents.sort(function(a,b){ + return Y.op.cmp( + manhattan(a.midpoint,mid), + manhattan(b.midpoint,mid) ); // FIXME: midpoint-to-midpoint is wrong -- should use pt on closest boundary-side of object + }); + + return agents.attr(0); + }, + + willCollide : function willCollide(bullets, wiggle){ + bullets = ( !Y.isArray(bullets) ? [bullets] : bullets ); + wiggle = wiggle || 0; + + var tank = this, bb = this.boundingBox + , w = bb.width+wiggle, h = bb.height+wiggle ; + // , w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2 ; + + return bullets.filter(function(b){ + var trj = b.trajectory; + return ( !b.dead + && trj.comesWithin(tank, w,h) + && !trj.pathBlocked(tank) ); }); }, + /** + * Fires this agent's cannon. If a target location is omitted, the shot + * will be fired in the direction of the tank's current barrel rotation. + * + * @param {Number} [x] Target X coordinate. + * @param {Number} [y] Target Y coordinate. + */ shoot : function shoot(x,y){ var WIGGLE = 4 // additional space which must be clear in front of the barrel , xydef = (x !== undefined && y !== undefined) @@ -246,7 +214,8 @@ Tank = Thing.subclass('Tank', { , blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this) ; - if ( blockers.size() ) return; // console.log('squelch!', blockers); + if ( blockers.size() ) + return null; // console.log('squelch!', blockers); if (!xydef) { x = x0 + REF_SIZE*cos; @@ -263,21 +232,9 @@ Tank = Thing.subclass('Tank', { return p; }, - getTurretLoc : function getTurretLoc(){ - var loc = this.loc, mid = this.midpoint - , barrel = this.barrel - - , theta = barrel.transform.rotate - , sin = Math.sin(theta), cos = Math.cos(theta) - , len = barrel.boundingBox.width - - , x = mid.x + len*cos - , y = mid.y + len*sin - ; - // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]); - return new math.Vec(x,y); - }, - + /** + * @return {this} + */ move : function move(x,y){ var bb = this.boundingBox , w2 = bb.width/2, h2 = bb.height/2 @@ -287,9 +244,7 @@ Tank = Thing.subclass('Tank', { return this.moveByAngle( this.angleTo(x,y), x2,y2 ); }, - /** - * @protected This method does not update this.trajectory -- call this.move(x,y) instead. - */ + /** @protected This method does not update this.trajectory -- call this.move(x,y) instead. */ moveByAngle : function moveByAngle(theta, targetX,targetY){ var abs = Math.abs , bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2 @@ -315,6 +270,84 @@ Tank = Thing.subclass('Tank', { }, + continueMove : function continueMove(){ + if ( !this.currentMove || this.currentMoveLimit <= NOW ){ + var t = this.findNearEnemies(10000).shift(); + if (t) this.calculatePath(t.midpoint); + } + + var to = this.currentMove; + if (!to) return; + + this.move(to.x, to.y); + if ( this.midpoint.equals(to) ) { + this.currentMove = null; + this.forceCurrentMove = false; + this.currentMoveLimit = -1; + } + }, + + calculatePath : function calculatePath(end){ + if (!end) return; + + // this.forceCurrentMove = Y.op.K(true); + // this.currentMoveLimit = NOW + 750; + this.forceCurrentMove = false; + this.currentMoveLimit = -1; + + // console.log(this, 'moving toward', t); + var pm = this.game.pathmap + , mid = this.midpoint, start = mid + + , path = this.lastPath = pm.path(start, end, this.id) + , to = this.currentMove = path[0] + ; + + // VVV We only really need this code if we're going to recalculate before we reach the currentMove + + // we may need to move closer to start if we occupy multiple grid-squares + // var tosq = pm.vec2Square(to), manhattan = math.Vec.manhattan + // , bb = this.boundingBox + // , tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1) + // , bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2) + // , straddles = [tl, tr, bl, br] + // ; + // + // + // if ( !tl.equals(br) ) { + // tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1); + // bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y); + // straddles.sort(function(a,b){ + // return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq)); + // }); + // to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt); + // path.unshift(to); + // } + + // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to); + }, + + + ableToShoot : function ableToShoot(){ + return this.nShots < this.stats.shots && this.cooldowns.attack.ready; + }, + + getTurretLoc : function getTurretLoc(){ + var loc = this.loc, mid = this.midpoint + , barrel = this.barrel + + , theta = barrel.transform.rotate + , sin = Math.sin(theta), cos = Math.cos(theta) + , len = barrel.boundingBox.width + + , x = mid.x + len*cos + , y = mid.y + len*sin + ; + // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]); + return new math.Vec(x,y); + }, + + /// Rendering Methods /// @@ -386,3 +419,6 @@ Tank = Thing.subclass('Tank', { } }); + + +})(); \ No newline at end of file diff --git a/src/tanks/ui/config.js b/src/tanks/ui/config.js index d74e090..479fc9d 100644 --- a/src/tanks/ui/config.js +++ b/src/tanks/ui/config.js @@ -1,8 +1,60 @@ (function(){ -function splitCamel(s){ +var upperPat = /^[A-Z]+$/ +, symbolPat = /^[^a-zA-Z]+$/ +; + +Y.YString.prototype.splitCamel = + function splitCamel(s){ + return this.reduce( + function(acc, ch, i){ + return acc + ( + symbolPat.test(ch) ? '' : + (upperPat.test(ch) ? ' '+ch : ch) ); + }, '') + .split(' '); + }; + + +// TODO +// ==== +// - store at tanks.config.values, but clone at start to t.c.defaults +// - need Config class to run triggers on changes +// - button to clear saved config +// +// init +// - For each item in tanks.config (recursive): +// - Create a UI box based on type, with a name based on its dotted path +// - Check cookies -- fill it with the value from cookies or default +// +// update +// - For each config element: +// - Extract value, typecast based on default's type +// - If different from default, set cookie +// - Update tanks.config.values + +var ns = tanks.ui.config = {} +, c = tanks.config +, p = c.pathing +; + +ns.init = function initConfigUi(){ + $('#config [name=pathmap]').attr('checked', p.overlayPathmap); + $('#config [name=aipaths]').attr('checked', p.overlayAIPaths); + $('#config [name=trajectories]').attr('checked', p.traceTrajectories); + $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords); + $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords'); +}; + +ns.update = function updateConfigUi(evt){ + p.overlayPathmap = $('#config [name=pathmap]').attr('checked'); + p.overlayAIPaths = $('#config [name=aipaths]').attr('checked'); + p.traceTrajectories = $('#config [name=trajectories]').attr('checked'); -} + c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked'); + $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords'); +}; + })(); diff --git a/src/tanks/ui/main.js b/src/tanks/ui/main.js index d966db4..e6c3598 100644 --- a/src/tanks/ui/main.js +++ b/src/tanks/ui/main.js @@ -1,5 +1,3 @@ -tanks.ui = {}; - (function(){ jQuery(main); @@ -37,7 +35,7 @@ function main(){ $('#debug').bind('mousedown', Y.op.K(false)); // Update config when changed - $('#debug input').bind('change', updateConfig); + $('#debug input').bind('change', tanks.ui.config.update); // Create #pause box @@ -94,7 +92,7 @@ function setupUI(){ if (!qkv.ai) $('#welcome').show(); LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0); - initConfig(); + tanks.ui.config.init(); LBT.root.draw(); // Start button (click or return key) @@ -167,54 +165,6 @@ function gameover(headline, button, body, evt){ gameover = Y(gameover).curry(); -function initConfig(){ - var c = tanks.config - , p = c.pathing - ; - - $('#config [name=pathmap]').attr('checked', p.overlayPathmap); - $('#config [name=aipaths]').attr('checked', p.overlayAIPaths); - $('#config [name=trajectories]').attr('checked', p.traceTrajectories); - - $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords); - $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords'); -} - -function updateConfig(evt){ - var c = tanks.config - , p = c.pathing - ; - - p.overlayPathmap = $('#config [name=pathmap]').attr('checked'); - p.overlayAIPaths = $('#config [name=aipaths]').attr('checked'); - p.traceTrajectories = $('#config [name=trajectories]').attr('checked'); - - c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked'); - $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords'); -} - -// Update performance info periodically -function updateInfo(){ - var loop = LBT.loop - , fps = loop.fps() - , n_active = LBT.active.size() - , n_units = LBT.units.size() - , n_projs = LBT.bullets.size() - ; - - $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') ); - $('#info [name=fps]').val( fps.toFixed(2) + " / " + loop.framerate ); - $('#info [name=frame]').val( loop.frametime().toFixed(3)+" ms" ); - - $('#info [name=active]').val( n_active ); - $('#info [name=units]').val( n_units ); - $('#info [name=bullets]').val( n_projs ); - - loop.spark.drawTimes(); - - return false; -} - function countdownToStart(n, fn){ var el , showFor = 750 @@ -272,6 +222,27 @@ function readyToStart(){ toggleGame(); } +// Update performance info periodically +function updateInfo(){ + var loop = LBT.loop + , fps = loop.fps() + , n_active = LBT.active.size() + , n_units = LBT.units.size() + , n_projs = LBT.bullets.size() + ; + + $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') ); + $('#info [name=fps]').val( fps.toFixed(2) + " / " + loop.framerate ); + $('#info [name=frame]').val( loop.frametime().toFixed(3)+" ms" ); + + $('#info [name=active]').val( n_active ); + $('#info [name=units]').val( n_units ); + $('#info [name=bullets]').val( n_projs ); + + loop.spark.drawTimes(); + + return false; +} diff --git a/src/tanks/ui/ui.js b/src/tanks/ui/ui.js new file mode 100644 index 0000000..a1ca700 --- /dev/null +++ b/src/tanks/ui/ui.js @@ -0,0 +1 @@ +tanks.ui = {}; diff --git a/tanks.php b/tanks.php index 40693fc..2a137d9 100644 --- a/tanks.php +++ b/tanks.php @@ -33,7 +33,9 @@ class Tanks { "src/tanks/map/level.js", "src/tanks/map/pathmap.js", + "src/tanks/ui/ui.js", "src/tanks/ui/grid.js", + "src/tanks/ui/config.js", "src/tanks/game.js" );