Building A Game From Start To Finish With Play My Code – Part 1

Posted: by Seb under Game Development

Below is my latest creation: Bombs ‘n’ Booty! Have a quick go: use the WASD keys to move, avoid the bombs, collect the valuable booty and see how high you can score.

Now, the reason I built this game was so I could guide both novice coders and the more experienced alike through the process of making a complete (if, simple) game using the Play My Code platform. While this post isn’t intended as a complete programming guide or reference, it will provide some context from which you can further your programming knowledge as well as giving you a simple codebase which you can tweak, extend and mod into your own creations. Strap yourself in, as we’re going to be running through this very quickly! Don’t worry if you come across something which you don’t understand, just keep going and all the bits will fall into place by the end.

Language Basics

Play My Code games are written using a language called Quby, which makes creating browser-based games fun and simple. The language’s simplicity means a beginner can hack out a basic game within a few hours, while being sophisticated enough to cater for more experienced coders. The Quby API provides lots of “helper” methods and functions out-of-the-box, geared towards game development, which aren’t present in most other languages used for browser-based game creation. Once finished, projects are compiled into native JavaScript, meaning they run blazing fast in all modern browsers without needing any plugins. If you want to know more, have a nose through the documentation- otherwise, read on…

Prepare Yourself

Make sure you’ve signed up to Play My Code, signed in and created a new project before you start. You will not be able to complete this tutorial unless you have done this. For more info on signing up and managing game projects, please read the General Site Usage and Game Development documents. Enter a title and click Edit to, well, edit your new project. You will now be in the online Integrated Development Environment (what us pros refer to as an IDE), ready to start. Click into the left, dark grey window (the code pane), and you should see the following:

/* 
 * This is example code to help you get started
 * writing a new project.
 *
 * Edit this file and then click run!
 */

setFont( 'Arial', 32, 'italic' )

// The code inside this do block is run each frame
onEachFrame() do
    fill( 0, 0, 0 )
    
    setColor( 255, 255, 255 )
    fillText( "Hello World!", 10, 32 )
end

This is example code, to help you get started. Click the green “run” button beneath the code window and this code should execute, displaying the message “Hello World!”. Select and delete all of this code and replace it with the following:

/*
images
*/

/* 
game objects
*/

/*
main loop
*/
onEachFrame() do

end

/* 
classes
*/
​

All we’ve done here is gotten rid of the test Hello World code to provide a clean slate to work from. The above doesn’t “do” anything as such, other than provide a skeleton structure under which we’re going to be putting our code. Please note that…

//lines preceded with "//" are single line comments

/*
Lines between the above and below symbols are
multi-line comments.
Comments are ignored by the compiler.
*/

Comments are used to help programmers remember, or inform others, of what tricky bits of code do. They are ignored by the compiler, so feel free to leave comments so you can recall where you left off when you come back to a project.We’ve used comments here to define our sections.

What Little Games Are Made Of

Now, for some simplified programming background… Games are generally made up of a few things: code (the programming itself), assets (your images and sounds) and also data (for example, the lines of dialogue in an RPG). A simple game like Bombs ‘n’ Booty doesn’t really use any data, so we can forget about that in this instance. Every object in a game, from the background right up to the player character, is created from what is known as a “class”. A class is essentially a blueprint for an object, it defines that object’s characteristics and what it can do- for example, a Dog object would have 4 legs and be able to bark. Once defined, we could use the Dog class to create as many Dog objects as we need. Don’t worry if this isn’t making sense: read on, we will learn by doing.

Another key point about game creation is to always plan your projects, no matter how simple. Think about what each object will need to do in your game- shoot, move, be collected and so on. Make a plan, and stick to it. We will see this in action in just a moment…

A Little Background

We’re building from the ground-up here, so let’s start by implementing the game’s background. Save the below image- make sure it’s saved as background.png- and then use the Image Manager in the IDE to upload it to your project. You can access this by clicking the Images icon beneath the code pane. If you get stuck, have a look in the Game Development guide for some more info on using this.

background

Once the image has been uploaded, one more step before we can use it in our project- it has to be assigned to a variable. Enter the following into your project, under the Images section:

backgroundImage = new Image( "background.png" )

Now, a Background object doesn’t need to do a great deal in this context: all it really needs is to be able to draw itself, and the only characteristic it needs is its own image. So, the Background class is quite simple:

class Background
    def new( image )
        @image = image
    end

    def draw()
        drawImage( @image, 0, 0 )
    end
end

Copy-paste this into your code under Classes. It should be right at the bottom.

Here’s what we’re doing here: first, we define the Background class (please observe that class names should always be capitalised). Then, we define the new() function, which is known in most programming languages as a “constructor”. When any object is first created (and do remember- we haven’t actually created anything yet, this is just the blueprint) the new() function is run. Any characteristics of the object, referred to as “fields”, are defined here also- observe that the only field a Background object needs is a reference to the graphic image it uses. You will notice here that a variable called “image” is passed in (a variable passed into a function or method is known as a “parameter”) and set to the Background object’s @image field. In this case, image is a temporary variable- think of it as a placeholder for the “real” background image parameter which will be passed in when we finally create our background object.

Once we’re done with the constructor we can move on to defining what are known as “methods”. A method is a function within a class which represents something the object should be able to do, and as mentioned earlier a Background only needs to do one thing: draw itself. The draw() method should be fairly self-explanatory: when used it calls background’s drawImage function and draws the image defined in our constructor at the co-ordinates 0,0.

Now, under Game Objects, enter the following:

background = new Background( backgroundImage )

Here we’re using the Background class to define a background object, which will have all the fields and methods described in its class. Note that we pass in our backgroundImage variable, which will then be assigned to its @image field.

Now, in the game loop itself (between “onEachFrame() do” and “end”), enter the following:

background.draw()

Our Background object instance will draw itself using its draw() method. Because this is occurring in the main loop it will occur on every frame. Click the Run icon underneath the code pane and you should see the background displayed in the game window. All good? Don’t forget to save, by clicking the Save icon beneath the code pane.

Errors?

You may encounter errors in the Error Console (beneath the game window, bottom right) if you’ve made mistakes in your code. A full explanation of the many errors you may encounter is beyond the scope of this guide, however some general rules can be applied:

  • Errors will usually be accompanied by a line number: when searching for the cause of the error, start at the line referenced.
  • Always attempt to correct the first error listed, as sometimes this will be the only “real” one there- the others being caused by this original error.
  • The spelling of variable and object names, methods and so on has to be perfect. Sometimes errors are caused by mis-spellings or typing slip-ups. This also applies to the filenames of uploaded images, so do double-check.
  • Make sure all blocks and nesting (class blocks, if blocks, loops etc) are properly implemented. Opening brackets or blocks should always be closed or ended appropriately.

If you’re really stuck, compare what you’ve got with what it should look like:

/*
images
*/
backgroundImage = new Image( "background.png" )

/*
game objects
*/
background = new Background( backgroundImage )

/*
main loop
*/
onEachFrame() do
    background.draw()
end

/*
classes
*/
class Background
    def new( image )
        @image = image
    end

    def draw()
        drawImage( @image, 0, 0 )
    end
end​

The Player Character

Right, now on to the exciting part: getting a controllable player character on screen. Before we write a single line of code, we should plan what the Player object needs to be able to do and what fields it needs. A Player needs to do the following:

  • Respond to keyboard input to set which direction it’s moving in.
  • Update its co-ordinates, causing it to physically move across the screen depending on which direction it’s moving in.
  • Check its bounds, to stop itself from moving off the side of the screen.
  • And finally, draw itself.

The following fields are needed:

  • X & Y values to set its co-ordinates
  • The direction it is heading in
  • Width & height- needed for collision detection and bounds-checking
  • A reference to its graphic image

There are more fields and methods a Player object would need, such as being able to collect treasure or be killed, but for the sake of keeping this tutorial simple we will leave these until we implement the titular bombs & booty.

Now, let’s get down to creating our Player class. First, here’s the Player graphic image. Save it as “player.png” and upload it into your project using the Image Manager.

player

Then, assign the uploaded image to a variable like we did with the background. Add the following line to the Images section:

