Fluid animations

This commit is contained in:
Geoffrey Frogeye 2015-06-05 17:41:07 +02:00
parent 48397199c5
commit 2aa8ec685a

181
script.js
View file

@ -1,20 +1,9 @@
function AnimatedValue(object, name, value, timeout) { function AnimatedValue(value) {
this.overwrite(value); this.write(value);
this.timeout = timeout;
var _this = this;
Object.defineProperty(object, name, {
get: function() {
return _this.get();
},
set: function(x) {
return _this.set(x);
}
});
object[name + 'handler'] = this;
} }
AnimatedValue.prototype = { AnimatedValue.prototype = {
get: function() { get: function () {
var now = performance.now(), var now = performance.now(),
end = this.frTime + this.timeout; end = this.frTime + this.timeout;
if (now >= end) { if (now >= end) {
@ -23,17 +12,19 @@ AnimatedValue.prototype = {
return this.toVal - (this.toVal - this.frVal) * (end - now) / this.timeout; return this.toVal - (this.toVal - this.frVal) * (end - now) / this.timeout;
} }
}, },
set: function(value) { set: function (value, timeout) {
if (value != this.toVal) { if (value != this.toVal) {
this.timeout = timeout;
this.frVal = this.get(); this.frVal = this.get();
this.toVal = value; this.toVal = value;
this.frTime = performance.now(); this.frTime = performance.now();
} }
}, },
overwrite: function(value) { write: function (value) {
this.frVal = value; this.frVal = value;
this.toVal = value; this.toVal = value;
this.frTime = performance.now() - this.timeout; // so end == now this.timeout = 0;
this.frTime = performance.now(); // so end == now
} }
}; };
@ -42,53 +33,49 @@ function BallView(main, ball) {
this.ball = ball; this.ball = ball;
this.graphic = new PIXI.Graphics(); this.graphic = new PIXI.Graphics();
new AnimatedValue(this, 'x', 0, 100); this.x = new AnimatedValue(0);
new AnimatedValue(this, 'y', 0, 100); this.y = new AnimatedValue(0);
new AnimatedValue(this, 's', 0, 100); this.s = new AnimatedValue(0);
var _this = this; var _this = this;
// console.log(_this.ball.id, 'NEW');
this.appear(); this.appear();
this.ball.on('appear', function() { this.ball.on('appear', function () {
_this.appear(); _this.appear();
}); });
this.ball.on('destroy', function() { this.ball.on('destroy', function () {
// console.log(_this.ball.id, 'DES');
_this.main.stage.removeChild(_this.graphic); _this.main.stage.removeChild(_this.graphic);
}); });
this.ball.on('disappear', function() { this.ball.on('disappear', function () {
// console.log(_this.ball.id, 'DIS');
_this.main.stage.removeChild(_this.graphic); _this.main.stage.removeChild(_this.graphic);
}); });
this.ball.on('move', function() { this.ball.on('move', function () {
_this.x = _this.ball.x; _this.x.set(_this.ball.x, 100);
_this.y = _this.ball.y; _this.y.set(_this.ball.y, 100);
}); });
this.ball.on('resize', function() { this.ball.on('resize', function () {
_this.s = _this.ball.size; _this.s.set(_this.ball.size, 100);
}); });
} }
BallView.prototype = { BallView.prototype = {
appear: function() { appear: function () {
// console.log(this.ball.id, 'APP'); this.x.write(this.ball.x);
this.x = this.ball.x; this.y.write(this.ball.y);
this.y = this.ball.y; this.s.write(this.ball.size);
this.s = this.ball.size;
this.shape(); this.shape();
this.main.stage.addChild(this.graphic); this.main.stage.addChild(this.graphic);
}, },
shape: function() { shape: function () {
this.graphic.clear(); this.graphic.clear();
this.graphic.beginFill(this.ball.color.replace('#', '0x'), this.graphic.beginFill(this.ball.color.replace('#', '0x'),
this.ball.virus ? 0.5 : 0.9); this.ball.virus ? 0.5 : 0.9);
this.graphic.drawCircle(0, 0, 1); this.graphic.drawCircle(0, 0, 1);
this.graphic.endFill(); this.graphic.endFill();
}, },
render: function() { render: function () {
this.graphic.position.x = this.x; this.graphic.position.x = this.x.get();
this.graphic.position.y = this.y; this.graphic.position.y = this.y.get();
this.graphic.scale.x = this.graphic.scale.y = this.s; this.graphic.scale.x = this.graphic.scale.y = this.s.get();
} }
}; };
@ -102,7 +89,7 @@ function Viewer(client, container) {
this.addRenderer(); this.addRenderer();
this.addStats(); this.addStats();
var _this = this; var _this = this;
client.once('mapSizeLoad', function(min_x, min_y, max_x, max_y) { client.once('mapSizeLoad', function (min_x, min_y, max_x, max_y) {
_this.gameWidth = max_x; _this.gameWidth = max_x;
_this.gameHeight = max_y; _this.gameHeight = max_y;
_this.initStage(); _this.initStage();
@ -111,52 +98,54 @@ function Viewer(client, container) {
_this.animate(); _this.animate();
_this.emit('launched'); _this.emit('launched');
}); });
window.addEventListener('resize', function() { window.addEventListener('resize', function () {
_this.updateSize(); _this.updateSize();
}); });
} }
Viewer.prototype = { Viewer.prototype = {
getSize: function() { getSize: function () {
this.width = window.innerWidth; this.width = window.innerWidth;
this.height = window.innerHeight; this.height = window.innerHeight;
}, },
addRenderer: function() { addRenderer: function () {
this.getSize(); this.getSize();
this.renderer = PIXI.autoDetectRenderer(this.width, this.height, { this.renderer = PIXI.autoDetectRenderer(this.width, this.height, {
antialias: true antialias: true
}); });
this.container.appendChild(this.renderer.view); this.container.appendChild(this.renderer.view);
}, },
updateSize: function() { updateSize: function () {
this.getSize(); this.getSize();
this.renderer.resize(this.width, this.height); this.renderer.resize(this.width, this.height);
}, },
initStage: function() { initStage: function () {
this.stage = new PIXI.Container(); this.stage = new PIXI.Container();
this.cam = {}; this.cam = {
new AnimatedValue(this.cam, 'x', this.gameWidth / 2, 100); x: new AnimatedValue(this.gameWidth / 2),
new AnimatedValue(this.cam, 'y', this.gameHeight / 2, 100); y: new AnimatedValue(this.gameHeight / 2),
new AnimatedValue(this.cam, 's', 0.5 / 2, 100); s: new AnimatedValue(0.5)
};
}, },
addListners: function() { addListners: function () {
var _this = this; var _this = this;
this.client.on('ballAppear', function(id) { this.client.on('ballAppear', function (id) {
if (!_this.balls[id]) { if (!_this.balls[id]) {
_this.balls[id] = new BallView(_this, this.balls[id]); _this.balls[id] = new BallView(_this, this.balls[id]);
} else {
} }
}); });
this.client.on('ballDestroy', function(id) { // this.client.on('ballDestroy', function(id) {
delete this.balls[id]; // delete this.balls[id];
}); // });
}, },
addBorders: function() { addBorders: function () {
this.borders = new PIXI.Graphics(); this.borders = new PIXI.Graphics();
this.borders.lineStyle(5, 0xFF3300, 1); this.borders.lineStyle(5, 0xFF3300, 1);
this.borders.drawRect(0, 0, this.gameWidth, this.gameHeight); this.borders.drawRect(0, 0, this.gameWidth, this.gameHeight);
this.stage.addChild(this.borders); this.stage.addChild(this.borders);
}, },
addStats: function() { addStats: function () {
this.stats = new Stats(); this.stats = new Stats();
this.stats.setMode(1); this.stats.setMode(1);
this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.position = 'absolute';
@ -164,7 +153,7 @@ Viewer.prototype = {
this.stats.domElement.style.top = '0px'; this.stats.domElement.style.top = '0px';
document.body.appendChild(this.stats.domElement); document.body.appendChild(this.stats.domElement);
}, },
posCamera: function() { posCamera: function () {
var x = y = p = 0; var x = y = p = 0;
for (var ball_id in this.client.my_balls) { for (var ball_id in this.client.my_balls) {
var ball = this.client.balls[this.client.my_balls[ball_id]]; var ball = this.client.balls[this.client.my_balls[ball_id]];
@ -174,13 +163,13 @@ Viewer.prototype = {
p += ball.size; p += ball.size;
} }
if (p > 0) { // if we have visible ball(s) if (p > 0) { // if we have visible ball(s)
this.cam.x = x / p; this.cam.x.set(x / p, 100);
this.cam.y = y / p; this.cam.y.set(y / p, 100);
this.cam.s = 0.04 * this.width / p; // Scale this.cam.s.set(0.04 * this.width / p, 500); // Scale
// TODO Better scale calculation // TODO Better scale calculation
} // else: don't move the camera } // else: don't move the camera
}, },
render: function() { render: function () {
for (var ball_id in this.client.balls) { for (var ball_id in this.client.balls) {
var ball = this.balls[ball_id]; var ball = this.balls[ball_id];
if (ball) { if (ball) {
@ -188,18 +177,18 @@ Viewer.prototype = {
} }
} }
}, },
animate: function() { animate: function () {
this.stats.begin(); this.stats.begin();
this.render(); this.render();
this.posCamera(); this.posCamera();
this.stage.scale.x = this.stage.scale.y = this.cam.s; this.stage.scale.x = this.stage.scale.y = this.cam.s.get();
this.stage.position.x = -this.cam.x * this.stage.scale.x + this.width / 2; this.stage.position.x = -this.cam.x.get() * this.stage.scale.x + this.width / 2;
this.stage.position.y = -this.cam.y * this.stage.scale.y + this.height / 2; this.stage.position.y = -this.cam.y.get() * this.stage.scale.y + this.height / 2;
this.renderer.render(this.stage); this.renderer.render(this.stage);
this.stats.end(); this.stats.end();
this.emit('animate'); this.emit('animate');
var _this = this; var _this = this;
requestAnimationFrame(function() { requestAnimationFrame(function () {
_this.animate(); _this.animate();
}); });
} }
@ -218,19 +207,19 @@ function Pointer(viewer) {
y: 0 y: 0
}; };
var _this = this; var _this = this;
this.viewer.once('launched', function() { this.viewer.once('launched', function () {
_this.viewer.stage.interactive = true; _this.viewer.stage.interactive = true;
_this.viewer.stage.on('mousemove', function(e) { _this.viewer.stage.on('mousemove', function (e) {
_this.pointermove(e); _this.pointermove(e);
}); });
_this.viewer.stage.on('touchmove', function(e) { _this.viewer.stage.on('touchmove', function (e) {
_this.pointermove(e); _this.pointermove(e);
}); });
_this.viewer.on('animate', function(e) { _this.viewer.on('animate', function (e) {
_this.move(); _this.move();
}); });
}); });
window.addEventListener('keydown', function(e) { window.addEventListener('keydown', function (e) {
if (e.keyCode == 87) { if (e.keyCode == 87) {
_this.client.eject(); _this.client.eject();
} else if (e.keyCode == 32) { } else if (e.keyCode == 32) {
@ -240,14 +229,14 @@ function Pointer(viewer) {
} }
Pointer.prototype = { Pointer.prototype = {
move: function() { move: function () {
this.client.moveTo(this.viewer.cam.x + this.dest.x, this.viewer.cam.y + this.dest.y); this.client.moveTo(this.viewer.cam.x.get() + this.dest.x, this.viewer.cam.y.get() + this.dest.y);
}, },
pointermove: function(e) { pointermove: function (e) {
var gamePos = e.data.getLocalPosition(this.viewer.stage); var gamePos = e.data.getLocalPosition(this.viewer.stage);
this.dest = { // TODO deadzone this.dest = { // TODO deadzone
x: gamePos.x - this.viewer.cam.x, x: gamePos.x - this.viewer.cam.x.get(),
y: gamePos.y - this.viewer.cam.y y: gamePos.y - this.viewer.cam.y.get()
}; };
this.move(); this.move();
} }
@ -288,7 +277,7 @@ function Controller(client) {
} }
var _this = this; var _this = this;
client.on('connected', function() { client.on('connected', function () {
_this.servgui.close(); _this.servgui.close();
_this.cellgui.open(); _this.cellgui.open();
_this.leadergui.open(); _this.leadergui.open();
@ -296,18 +285,18 @@ function Controller(client) {
_this.spawn(); _this.spawn();
} }
}); });
client.on('reset', function() { client.on('reset', function () {
_this.servgui.open(); _this.servgui.open();
_this.cellgui.close(); _this.cellgui.close();
_this.leadergui.close(); _this.leadergui.close();
_this.resetLeader(); _this.resetLeader();
}); });
client.on('lostMyBalls', function() { client.on('lostMyBalls', function () {
if (_this.autoRespawn) { if (_this.autoRespawn) {
_this.spawn(); _this.spawn();
} }
}); });
client.on('leaderBoardUpdate', function(old, leaders) { client.on('leaderBoardUpdate', function (old, leaders) {
for (var i in leaders) { for (var i in leaders) {
var rank = parseInt(i) + 1; var rank = parseInt(i) + 1;
_this.leaders[rank] = this.balls[leaders[i]].name || 'An unnamed cell'; _this.leaders[rank] = this.balls[leaders[i]].name || 'An unnamed cell';
@ -319,7 +308,7 @@ function Controller(client) {
} }
Controller.prototype = { Controller.prototype = {
findServer: function() { findServer: function () {
// Because of SOP, this will never work // Because of SOP, this will never work
x = new XMLHttpRequest(); x = new XMLHttpRequest();
x.open('POST', 'http://m.agar.io', false); x.open('POST', 'http://m.agar.io', false);
@ -336,16 +325,16 @@ Controller.prototype = {
this.servgui.__controllers[i].updateDisplay(); this.servgui.__controllers[i].updateDisplay();
} }
}, },
connect: function() { connect: function () {
this.client.connect('ws://' + this.server.ip + ':' + this.server.port); this.client.connect('ws://' + this.server.ip + ':' + this.server.port);
}, },
disconnect: function() { disconnect: function () {
this.client.disconnect(); this.client.disconnect();
}, },
spawn: function() { spawn: function () {
this.client.spawn(this.nick); this.client.spawn(this.nick);
}, },
resetLeader: function() { resetLeader: function () {
for (var i = 1; i <= 10; i++) { for (var i = 1; i <= 10; i++) {
this.leaders[i] = '---'; this.leaders[i] = '---';
} }
@ -358,25 +347,25 @@ function IA(client) {
} }
IA.prototype = { IA.prototype = {
begin: function() { begin: function () {
var _this = this; var _this = this;
this.interval = setInterval(function() { this.interval = setInterval(function () {
_this.food(); _this.food();
// _this.decide(); // _this.decide();
}, 100); }, 100);
}, },
end: function() { end: function () {
clearInterval(this.interval_id); clearInterval(this.interval_id);
}, },
getDistanceBetweenBalls: function(ball_1, ball_2) { //this calculates distance between 2 balls getDistanceBetweenBalls: function (ball_1, ball_2) { //this calculates distance between 2 balls
return Math.sqrt(Math.pow(ball_1.x - ball_2.x, 2) + Math.pow(ball_2.y - ball_1.y, 2)); return Math.sqrt(Math.pow(ball_1.x - ball_2.x, 2) + Math.pow(ball_2.y - ball_1.y, 2));
}, },
getAngleBetweenBalls: function(b1, b2) { // output in rad getAngleBetweenBalls: function (b1, b2) { // output in rad
dX = b2.x - b1.x; dX = b2.x - b1.x;
dY = b2.y - b1.y; dY = b2.y - b1.y;
return Math.tan(dY / dX); return Math.tan(dY / dX);
}, },
food: function() { food: function () {
var candidate_ball = null; //first we don't have candidate to eat var candidate_ball = null; //first we don't have candidate to eat
var candidate_distance = 0; var candidate_distance = 0;
var my_ball = this.client.balls[this.client.my_balls[0]]; //we get our first ball. We don't care if there more then one, its just example. var my_ball = this.client.balls[this.client.my_balls[0]]; //we get our first ball. We don't care if there more then one, its just example.
@ -399,7 +388,7 @@ IA.prototype = {
this.client.log('closest ' + candidate_ball + ', distance ' + candidate_distance); this.client.log('closest ' + candidate_ball + ', distance ' + candidate_distance);
this.client.moveTo(candidate_ball.x, candidate_ball.y); //we send move command to move to food's coordinates this.client.moveTo(candidate_ball.x, candidate_ball.y); //we send move command to move to food's coordinates
}, },
decide: function() { decide: function () {
var my_ball = this.client.balls[this.client.my_balls[0]]; // TODO Handle more balls var my_ball = this.client.balls[this.client.my_balls[0]]; // TODO Handle more balls
if (!my_ball) return; if (!my_ball) return;
var candidates = []; var candidates = [];
@ -448,7 +437,7 @@ IA.prototype = {
}; };
var d = {}; // DEBUG Allow access from console var d = {}; // DEBUG Allow access from console
window.onload = function() { window.onload = function () {
d.client = new Client('worker'); d.client = new Client('worker');
d.viewer = new Viewer(d.client, document.getElementById('viewer')); d.viewer = new Viewer(d.client, document.getElementById('viewer'));
d.controller = new Controller(d.client); d.controller = new Controller(d.client);