Making Games With Ruby

Last updated: 2010-10-20 22:42:11 -0700

What is This?

I don't post the scripts or the code for each episode on the main website until I've completed the video and uploaded it online. For the Making Games With Ruby series, I plan to complete the writing of the entire series before I create any more videos, so the impatient can read the tutorial here before it comes out in video format.

The scripts for episodes 1 through 3 are sub-par, so I haven't included them here, links to the videos are available on this page: http://manwithcode.com/making-games-with-ruby/.

Please note that the episodes here are as they appear on my computer, and are in a format that's helpful for me, not necesarily you the general public (I mean, check out the outline, that probably doesn't mean anything to you, eh? But it's helpful for me, and I included it here just in case someone else found it useful). Everything is subject to change, it's entirely possible that some of what I've written is wrong.

If you see anything I say that's wrong, is confusing, needs more explanation, if there's any code that doesn't work, or if you have any questions, feel free to email me at tyler@manwithcode.com.

Tyler J. Church

Table of Contents

Outline

Outline
=======

Disclaimer (put an edited version in episode 13):
    There are many ways to make a pong game, this is by far not the only
one, and whether or not it's the best or the worst implementation depends
completely on your perspective. The main goal was to make your learning and
my teaching as easy as possible, we'll have to wait and see to find out
whether or not I succeeded. As this tutorial progresses, you'll probably see
me make some mistakes, implement some hacks, or backtrack and add things
that probably should've been there before. This is all part of the
development process, and as much as I'd like to make everything kosher and
neat, it wouldn't reflect real life, or at least that's my excuse for not
spending my time forever reworking this tutorial. And while it might be
difficult to rework this tutorial, it's not hard to rework and refactor
code. Which is why episode 13 exists, all the things that should've or
could've made it into the tutorial, but didn't.

Something I'd like to cover somewhere:
    Just 'cus I'm weird this way, I usually like to keep the code for
simple games all in one file, instead of breaking it up, since I find it
easier to CTRL+F to classes and methods, etc. and I tend to lose track of
things when they're spread across files (though for ROG, the code is split
up based on game states and components, and since my attention seems to stay
in one individual area, it's not a problem for the code to be split across
files). I guess this is the real point:
    I split code into files according to attention, it works best for
me, it might not for you.

Something else that might be interesting:
    I've kind of fallen into a pattern in the way all my games are
structured. Even games created with my own engine have this same structure,
with the engine interalizing some of the repetiveness. It might be
interesting to research and present the different ways people tend to code
their Ruby-based games. Or maybe it'd only be interesting to me, not sure,
but it's worth looking into either way.

Core
----

Ep. 1 - Intro
* What you'll learn
* What we're using
* What I'm assuming about you
* Why I'm teaching this

Ep. 2 - Setup
* On Windows
* On Linux
* On Mac

Ep. 3 - Basics
Ep. 4 - (WRITTEN) Drawing
Ep. 5 - (WRITTEN) Input and Movement
Ep. 6 - (WRITTEN) The Ball
Ep. 7 - (WRITTEN) Keeping Score

Finishing Touches
-----------------

Ep. 8 - (WRITTEN) Win condition, play again?
Ep. 9 - (WRITTEN) Game States (Title, About, and Pause)
    * Use pause screen as a simple example of states