playerImage = new Image( "player.png" )

Here is the basic Player class. Enter this into the Classes section of your code, above or below the Background class:

class Player
    def new( image )
        @image = image

        @positionX = getScreenWidth() / 2 - ( getWidth() / 2 )
        @positionY = getScreenHeight() / 2 - ( getHeight() / 2 )

        @direction = "none"
        @speed = 4
        @score = 0
        @isAlive = true
    end

    def draw()
        if( @isAlive )   //Only draw him if he's alive
            drawImage( @image, @positionX, @positionY )
        end
    end

    def getWidth()
        return @image.getWidth()
    end

    def getHeight()
        return @image.getHeight()
    end
end

The above should be fairly self-explanatory, but for a very important point regarding the width and height of the Player object which I will explain first: to get the width and height of the Player image (and hence a Player itself) we have to write a couple of methods known as “getters”, which you should notice below the Player’s draw() method in the code snippet above. Image dimensions are obtained in this way to enable the correct value to be assigned, and also to ensure the code still works if one swaps the player image for another of different dimensions. You can’t just enter numbers into @width or @height fields- well, you could but it might cause problems depending on the situation. It’s hard to explain why without going into great detail, so for the time being just keep in mind that it works and should usually be done this way.

As for the rest of the constructor: we’ve a reference to our Player image parameter, which is passed in when a Player object, the same as the above-mentioned Background class.

