diff options
Diffstat (limited to 'jaws/src/viewport.js')
| -rwxr-xr-x | jaws/src/viewport.js | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/jaws/src/viewport.js b/jaws/src/viewport.js new file mode 100755 index 0000000..992ccff --- /dev/null +++ b/jaws/src/viewport.js @@ -0,0 +1,214 @@ +var jaws = (function(jaws) { + +/** + * + * @class A window (Rect) into a bigger canvas/image. Viewport is always contained within that given image (called the game world). "Field Summary" contains options for the Viewport()-constructor. + * + * @property {int} width Width of viewport, defaults to canvas width + * @property {int} height Height of viewport, defaults to canvas height + * @property {int} max_x Maximum x-position for viewport, defaults to canvas width + * @property {int} max_y Maximum y-position for viewport, defaults to canvas height + * @property {int} x X-position for the upper left corner of the viewport + * @property {int} y Y-position for the upper left corner of the viewport + * + * @example + * // Center viewport around players position (player needs to have x/y attributes) + * // Usefull for sidescrollers + * viewport.centerAround(player) + * + * // Common viewport usage. max_x/max_y could be said to set the "game world size" + * viewport = viewport = new jaws.Viewport({max_x: 400, max_y: 3000}) + * player = new jaws.Sprite({x:100, y:400}) + * viewport.centerAround(player) + * + * // Draw player relative to the viewport. If viewport is way off, player won't even show up. + * viewport.apply( function() { + * player.draw() + * }); + * + */ + + +jaws.Viewport = function ViewPort(options) { + if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); + + jaws.parseOptions(this, options, this.default_options) + + /* This is needed cause default_options is set loadtime, we need to get width etc runtime */ + if(!this.context) this.context = jaws.context; + if(!this.width) this.width = jaws.width; + if(!this.height) this.height = jaws.height; + if(!this.max_x) this.max_x = jaws.width; + if(!this.max_y) this.max_y = jaws.height; + + var that = this + + /** Move viewport x pixels horizontally and y pixels vertically */ + this.move = function(x, y) { + x && (this.x += x) + y && (this.y += y) + this.verifyPosition() + }; + + /** Move viewport to given x/y */ + this.moveTo = function(x, y) { + if(!(x==undefined)) { this.x = x } + if(!(y==undefined)) { this.y = y } + this.verifyPosition() + }; + + /** + * Returns true if item is outside viewport + * @example + * + * if( viewport.isOutside(player)) player.die(); + * + * // ... or the more advanced: + * bullets = new SpriteList() + * bullets.push( bullet ) + * bullets.removeIf( viewport.isOutside ) + * + */ + this.isOutside = function(item) { + return(!that.isInside(item)) + }; + + /** Returns true if item is inside viewport */ + this.isInside = function(item) { + return( item.x >= that.x && item.x <= (that.x + that.width) && item.y >= that.y && item.y <= (that.y + that.height) ) + }; + + /** Returns true if item is partly (down to 1 pixel) inside viewport */ + this.isPartlyInside = function(item) { + var rect = item.rect() + return( rect.right >= that.x && rect.x <= (that.x + that.width) && rect.bottom >= that.y && item.y <= (that.y + that.height) ) + }; + + /** Returns true of item is left of viewport */ + this.isLeftOf = function(item) { return(item.x < that.x) } + + /** Returns true of item is right of viewport */ + this.isRightOf = function(item) { return(item.x > (that.x + that.width) ) } + + /** Returns true of item is above viewport */ + this.isAbove = function(item) { return(item.y < that.y) } + + /** Returns true of item is above viewport */ + this.isBelow = function(item) { return(item.y > (that.y + that.height) ) } + + + /** + * center the viewport around item. item must respond to x and y for this to work. + * Usefull for sidescrollers when you wan't to keep the player in the center of the screen no matter how he moves. + */ + this.centerAround = function(item) { + this.x = Math.floor(item.x - this.width / 2); + this.y = Math.floor(item.y - this.height / 2); + this.verifyPosition(); + }; + + /** + * force 'item' inside current viewports visible area + * using 'buffer' as indicator how close to the 'item' is allowed to go + * + * @example + * + * viewport.move(10,0) // scroll forward + * viewport.forceInsideVisibleArea(player, 20) // make sure player doesn't get left behind + */ + this.forceInsideVisibleArea = function(item, buffer) { + if(item.x < this.x+buffer) { item.x = this.x+buffer } + if(item.x > this.x+jaws.width-buffer) { item.x = this.x+jaws.width-buffer } + if(item.y < this.y+buffer) { item.y = this.y+buffer } + if(item.y > this.y+jaws.height-buffer) { item.y = this.y+jaws.height-buffer } + } + + /** + * force 'item' inside the limits of the viewport + * using 'buffer' as indicator how close to the 'item' is allowed to go + * + * @example + * viewport.forceInside(player, 10) + */ + this.forceInside = function(item, buffer) { + if(item.x < buffer) { item.x = buffer } + if(item.x > this.max_x-buffer) { item.x = this.max_x-buffer } + if(item.y < buffer) { item.y = buffer } + if(item.y > this.max_y-buffer) { item.y = this.max_y-buffer } + } + + + /** + * executes given draw-callback with a translated canvas which will draw items relative to the viewport + * + * @example + * + * viewport.apply( function() { + * player.draw(); + * foo.draw(); + * }); + * + */ + this.apply = function(func) { + this.context.save() + this.context.translate(-this.x, -this.y) + func() + this.context.restore() + }; + + /** + * if obj is an array-like object, iterate through it and call draw() on each item if it's partly inside the viewport + */ + this.draw = function( obj ) { + this.apply( function() { + if(obj.forEach) obj.forEach( that.drawIfPartlyInside ); + else if(obj.draw) that.drawIfPartlyInside(obj); + // else if(jaws.isFunction(obj) {}; // add apply()-functionally here? + }); + } + + /** + * draws all items of 'tile_map' that's lies inside the viewport + * this is simular to viewport.draw( tile_map.all() ) but optmized for Huge game worlds (tile maps) + */ + this.drawTileMap = function( tile_map ) { + var sprites = tile_map.atRect({ x: this.x, y: this.y, right: this.x + this.width, bottom: this.y + this.height }) + this.apply( function() { + for(var i=0; i < sprites.length; i++) sprites[i].draw(); + }); + } + + /** draws 'item' if it's partly inside the viewport */ + this.drawIfPartlyInside = function(item) { + if(that.isPartlyInside(item)) item.draw(); + } + + /** @private */ + this.verifyPosition = function() { + var max = this.max_x - this.width + if(this.x < 0) { this.x = 0 } + if(this.x > max) { this.x = max } + + var max = this.max_y - this.height + if(this.y < 0) { this.y = 0 } + if(this.y > max) { this.y = max } + }; + + this.moveTo(options.x||0, options.y||0) +} + +jaws.Viewport.prototype.default_options = { + context: null, + width: null, + height: null, + max_x: null, + max_y: null, + x: 0, + y: 0 +}; + +jaws.Viewport.prototype.toString = function() { return "[Viewport " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]" } + +return jaws; +})(jaws || {}); + |
