Skip to content

Instantly share code, notes, and snippets.

@b1nary
Created October 14, 2012 06:58
Show Gist options
  • Select an option

  • Save b1nary/3887676 to your computer and use it in GitHub Desktop.

Select an option

Save b1nary/3887676 to your computer and use it in GitHub Desktop.
My awesome Game Engine in JS

MageJS

My awesome Game Engine in JS

  • Grown on Modern Browser Standards
  • Aims to be lightweight
  • Modular build

INDEV

/**********************************************
*
* MageJS ~ My awesome Game Lib in Javascript
*
* VERSION: 0.1
* LICENSE: MIT
*
* Base class
*
***********************************************/
(function(){
var Mage = {
version: "0.1",
maxSkip: 10,
fps: 60,
}
var timeout;
Mage.running;
Mage.paused;
Mage.debug = true;
/* Resize magic */
function resizeCanvas(){
if(game.fullscreen){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
} else {
canvas.width = game.width;
canvas.height = game.height;
}
if(game.resize != undefined){
game.resize();
}
}
function countFps(){
if((new Date).getSeconds() != ls){
Mage.fps = fps_count;
ls = (new Date).getSeconds();
fps_count = 0;
} else {
fps_count++;
}
}
Mage.clone = function(obj){
var clone = {};
for(var i in obj) {
if(typeof(obj[i])=="object")
clone[i] = cloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}
// This is supported by FF & Chrome > 21
Mage.retro = function(){
ctx.patternQuality = "fast";
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.oImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
}
Mage.random = function(_min,_max){
return Math.floor(Math.random()*(_max-_min+1)+_min);
}
Mage.range = function(start, end){
var foo = [];
for (var i = start; i <= end; i++)
foo.push(i);
return foo;
}
Mage.clear = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
Mage.log = function(msg){
if(Mage.debug){
console.log(msg);
}
}
Mage.draw = function(obj) {
obj.draw( ctx );
}
Mage.getCanvas = function(){
return canvas;
}
Mage.collide = function(obj1,obj2,tolerance){
if (tolerance == undefined){ tolerance = 0; }
if((obj1.x >= obj2.x-tolerance && obj1.x <= obj2.x + obj2.width + tolerance)
&& (obj1.y >= obj2.y-tolerance && obj1.y <= obj2.y + obj2.height + tolerance)){
return true;
} else { return false; }
}
/* Starter */
Mage.run = function(gam){
Mage.running = true;
game = gam;
Mage.canvas_id = game.canvas;
fps_count = 0;
ls = (new Date).getSeconds();
canvas = document.getElementById(game.canvas) || document.getElementsByTagName('canvas')[0];
ctx = canvas.getContext("2d");
if(game.retro){ Mage.retro(); }
resizeCanvas();
window.addEventListener('resize', resizeCanvas, false); // doesnt work :3
game.init();
_id = Mage.requestInterval( Mage.loop );
}
/* Loop Job */
Mage.loop = function() {
var loops = 0;
var skipTicks = 500 / game.fps;
var maxFrameSkip = 10;
var nextGameTick = (new Date).getTime();
Mage.clear();
if(game.retro){ Mage.retro(); }
loops = 0;
if(game.lower_draw){
while ((new Date).getTime() > nextGameTick && loops < maxFrameSkip) {
game.update();
nextGameTick += skipTicks;
loops++;
}
} else {
game.update();
}
game.draw();
countFps();
//_id = Mage.requestInterval( Mage.loop() );
}
/* Stop Animation */
Mage.stop = function(){
Mage.running = false;
window.clearInterval(timeout);
}
/* Interval, try to get a browser native one (runs @~30 for me in chromium) */
Mage.requestInterval = function(callback){
if(game.auto_fps !== true){
timeout = window.setInterval(callback, 1000 / game.fps);
} else if(window.requestAnimationFrame){
var _fu = function(){ callback(); if(Mage.running == true){ window.requestAnimationFrame(_fu) } };
_fu();
} else if(window.webkitRequestAnimationFrame){
var _fu = function(){ callback(); if(Mage.running == true){ window.webkitRequestAnimationFrame(_fu) } };
_fu();
} else if(window.mozRequestAnimationFrame){
var _fu = function(){ callback(); if(Mage.running == true){ window.mozRequestAnimationFrame(_fu) } };
_fu();
} else if(window.oRequestAnimationFrame){
var _fu = function(){ callback(); if(Mage.running == true){ window.oRequestAnimationFrame(_fu) } };
_fu();
} else if(window.msRequestAnimationFrame){
var _fu = function(){ callback(); if(Mage.running == true){ window.msRequestAnimationFrame(_fu) } };
_fu();
} else {
timeout = window.setInterval(callback, 1000 / game.fps);
}
}
// X, Y, WIDTH, HEIGHT, COLOR, OUTLINE, OUTLINE-SIZE
Mage.drawRect = function(x,y,w,h,c,o,l){
var color = c || false;
if(color !== false){
ctx.fillStyle = color;
ctx.fillRect(x,y,w,h);
}
var outline = o || false;
if(outline !== false){
ctx.strokeStyle = outline;
ctx.lineWidth = l || 2;
ctx.strokeRect(x, y, w, h);
}
}
// X, Y, RADIUS, COLOR, OUTLINE, OUTLINE-SIZE
Mage.drawArc = function(x,y,r,c,o,l){
var outline = o || false;
if(outline !== false){
ctx.fillStyle = outline;
lineWidth = l || 2;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
var color = c || false;
if(color !== false){
ctx.fillStyle = color;
}
ctx.beginPath();
if(lineWidth > 0){ r = r-(lineWidth) }
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
// Return canvas context for own use
Mage.getContext = function(){
return ctx
}
window.Mage = Mage;
})() ;
/**********************************************
*
* MageJS ~ My awesome Game Lib in Javascript
*
* ~ Key Manager class
*
* Uses HTML5 <audio> API to create sound
* elements and control them.
*
* Functions
* MageKeyManager.init()
* MageKeyManager.keys[] : current key map
* MageKeyManager.keystrings[] : replace map (strings)
* MageKeyManager.keyreplace[] : replace map (keycodes)
* MageKeyManager.keyExists(key) : if string key is in map
* MageKeyManager.getKeyCode(event)
* MageKeyManager.isPressed(String|KeyCode) : true|false
* MageKeyManager.returnKeys(keys) : create a true/false hash with the keys
*
***********************************************/
(function(){
MageKeyManager = {
version: "0.1",
}
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
MageKeyManager.keys = {};
MageKeyManager.keystrings = ["backspace","tab","enter","shift","ctrl","alt","caps","esc","page_up","page_down",
"end","home","left","up","right","down","insert","delete","0","1","2","3","4","5",
"6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
"q","r","s","t","u","v","w","x","y","z","space","super","num0","num1","num2","num3",
"num4","num5","num6","num7","num8","num9","f1","f2","f3","f4","f5","f6","f7","f8",
"f9","f10","f11","f12"];
MageKeyManager.keyreplace = [8,9,13,16,17,18,20,27,33,34,35,36,37,38,39,40,45,46,48,49,50,10,52,53,54,55,56,57,
65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,32,
91,96,97,98,99,100,101,102,103,104,105,112,113,114,115,116,117,118,119,120,121,
122,123];
MageKeyManager.init = function(keys){
MageKeyManager.keys = MageKeyManager.returnKeys( keys );
var body = document.getElementsByTagName('body')[0];
body.setAttribute('onkeydown','MageKeyManager.keyDown(event)');
body.setAttribute('onkeyup','MageKeyManager.keyUp(event)');
}
MageKeyManager.keyExists = function(obj) {
var i = MageKeyManager.keystrings.length;
while (i--) {
if (MageKeyManager.keystrings[i] === obj) {
return i;
}
}
return false;
}
MageKeyManager.returnKeys = function(keys){
var okeys = {};
for(var key in keys){
var con = MageKeyManager.keyExists(keys[key]);
if(con != false){
okeys[MageKeyManager.keyreplace[con]] = false;
} else {
okeys[keys[key]] = false;
}
}
return okeys;
}
MageKeyManager.getKeyCode = function(event){
xvent = event || window.event;
return xvent.keyCode;
}
MageKeyManager.keyDown = function(event){
var keycode = MageKeyManager.getKeyCode(event);
if(MageKeyManager.keys[keycode] != undefined){
if(MageKeyManager.keys[keycode] == false){
MageKeyManager.keys[keycode] = true;
}
}
}
MageKeyManager.keyUp = function(event){
var keycode = MageKeyManager.getKeyCode(event);
if(MageKeyManager.keys[keycode] != undefined){
if(MageKeyManager.keys[keycode] == true){
MageKeyManager.keys[keycode] = false
}
}
}
MageKeyManager.isPressed = function(input){
if(!isNumber(input)){
var con = MageKeyManager.keyExists(input);
if(con != false){
input = MageKeyManager.keyreplace[con];
}
}
return MageKeyManager.keys[input]
}
Mage.KeyManager = MageKeyManager;
})();
/**********************************************
*
* MageJS ~ My awesome Game Lib in Javascript
*
* ~ Mouse class
*
* Tracks mouse movements
*
* Functions
* MageMouse.init()
* MageMouse.x
* MageMouse.y
*
***********************************************/
(function(){
MageMouse = {
version: "0.1"
}
MageMouse.curLeft = 0;
MageMouse.curTop = 0;
MageMouse.x = 0;
MageMouse.y = 0;
MageMouse.move = function(event){
event = (event) ? event : ((window.event) ? window.event : "");
MageMouse.x = (event.pageX - MageMouse.curleft) || (event.clientX - MageMouse.curleft);
MageMouse.y = (event.pageY - MageMouse.curTop) || (event.clientY - MageMouse.curTop);
}
MageMouse.alert = function(){
alert(MageMouse.x +"/"+ MageMouse.y);
}
MageMouse.resize = function(){
body = document.getElementById(Mage.canvas_id) || document.getElementsByTagName('canvas')[0];
calcOffset(body);
}
function calcOffset(obj){
do {
MageMouse.curLeft += obj.offsetLeft;
MageMouse.curTop += obj.offsetTop;
} while (obj = obj.offsetParent);
}
MageMouse.init = function(elem){
body = document.getElementById(elem) || document.getElementsByTagName('canvas')[0];
body.setAttribute('onmousemove','MageMouse.move(event)');
calcOffset(body);
}
Mage.Mouse = MageMouse;
})();
/**********************************************
*
* MageJS ~ My awesome Game Lib in Javascript
*
* ~ Sound class
*
* Uses HTML5 <audio> API to create sound
* elements and control them.
*
* Functions
* MageSound.init()
* MageSound.volume(Container_Id, 0..1)
* MageSound.play(Container_Id, Boolean loop)
* MageSound.pause(Container_Id)
* MageSound.remove(Container_Id)
* MageSound.create(Container_Id, [Files Array], Boolean autoplay, Boolean loop)
*
***********************************************/
(function(){
MageSound = {
version: "0.1",
container: "mage-sound",
base_volume: 0.1
}
MageSound.init = function(){
var body = document.getElementsByTagName('body')[0];
var div = document.createElement('div');
div.setAttribute("id",MageSound.container+"_box");
div.style.display = 'none';
body.appendChild(div);
}
// Set volume to container (Does not work with chrome? :3)
MageSound.volume = function(container, volume){
var elem = document.getElementById(MageSound.container+"_audio_"+container);
elem.volume = volume;
}
MageSound.play = function(container,loop){
var elem = document.getElementById(MageSound.container+"_audio_"+container);
if(loop == true){ elem.setAttribute("loop","true"); } else { elem.removeAttribite('loop'); }
elem.play();
}
MageSound.pause = function(container){
var elem = document.getElementById(MageSound.container+"_audio_"+container);
elem.pause();
}
MageSound.remove = function(container){
var body = document.getElementsByTagName('body')[0];
var div = document.getElementById( MageSound.container+"_container_"+container );
body.removeChild(div);
}
// Play sound (String file, String container_id, Boolean loop)
MageSound.create = function(container, files, play, loop) {
if(document.getElementById( MageSound.container+"_container_"+container )) {
MageSound.remove(container);
}
var box = document.getElementById(MageSound.container+"_box");
var dix = document.createElement('audio');
dix.setAttribute("id",MageSound.container+"_audio_"+container);
dix.setAttribute("volume",""+MageSound.base_volume+"");
dix.setAttribute("controls","false");
if(play == true){ dix.setAttribute("autoplay","true"); }
dix.setAttribute("preload","true");
dix.setAttribute("autobuffer","true");
dix.volume = MageSound.base_volume;
if(loop == true){ dix.setAttribute("loop","true"); }
box.appendChild(dix);
for(var file in files){
var srx = document.createElement('source');
var type = "audio/mpeg";
if(files[file].indexOf('.ogg')>0){
type = "audio/ogg";
} else if(files[file].indexOf('.wav')>0){
type = "audio/wav";
}
srx.setAttribute('src',files[file]);
srx.setAttribute('type',type);
dix.appendChild(srx);
}
}
Mage.Sound = MageSound;
})();
/**********************************************
*
* MageJS ~ My awesome Game Lib in Javascript
*
* ~ Sound class
*
* Uses HTML5 <audio> API to create sound
* elements and control them.
*
* Functions
* MageSprite.create(image,width,height,x,y,speed)
* MageSprite.update()
* MageSprite.draw()
* MageSprite._log()
*
* Accessor
* image
* version
* x
* y
* width
* height
* sprite_x
* sprite_y
* scale
* animation[]
* current_animation
* current_frame
* complete : loaded?
* speed : Milliseconds
*
***********************************************/
/**
* Sprite Class
* @class
*/
function MageSprite(){
this.image;
this.version = "0.1";
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this.sprite_x = 0;
this.sprite_y = 0;
this.scale = 1;
this.animation = new Object();
this.current_animation = "ALL";
this.current_frame = 0;
this.complete = true;
this.lastmove = 0;
this.speed = 500;
/**
Creates a Sprite
@param {string} image - Source of the image
@param {integer} width - Width of the *shown* image
@param {integer} height - Height of the *shown* image
@param {integer} x - X position on Canvas
@param {integer} y - Y position on Canvas
@param {integer} speed - Time for animation in Milliseconds
*/
this.create = function(image,width,height,x,y,speed){
this.image = new Image();
this.image.src = image;
this.speed = speed || 500;
this.height = height || 16;
this.width = width || 16;
this.x = x || 0;
this.y = y || 0;
this.image.width = 64;
this.image.height = 64;
this.animation["ALL"] = Mage.range(0,(this.image.width/this.width)*(this.image.height/this.height)-1);
return this;
}
/**
Updates Animation (calculates if needed)
*/
this.update = function(){
if(this.complete && this.lastmove < (new Date).getTime()-this.speed){
this.lastmove = (new Date).getTime();
var current = this.animation[this.current_animation][this.current_frame];
var row = Math.floor(current*this.height/this.image.height);
this.sprite_y = row * this.height;
this.sprite_x = Math.floor(current - ((this.image.width/this.width)*row) ) * this.width;
if(this.current_frame >= this.animation[this.current_animation].length-1)
{ this.current_frame = 0; } else { this.current_frame++; }
}
}
/**
Draws a sprite
@param {CanvasContext} ctx - Canvas Context to draw on
*/
this.draw = function(ctx){
if(this.complete){
ctx.drawImage(this.image,this.sprite_x,this.sprite_y,this.width,this.height,this.x,this.y,this.width*this.scale,this.height*this.scale);
}
}
/**
Dumps some Sprite info to the log
*/
this._log = function(){
console.log("X: "+this.x+" Y: "+this.y+" WIDTH: "+this.width+" HEIGHT: "+this.height)
}
}
Mage.Sprite = MageSprite;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment