Wednesday, March 31, 2010

Getting started with Rosewood

Rosewood is a JavaScript library/engine for creating 2d browser-based games that are somewhat in the spirit of old 16-bit games.

With its simple, but powerful API and fast runtime, Rosewood makes game design quick, easy and fun.

In the following introduction, I'll walk you through the creation of an incredibly simple game and introduce you to many of Rosewood's features. Ready? Let's get started!

Setting things up

First up, we'll need a copy of Rosewood before we can do anything. So head on over to Rosewood's page on Github and download a copy (for those with git, simply enter "git clone git://github.com/vonkow/Rosewood.git" in your terminal, to suck down the newest release).

You'll find a number of files inside the Rosewood folder (including a number of examples), but the only one we're concerned with at the moment is "rosewood.js".

Create a new folder for your game somewhere, name it "rosewood_game" (or something of the like) and place a copy of rosewood.js inside of it.

Next, create another folder, called "sprites" inside this folder, this is where we will store any images that our game is to use.

Now, open up your favorite text editor and create a new file called "play.html" inside the game folder. Edit play.html so that it looks like this:

<html>
 <head>
  <title>My first Rosewood Game</title>
  <script type="text/javascript" src="rosewood.js"></script>
  <script type="text/javascript" src="game.js"></script>
 </head>
 <body>
  <script type="text/javascript">
   startGame();
  </script>
 </body>
</html>

The html here is pretty simple, we put a link to rosewood.js in the head and another link to a non-existent file game.js. In the body, we've added a call to a function named startGame(). This function doesn't exist yet, so let's save play.html and move on to creating game.js and the startGame() function (Note: This is all the html we will need to write to create our game, Rosewood takes care of the rest).

Our first function

With your text editor, create another file called game.js and edit it thusly:

var startGame = function() {
   rw.init(500,500)
   .start();
}

Now that we've fixed our problem with a non-existent file and function, open up play.html in a web browser and let us marvel at our creation.

