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:
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;} |
<!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> |
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; | |
}); |