-
Notifications
You must be signed in to change notification settings - Fork 0
Integrating the new Design
Since receiving the new requirements, the designers have been hard at work on design and idea for the game. They have decided to render the Game with the excellent Raphael JavaScript library. The designers have handed over some JavaScript script code and we are going to work it into the design area of our project.
We will first add a new page to the Design area of our project where we can work on the UI for the game.
In the file tools/public/design.html add the following link
<li><a href="/design/game.html">Game</a></li>and then add the file app/templates/game.html.
<_within file="application.html">
<div id="content">
<div class="row-fluid" template="tutorial" field="id:id">
<div id="game-board"></div>
<!-- We need this because of a bug in Pedestal. Remove when
fixed. -->
<div field="content:something"></div>
</div>
</div>
<script id="script-driver" src="/game-driver.js"></script>
</_within>The new UI will require raphael.js. Download this library and put it
in app/assets/javascripts/. Next to this file create a JavaScript
file named game.js. This file will contain all of our game rendering
code. In the HTML file above, we refer to the game-driver.js
file. Create this file next to game.js as well.
Finally, add the following scripts tags to application.html just
before the <script id="script-driver"></script> tag at the bottom of
the body section.
<script src="/raphael.js"></script>
<script src="/game.js"></script>The designers on this project have created the UI for the game in JavaScript. This could also be written in ClojureScript. For this project, to emphasize the difference between rendering and application state, we will use the JavaScript for the UI.
The code for the game is shown below. We will add a brief comment for
each section. All of this code should be go into the file game.js.
// A multi-valued bar
// ================================================================================
var Bar = function(paper, x, y, vals) {
var barAnimateTime = 2000;
var barHeight = 20;
var colors = ["#0f0", "#00f", "#f00"];
var rect = function(x, y, w, h, color) {
return paper.rect(x, y, w, h).attr({fill: color, stroke: "none"});
}
var bars = {};
for(var i in vals) {
var b = vals[i];
var size = b.size || 0;
b.bar = rect(x, y, size, barHeight, colors[i % colors.length]);
bars[b.name] = b;
}
var resizeBar = function(bar, size) {
bar.animate({width: size}, barAnimateTime);
}
return {
setSize: function(name, n) {
resizeBar(bars[name].bar, n);
},
vals: vals
}
}// A group of bars with unique value names
// ================================================================================
var Bars = function(bars) {
var index = {};
for(var i in bars) {
var bar = bars[i];
var vals = bar.vals;
for(var j in vals) {
var val = vals[j];
index[val.name] = bar;
}
}
return {
setSize: function(name, n) {
var b = index[name];
if(b)
b.setSize(name, n);
}
}
}// Circles
// ================================================================================
var Circles = function(paper, w, h) {
var defaultRadius = 20;
var padding = 50;
var createAnimateTime = 500;
var removeAnimateTime = 200;
var moveAnimateTime = 1000;
var reportScoreFn;
var removeCounter = 0;
var randomPoint = function() {
var maxHeight = h - padding;
var x = Math.floor(Math.random() * w);
if(x < padding)
x = padding;
var y = Math.floor(Math.random() * h);
if(y < padding)
y = padding;
if(y > maxHeight)
y = maxHeight;
return {x: x, y: y};
}
var removeCircle = function(c) {
c.animate({r: 0}, removeAnimateTime, function() {
c.remove();
});
}
var moveCircle = function(c) {
if(c) {
var point = randomPoint();
c.animate({"cx": point.x, "cy": point.y}, moveAnimateTime, function() {
if(removeCounter > 0) {
c.animate({fill: "#000"}, 100)
removeCircle(c);
removeCounter--;
}
moveCircle(c);
});
}
}
var makeCircle = function() {
var point = randomPoint();
var circle = paper.circle(point.x, point.y, 0).attr({
fill: "#f00", stroke: "none", opacity: 0.6
});
circle.animate({r: defaultRadius}, createAnimateTime);
moveCircle(circle);
circle.mouseover(function() {
if(reportScoreFn)
reportScoreFn(1);
removeCircle(circle);
});
}
return {
addCircle: function() {
makeCircle();
},
removeCircle: function() {
removeCounter++;
},
addScoreReporter: function(f) {
reportScoreFn = f;
}
}
}// Player
// ================================================================================
var Player = function(paper, x, y, name) {
var nameLength = 150;
var fontSize = 20;
var score = 0;
var nameText = paper.text(x, y, name).attr({
"font-size": fontSize,
"text-anchor": "start"});
var scoreText = paper.text(x + nameLength, y, score).attr({
"font-size": fontSize,
"text-anchor": "end"});
var st = paper.set();
st.push(nameText, scoreText);
return {
setScore: function(n) {
score = n;
scoreText.attr({text: score});
},
moveTo: function(y) {
st.animate({y: y}, 400);
}
}
}// Leaderboard
// ================================================================================
var Leaderboard = function(paper, x, y) {
var playerSpacing = 30;
var players = {};
var playerY = function(i) {
return 50 + (i * playerSpacing);
}
var countPlayers = function() {
var count = 0;
for(var i in players) {
if(players.hasOwnProperty(i))
count++;
}
return count;
}
return {
addPlayer: function(name) {
var i = countPlayers();
var p = Player(paper, x, playerY(i), name);
players[name] = p;
},
setScore: function(name, score) {
var p = players[name];
p.setScore(score);
},
setOrder: function(name, i) {
var p = players[name];
p.moveTo(playerY(i));
},
count: function() {
return countPlayers();
}
}
}// The Bubble Game
// ================================================================================
var BubbleGame = function(id) {
var paper = Raphael(id, 800, 400);
var bars = Bars([Bar(paper, 0, 380, [{name: "total-count"},
{name: "max-count"},
{name: "avg-count"}]),
Bar(paper, 0, 357, [{name: "max-dataflow"},
{name: "avg-dataflow"},
{name: "current-dataflow"}])]);
var circles = Circles(paper, 500, 380);
var leaderboard = Leaderboard(paper, 550, 0);
// This will be removed as we make improvements to the game.
// The dataflow will control when circles are created.
var makeCircles = function() {
var p = leaderboard.count();
for(var i=0;i<p;i++) {
circles.addCircle();
}
}
setInterval(makeCircles, 2000);
return {
addHandler: circles.addScoreReporter,
addPlayer: leaderboard.addPlayer,
setScore: leaderboard.setScore,
setOrder: leaderboard.setOrder,
setStat: bars.setSize,
addBubble: circles.addCircle,
removeBubble: circles.removeCircle
}
}// Game API
// ================================================================================
var createGame = function(id) {
return BubbleGame(id);
}
var addPlayer = function(g, name) {
g.addPlayer(name);
}
var addHandler = function(g, f) {
g.addHandler(f);
}
var setScore = function(g, n, x) {
g.setScore(n, x);
}
var addBubble = function(g) {
g.addBubble();
}
var removeBubble = function(g) {
g.removeBubble();
}
var setStat = function(g, n, v) {
g.setStat(n, v);
}
var setOrder = function(g, n, i) {
g.setOrder(n, i);
}var game = BubbleGame("game-board");
var me = {name: "Me", score: 0};
var players = [me,
{name: "Fred", score: 0},
{name: "ahbhgtre", score: 0}];
var sortPlayers = function() {
players.sort(function(a, b) {
if(a.score < b.score) return 1;
if(a.score > b.score) return -1;
return 0;
});
for(var i=0;i<players.length;i++) {
players[i].newIndex = i;
}
}
for(var i in players) {
game.addPlayer(players[i].name);
}
var updateCounts = function() {
var total = 0;
var max = 0;
for(var i in players) {
var score = players[i].score;
total += score;
if(score > max)
max = score;
}
var avg = total / players.length;
game.setStat("total-count", total);
game.setStat("max-count", max);
game.setStat("avg-count", avg);
}
setInterval(updateCounts, 1000);
game.addHandler(function(points) {
me.score += points;
game.setScore("Me", me.score);
updateCounts();
});
var rand = function(n) {
return Math.floor(Math.random() * n) + n;
}
var randPlayer = function() {
return Math.floor(Math.random() * players.length);
}
var updateDataflowStats = function() {
game.setStat("max-dataflow", rand(100));
game.setStat("avg-dataflow", rand(50));
game.setStat("current-dataflow", rand(10));
}
setInterval(updateDataflowStats, 1000);
var updatePlayerScores = function() {
var p = players[randPlayer()];
if(p.name != "Me") {
p.score += 1;
game.setScore(p.name, p.score);
game.removeBubble();
}
updateCounts();
}
setInterval(updatePlayerScores, 1000);
var updatePlayerOrder = function() {
sortPlayers();
for(var i in players) {
var p = players[i];
game.setOrder(p.name, i);
}
}
setInterval(updatePlayerOrder, 2000);The tag for this step is step11.