In this tutorial we're going to create a simple online multiplayer game on Derby.js
The game
The professor creates a question: “Guess the number of jelly beans in the jar.” Everyone has to enter. Once the professor closes the game, the average is displayed to everyone. No more people can enter the game. People can enter the game without a login.
Generating project
Create a project using
generator-derby
:Creating game
We want to create a game which has a name and a question in it. Create two inputs and a ‘Create new game!’ button, wrapped in a form which has a ‘post’ method.
home.jade
Write a controller that handles
/create_game
action.
index.coffee
Here we create a game: add a new
games
document with a name
, a question
, an empty object with players
and an array with userIds
(we need them to easily loop through all players in the game). model.add also generates an id for each game. As for form variables (name
, question
), we get them from params.body
in the controller. We redirect to /
page, because we want to stay on the same page after creating the game. Let’s display the games on the home page. Each game will be a link.List of games
To be able to loop through the games in
games
collection (in order to display the list of games) we need to create a local db out of games
, convert it to an array and make a reference in private _page
collection.
index.coffee
We are subscribing for
games
collection to make it accessible on /
page. model.at
creates a local games
db,filter()
converts games
to an array and ref()
creates a reference from this array. Now we can loop through the array of games in views:
home.jade
We are displaying each game as a link which will redirect us to the game page. id and name are the fields in game object. Let’s add a game.
Deleting games
To delete all the games add a ‘Delete all games!’ input.
home.jade
When clicking it,
deleteGames
function is triggered.
index.coffee
We need to loop through games deleting each game from
games
collection. It is necessary to subscribe for games
and set it into a variable. for of
loop goes through each game and model.del
deletes each game. Notice that in the last line we used double quotes. String in double quotes supports interpolation. In this case, we interpolate key, that is game id.Adding player’s name
Let’s add player’s name.
home.jade
We are binding the value of the input. Submitting the form triggers
setUserName
function.
index.coffee
We get
userName
from _page.userName
, trim and set it to users
collection. If an empty input is submitted we show "Name cannot be empty!" alert message Also, let’s greet the player. To do this, we subscribe for current user (not for players, because name is stored in users
collection). We also need to get userId, which is stored in private collection_session.userId
and set it in a variable to be able to subscribe for current user.
index.coffee
There is a reference (
model.ref
) on the current user and now we can check in views if the user has name. If he does, the greeting appears on the page.Game page
Add
game.jade
file to /views/app
and import it to index.jade
game.jade
index.jade
Create a controller that handles game page.
index.coffee
Display the question
We want to display the question in the game. To do that, subscribe for the game (and make a reference for easy access), set the game id and make a reference, so that we can access the game id in views.
Here comes the views.
game.jade
Add input for the answer
game.jade
Let’s subscribe for the users in the game (because we want to display players in the game) and make references to players, current user, current game and current player. To subscribe for users in the game, we need to make a query request.
Add player to the game
index.coffee
When opening the game page, the player is added to the game, unless he is already there. model.add adds a new document with random id. It is possible to specify an id. We want players’ ids to be the same as users’ ids, that is why we set id as userId. When the player is added, his id is saved into game’s userIds array.
Save the answer
index.coffee
We are get the answer from
_page.answer
and set it to answer, checking whether it is not undefined
or null
and parsing it to integer. If the answer is NaN
, we alert "The answer must be a number!". Otherwise the answer is set to games
collection.
Since we’ve made a reference, we don’t need to write the full path (
'games.' + params.gameId + '.players.' + userId
) — we are setting the answer to _page.player.answer
. We also want to display the answer on the game page. When the player changes his mind and enters a different guess, the answer on the page will be changed instantly. The answer should be displayed only if there is one, so we make an if-statement checking if there is an answer.
game.jade
List of players in the game
Let’s display all players in the game.
game.jade
Giving control to prof
Only the professor should be able to create a game, not the students. Add an input (type checkbox). If it is checked,
prof
field (this field is created in _page.user
when the checkbox is checked) becomes true
. Only when it is true
the game can be created.
home.jade
Besides, only the professor should be able to finish the game. We have already subscribed for the current user and made a reference (
_page.user
).
Write an
if
-statement which will check if the user is the professor. If he is, then the "Finish the game!" button will be displayed.
game.jade
Finish the game
Now let’s write the controller that handles finishing the game. The aim here is to collect the answers, strike an average and finish the game. To collect the answers, use the
for of
loop. The controller sets _page.game.finish
to true
.
index.coffee
No going back
When the game is finished by professor, we want all the players inside the game to automatically go to the results page. To do that, we need to create a component from the view we render. For convenience, the name of the component should match the name of the template it’s associated with.
We use component’s
create
method to execute code right after this component (in our case it’s the whole page) has been rendered. When the professor clicks "Finish the game!", _page.game.finish
becomes true
. So we want to listen for_page.game.finish
variable changes. And if the game is finished, we want players to be redirected to the page with the results.
index.coffee
New players shouldn’t be able to enter the game if it has been finished, so when going to the game page, we are checking whether the game is finished or not.
Results
index.coffee
Add
results.jade
file to /views/app
and import it to index.jade
:
result.jade
index.jade
Here is the controller that handles results page.
index.coffee
We subscribe for the game and make a reference to the average, which is stored in
games
collection. And, finally, display the results on the results page.
results.jade
Final code
index.coffee
home.jade
game.jade
results.jade
No comments:
Post a Comment