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

  • 455