Outils pour utilisateurs

Outils du site


wiki:projets:crowd

Ceci est une ancienne révision du document !


Cro(ne)wd

Daphné Chamot-Rooke (contact : daphne.chamot-rooke@etu.upmc.fr)
Himany Seri (contact : himany.seri@etu.upmc.fr)

Début : Février 2019
Fin : mai 2019

Objectif : créer un dispositif qui modifie la perception d'autrui

Matériel :

  • plusieurs PC avec souris
  • une connexion internet
  • logiciel Processing installé

Cahier des charges

Introduction

De nos jours l’étude des mouvements de foule permet de simuler la mécanique des foule : ses mouvements semblables à la physique des fluides et sa psychologie sociale. Dans de nombreuses situations nous avons affaire à des mouvements de foule, que ce soit dans des concerts, des conventions, des manifestations, et même sur les réseaux sociaux.
Comment l’individu influence-t-il la foule et comment celle-ci influence telle l’individu ? Comment initie-t-on un mouvement de foule ?
Nous avons décidé d'orienter notre projet sur comment percevoir la foule et comment *se* percevoir dans la foule. Les mécanismes de l'individu à la foule sont en effet des allers-retours, comme lors d'un croisement de regard.

Description et évolution du projet

Nous avions d'abord pensé à travailler sur les émotions, les émotions dans la foule. Nous avions eu l'idée d'un bracelet retranscrivant l'émotion globale de la foule. Puis nous nous sommes orientées vers des applications, car cela nous semblait plus réalisable pour travailler avec de nombreuses personnes. Nous avons choisi de créer un programme pour ordinateur, un jeu multijoueur qui permettrait de retranscrire des mouvements de foule.
Notre jeu se compose d'un avatar représentant soi-même à la 3e personne, vu du dessus. Un “monstre” invisible “mange” les avatars lorsqu'il croise leur chemin. Il faut donc faire attention à son entourage, suivre les autres.
Nous voulons faire plusieurs “salles” avec plusieurs consignes, peut-être en faire une où l'on se voit à la première personne, et une autre avec des consignes différentes pour chaque individu afin de créer un tout avec l'action de chaque individu.

Expression fonctionnelle du besoin

Veille

Veille des dispositifs

Veille graphique

Veille typographique

Réalisation

Code

Code JavaScript pour le serveur :

//this code is based on https://github.com/CodingTrain/website/tree/master/CodingChallenges/CC_032.2_agar.io_sockets/Node

var blobs = [];					//liste pour stocker les blobs

function Blob(id, x, y) {		//définit l'objet blob
  this.id = id;
  this.x = x;
  this.y = y;
}

var monstres = [];				//liste pour stocker les monstres

function Monster (xm, ym) {		//définit l'objet monstre
    this.xm = xm;
	this.ym = ym;
	
}

// Using express: http://expressjs.com/
var express = require('express');
// Create the app
var app = express();

// Set up the server
// process.env.PORT is related to deploying on heroku
var server = app.listen(process.env.PORT || 3000, listen);

// This call back just tells us that the server has started
function listen() {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://' + host + ':' + port);
}

app.use(express.static('public'));


// WebSocket Portion
// WebSockets work with the HTTP server
var io = require('socket.io')(server);

setInterval(heartbeat, 33);

function heartbeat() {
	io.sockets.emit('heartbeat_blobs', blobs);
	io.sockets.emit('heartbeat_monstre', monstres);
}


// Register a callback function to run when we have an individual connection
// This is run for each individual user that connects
io.sockets.on('connection',
  // We are given a websocket object in our function
  function(socket) {

    console.log("We have a new client: " + socket.id);

    socket.on('start_blobs',
      function(data) {
        console.log(socket.id + " " + data.x + " " + data.y);
		socket.broadcast.emit('start_blobs', data);
        var blob = new Blob(socket.id, data.x, data.y);
        blobs.push(blob);
	  }
	);
	
	socket.on('start_monstres',
      function(datam) {
        console.log(datam.xm + " " + datam.ym);
		socket.broadcast.emit('start_monstres', datam);
        var monstre = new Monster(datam.xm, datam.ym);
        monstres.push(monstre);
	  }
	);

    socket.on('update',
      function(data) {
		//console.log(blobs);
        for (var i = 0; i < blobs.length; i++) {
          if (socket.id == blobs[i].id) {
			//console.log(blobs[i].id);
			blobs[i].x = data.x;
			blobs[i].y = data.y;
          }
		}
      }
    );
	
	socket.on('move',
      function(datam) {
		//console.log(blobs);
        for (var i = 0; i < monstres.length; i++) {
			monstres[i].xm = datam.xm;
			monstres[i].ym = datam.ym;
		}
      }
    );
	
	socket.on('eaten', function (data) {
            // remove the eaten blob
            // set array of blobs excluding the eaten blob

            var eaten = {
                id: ''
            };
			
			var newblob = blobs.filter(function( blobs ) {
                if (blobs.id === data.eatenId) {
                    eaten.id = blobs.id;
                }

                return blobs.id !== data.eatenId;
            });
			blobs = newblob;

            io.sockets.emit('', blobs);
	});
           
	socket.on('disconnect', function() {
        console.log("Client has disconnected" + " " + socket.id);
            blobs = blobs.filter(function( blobs ) {
                return blobs.id !== socket.id;
            });
        });
    }
);