Next come the X/Y co-ordinates of a player object, and these are set using some built-in Quby helper functions (getScreenWidth() & getScreenHeight() in conjunction with our afore-mentioned “getter” methods getWidth() and getHeight(). The way these are set causes a newly-minted Player to spawn right in the middle of the screen.

Then we have the direction, which will be set to “none” for the time being. Then movement speed. Finally, the score and @isAlive, which is set to what is known as a boolean value- one which can only ever be either true or false, and is hence appropriate for representing whether a character is alive or dead.

Currently, a Player object can only initialise and draw itself: it can’t actually do anything useful (notice that the draw() method is slightly different to the Background class’s draw(), the if statement stops the player graphic from being drawn if the @isAlive field equals true- ie if the player has not died). Add the following methods to the class, making sure they’re within the Player class yet not inside the constructor or any other methods:

def setDirection()
    controls = getControls()
    /*
    Get keyboard input, if correct key is pressed set
    the corresponding direction
    */
    if controls.isKeyPressed( "w" )
        @direction = "north"
    else if controls.isKeyPressed( "d" )
        @direction = "east"
    else if controls.isKeyPressed( "s" )
        @direction = "south"
    else if controls.isKeyPressed( "a" )
        @direction = "west"
    end
end

The setDirection() method creates a local variable- controls- inside itself. A variable like this gets “forgotten” when the method gets to the end of its instructions, and hence is for temporary use. Keyboard input is assigned to this variable, and if certain keys are pressed the Player object’s direction is set.

def move()   //This updates his co-ordinates to make him move
    if( @isAlive )   //Only move if he's alive
        if( @direction == "north" )
            @positionY = @positionY - @speed
        else if( @direction == "east" )
            @positionX = @positionX + @speed
        else if( @direction == "south" )
            @positionY = @positionY + @speed
        else if( @direction == "west" )
            @positionX = @positionX - @speed
        end
    end
end

Depending on its direction, move() will update our Player object’s X/Y co-ordinates. Calling setDirection() and move() on every frame in the main loop gives us a moveable character. However, the character will slide off the sides of the screen, unless we add the following method:

def checkBounds()   //This stops the player from going offscreen
    if( @positionX <= 0 )
        @positionX = 0
    else if( @positionX >= getScreenWidth() - getWidth() )
        @positionX = getScreenWidth() - getWidth()
    end
    if( @positionY <= 0 )
        @positionY = 0
    else if( @positionY >= getScreenHeight() - getHeight() )
        @positionY = getScreenHeight() - getHeight()
    end
end

Here, we’re checking if the character’s X & Y co-ordinates are less than zero or greater than the screen’s width/height. If they are, they get reset to stop the character from leaving the bounds of the screen. Notice we need to account for the Player’s width/height when checking against the right or bottom.

Once those methods are added, it’s time to create the Player object itself. Add the following to the Game Objects section:

player = new Player( playerImage )

And finally, new Player object needs to call its methods in the main loop. Add the following to the main loop, below background.draw() (or else the background wil be drawn on top of the player and you won’t be able to see it):

player.setDirection()
player.move()
player.checkBounds()
player.draw()

Run the code, and you should have a controllable player character! Don’t forget to save. Do also note that we work out the Player object’s logic (where it’s positioned, whether a key has been pressed, etc) before we draw it to the screen. Generally, drawing should always be done after any calculations involving that object have finished. If it doesn’t work and you can’t work out why, see a finished example of what we’ve got so far here.

Wrapping Up

We’ve covered a lot of ground today, so don’t worry if this still seems confusing. Don’t be afraid to experiment with the code, change variables or remove bits to see what happens- this will help all the pieces fall into place. In part 2 of this tutorial we’ll turn this from a mere demo into a “real” game by adding our bombs and booty, but until then try modifying or extending what you’ve got already. Try making another instance of the Player object, copying and altering the Player class to create an Enemy class, altering the Player so it only moves when a key is held down rather than “sliding” along, and so on. If you don’t fully understand what is happening and why in terms of the code itself, don’t worry: keep hacking away and everything will melt into place eventually.

Meanwhile, we’ll pick this up again in part 2. Please feel free to follow us on Twitter- give us a shout if you get stuck, plus don’t forget you can subscribe to our blog via RSS too. See you soon, and happy coding!

8 Responses to Building A Game From Start To Finish With Play My Code – Part 1

The whole checkBounds() method could be removed by altering the move() method like this: def move() //This updates his co-ordinates to make him move if( @isAlive ) //Only move if he's alive if( @direction == "north" ) @positionY = (@positionY - @speed).max(0) else if( @direction == "east" ) @positionX = (@positionX + @speed).min(getScreenWidth() - getWidth()) else if( @direction == "south" ) @positionY = (@positionY + @speed).min(getScreenHeight() - getHeight()) else if( @direction == "west" ) @positionX = (@positionX - @speed).max(0) end end end

Seb

There's a few reasons as to why I kept these as two separate methods: first off, I think it would be easier for beginners to understand, secondly one could perhaps argue that combining move() and checkBounds() could compromise reuse and thirdly separating methods out makes for easier debugging in my opinion? I would love to see someone reproduce this entire example with as little code as possible though! :-)

Here you are: backgroundImage = new Image("background.png") player = new Player(new Image("player.png")) onEachFrame() do drawImage(backgroundImage, 0, 0) player.move() end class Player def new(image) @image = image @activeArea = [getScreenWidth() - image.getWidth(), getScreenHeight() - image.getHeight()] @position = [@activeArea[0] / 2, @activeArea[1] / 2] @speed = [0, 0] @score = 0 @isAlive = true end def move() if @isAlive controls = getControls() {"w" : [0, -4], "a" : [-4, 0], "s" : [0, 4], "d" : [4, 0]}.each(){|k, v| if controls.isKeyPressed(k); @speed = v; end} @position.eachMapping(){|i, v| @position[i] = (v + @speed[i]).limit(0, @activeArea[i])} drawImage(@image, @position[0], @position[1]) end end end

Seb

That's amazing! :-)

  • Pingback: Behind “Play My Code,” an IDE for Creating and Sharing HTML5 Games - HTML5 Grind

  • Hey Seb and joss, I'm completely new to programming - Are there any speed/performance benefits to writing the code in each of the alternate ways? Is that even a consideration for PMC games compiled to JavaScript? Cheers, Andrew

    Seb

    In this instance I would say there's no practical difference at all. The best code is that which is easiest to read, especially if you're a beginner :-) Generally on Play My Code you shouldn't really need to optimise much unless you're doing something a bit out of the ordinary (like Joe's Raycaster engine demo), as we've tried to make the compilation from Quby to native JavaScript as efficient as possible.

    Thanks, cool bananas.

    Leave a Response

    Your email address will not be published. Required fields are marked *