Retour d'expérience
Sur FarmAttack
FarmAttack
Un jeu multi-joueurs en temps réel en 3D isométrique

Problématiques
- Un jeu sur navigateur
- 3D isométrique
- Une plateforme pour gérer le contenu du jeu
- Authentification
- Multi-joueurs, interaction entre joueurs.
- Un monde persistant, les joueurs évoluent dans un environnement dynamique.
- Carte infinie
Technologies
Une plateforme pour gérer le contenu du jeu

Node.js® is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
NodeJS
console.log("first");
setTimeout(function() {
console.log("second");
}, 0);
console.log("third");
// output
first
third
second
ExpressJS
// app.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('<h1>Hello World!</h1>');
});
var server = app.listen(3000, function () {
console.log('Example app listening at http://localhost:3000');
});
# Install
$ npm install express
# Launch application
$ npm app.js

Un moteur de template ?
// app.js
var express = require('express');
var app = express();
app.set('view engine', 'jade'); // default value
Twig aussi !
# Install
$ npm install twig
// app.js
require("twig");
var express = require('express');
var app = express();
app.set('view engine', 'twig');
app.set('views', __dirname + '/../view');
// app.js
// ...
app.get('/', function(req, res){
res.render('index', {
message : "Hello World"
});
});

Message of the moment: <b>{{ message }}</b>
view/index.twig
Authentification ?

$ wget http://download.redis.io/releases/redis-3.0.0.tar.gz
$ tar xzf redis-3.0.0.tar.gz
$ cd redis-3.0.0
$ make
# or
$ brew install redis
# etc..
$ redis-server
C'est tout !
Installation
$ npm install redis
Ba voui ba non !
// app.js
var express = require('express');
var app = express();
var redis = require('redis');
app.get('/login', function (req, res) {
res.render('login');
});
app.post('/login', function (req, res) {
player = playerModel.findOne( //... some logic);
var token = generateToken(32);
redis.set(player.username, token);
res.cookie('__secretTokenDontStealMeDontInjectMePlease#Yolo', token);
res.redirect('/');
});
Multi-joueurs, interaction entre joueurs
Socket.IO enables real-time bidirectional event-based communication.
# Install
$ npm install socket.io
// app.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.render('index');
});
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
});
<html>
<body>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script>
var socket = io();
</script>
<body>
</html>

Et maintenant ?
// app.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
socket.on('chat message', function(msg){
// Broadcast
io.emit('chat message', msg);
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
3D isométrique





- Représentation d'un objet 3D en 2D
- Pas de distortions
- Nécessite peu de ressources
- Plus simple que la 3D

Les tuiles


Dessiner une tuile

Tileset
Générer la carte
isoToCart: function(isoX, isoY)
{
var height = 50;
var width = 100;
var x = (isoX - isoY) * width / 2;
var y = (isoX + isoY) * height / 2;
return {
x: x,
y: y
};
},






Gestion de la profondeur :
i + j


8 directions


Les personnages

Déplacements et colision
- Algorithme A*
- Tuiles non-walkable
EaselJS

DisplayObject
- Tout les objets visibles sont des DisplayObject.
- Images : Bitmap
- Vecteurs : Shape
- Animations : SpriteSheet
- Text
- Container
var bitmap = new createjs.Bitmap("imagePath.jpg");
var shape = new createjs.Shape();
shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
Container
- Permet de grouper des éléments graphiques
- Permet de cacher un groupe d'objets
- Permet d'effectuer du sorting
var container = new createjs.Container();
container.addChild(bitmapInstance, shapeInstance);
container.x = 100;
stage = new createjs.Stage("canvas");
Stage
- Wrap l'element canvas
- Container principal
- stage.update();
Ticker
var stage, circle;
function init() {
stage = new createjs.Stage("canvas");
circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 40);
circle.y = 50;
stage.addChild(circle);
createjs.Ticker.addEventListener("tick", tick);
createjs.Ticker.setFPS(30);
}
function tick(event) {
circle.x = circle.x + 5;
if (circle.x > stage.canvas.width) { circle.x = 0; }
stage.update();
}
Architecture

Carte infini !?

# Install
$ npm install mongoose
// db.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Cat = mongoose.model('Cat', { name: String });
var kitty = new Cat({ name: 'Zildjian' });
kitty.save(function (err) {
if (err) // ...
console.log('meow');
});
// model/tile.js
var tileSchema = new mongoose.Schema({
_groupTileParent: {type: Schema.Types.ObjectId, ref: 'groupTile'},
x: Number,
y: Number,
walkable: {type: Boolean, default: true},
owner: {type: String, default: 'NEUTRAL'},
fertility: Number,
maxFertility: Number,
humidity: Number,
crop: String,
cropMaturity: Number,
imageIndex: Number,
building: {
name: String,
crop: [{
name: String,
quantity: Number
}]
}
});
var groupTileSchema = new mongoose.Schema({
player : {type: String, default: 'NULL'},
enable : {type: Boolean, default: false},
tiles : [{type: Schema.Types.ObjectId, ref: 'tile'}],
created_at: {type: Date, default: Date.now}
});
FarmAttack
By skigun
FarmAttack
- 479