Code JavaScript pour le client :

//déclaration de variables
var socket;
var blob;           //personnage
var blobs = [];		//liste de blobs
var monstres = [];
var monstre;        //monstre
var timer;			//bool pour le timer
var jeu = false;    //bool pour le jeu
var intro = true;   //bool pour l'intro
const radius = 30;     //rayon du monstre
var alive = true;

function setup() {
    
    createCanvas(900, 600);
	if (alive) {
		socket = io.connect('http://localhost:3000');
	}
    
	//nouveaux blob, monstre et timer
    timer = new Timer();
    blob = new Blob(random(width), random(height));
	monstre = new Monster(random(width), random(height));
	
	//data pour la position du blob
	var data = {
		x: blob.pos.x,
		y: blob.pos.y,
	};
	
	//data pour la position du monstre
	var datam = {
		xm: monstre.pos.x,
		ym: monstre.pos.y,
	};

	socket.emit('start_blobs', data);			//partage les data
	socket.emit('start_monstres', datam);
	alive = true;
	
	socket.on('heartbeat_blobs',
		function(data) {
		var stillAlive = false;
		for (var i = 0; i < data.length; i++) {
            if (socket.id == data[i].id) {
                stillAlive = true;
                //console.log('heart beat'); 
            }
		}
		//console.log(data);
		alive = stillAlive;
		blobs = data;
		});
		
	socket.on('heartbeat_monstre',
	function(datam) {
		monstres = datam;
		});
}

function draw() {
    background(0);              //fond noir, taille du texte 30 et écrit en blanc
    textSize(30);
    fill(255);
    a=timer.minute();
    b=timer.second();
    temps = a + ":" + b;        //on stocke les secondes et les minutes dans un string
		
    if (socket.connected && alive && jeu) {
        timer.stop();                   //démarre le timer
		//	if (isNaN(blob.pos.x) || isNaN(blob.pos.y)) {
		//        alive = false;
		//  }
		blob.show();                    //dessine le blob
		//if(b%5==0) {
		//monstre.show();      //dessine le monstre de temps en temps
		//}
		blob.update();
		monstre.deflect();              //empêche que le monstre sorte des bords
		monstre.move();                 //le monstre bouge
		for (var i = blobs.length - 1; i >=0; i--) {
			if (socket.id !== blobs[i].id) {
				fill(255);
				circle(blobs[i].x, blobs[i].y, 20);
			} else {
				var newblob = new Blob(blobs[i].x, blobs[i].y, 20);
				if (mange(newblob)) {
					var data = {
						eatenId: blobs[i].id
					};
				socket.emit('eaten', data);
				}
			}
		}
	
		for (var i = monstres.length - 1; i >=0; i--) {
			fill(blob.c);
			//noFill();
			circle(monstres[i].xm, monstres[i].ym, radius*2)
		}
		
		
        //jeu = mange(blob);                  //mange le blob si ils se rencontrent
		score = temps;
        text(temps, width/2 - 40, 50);  //affiche le temps en haut de l'écran		
		 var data = {
			x: blob.pos.x,
			y: blob.pos.y,
		};

		 var datam = {
			xm: monstre.pos.x,
			ym: monstre.pos.y,
		};
		
		socket.emit('update', data);
		socket.emit('move', datam);
		
    } if (!jeu) {
        timer.start();
        fill(255);
        stroke(255);
        textSize(50);
        
        if (intro) {
            //si c'est l'intro : titre du jeu et démarrer
            text("Foule", width/2 - 70, height/2 - 100);
            text("Cliquer pour jouer", width/2 - 200, height/2);
        } else {
            //si c'est la fin du jeu : Game Over et le temps final s'affichent
            text("Game Over", width/2 - 140, height/2 - 100);
            text("Score : " + score, width/2 - 150, height/2);
			text("Appuyer sur F5 pour redémarrer", width/20, height/2 + 100);
        }
    }
}

function reset() {          //quand c'est la fin du jeu, permet de redémarrer une partie juste en cliquant
	console.log('allez');
    alive=true;
	stillAlive=true;
	jeu = true;
    //blob = new Blob(random(width), random(height));
    //monstre = new Monster(random(radius,width-radius),random(radius,height-radius));
    timer.stop();   //redémarre le timer à 0
}