You should be staring at a blank 500 by 500 pixel box. Inside this box is the Rosewood game board, which is currently doing nothing at 20 Frames Per Second (Rosewood's default framerate).

Before we start adding things, let's take a quick look at the startGame() function and see what it's doing.

The first thing startGame() does is calls a function named rw.init(500, 500). (all Rosewood functions start with rw)

The rw.init() function takes 2 arguments, x and y, and creates a game board that is x pixels wide and y pixels high. All of the action in our game will be contained inside this board (which is a div element with the id "board").

By default, the board is appended to the body of our html document, but we can change this if we like. rw.init() takes a third, optional, argument, the id of a target element to attach the board to. For example:

rw.init(500, 500, 'main')

would create the same 500 by 500px board, but would append it to an element with the id "main".

In addition, the rw.init() function sets up a number of things behind the scenes, allowing Rosewood to begin tracking keyboard and mouse inputs.

The rw.init() function, like many Rosewood functions, returns the rw object, allowing us to chain the next function to it. Let's have a look at the next function, rw.start().

The rw.start() function is simple, if the engine is not running, it starts it, at the current framerate. Until specified otherwise, Rosewood defaults to a framerate of 20 FPS (frames per second). We can change the framerate using the rw.setFPS() function, for example:

var startGame=function() {
   rw.init(500,500)
   .setFPS(40)
   .start();
}

would create the same 500 by 500px board, but running at a framerate of 40 FPS instead of 20 (as you can see, rw.setFPS() is chainable like rw.init() and rw.start()).

This is all well and good, but a game that does nothing (be it 20 or 40 times a second) isn't very exciting so, let's move on and start adding things to our game.

Introducing ents

The most important type of objects in Rosewood are called entities, or simply ents. Ents are similar to many gaming libraries' concept of sprites. They can have images that are displayed on the game board and they are tracked by Rosewood. What this means, is that once per frame Rosewood updates the position and behavior of every ent that is being actively tracked.

Before we can create our first ent, we need to add graphics for it. Download this .zip file and extract it into the folder called sprites. It should create a new folder called "hero", which contains a number of images. We will use these images as the sprites for our first ent.

Now, open up game.js in your text editor and add the following above the startGame() function

var hero = function(name) {
   this.base = new rw.ent(name, 'hero', 'd', 'png', 32, 32);
   this.update = function() {
   };
}

This function will be used to create our first ent. As you can see, this function takes one argument (name) and creates an object with 2 properties, base and update.

The base property is of utmost importance. All entities must have a "base" property with a "new rw.ent()" as its value.

The rw.ent() object that we assigned to the "base" property is what Rosewood uses to track and display an entity. rw.ent() (through the "base" property) also contains many function methods needed for changing an ent's behavior. rw.ent() takes a number of arguments, as follows:
  • name: this is the absolute name of the ent, MUST BE UNIQUE
  • spriteFolder: this is the name of the folder inside of the sprites folder where images for this ent are stored (the folder called "hero" in this case)
  • defaultSprite: this is the name of the default sprite to show ("d", in this case).
  • spriteExt: the filetype extension of the ent's sprites ("png" in this case).
  • width: the ent's width, in pixels (32 in this case).
  • height: the ent's height, in pixels (32 in this case).

The second property defined in the hero() function is update, a blank function. Once per frame, Rosewood calls the update() function of every active ent, allowing us to control the movement and behavior of every ent through time within the game.

We'll leave this function blank for now, but will come back to it shortly.

Adding an ent

Now that we have a function to create an ent and have added graphics for it, let's add it to the game. Edit the startGame() function so that it looks like this:

var startGame=function() {
   rw.init(500, 500)
   .newEnt(new hero('hero'))
      .base.display('d', 234, 234, 234)
      .end()
   .start();
}

We've added 3 lines here, let's go over what they do.

The first line:
.newEnt(new hero('hero'))
creates a new ent named 'hero' using our hero() function. Instead of returning the rw object, the rw.newEnt() function returns the ent we have just created, allowing us to immediately start working with the ent.

The second line:
.base.display('d', 234, 234, 234)
accesses the ent's base property (an rw.ent()) and calls the built-in method .display().
The base.display() function accepts 4 arguments, the sprite to display and the x, y, and z cooridnates of where it should be located on the game board. In this instance, we are telling Rosewood to display the ent using the image 'd' (which is actually "sprites/hero/d.png") and to place it at the location 234,234,234 on the board. This function returns the ent.base property, allowing us to call the next function.

The third line:
.end()
is a call to another method in the base property. The function base.end() simply returns the rw object, bringing us out of the ent sub-chain and back to the rw chain. Save game.js and go back to your browser.

Reload play.html in the browser and you should see our hero standing in the middle of the board, doing nothing (at 20 FPS). It's slightly more exciting to have something do nothing than nothing do nothing, but not by much. Let's add some interactivity.

Using the keyboard

We're going to revisit the blank update() function inside our hero() function. Change it to the following:

this.update = function() {
      if (rw.key('ua')) this.base.move(0, -1);
      if (rw.key('da')) this.base.move(0, 1);
      if (rw.key('la'))this.base.move(-1, 0);
      if (rw.key('ra')) this.base.move(1, 0);
   }

Save the file, reload play.html and try using the arrow keys. Movement! A quick look at our new update() function will reveal a few things.

There are 4 'if' statements inside the function. Each 'if' calls a function named rw.key(). Remember when I said that the rw.init() function did a number of things behind the scenes to start tracking keyboard an mouse input? Once we have called rw.init(), we can use the rw.key() function to tell if a certain key is being pressed. If the specified key is being pressed rw.key(key) will return true, otherwise it returns false. In this case, each 'if' statement is checking to see if one of the 4 arrow keys is being pressed ('ua' = up arrow, etc).

Looking at the first 'if' statement, we can see that it is checking if the up arrow key is being pressed. If it is, we call this.base.move(0, -1). The base.move(x, y) function moves an ent x pixels horizontally and y pixels vertically. In this case, if the up arrow is pressed, we move the ent 0 pixels horizontally and -1 pixels vertically.

The 3 other 'if' statements do the same for the other arrows, letting us now move the ent around. You'll notice that if you press a vertical and a horizontal key at the same time, the ent moves diagonally. This is because the base.move() function is additive. What this means is that the total of all calls to base.move() in one pass through the update() function are added together before the ent is moved (for example, if you press both the up and down arrows, the ent will not move, as it is being told to move -1+1 pixels vertically).

Summary

Let's sum up what our psudo-game does now:
  • First, we create the game board with rw.init()
  • Next, we add an ent to the board and set its initial position and graphic, using rw.newEnt() and the ent's base.display() function.
  • Then, we tell Rosewood to start running with the rw.start() function.
  • Once the engine is running, every frame (ie, 20 times a second) Rosewood calls the ent's update() function, which does the following:


    • Check to see if any of the 4 arrow keys is being pressed.
    • If an arrow key is being pressed, move the ent 1 pixel in that direction.
It's pretty simple, but a good introduction to the basics of Rosewood for now. That's it for Tutorial 1, in the next tutorial, we will begin to improve upon our hero and create some more entities to interact with.

Below is the full game.js file after completing this tutorial:
var hero = function(name) {
   this.base = new rw.ent(name, 'hero', 'd', 'png', 32, 32);
   this.update = function() {
      if (rw.key('ua')) this.base.move(0, -1);
      if (rw.key('da')) this.base.move(0, 1);
      if (rw.key('la')) this.base.move(-1, 0);
      if (rw.key('ra')) this.base.move(1, 0);
   };
}

var startGame=function() {
   rw.init(500, 500)
   .newEnt(new hero('hero'))
      .base.display('d', 234, 234, 234)
      .end()
   .start();
}

1 comment: