lettuce
Version:
Lettuce JS, Mini Mobile Framework for Romantic with DSL.
434 lines (358 loc) • 15.8 kB
JavaScript
/*
* MelonJS Game Engine
* Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod
* http://www.melonjs.org
*
* a simple debug panel plugin
* usage : me.plugin.register(debugPanel, "debug");
*
* you can then use me.plugin.debug.show() or me.plugin.debug.hide()
* to show or hide the panel, or press respectively the "S" and "H" keys.
*
* note :
* Heap Memory information is available under Chrome when using
* the "--enable-memory-info" parameter to launch Chrome
*/
(function () {
// ensure that me.debug is defined
me.debug = me.debug || {};
var DEBUG_HEIGHT = 50;
/**
* @class
* @public
* @extends me.plugin.Base
* @memberOf me
* @constructor
*/
me.debug.Panel = me.plugin.Base.extend(
/** @scope me.debug.Panel.prototype */
{
/** @private */
init : function (showKey, hideKey) {
// call the super constructor
this._super(me.plugin.Base, "init");
// minimum melonJS version expected
this.version = "2.1.0";
// to hold the debug options
// clickable rect area
this.area = {};
// panel position and size
this.rect = null;
// for z ordering
// make it ridiculously high
this.z = Infinity;
// visibility flag
this.visible = false;
// frame update time in ms
this.frameUpdateTime = 0;
// frame draw time in ms
this.frameDrawTime = 0;
// set the object GUID value
this.GUID = "debug-" + me.utils.createGUID();
// set the object entity name
this.name = "me.debugPanel";
// persistent
this.isPersistent = true;
// a floating object
this.floating = true;
// renderable
this.isRenderable = true;
// always update, even when not visible
this.alwaysUpdate = true;
// WebGL/Canvas compatibility
var canvas = me.video.renderer.getCanvas();
this.canvas = me.video.createCanvas(canvas.width, DEBUG_HEIGHT, true);
this.context = me.video.renderer.getContext2d(this.canvas);
this.overlay = new me.Color(255, 255, 255, 1.0);
// Size
this.rect = new me.Rect(0, 0, canvas.width, DEBUG_HEIGHT);
// create a default font, with fixed char width
var s = 10;
this.mod = 1;
if (me.game.viewport.width < 500) {
s = 7;
this.mod = 0.7;
}
this.font = new me.Font("courier", s, "white");
// clickable areas
var size = 12 * this.mod;
this.area.renderHitBox = new me.Rect(163, 5, size, size);
this.area.renderVelocity = new me.Rect(163, 20, size, size);
this.area.renderQuadTree = new me.Rect(253, 5, size, size);
// some internal string/length
this.help_str = "(s)how/(h)ide";
this.help_str_len = this.font.measureText(this.context, this.help_str).width;
this.fps_str_len = this.font.measureText(this.context, "00/00 fps").width;
this.memoryPositionX = this.font.measureText(this.context, "Draw : ").width * 2.2 + 310 * this.mod;
// enable the FPS counter
me.debug.displayFPS = true;
// bind the "S" and "H" keys
me.input.bindKey(showKey || me.input.KEY.S, "show", false, false);
me.input.bindKey(hideKey || me.input.KEY.H, "hide", false, false);
// add some keyboard shortcuts
var self = this;
this.keyHandler = me.event.subscribe(me.event.KEYDOWN, function (action) {
if (action === "show") {
self.show();
}
else if (action === "hide") {
self.hide();
}
});
// memory heap sample points
this.samples = [];
//patch patch patch !
this.patchSystemFn();
// make it visible
this.show();
},
/**
* patch system fn to draw debug information
*/
patchSystemFn : function () {
// add a few new debug flag (if not yet defined)
me.debug.renderHitBox = me.debug.renderHitBox || false;
me.debug.renderVelocity = me.debug.renderVelocity || false;
me.debug.renderQuadTree = me.debug.renderQuadTree || false;
var _this = this;
// patch timer.js
me.plugin.patch(me.timer, "update", function (dt) {
// call the original me.timer.update function
this._patched(dt);
// call the FPS counter
me.timer.countFPS();
});
// patch me.game.update
me.plugin.patch(me.game, "update", function (dt) {
var frameUpdateStartTime = window.performance.now();
this._patched(dt);
// calculate the update time
_this.frameUpdateTime = window.performance.now() - frameUpdateStartTime;
});
// patch me.game.draw
me.plugin.patch(me.game, "draw", function () {
var frameDrawStartTime = window.performance.now();
this._patched();
// calculate the drawing time
_this.frameDrawTime = window.performance.now() - frameDrawStartTime;
});
// patch sprite.js
me.plugin.patch(me.Sprite, "draw", function (renderer) {
// call the original me.Sprite function
this._patched(renderer);
// draw the sprite rectangle
if (me.debug.renderHitBox) {
renderer.save();
renderer.setColor("green");
renderer.strokeRect(this.left, this.top, this.width, this.height);
renderer.restore();
}
});
// patch entities.js
me.plugin.patch(me.Entity, "draw", function (renderer) {
// call the original me.game.draw function
this._patched(renderer);
// check if debug mode is enabled
if (me.debug.renderHitBox) {
renderer.save();
renderer.setColor("orange");
renderer.setLineWidth(1);
// draw the bounding rect shape
renderer.drawShape(this.getBounds());
renderer.translate(this.pos.x, this.pos.y);
// draw all defined shapes
renderer.setColor("red");
for (var i = this.body.shapes.length, shape; i--, (shape = this.body.shapes[i]);) {
renderer.drawShape(shape);
}
renderer.restore();
}
if (me.debug.renderVelocity) {
// draw entity current velocity
var x = ~~(this.pos.x + this.hWidth);
var y = ~~(this.pos.y + this.hHeight);
renderer.save();
renderer.setColor("blue");
renderer.setLineWidth(1);
renderer.strokeLine(x, y, x + ~~(this.body.vel.x * this.hWidth), y + ~~(this.body.vel.y * this.hHeight));
renderer.restore();
}
});
},
/**
* show the debug panel
*/
show : function () {
if (!this.visible) {
// register a mouse event for the checkboxes
me.input.registerPointerEvent("pointerdown", this.rect, this.onClick.bind(this), true);
// add the debug panel to the game world
me.game.world.addChild(this, Infinity);
// mark it as visible
this.visible = true;
}
},
/**
* hide the debug panel
*/
hide : function () {
if (this.visible) {
// release the mouse event for the checkboxes
// me.input.releasePointerEvent("pointerdown", this.rect);
this.canvas.removeEventListener("click", this.onClick.bind(this));
// remove the debug panel from the game world
me.game.world.removeChild(this);
// mark it as invisible
this.visible = false;
}
},
/** @private */
update : function () {
if (me.input.isKeyPressed("show")) {
this.show();
}
else if (me.input.isKeyPressed("hide")) {
this.hide();
}
return true;
},
/**
* @private
*/
getBounds : function () {
return this.rect;
},
/** @private */
onClick : function (e) {
// check the clickable areas
if (this.area.renderHitBox.containsPoint(e.gameX, e.gameY)) {
me.debug.renderHitBox = !me.debug.renderHitBox;
}
else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) {
// does nothing for now, since velocity is
// rendered together with hitboxes (is a global debug flag required?)
me.debug.renderVelocity = !me.debug.renderVelocity;
}
else if (this.area.renderQuadTree.containsPoint(e.gameX, e.gameY)) {
me.debug.renderQuadTree = !me.debug.renderQuadTree;
}
// force repaint
me.game.repaint();
},
/** @private */
drawQuadTreeNode : function (renderer, node) {
var bounds = node.bounds;
// draw the current bounds
if (node.nodes.length === 0) {
// cap the alpha value to 0.4 maximum
var _alpha = (node.objects.length * 0.4) / me.collision.maxChildren;
if (_alpha > 0.0) {
renderer.save();
renderer.setGlobalAlpha(_alpha);
renderer.setColor("red");
renderer.fillRect(bounds.pos.x, bounds.pos.y, bounds.width, bounds.height);
renderer.restore();
}
}
else {
//has subnodes? drawQuadtree them!
for (var i = 0; i < node.nodes.length; i++) {
this.drawQuadTreeNode(renderer, node.nodes[i]);
}
}
},
/** @private */
drawQuadTree : function (renderer) {
// save the current globalAlpha value
var _alpha = renderer.globalAlpha();
var x = me.game.viewport.pos.x;
var y = me.game.viewport.pos.y;
renderer.translate(-x, -y);
this.drawQuadTreeNode(renderer, me.collision.quadTree);
renderer.translate(x, y);
renderer.setGlobalAlpha(_alpha);
},
/** @private */
drawMemoryGraph : function (context, endX) {
if (window.performance && window.performance.memory) {
var usedHeap = Number.prototype.round(window.performance.memory.usedJSHeapSize / 1048576, 2);
var totalHeap = Number.prototype.round(window.performance.memory.totalJSHeapSize / 1048576, 2);
var len = ~~(endX - this.memoryPositionX - 5);
// remove the first item
this.samples.shift();
// add a new sample (25 is the height of the graph)
this.samples[len] = (usedHeap / totalHeap) * 20;
// draw the graph
for (var x = len; x >= 0; x--) {
var where = endX - (len - x) - 5;
context.beginPath();
context.strokeStyle = "lightblue";
context.moveTo(where, 40 * this.mod);
context.lineTo(where, 40 * this.mod - (this.samples[x] || 0));
context.stroke();
}
// display the current value
this.font.draw(context, "Heap : " + usedHeap + "/" + totalHeap + " MB", this.memoryPositionX, 5 * this.mod);
}
else {
// Heap Memory information not available
this.font.draw(context, "Heap : ??/?? MB", this.memoryPositionX, 5 * this.mod);
}
},
/** @private */
draw : function (renderer) {
this.context.save();
// draw the QuadTree (before the panel)
if (me.debug.renderQuadTree === true) {
this.drawQuadTree(renderer);
}
// draw the panel
this.context.globalAlpha = 0.5;
this.context.fillStyle = "black";
this.context.fillRect(this.rect.left, this.rect.top,
this.rect.width, this.rect.height);
this.context.globalAlpha = 1.0;
this.font.draw(this.context, "#objects : " + me.game.world.children.length, 5 * this.mod, 5 * this.mod);
this.font.draw(this.context, "#draws : " + me.game.world.drawCount, 5 * this.mod, 20 * this.mod);
// debug checkboxes
this.font.draw(this.context, "?hitbox [" + (me.debug.renderHitBox ? "x" : " ") + "]", 100 * this.mod, 5 * this.mod);
this.font.draw(this.context, "?velocity [" + (me.debug.renderVelocity ? "x" : " ") + "]", 100 * this.mod, 20 * this.mod);
this.font.draw(this.context, "?QuadTree [" + (me.debug.renderQuadTree ? "x" : " ") + "]", 190 * this.mod, 5 * this.mod);
// draw the update duration
this.font.draw(this.context, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 285 * this.mod, 5 * this.mod);
// draw the draw duration
this.font.draw(this.context, "Draw : " + this.frameDrawTime.toFixed(2) + " ms", 285 * this.mod, 20 * this.mod);
// draw the memory heap usage
var endX = this.rect.width - 25;
this.drawMemoryGraph(this.context, endX - this.help_str_len);
// some help string
this.font.draw(this.context, this.help_str, endX - this.help_str_len, 20 * this.mod);
//fps counter
var fps_str = me.timer.fps + "/" + me.sys.fps + " fps";
this.font.draw(this.context, fps_str, this.rect.width - this.fps_str_len - 5, 5 * this.mod);
this.context.restore();
me.video.renderer.setGlobalAlpha(0.7);
var color = me.video.renderer.getColor();
me.video.renderer.setColor(this.overlay);
me.video.renderer.drawImage(
this.canvas, 0, 0,
this.canvas.width, this.canvas.height,
0, 0, this.rect.width, this.rect.height
);
me.video.renderer.setColor(color);
me.video.renderer.setGlobalAlpha(1.0);
},
/** @private */
onDestroyEvent : function () {
// hide the panel
this.hide();
// unbind keys event
me.input.unbindKey(me.input.KEY.S);
me.input.unbindKey(me.input.KEY.H);
me.event.unsubscribe(this.keyHandler);
}
});
/*---------------------------------------------------------*/
// END END END
/*---------------------------------------------------------*/
})();