Ep. 10 - (WRITTEN) Sound
    * [GOT IT] Popping sound for bounce
    * [CAN'T FIND IT] 8-bit-ish sound for score
    * [GOT IT] light, simple background music
Ep. 11 - (WRITTEN) Options
    + How high the score can go
    + Ball speed
    + Toggle Music
    + Toggle FX
    + Option Reset

Where to Go From Here
---------------------

Ep. 12 - (TODO) Packaging Your Game ('cus you wanna send it to all your friends!)
    * Picking a license
    * Creating a README
    * rubyscript2exe, ocra, etc.
Ep. 13 - (TODO) Tweaking the Game
    * There are many different ways to write pong, this isn't necesarily
      the only way, or the best way
    * Not starting the ball in the same place w/ the same velocity each
      time (see rand())
    * Better ball bouncing angle (see: gravipong)
    * Mouse controls for the menu
    * Different colored players
    * Using images for players, background, instead of using primitives
    * VS computer
    * High score table?
    * Rubygame 3 compatability
    * Different resolutions
    * Fullscreen
    * State stack
Ep. 14 - (TODO) Resources
    * gamedev.net (no forum dedicated to Ruby, but people there know a
heck of a lot about game development, useful articles too)
    * Rubygame.org wiki, forum, documentation
    * Me, email me at tyler@manwithcode.com

Possible future
----------------
Managing game states
lots of GameObjects
creating an engine
Automated game testing

Back to Top

Ep. 4 - Drawing

Hello Everybody and welcome to Making Games with Ruby episode 4, Drawing. I'm Tyler, and these videos are brought to you by manwithcode.com.

In this episode we are going to be getting some graphics up on the screen, namely the background and the player paddles.

Lets get started!

GameObjects - Our Foundation

Games have many different types of objects, our pong game will have the background, the player paddles, and the ball. I've found, as others have, that it's useful to create a sort-of lowest common denominator class that has the basic functionality and properties you want every object in the game to have.

For our simple pong game, I've decided that every object will have:

for it's properties, and it will:

We embody this description by creating a simple class:

It's a pretty simple class, and you should notice that the GameObject class doesn't do much, this is intentional. The GameObject class is meant to be inherited from by classes that will implement real functionality.

The update and handle_event methods are left blank because there's nothing we want every GameObject to do in either of those cases, and we will add specific functionality to the classes we create later on.

The draw_screen method is worth a little explaination:

The Rubygame::Surface#blit method is used to draw a surface onto another surface, blit takes 3 arguments, the third one is optional. The two we provide here are:

  1. Which surface we are drawing to, in this case it's the screen
  2. And where on that surface to draw the surface, we use the GameObject's x, y position for this.

If you remember algebra, graphing, or other similar things from your school math days, x, y coordinates should be familiar to you, but they work a little differently in computer graphics than they did in math class.

X and y are the distance in pixels from the "origin". The origin is the top left corner of your game window. As you get farther to the right of the origin, x goes up, as you get farther down from the origin, y goes up.

When you draw to the screen, surfaces's x and y positions are measured from their top-left corner.

Here's a picture to help you understand this better:

Coordinates

Background

Now that we have the groundwork in place, we can start to do some more interesting things like drawing the background!

Let's start by making a small skeleton class called Background:

And let's initialize it in the Game class:

Do you see that lonely, unfullfilled draw function in the Game class? Well now it's gonna have some code added to it!

There are a couple of things that you need to know to understand this code. The first is how colors work in Rubygame. Colors are represented as an array of three numbers, these three numbers represent the amount of red, green, and blue that will be mixed together to form the final color. The numbers you can provide range from 0 to 255. In this case we're drawing black, so we set everything to zero.

You can mess around with these numbers and see how to get different colors (you'll have to comment out @background.draw to see the different colors). You can google search "RGB colors" (without quotes) for some lists of different colors.

"But Tyler!" you might be thinking, "this is sooooo weird! Why can't we just call colors by name?" Well, you can. Instead of passing an array of three colors to @screen.fill pass Rubygame::Color[:black]. You can change the symbol black to a different color, like green, grey, pink, magenta, etc. But keep in mind that not all colors may be available.

So if you can call colors by name, why do I call them by number? First, it's because it's a habit I've gotten into, and habits are hard to break. And second, using numbers allow you to have a finer grain of control over the color, since it's possible that the shade of green Rubygame calls green isn't the shade that you want.

It turns out that since our background takes up the whole screen, we don't need to fill the screen with any color at all, but I do so anyway because not all games have a background, and you may need to use Rubygame::Screen#fill on one of your own games.

The second line is pretty self-explanitory, we call @background's draw method.

The third needs some explaination. Everything we're drawing, isn't actually being drawn to the screen, it's being drawn on a different surface that's off-screen. Rubygame::Surface#flip displays what we've been drawing to the actual screen.

Why does Rubygame work this way? Why not draw directly to the screen? If we drew directly to the screen, we'd start seeing things flicker because we're watching them get drawn (we'd see the black background get drawn, then our pong background, then our paddles, then we'd see them drawn over again, on and on forever). This way everything gets drawn as a cohesive whole, and eliminates the flickering problem.

If you've run the game, you'll see that we still have just a plain ole black background, let's fix that by adding some drawing code to the Background class:

If you run it now, you should see a beautiful pong background! So how does it work?

We're creating our background using what's called "graphics primitives", which are really basic things like lines, boxes, and circles. Rubygame::Surface#draw_box_s draws a filled-in box (as opposed to draw_box, which draws the outline of a box).

The arguments draw_box_s takes are:

  1. The upper-left corner
  2. The bottom-right corner
  3. The color to draw with

You probably can see the madness I created with all the addtion and subtraction, I know it's hard to understand, but I did it so I the background would look correct no matter the screen size (change the size of the screen, and see the background strech to the same size! Oooo... Ahhh...).

That's it for the background! Here's how the code should look by now:

Paddle - The Player Class

Compared to everything else, getting the players in the game is relatively simple and easy. First we create the Paddle class:

Create the player and the enemy in Game#initialize:

And add them into the draw method:

(Be sure to draw the players after the background or else you won't be able to see them!)

And now we have the two players in the game!

Now, personally, I think it'd look a bit nicer if we centered the players about the y-axis, so here's a simple method to do just that:

Add these lines to Game#initialize:

And you're all set! We now have our players on the screen!

Here's how the code should look now:

Conclusion

So now there's a little more excitement, we have players and a background! But wait, what's this? The paddles don't move!? Next expisode we're going to start accepting more input from the players and add the ability to move the paddles around.

If you have any questions, comments, or suggestions, leave a comment on this page or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 5 - Input and Movement

Hello Everybody and welcome to Making Games with Ruby episode 5, Input and Movement. I'm Tyler, and these videos are brought to you by manwithcode.com

In this episode we're going to be getting those oh-so-beautiful paddles we drew in the last episode to move! So let's get started!

Handling Keypresses

When the user presses a key down Rubygame sends a KeyDownEvent to our event queue, when that key is released Rubygame sends us a KeyUpEvent. Armed with this knowledge, we can start sending the paddles events:

And we can start handling them:

Most of the code is pretty easy to understand. The case and when statements are similar to how we handle QuitEvent's. The meaning of these expressions though, might not be immeadiately obvious:

When we're handling KeyDownEvents and KeyUpEvents, the event object has a quite useful attribute called key, which tells us which key we're interacting with. The value of event.key is an integer instead of a string or symbol like you might expect (this will be fixed in Rubygame 3.0). Rubygame has a nice set of constants defined to deal with this (like K_UP, K_DOWN, K_a, K_5, K_SPACE, etc.). You can see a list of the constants in the Rubygame module section of the Rubygame documentation.

So now pressing the up and down arrows moves the paddles! This isn't without some faults, most noteably that both paddles move to the same keys and that they move only once when the key is pressed, not while the key is being held down.

To fix the second problem, we'll need to have two variables, one that keeps track of if the player is moving up, and one for if the player is moving down. Let's initialize these:

Now when a key is pressed down we'll set the corresponding variable to true, and when it's released we'll set it to false (we're using KeyUpEvent now too!):

And we'll fill in our update method now to respond to this:

If we run the game now, nothing will happen because we haven't plugged in the calls to the pladdles's update methods, so let's do that!:

Now we can run it, and... IT'S ALIVE!!! I mean... They move smoothly now! Yay!

Both paddles still respond to the same keys, luckily it's pretty straight forward to fix this, though it requires a little bit of bouncing around in the code. What we'll do is create two variables called @up_key and @down_key, and when we create the paddles in Game#initialize we'll give each paddle separate keys to react to.

So here's the parameter changes for Paddle#initialize:

We also have to change Paddle#handle_event so it uses these new variables:

Last, but definately not least, add the keys to the arguments we pass in Game#initialize:

Now the left paddle moves with W and S, and the right one moves with the up and down arrows. Cool!

Here's how the code should look so far:

Tweaks

Our game is looking pretty good so far, but there are a few small tweaks that I think should be made. These changes aren't essential to the main gameplay, and you don't have to make them if you don't want to, 'cus hey, it's your game!

Limiting Movement

Right now the paddles can go off the screen and keep going and going as far as they want to, this we cannot have! They must stay on the screen!

It's pretty easy to implement, first we add some parameters to Paddles#initialize for the top and bottom movement limits:

And then we add some conditionals for the movement in update:

And then pass these limits in Game#initialize in a resolution independant way:

Closing the Window with ESC

You might've noticed that it's kinda annoying to have to hit the "x" in the top-right corner of the window every time you want to close the game. Quite a few programmers (me included) like to hook up the ESC key to close the game, which is really easy to do now that we know how to handle key presses:

Now we can close the game with ESC, awesome!

One new thing in this piece of code: Rubygame::EventQueue#push. #push allows us to send our own events onto the event queue, thus saving us from duplicating the quitting code, or separating it out into a method.

Here's how the code should look now:

Conclusion

That brings us to the end of another episode, I hope you've enjoyed it. Next up we're going to get the staple of all pong games moving around on screen: the ball!

If you have any questions, comments, or suggestions, leave a comment on this page or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 6 - The Ball

Hello Everybody and welcome to Making Games With Ruby episode 6, The Ball. I'm Tyler and these videos are brought to you by manwithcode.com.

In this episode we're going to load up a ball image, and get it boucing around on the screen, so let's get started!

Loading an Image

I've created a simple white circle in Inkscape (a vector graphics program) and rendered it to a .png file. This will be our ball! You can download it from this link (right click -> Save As): Ball. Be sure to put this image in the same folder as game.rb!

First things first we're going to create a new class for our ball:

And create an instance of it in Game#initialize:

And add it to Game#draw:

If you run the game now you can see our ball peeking out from the centerline. Woo hoo! We loaded an image from a file!

The only real new code here is Rubygame::Surface#load, which if you can't tell, gets an image from a file and turns it into a Rubygame::Surface object. According to Rubygame's documentation, Rubygame supports these image formats:

I usually use PNG images since they seem to give me the best file size and image quality. I haven't tested this scientifically, so feel free to prove me wrong or use whatever format that makes you happy!

Bouncing Around On Screen

Now a ball just sitting around isn't very interesting, so our first task is to get it moving. How will we do this exactly? First we're going to give our Ball object a velocity along the x and y axes. Each time Ball#update is called we'll move the ball with those velocities. If we want to change the direction the ball goes, all we have to do is change the velocity, you'll see the power of this in a few minutes.

So let's add that velocity, shall we?:

And don't forget to call Ball#update in Game#update:

Now the ball moves! But it quickly goes off-screen, never to return. Really we'd like to to bounce around the sides of the screen and off of the paddles. Let's take care of the screen sides now.

Since we'll need some information about the screen, we'll pass the @screen object to Ball#update:

And change the Ball#update method to utilize this:

It's pretty simple, if the ball hits the left or right side, we reverse the x velocity, if it hits the top or bottom, we reverse the y velocity. Now we have a ball bouncing around the screen! Excelent! But the ball completely ignores those paddles, let's fix this.

Bouncing Against the Paddles

First order of business: Letting the ball know when it hits the paddle. There are a bunch of ways to do this, and there's no clear "best way". I'm going to pick something that's worked well for me in the past, it involves using the Game class as the great overseer of all collisions, and a rather simple method of collision detection.

But wait, collision? That's a pretty broad term. What exactly constitutes a "collision"? To me, and to the collision detection code I'm about to show you, a "collision" is when two objects are touching in any way. Two GameObjects sitting next to each other without any space between them is a collision, overlapping is also a collision, and everything in between.

So in the Game class we'll create the collision? method that checks to see whether or not two objects are colliding:

It might look complicated, but it's actually pretty simple. It works by seeing if it can rule out the possibility of a collision. The first line, for example, checks to see if the bottom of obj1 has a y position higher up than the top of obj2, if it does there's no chance that they could be colliding, so the method returns false. If nothing can rule out a collision, then the objects must be colliding, and the method returns true.

Let's put this new piece of code into action!:

Hopefully that code explains itself. But it's completely useless without the Ball#collision method:

Now that is a lot of code! Thankfully it's not too complicated. First things first we figure out which side of the screen we're on, which should reveal which paddle we're colliding with. Once that's established, we make sure the ball isn't behind or above the paddle when the collision is detected.

After that's been checked, we move the ball one pixel in front of the paddle (so it isn't continuously colliding with the paddle), and we reverse the ball's direction.

Whew! Done! The ball now bounces off the paddles!

Here's how the code should look after all that:

Conclusion

That brings us to the end of another episode! I hope you liked it! Next up we'll be getting the scoring system in place, which means drawing text on the screen! Yay!

If you have any questions, comments, or suggestions, leave a comment on this page or email me at tyler@manwithcode.com

Thank you very much for watching! Goodbye!


Back to Top

Ep. 7 - Keeping Score

Hello Everybody and welcome to Making Games With Ruby episode 7, Keeping Score. I'm Tyler and these videos are brought to you by manwithcode.com.

In this episode we'll be giving players the ability to score on each other, and we'll display this score using text that we render ourselves, so let's get started!

Getting Text on the Screen

Before we can even think about drawing the text, we need to find a font that will be suitable for our game. I found one I like called "Freshman" which you can download here: http://www.dafont.com/freshman.font

The site says the font is free, but nothing more than that, so my apologies to William Boyd if we're actually not allowed to use this.

So download that font, and put it in the same folder as the other files for the game, and we can get started.

To be able to use text in Ruby game, we have to call Rubygame::TTF.setup so put that right after you require rubygame:

With that done, let's create a class called Text:

It's a simple class, but it's dominated by two things we haven't yet encountered: Rubygame::TTF.new and Rubygame::TTF#render.

First, TTF.new. To use a font, first we have to load it up by creating a new instance of the Rubygame::TTF class. It's important to note that I've renamed my font file to just "font.ttf". The size argument is like the font size in any old word processor, 48 is a good default for now.

TTF#render takes a string and turns it into a Rubygame::Surface for us. It takes four arguments:

Now let's test it! Add this line to Game#initialize:

And call the draw method:

Then run it and... Yay! "Hello, World!"

But, there's one problem: For the score we'll often need to change the string that get's rendered, but we can't do this after the Text class has been initialized, not easily anyway. Let's fix this!

Crack open the Text class once more and add this code:

Hopefully this code explains itself. In rerender_text we set width and height to the correct values, and then render it like we did before. Since the rendering is the last thing that takes place in the method, it returns the rendered surface, so we call rerender_text as the surface argument for super. We also create our own attr_accessor of sorts with the only difference being that when text= is called, we rerender.

Now let's test this:

It works, woo hoo! Now remove all that test code and we can get started on what we really came here for.

Giving the Paddles a Score

Now to give the paddles a score:

Small changes, you'll notice that we're now using all of the arguments for our Text class, and that we've created our own attr_accessor for score. I decided to have the drawing for @score_text be handled by the Paddle class, since I felt there was no real need to add to the long list of objects in Game#draw.

If we head back up to Game#initialize we can pass the two new arguments to Paddle#new:

I used percentages of the screen width to hopefully keep the scores in a good position no matter what the resolution is, as well as formatting the arguments so they weren't just looooooooooooooooooooooong lines. Other than that there isn't anything new here.

Scoring

We can see the score, but no matter where the ball goes, the score never changes. Instead of the ball bouncing on the left and right sides of the screen, we want it to give the player who scored a point, and have the ball reset somewhere else. Let's add this:

The collision code for the left and right sides is now separated out so that the score can be applied to the right place, and Ball now has a score method which reverses the direction of the ball and moves it.

We also can't forget to pass the player and enemy variables in Game#update:

Now players can score on each other! Excellent!

Here's how the code should look so far:

Conclusion

That's all for this episode, I hope you've enjoyed watching it. Next episode we'll make it so that one of the players can actually win, so the game won't just go on and on forever.

If you have any questions, comments, or suggestions, leave a comment on this page, or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 8 - Winning and Playing Again

Hello Everybody and welcome to Making Games With Ruby episode 8, Winning and Playing Again. I'm Tyler and as always these videos are brought to you by manwithcode.com.

In this episode we're going to stop the game from going on and on forever by letting one of the players win, and then we'll let the players choose whether they want to play again or quit.

Winning

For a player to win, they have to achieve some goal, for our game it will be a certain number of points, let's go with 3. Not an especially magical number by any means, but it keeps the game short and sweet, which I think is a good thing.

When a player wins we'll display some text stating "Player X Wins", with 'X' being either player one or player two. But before we can do that, we need to be able to detect when a player has won, so let's do that:

When a player wins it prints a message out to the console and exits the game, it works, but it's crude. Let's add some text to celebrate the player (this is gonna involve a lot of jumping around, so please bear with me) :

And a Game#win method to set this all up right:

Now this code uses the center_y method that the Paddles use, but Text definately isn't a Paddle, so let's move that method into GameObject, and create a center_x method.

And make sure to draw the text:

When someone wins we hide everything else, so nothing gets in the way of the win screen. And the game is over anyway, no need to show anything else, right? Right, but this is deceptive, because even though we can't see it, the ball is still moving around, we need to stop it from updating so it won't score any more points:

Whew! That was a lot of code... Get through it ok? I barely made made it myself. But now we can see beautiful text displayed when a player wins! Yay! You'll notice that I snuck in the "Play Again" text, which dutifully informs us to press Y or N depending on whether we want to play again, except... it doesn't work, let's fix that!:

Playing Again

Implementing this is a relatively simple handling of a KeyDownEvent in Game#update:

If the game has been won, and someone presses the 'Y' key, we reset the game to the way it's found when the game first starts. And if someone presses 'N', we call Rubygame.quit which makes sure Rubygame and SDL clean up nicely, and then we call Ruby's exit method, terminating the game.

Here's how the code looks now:

Conclusion

That about wraps it up for this episode, and most all of the gameplay aspects of this game, next episode we're going to be creating the different screens needed to call this a complete game, namely the title screen, pause screen, about/credits, and help.

If you have any questions, comments, or suggestions, feel free to leave a comment on this page, or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 9 - Game States (Title, About, and Pause)

Hello Everybody and welcome to Making Games With Ruby episode 9, Game States. I'm Tyler and these videos are brought to you by manwithcode.com.

In this episode we're going to be talking about a game programming concept called "game states", which will allow us to easily impliment a pause screen, title screen, and about screen.

Game States

Now what exactly are game states? And why would we want to use them? Game states (sometimes also called stages, scenes, rooms, and probably other things too) are a way of separating and managing the different sections of functionality in a game. By that I mean, a title screen will work differently than the pause screen, and both will work differently than the main gameplay state. Managing all that functionality would be a huge hassle with the current structure of our code, involving any number of extra variables and huge if blocks. The solution is to implement game states.

How do we go about doing this? First we have to separate out our state-specific code, and code that will be shared between states, into their own files.

Create a new folder named lib and create the files shared.rb and ingame.rb inside of it. shared.rb will hold any code that needs to be shared between game states, which would be the GameObject class and the Text class. So here's the content of lib/shared.rb:

Because we're going through a restructuring of the game files already, I've decided it would be a good idea to keep all our non-code assets (things like graphics, font files, and later on sounds) in their own folder named media. You can see in lib/shared.rb that the load paths of ball.png and font.ttf have changed to media/ball.png and media/font.ttf, so please move the files themselves to reflect this.

We also need to move the game specific code into lib/ingame.rb, and put it in a class called InGame:

You'll notice that pretty much everything is the same, except for when we handle events; the code to handle QuitEvent's and when the player presses escape have disappeared. And you'll also see that we get @game, @screen, and @queue from a variable called game, instead of creating them ourselves. That stuff is still handled by the Game class, which we'll get to right now.

game.rb:

game.rb warrents some explanation. First we've added two new require's for shared.rb and ingame.rb. We now have attr_accessors for screen, queue, clock, and state. Game#initialize works basically the same way it used to, except we now initialize @state and @state_buffer to nil.

The handling of events in Game#update is the same, except you'll see that instead of using @queue.each, we're using @queue.peek_each, why? Well each deletes the events after we've looped over them, working off of the assumption that we've successfully handled the events, and won't need to see them again. peek_each on the other hand let's us loop over all the events in the event queue, but doesn't delete them afterwords, this way we can handle QuitEvent's in the Game class, and the current game state gets to handle the rest of the events. Game#update and Game#draw both call the corresponding methods for the current game state.

The Game class now has a Game#switch_state method, which allows us to change the game state (and since there is no default game state, Game#switch_state has to be called before Game#run!). Now, why is it that we have a variable called @state_buffer? Why don't we just change the value of @state as soon as Game#switch_state is called? The reason I did it this way is because Game#switch_state is almost garunteed to be called in a game state's update method, which probably won't have finished running when it calls Game#switch_state. It just seems wrong to me for the value of @state to be something different than what the currently running state actually is. In practice it might not cause any issues, but I believe it keeps things cleaner and more consistant.

Last, but not least, we create an instance of the Game class, and switch states into a new instance of InGame, which starts our game running! Woo hoo! Nothing has changed as far as the player is concerned, but we have now set the stage to easily add more game states.

Oh, and if the previous activities of moving files around got you a little confused, here's how the directory structure looks now:

Pause Screen

With all that code-shuffling out of the way, we can now get around to adding some new game features, namely: the pause screen! The pause screen will be pretty simple, while you're in the game you can get to it by pressing P, and you can exit it by pressing P again.

First let's require in lib/pause.rb:

All states are classes, so here's the Pause class (in lib/pause.rb):

There's nothing here that's really new or noteworty, except for the fact that we switch back to a game state that already exists. We don't need to create a new one, and all the objects and everything in old_state are perfectly preserved!

In InGame we need to hook up the P button to switch to the pause state:

Again, it's pretty simple, we create a new instance of the Pause class, give it @game, and the current instance of InGame as the old state, and the pause screen works!

Title Screen

With two relatively easy states under our belts, let's try something a little more difficult: the title screen.

First things first, let's create the file lib/title.rb and load that up in game.rb:

And let's make Title the state that's switched to at startup:

Oh, what's in a name? Err... I mean, what's in a title screen? Well, I figure having the title of the game, a menu choice to play the game, one to look at the about/credits screen, and one to quit the game seems good to me, so let's put all that in our Title class.

Before we get into any of the menu logic, let's just get the graphics up on screen:

And now we have a pretty good looking menu! Not award winning by any means, but it'll do it's job well.

Now onto the menu logic, which gets a little more complicated:

The most important logic here is that for handle_event, which increments @choice when down is pressed, and decrements it when up is pressed. @choice is available to the outside world, so it can be read by our Title class to enable the proper state changes.

The more flashy side of our menu takes up a few more lines of code. update isn't too complicated, it's main purpose in life is to move the two little paddles that visualize our menu selection to the correct place. I chose to do it this way, instead of the simpler method of instantly moving the selectors, just because it looks cooler (and I believe that the initial movement makes the selector's purpose a bit more obvious to the player). I feel it's important to note the not-so-obvious purpose of the first if's in update, which will set the position exactly to the target position if the if evaluates to true. Now why would we need to do this? Wouldn't just adding the speed get the selectors to where we want them to be? Well, as it turns out, it will, but most of the time we'll be off by a few pixels, causing the selectors to jitter back and forth because they never get to the exact position they want to be in. Just figured I'd make a note of that.

Now let's add our menu selector to the game:

If we run it now, we can see our menu selector in action! Cool! Except, we can't actually pick anything... Only move between choices, let's fix that!:

This code isn't very complicated, whenever the player presses Enter (or Return, if you prefer to call it that, as Rubygame does) we check to see what choice the player has selected, and either change to the correct state or quit the game completely. Notice that the second selection (which leads to the About state), hasn't actually been implemented, and will cause the game to crash if you try to choose it. We'll get to the About state in the upcoming section.

On the surface, it would appear that we're finished with the Title state, and for the most part, we are! But there's the issue of quitting the game: We can currently quit the game from anywhere by pressing escape, by pressing N when a game ends, as well as by pressing the quit button on the Title state, and there's no way to get back to the menu! So let's make it so the game can only be quit from the title screen, and that pressing Escape will quit the Pause and InGame states.

First, the InGame state:

Now, the Pause state:

One could almost be led to believe that this would work, except it doesn't, the Game class still intercepts the escape button being pressed, and quits the game completely no matter what the state is. We can fix this by adding a simple "and" to the if statement, though to me this seems a little hacky, so I added a notice in the comments about it:

Ok, now we're done with the Title state! Now we just add in the About state, and we're done!

About Screen

The About screen simply shows the credits for the game, there's nothing here that's new or worthy of explaination, so here's the source code:

This is your game too, so feel free to change the credits to whatever you want!

Oh, and we can't forget to load up ./lib/about.rb in game.rb:

And we're done! Yay! :D

A Last Tweak

Or almost done... You see, there's one thing about our current menu setup that bothers me, no matter which state we leave (About or InGame), the menu choice always goes back to "Play Game", which I think is a little odd. If you just came back from About, you should still be on About!

For such a relatively small issue, there is a surprising number of ways we can solve it.

  1. We can let the game states switching to Title choose which menu option to start on.
  2. We can use a class variable in Title to keep track of the option that was chosen before the state was switched.
  3. We can implement a "state stack" that would provide an easier way of managing states that we want to be persistent.

Option three, the state stack, is the cleanest and most "proper" way of solving this problem, but it would take the longest, and this episode has gone on for long enough. Option one necesitates that other game states have knowledge of the Title state, which they shouldn't have to deal with.

That leaves option two, giving Title a class variable:

Now when the player presses Enter, the current choice is saved into @@choice. This same variable gets passed in to Selector#initialize's new choice argument. Maybe it's a little hacky, but it's quick, simple, and it works!

Now we're done! :D

Here's how the directory structure should look after all this:

And the following is the full source for all the files (excluding lib/about.rb, which was listed above):

game.rb:

lib/shared.rb:

lib/ingame.rb:

lib/pause.rb:

lib/title.rb:

Conclusion

That brings us to the end of another episode, next up we'll be adding sound to our game, which should be lots of fun!

If you have any questions, comments, or suggestions, feel free to leave a comment on this page, or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 10 - Sound

Hello Everybody and welcome to Making Games With Ruby episode 10, Sound. I'm Tyler and as always these videos are brought to you by manwithcode.com

In this episode we're going to be adding some sound to our thus far silent game. Sound is essential to any good game, and adding it is drop dead simple, so let's get started!

The Sound Files

First things first, if we're going to be playing some sound in our game, we'll need some sound files to play. According to Rubygame's documentation, Rubygame supports the following audio formats:

Because it's the easiest for me, relatively ubiquitous, and has a decent compression rate, we'll be using the ogg vorbis format.

Our Pong game will have two different sounds, a piece of background music, and a popping noise sound effect for when the ball hits the paddles or the sides of the screen. I made the sound effect myself, but not the background music. The background music is RoboWizard [EC] by the user EmperorCharlemagne on newgrounds.com, and is released under the Creative Commons Attribution-Noncommercial-ShareAlike license. I'm releasing my sound effect into the public domain.

And with all that out of the way, here's a direct link to both files, save these into your game's media folder (right click->Save As):

Pop!

Alright, let's start with the easier of the two, our sound effect. Basically we want the ball to play the popping sound whenever it hits a paddle or the top or bottom sides of the screen. Before we can do that, we need to load the sound, so crack open lib/ingame.rb and add the @hit_sound variable to the Ball:

Yep, it's that's all it takes to load up a sound file! And playing it is as easy as calling @hit_sound.play:

And that's it! Now our ball plays a lovely popping noise whenever it hits something!

Our Background Musician

Our background music will work in almost exactly the same way that our sound effect did, execpt for some small differences. The first difference that you'll notice is that we'll be using the Rubygame::Music class instead of the Rubygame::Sound class. The Music class allows us to manipulate the sound file in a few extra ways that the Sound class didn't allow, and it also loads the sound file more efficiently than the Sound class does (by keeping only part of the file in memory at a time), because music files are often larger than their sound effect counterparts.

The music variable will be a class variable so we don't keep reloading it each time we enter the InGame state, and so we can keep track of where the music left off. Ideally we would create a music manager of somekind, since class variables are kind of hackish, but this'll work for now:

It works! But not quite perfectly. First, the music keeps playing after we leave the InGame state, it should pause when we leave, and then resume when we come back. Second, the background music is a loop, meaning that once we reach the end of the song, we're supposed to seamlessly return to the beginning, but this doesn't happen.

To fix the first one, we'll have to take a brief tangent, we'll get back to the second problem in a minute. Remove the call to @@music.play and then we'll get going.

Design Flaw: There is No State Class

To properly manage the pausing and unpausing of the music, we need to have the Game class tell the InGame state when the state is changing. Since this could be useful in other contexts, we'll want the Game state to inform all relevant states when a state change takes place, not just our InGame class.

To do this, the Game class will call a state_change method. Not all states will need to respond to the state changing, and it seems kind of ridiculous and sloppy to be defining a blank state_change method in all the states other than InGame. Let's take a step back for a second, and think about why we're being forced to do this, and if there's a better way. We're having to do this because our states need to respond to all the methods that we've decided they should, at this point it's update, draw, and state_change. There's no formal definition of this, it only exists within our minds, and it's reflected in the game states we currently have implemented. Having no formal definition, and having to implement blank methods are design flaws, but luckily the fix is pretty easy, we just need to implement a State class for all our other states to inherit from.

Load up lib/shared.rb, and add the following class definition:

And change all the game states to inherit from this class:

lib/pause.rb:

lib/title.rb:

lib/about.rb:

lib/ingame.rb:

There! Now we can add new functionality to all State's easily and relatively effortlessly. To be honest, the State class probably should've existed as soon as we added game states, but I didn't think of it. 'Tis the joy of programming, mistakes are made, but it's usually easy to recover.

Changing State? I'll Alert the Media

With that out of the way, the Game class can now inform the states when a state change is taking place. Open up game.rb, and edit Game#switch_state so it looks like this:

The changes are pretty simple, with a call to state_change on the state that we're changing out of, and another call to the state we're going into, as indicated by the argument we pass. This works, but it doesn't actually do anything from the player's perspective, let's change that by implementing InGame#state_change:

This one's another easy one, we pause the music when we're changing out of the InGame state. If we're changing into the InGame state, we check to see if the music is paused (and thus had started playing previously), if it is paused, we unpause it, if it's not, we start playing it from the beginning. Remember how we had the issue with the background music not looping? The :repeats option on @@music.play fixes this. :repeats sets the number of times the music will repeat, -1 makes it repeat indefinitely.

And, woah, what's this? The music plays and pauses? That must mean... We're done! Not that difficult, eh?

Notes on Sound

While we might be done for now, I still want to urge you to check out the documentation on Rubygame::Sound and Rubygame::Music, and see all the available options. Two things you're probably be most interested in are adding a fade effect to sounds, and changing the volume level that they play at.

Also, keep in mind that while you can load and play as many Rubygame::Sound's as you want, you can only ever have one Rubygame::Music playing at a time.

Source

Here's the full source for all the files thus far:

Directory Structure:

game.rb:

lib/shared.rb:

lib/ingame.rb:

lib/about.rb:

lib/pause.rb:

lib/title.rb:

Conclusion

And that's it for this episode, are those sounds annoying you yet? Are you starting to wish you had a convenient way to toggle them on or off in the game? We'll you're in luck! Next episode we'll be adding options to our game.

If you have any questions, comments, or suggestions, feel free to leave a comment on this page, or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top

Ep. 11 - Options

Hello Everybody and welcome to Making Games With Ruby episode 11, Options. I'm Tyler and these videos are brought to you by manwithcode.com.

Not everyone will want to play our pong game in the same way. Some might want the ball to move faster or slower, some might want the winning score to be higher, some might want to disable the sound effects or background music. Today, our goal is to make all this possible, so let's get started!

Creating the Conf File

So how does one go about making a game configurable? On the surface, it's pretty easy, we just add some variables and allow the player to change them. The only problem is that as soon as the game is closed, we lose any customizations that the player made. Not good.

The solution is to keep the configuration inside a file. We could create our own custom configuration file reader and writer, but that's a hassle we don't need to deal with. YAML to the rescue! What's YAML, you ask? YAML is two things: First and foremost, it's a data format that's readable and writable by both humans and computers. Second, it's a library in Ruby's standard library that makes it really easy to store the data our programs use in the YAML format, and then later reload that data into our program, which is exactly what we need!

Crack open irb and we'll create our configuration file:

It's pretty easy, the only thing that's really new here is YAML.dump, which turns our conf hash into the text of the YAML file, which we then save to lib/conf.yml.

Here's what the file looks like:

We can load up the file just as easily:

Which gives us exactly the data we saved! Excellent!

Using the Conf File

Now that we have a conf file, the ways we could go about making it useful to us are many, but I figure that the easiest way is to have a constant named Conf that holds our settings, and a method that saves out the settings to a file.

Create the file lib/conf.rb, and add these lines:

And be sure to require lib/conf.rb in game.rb:

Now that the settings have been loaded, we can use them! Let's start with the music, open up lib/ingame.rb, goto InGame#state_change, and wrap it in this if statement:

Next up, the ball's sound effect:

The winning score:

And lastly, the ball speed:

And that's all there is to it! Try changing the configuration values in lib/conf.yml and see what effect it has. Pretty cool, eh?

Building the Options Screen

Sure, it's easy enough for us to edit lib/conf.yml, but our players sure as hell won't want to have to do it, not to mention that we have to restart the game to see the changes in the configuration. What we need is an Options screen, so let's start building one!

First up, we need to add an "Options" entry to the Title screen:

Basically the choices just got shuffled around, so there isn't much here that's worthy of explanation. One thing I would like to point out is this:

This is one of the lesser-known features of Ruby, basically it takes the items in an array and places them into another array (such as an argument list, or a literal array). So this:

Becomes this:

Alright, so now we have the entry on the Title screen, now let's start building the Options screen. Create lib/options.rb and add this skeleton for the Options state:

And require in options.rb in game.rb:

Now we need to think about how our options screen is going to look. In essence it'll be a list of the four configuration settings we have, that the player should be able to scroll through and change. We could tool ourselves a new mechanism to let the player select among items from a list, but if you'll remember, we've already written one for the Title screen! So take the code for Selector in lib/title.rb and move it over to lib/shared.rb so it's clear that it's meant to be shared among game states, and we can get started using it!

We really have three different types of options the player can alter. One is a simple true/false or on/off, the music is either playing or it's not. Another is a list or a range of choices, like how high the winning score goes, or how fast the ball goes. The third is just a reset, which restores the options to what they originally were.

Just because it's the simplest, let's start with the OnOff class:

It's not scary at all, see? OnOff#initialize just takes it's position, text size, the prefix (i.e. the name of the option), and the option in Conf that it'll be altering. OnOff#cycle changes flips the right option and displays the change. If the self.text= part looks a little odd to you, don't worry, it's just like changing the text for any other instance of the Text class, we have to call that method so we're sure the text gets rerendered correctly. Hopefully everything else is self-explanatory.

Now let's start using our new OnOff class!:

There we go! Not too difficult! Quite like our Title class, really.

Next up is the control for options that have a list of choices:

Strikingly similar to our OnOff class, except it cycles through a list of choices, not too difficult.

Now let's use the new List class:

Excellent! Now we can manipulate all the options! You might be wondering why excatly one of the options for @winning_score is -1, how could the score ever get to -1? Well, that's the point, it can't, so when the winning score is set to -1, the game will never end, and players can play for as long as they want to!

Now that the players can change every option that we've made available, it stands to reason that we should also implement a way to reset them to their defaults. First things first, we should add a reset method to Conf in lib/conf.rb:

And a Reset control in the Options screen to take advantage of this:

This Reset class takes advantage of reset methods in OnOff and List that haven't been implemented yet, so let's implement them!:

Basically all this does is regenerate the text for the controls, as well as resetting the internal @choice variable in List. You'll notice that Reset itself has a blank reset method, since it'll be included in the list of options, even though it isn't technically an option itself.

Now we can't forget to actually instantiate Reset:

And we're done! We can now alter all the options, and reset them back to their defaults! That's all there is to the Options screen!

Source

Here's the full source for the files so far:

Directory Structure:

game.rb:

lib/conf.rb:

lib/conf.yml:

lib/ingame.rb:

lib/shared.rb:

lib/title.rb:

lib/options.rb:

Conclusion

I hope you enjoyed this episode! The end of this episode actually marks the end of the core development on our Pong game, next episode we'll be talking about packaging the game so you can distribute it to friends, family, paying customers, or whoever else you want to!

If you have any questions, comments, or suggestions, feel free to leave a comment on this page, or email me at tyler@manwithcode.com.

Thank you very much for watching! Goodbye!


Back to Top