Browser, draw me something amazing


Recently I’ve seen an amazing demo of Assassin’s Creed Pirates inside browser: Assassin’s Creed. I was very impressed by this demo and looked a bit into the details.

So, this demo was written with a help of babylon.js engine. A lot of information about this framework can be found in this blog.

This engine is a very easy-to-use JavaScript library. All you need – is to download it and start to create amazing things. Let me show you a demo of Snake:
Snake

You can download a full version using this link.
Keys:

  • W – up;
  • A – left;
  • S – down;
  • D – right.

Hope you’ll enjoy it and will dig a little bit into the WebGL to create your own demo.

body {margin:0;}
body, canvas {width:100%; height:100%;overflow:hidden;}
div {position:absolute; width:100%; height: 100%; z-index:100;}

view raw
snake.css
hosted with ❤ by GitHub

<!DOCTYPE html>
<html>
<head>
<script data-main="javascript/index" src="javascript/libraries/require.js" type="text/javascript"></script>
<link href="css/main.css" type="text/css" rel="Stylesheet" />
</head>
<body>
<div style="display:none;"></div>
<canvas id="renderCanvas"></canvas>
</body>
</html>

view raw
snake.html
hosted with ❤ by GitHub

requirejs.config({
baseUrl: 'javascript'
});
define('index', ['ui/main'], function() {
});
define('game', [], function() {
var fieldSize = 15,
defaultInterval = 350,
intervalStep = 30,
intervalMinimum = 50,
snake = null,
applePosition = null,
events = {
'gameOver': [],
'move': [],
'increase': [],
'createApple': []
},
currentDirection = null,
intervalId = null,
currentInterval = null,
fireEvent = function(eventName, params) {
var i, handlers;
if (!events.hasOwnProperty(eventName)) {
throw 'Unsupported event';
}
handlers = events[eventName];
for (var i = 0; i < handlers.length; ++i) {
handlers[i].apply(this, params);
}
},
increaseSpeed = function() {
if (snake.length % 10 === 0 && currentInterval > intervalMinimum) {
clearInterval(intervalId);
currentInterval -= intervalStep;
intervalId = setInterval(move, currentInterval);
}
},
move = function() {
var headPosition = snake[0],
tailPosition = snake[snake.length 1],
newPosition = {x: headPosition.x + currentDirection.x, y: headPosition.y + currentDirection.y};
if (!isCorrectPosition(newPosition)) {
clearInterval(intervalId);
fireEvent('gameOver', [snake.length]);
return;
}
snake.unshift(newPosition);
if (newPosition.x === applePosition.x && newPosition.y === applePosition.y) {
createApple();
fireEvent('increase', [newPosition]);
increaseSpeed();
} else {
snake.pop();
fireEvent('move', [newPosition]);
}
},
getRandom = function() {
return Math.floor((Math.random()*fieldSize));
},
createApple = function() {
var correctPlace = false;
while (!correctPlace) {
applePosition = {x: getRandom(), y: getRandom()};
correctPlace = true;
for (var i = 0; i < snake.length; ++i) {
if (snake[i].x === applePosition.x && snake[i].y === applePosition.y) {
correctPlace = false;
break;
}
}
}
fireEvent('createApple', [applePosition]);
},
isCorrectPosition = function(position) {
var isCorrect = true;
if (position.x < 0 || position.x >= fieldSize || position.y < 0 || position.y >= fieldSize ) {
return false;
}
for (var i = 0; i < snake.length 2; ++i) {
if (position.x === snake[i].x && position.y === snake[i].y) {
isCorrect = false;
break;
}
}
return isCorrect;
},
isCorrectDirection = function(direction) {
if (currentDirection.x === 1*direction.x && currentDirection.y === 1*direction.y) {
return false;
}
return true;
},
startGame = function() {
var defaultState = {x: 0, y: 0};
snake = [defaultState];
clearInterval(intervalId);
currentInterval = defaultInterval;
intervalId = setInterval(move, currentInterval);
createApple();
};
return {
directions: {
up: {x: 0, y: 1},
down: {x: 0, y: 1},
left: {x: 1, y: 0},
right: {x: 1, y: 0}
},
addEventListener: function(eventName, callback) {
if (events.hasOwnProperty(eventName)) {
events[eventName].push(callback);
} else {
throw 'Unsupported event';
}
},
changeDirection: function(direction) {
if (currentDirection === null) {
currentDirection = direction;
startGame();
} else if (isCorrectDirection(direction)){
currentDirection = direction;
}
},
getFieldSize: function() {
return fieldSize;
}
};
});
define('engine', ['libraries/babylon'], function() {
if (!BABYLON.Engine.isSupported()) {
return;
}
var engine = {};
engine.canvas = document.getElementById('renderCanvas');
engine.engine = new BABYLON.Engine(engine.canvas, true);
engine.scene = new BABYLON.Scene(engine.engine);
engine.camera = new BABYLON.ArcRotateCamera("MainCamera", 7*Math.PI/16, Math.PI/4, 50, BABYLON.Vector3.Zero(), engine.scene);
engine.light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(0, 100, 0), engine.scene);
renderLoop = function () {
//engine.beginFrame();
engine.scene.render();
//engine.endFrame();
BABYLON.Tools.QueueNewFrame(renderLoop);
};
BABYLON.Tools.QueueNewFrame(renderLoop);
engine.camera.attachControl(engine.canvas);
return engine;
});
define('ui/appleElement', ['ui/baseObject', 'engine', 'libraries/babylon'], function(BaseObject, engine) {
var appleElement = function(position) {
this._createElement();
this.moveTo(position);
};
appleElement.prototype = new BaseObject();
appleElement.prototype._texture = 'images/appleTexture.jpg';
appleElement.prototype._createElement = function() {
this._object = BABYLON.Mesh.CreateSphere("Sphere", 16, this._size, engine.scene);
var material = new BABYLON.StandardMaterial("default", engine.scene);
material.diffuseTexture = new BABYLON.Texture(this._texture, engine.scene);
this._object.material = material;
};
return appleElement;
});
define('ui/baseObject', ['game'], function(game) {
var constructor = function() {};
constructor.prototype = {
_size: 2,
_fieldSize: game.getFieldSize(),
_object: null,
_convertPositionToCoordinate: function(position) {
var middlePosition = (this._fieldSize 1)/2,
coordinate = {
x: (position.x middlePosition)*2,
y: 0,
z: (position.y middlePosition)*2
};
return coordinate;
},
moveTo: function(position) {
var coordinates = this._convertPositionToCoordinate(position);
this._object.position.x = coordinates.x;
this._object.position.y = coordinates.y;
this._object.position.z = coordinates.z;
}
};
return constructor;
});
define('ui/main', ['engine', 'game', 'ui/snakeElement', 'ui/appleElement', 'ui/planeField'], function(engine, game, SnakeElement, AppleElement) {
var snake = [new SnakeElement({x: 0, y: 0})],
apple = null;
game.addEventListener('move', function(position) {
var tail = snake.pop();
tail.moveTo(position);
snake.unshift(tail);
});
game.addEventListener('increase', function(position) {
snake.unshift(new SnakeElement(position));
});
game.addEventListener('createApple', function(position) {
if (apple === null) {
apple = new AppleElement(position);
} else {
apple.moveTo(position);
}
});
game.addEventListener('gameOver', function(length) {
alert('Epic fail on length: ' + length);
});
window.addEventListener('keydown', function(event) {
switch(event.keyCode) {
case 87: //W
game.changeDirection(game.directions.up);
break;
case 65: //A
game.changeDirection(game.directions.left);
break;
case 83: //S
game.changeDirection(game.directions.down);
break;
case 68: //D
game.changeDirection(game.directions.right);
break;
}
});
return null;
});
define('ui/planeField', ['engine', 'game', 'libraries/babylon'], function(engine, game) {
var diameter = 2,
planeMaterial = new BABYLON.StandardMaterial("default", engine.scene),
plane = BABYLON.Mesh.CreatePlane('GameField', game.getFieldSize() * diameter, engine.scene);
plane.rotation.x = Math.PI/2;
plane.position.y -= diameter/2;
planeMaterial.diffuseTexture = new BABYLON.Texture("images/grassTexture.jpg", engine.scene);
planeMaterial.diffuseTexture.uScale = planeMaterial.diffuseTexture.vScale = game.getFieldSize();
plane.material = planeMaterial;
return null;
});
define('ui/snakeElement', ['ui/baseObject', 'engine', 'libraries/babylon'], function(BaseObject, engine) {
var snakeElement = function(position) {
this._createElement();
this.moveTo(position);
};
snakeElement.prototype = new BaseObject();
snakeElement.prototype._texture = 'images/snakeTexture.jpg';
snakeElement.prototype._createElement = function() {
this._object = BABYLON.Mesh.CreateSphere("Sphere", 16, this._size, engine.scene);
var material = new BABYLON.StandardMaterial("default", engine.scene);
material.diffuseTexture = new BABYLON.Texture(this._texture, engine.scene);
this._object.material = material;
};
return snakeElement;
});

view raw
snake.js
hosted with ❤ by GitHub

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s