function mousePressed() {   //on passe l'intro et la fin du jeu en cliquant
    intro=false;
	alive = true;
    if (jeu==false) {
        reset();
    }
}



// Classe Blob

class Blob {
	constructor(x,y) {
		this.pos = createVector(x, y);
		this.vel = createVector(0, 0);
		this.acc = createVector(0, 0);
		this.topspeed = 2;
		this.r = 20;
		this.c = color(int (50 + Math.random() * (220 - 50)), int (50 + Math.random() * (220 - 50)), int (50 + Math.random() * (220 - 50)));
      													//couleur ni trop sombre ni trop claire
	}	
    update () {
        this.mouse = createVector(mouseX, mouseY);      //position du curseur
        this.acc.add(this.mouse);
        this.acc.sub(this.pos);
        this.acc.setMag(0.2);                           //on définit le vecteur accélération
        this.vel.add(this.acc);                         //on ajoute l'accélération au vecteur vitesse
        this.mouse.sub(this.pos);                       //
        this.mouse.setMag(7);                           //magnitude force de rappel du blob vers le curseur
        this.pos.add(this.mouse);                       //on ajoute à la position courante
		this.vel.limit(this.topspeed);                  //valeur limite à la vitesse
        this.pos.add(this.vel);                         //on ajoute la vitesse au vecteur position
        this.pos.x=Math.min(this.pos.x,width-10);       //gestion des coins de la boite
        this.pos.x=Math.max(this.pos.x,10);
        this.pos.y=Math.min(this.pos.y,height-10);
        this.pos.y=Math.max(this.pos.y,10);

    }
    
    show () {          //dessine le blob
        fill(this.c);
        stroke(this.c);
        circle(this.pos.x, this.pos.y, this.r);
    }
}

//  Classe Monster

class Monster {
    constructor(x, y) {
        this.pos = createVector(x, y);
        this.vel = createVector(random(2,4),random(2,4));
        if (int(random(0, 2)) == 1) {
            this.vel.x *= -1;
        }
        if (int(random(0, 2)) == 1) {
            this.vel.y *= -1;
        }
    }
	


    move () {        //permet au monstre de bouger (à vitesse constante si epsi=0,0)
        this.epsi = createVector(random(-1,1), random(-1,1)); // Exciter la vitesse pour mettre un peu de piment ...
        this.vel.add(this.epsi);
        this.pos.add(this.vel);
		if(b%5==0) { 
			this.vel = createVector(random(2, 4), random(2, 4));
			if (int(random(0, 2)) == 1) {
				this.vel.x *= -1;
			}
			if (int(random(0, 2)) == 1) {
				this.vel.y *= -1;
			}
		}
    }

    /*show () {          //dessine le monstre
        strokeWeight(1);
        noFill();
        circle(this.pos.x, this.pos.y, radius*2);
    }*/

    deflect () {      //permet au monstre de ne pas sortir des bords, il "rebondit"
        if (this.pos.x + radius > width || this.pos.x - radius < 0) {
        this.vel.x *= -1;
		}
		if (this.pos.y + radius > height || this.pos.y - radius < 0) {
		this.vel.y *= -1;
		}
    }
	
}

mange = function(blob) {         //le monstre "mange" le blob s'ils se rencontrent et le jeu est fini
    if (this.blob.pos.x <= monstre.pos.x + radius && this.blob.pos.x >= monstre.pos.x - radius && this.blob.pos.y <= monstre.pos.y + radius && this.blob.pos.y >= monstre.pos.y - radius) {
		jeu = false;
		return true;
    } 
	return false;
}



//  Classe Timer

class Timer {
    constructor() {
        this.startTime = 0;
        this.stopTime = 0;
        this.running = false;
    }
    
    start () {
        this.startTime = new Date();
        this.running = true;
    }
    
    stop () {
        this.stopTime = new Date();
        this.running = false;
    }
    
    getElapsedTime () {
        if (this.running) {
            this.elapsed = (new Date() - this.startTime);
        } else {
            this.elapsed = (this.stopTime - this.startTime);
        }
        return this.elapsed;
    }
    
    second () {
        this.sec = int ((this.getElapsedTime() / 1000) % 60);
        if (this.sec<10) {
            this.sec="0" + this.sec;
        } else {
            this.sec="" + this.sec;
        }
        return this.sec;
    }
    
    minute () {
        this.min = int ((this.getElapsedTime() / (1000*60)) % 60);
        if (this.min<10) {
            this.min="0" + this.min;
        } else {
            this.min="" + this.min;
        }
        return this.min;
    }
}

Code html pour l'index :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
	    <script type="text/javascript" src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
            <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
	    <script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
            <script type="text/javascript" src="crowd.js"></script>
         </head>
    <body>
    </body>
</html>

Mémoire

wiki/projets/crowd.1558468657.txt.gz · Dernière modification: 2019/05/21 19:57 de daphne