Attention Rubyists: you can write video games
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Chamblin, Cory
Ruby may seem like a humdrum language best suited for writing web apps, but underneath that cool exterior lies plenty of power - power we can harness for making our own games! In this talk, we'll introduce Gosu, a sweet little game library. We'll talk about how games are structured in pretty much every language and framework ever, where to get ideas for things to make, and ways you can share your game with your friends and the world.
 So, hey everybody, my name is Corey Chamberlain. I am a programmer. I write back-end software at a company called PagerDuty. But today I'm not going to talk about any of that stuff. I'm going to talk about something way sweeter. I'm going to talk about how to build video games in Ruby. I don't do this for my job, but I like to write games. And I think Ruby is a good language to do it in. So the GitHub for the talk, github.com. You can go there, download the slides, download all the code for the talk. So first, we're going to talk about the reasons why you might want to make games, or why you might try to get into this as a hobby, just something to do on the weekends. We're going to talk about why Ruby is a pretty good choice for getting started making games. The second bit there, the main chunk of the talk, we're going to talk about how games are structured in pretty much every game framework out there. We're going to talk about GoSue, which is an amazing little library in Ruby for building your own games. And we're going to talk about how to write pieces of games that you might want to build by building a couple toy games using GoSue. And if we have a little bit of time at the end, we're going to talk about a few resources for aspiring independent game developers. So if you're not an artist, you can go get some open source artwork or some open source music, sound effects, and maybe some tools that you might use in building your own games. So first of all, why do I make games, and why should you make games? So I like to make games because it reminds me of when I very first started programming. Back in the day, we would get these giant books of basic programs. And they were games. And you would spend your whole afternoon typing this stupid program into your computer. And you would get to the end. And you would type run. And if you were very lucky and you didn't make any mistakes, you would blow yourself away with a really bad game. No, but it was exciting in the sense that I typed this thing in and something happened on my computer. And that is a real visceral, kind of creative feeling that I think all professional programmers need to find some way to get that. The first time I hooked up an Arduino to my computer and I made the little light blink on it, I wrote some code and do a thing, and I pressed play. And a thing that was not my computer did something else. That was a similar feeling for me. So I think you should try to write games if you don't already have an outlet like this. See if games are that for you. And if not, I highly encourage you to go find some way to creatively express yourself with your craft. Because it's very important to grow as an engineer, as a programmer, that you can express yourself creatively. So what about Ruby? Nobody considers Ruby a high performance programming language. So is Ruby the right tool for the job? Is it the right tool to build games in? So when I first became a professional programmer, I first had some interest in writing games. Because a lot of people get into programming because of writing games. And I think games are sweet. So I was like, OK, well, I can write stuff. I have a computer science degree. So what do I need to know to write games? And I thought, man, OK, well, I need to go learn C++. I need to go learn half a dozen graphics libraries. I need to know what SDL stands for. Like I felt like there was so much I had to learn to get out there to write games. But none of that is actually true. So almost every language out there, every language out there has some library where you can write really nice 2D independent style games, very commercially popular. These are the games I love to write. I love to play. And that's a ton of the kind of games. Ruby is no exception. We have Gosu and Ruby. And there's plenty of power in Ruby to write these simple games that are very full and very satisfying to write. Photographers often say that the best camera that you can have is the one you have with you. So if you want to make games, don't feel like, OK, well, there's just so much I need to know to do that. You can make games today. If you know how to write Ruby programs, you can write games. So go make your games with Ruby. If you feel like later on, OK, well, I'm feeling kind of hamstrung. I need a little more performance. The lessons you learn writing games in Ruby, the lessons you learn writing games in Gosu, ports very well to the more commercial tools. I poked around in Unity. I wrote a couple games using Unity, just having background writing games in Gosu. And the vocabulary, the semantics of writing games, the structure of games is very similar across the board. So you can kind of get your sea legs writing games in Ruby. And if you want to go do this professionally, totally room to grow into those higher end tools. But for me, I found out, hey, I love Ruby. Ruby's a great language. And I'm not making a ton of money writing games, so I'm going to have fun doing it. I'm going to write my games in Ruby. So let's get into the kind of meat of the talk here. Gosu games can just be one class. So we inherit from a window class in Gosu. And you only have to implement three methods, generally. And these three methods, you're going to see them all over in game development. So every game framework literally I've ever used has had these three methods be the primitives for building games. The first thing is the constructor of your game. So this is responsible for doing things like setting up the window details, like what's the size of the window? What's the title on top of it? Is the game full screen? What's the resolution, if so? Kind of the graphics setup of your game. It's responsible for loading assets from disks. So things like your image files, your sounds, and your music files, for reasons we'll talk about in a second, you don't want to be reading those things off disk in the middle of the game. You want quick access to them. So the constructor is responsible for loading all those things into memory. Finally, it's responsible for setting up the initial game state. So things like, well, I have three lives. So initialize says, lives equals three. It's not very complicated. Level is one. Very simple stuff. The state of the game, as the game begins, it's responsible for that. So this is called, when your game first runs, this is called the setup method in most game frameworks. So after this gets done running, the game enters into an infinite loop. And that infinite loop is called the game loop. And then we call the other two methods in sequence in that game loop over and over again. The first of those methods is the update method. And it's responsible for detecting and responding to user input. So did the user press the B button? OK, what do I do? Did the user press the right arrow? What do I do? It's also responsible for updating all of the state of the game. So if the user pressed the B button, I'm shooting a fireball. If the user pressed the right arrow, I'm adjusting the player's position five pixels to the right. That kind of stuff. Finally, the draw method. It's responsible for drawing the current state of the game to the screen. So it doesn't change anything about the game. It just goes through, say, all of our image assets and says, OK, well, based on the state of the game right now, I'm going to draw the hero right here. I'm going to put three hearts in the top right corner, all this kind of stuff. And so these are called in a loop, the game loop. And each time we go through that loop, that's called a frame. And however fast we're going through that loop, that's called the frame rate. And Ghost2Tries2 run games at 60 frames a second. But obviously, if you have a bunch of stuff in these two methods, that can slow it down. This is why we load images into memory. Because if you had to load all of your image files into memory on each frame, if you try to do that 60 times a second, your game is going to run really, really slowly. And you're going to turn your hard disk into a pile of slag. So that helps us keep up a frame rate. And that's basically, at a very high level, how games work. So to demonstrate these concepts, I wrote a really amazing game called Winner, Winner, Chicken Spinner. And how this game works is the chicken there, he spins until you press the spacebar. And then when you press the spacebar, the game ends. It's over. And if he stops in the highlighted area, you win. If he stops outside the highlighted area, you lose. The game is over. So the implementation of that game, it goes like this. So first, we're inheriting from Ghost2 Window. At the bottom there, we're instantiating our game, and we're calling the show method on it. I don't know why I named this game Window. That's a terrible name. It should have been Winner, Winner, Chicken Spinner Window. So anyway, here's the implementation of Initialize. We have the three responsibilities of Initialize. And I kind of made different code paragraphs for the responsibilities. First, it's responsible for setting up the window. It's saying, OK, super class, make the window 480 by 480. It's setting the title of the window to Winner, Winner, Chicken Spinner. If the game were full screen, we would set that up. Second, it's loading all of our assets into memory. So the way we do that is we just instantiate the image class. We give it a path. These are just PNG files. There's no fancy games-only image format here, just PNG files. We have the chicken plate disk thing. We have the arrow. And then we have two different images at the end of the game. If the player wins, we're going to draw that fantastic win logo. And if they lose, the sad lose logo on top of everything. The last bit there is setting up the initial state of the game. So Winner, Winner, Chicken Spinner has really three pieces of state. It has the current rotation of the chicken, because each frame we're going to rotate that chicken a little bit so he starts spinning. It has a game over flag, so we know to stop spinning the chicken when the user presses the space bar. And it has a one flag. So we can tell, are we showing the win image or the lose image on the game over screen? The update function, remember, is responsible for checking for user input. And it's responsible for updating the state of the game. So if the game is over, like the user's press space bar, this function does nothing, because the user can't press space bar again and they can't cheat. However, if the game is not over, we want to check to see if the user has pressed the space bar in this frame. So if they did, we can use the button down method. And it has a little constant called kb space. All the keys on the keyboard have these constants. They're all in the go-sue docs. But if they press the space bar, we're going to set that game over flag. We're going to set the one flag to whether or not we won. And that just looks at the current angle of the chicken to figure that out. And it was a lot of trial and error to come up with those numbers. There was no trick there. And if neither of those things are true, we're just going to rotate the chicken by 10 degrees by adding 10 to the chicken angle. And that is all update does. The draw method, remember, all it's doing is it's saying, I'm going to render the state of the game to the screen. Whatever the game looks like right now, I'm just going to draw that. So the little blue arrow that you saw, we're always going to draw that to the screen. That's at x equals 320, y equals 200. It's important to note in go-sue that the origin, 0, 0, is on the top left-hand corner of the game window, not the bottom left-hand corner. So as you go down, your y coordinate goes up. Some game frameworks like to do bottom left. Some like to do top left. I don't really know why, but go-sue is a top left kind of framework. The last number there is the z-index. And if you do any web development, you're probably familiar with that. It's just the draw order of the images. The arrow will be on top of anything with the z-index less than 1 and under anything with the z-index greater than 1. The next bit is drawing the chicken plate. So we're going to use the drawROT method because we want to rotate it. It's the same x, y, z stuff there, but we have the angle and degrees that we want to rotate the chicken. You'll note we're not setting it or changing the angle. We're simply drawing the angle that the update method has set. Finally, the game over state. So if the game is over, we want to draw the game over screen. And the game over screen depends on whether or not we won the game. And if we won the game, we want to draw the win image. Otherwise, we're going to draw the lose image. And we're going to draw it at z-index of 2, so it's on top of all the other stuff on the screen. And that's actually all the code there is in Winner, Winner, Chicken Spinner. But Winner, Winner, Chicken Spinner is a very, very simple game. And if you came to a talk about making games, you probably have ideas in your brain about games you want to build, and they don't look anything like that. So we're going to make another very simple toy game. But it's a little platform game. And that has a lot of important kind of mechanics, pieces of games that you will map to pretty much any kind of game you want to build. So you don't have to make that huge mental leap from Winner, Chicken Spinner to what I actually want to do. So let's jump right into the code on this guy. So first, the idea is we have a little guy, and we want to be able to move him around on the ground just like a real platform game. And we wanted to be able to jump. So we're going to make a new game. This one's called Running Hero. And we have two images right now, the Hero image and the background image. We're going to load those into memory, and then we're just going to draw them statically on the screen. You know how to do this from Winner, Winner, Chicken Spinner. The next bit is we're going to make our guy be able to walk, or at least be able to slide along the ground in an orderly fashion. So in a platform game, the rules of movement are that the player can move to the left or the right, but he can't move up and down. And if you want to build like your top down, like Zelda kind of game, you just change the perspective, and then the player can walk up and down too. The same principles apply though. So all we've got to do to walk, we're going to store the current position of the hero in an array with the x and the y coordinate. And in the update function, remember, update is going to check for that user input. It's also going to change the state of the game. So when they press the right arrow, we're going to want to update that position by changing the x coordinate. And when they press the left arrow, we're going to want to subtract from that x coordinate. So we wrote this little move function that does that. It just adds or subtracts five to the x coordinate based on the speed thing, which you can set and change to make your hero a little faster. The only change I had to make to the draw function was just quit using hard coded numbers and start using our position when we're rendering our hero. And that's all there is to drawing the hero and making him be able to slide across the screen. Jumping is a little trickier. And you kind of have to think about how jumping works in real life, and then you sort of have to dumb that down for games. And I kind of just made this up for this talk, so there's probably some way better way to do this. But this is a simple way, and it made sense to me. So how jumping works in running hero is you can only jump if you're on the ground, so there's no double jumping antics. But when you jump, you get a certain amount of vertical velocity. And then each frame, the game's going to look, does the hero have vertical velocity? If so, I'm going to move their y coordinate up by that number, and then I'm going to divide their vertical velocity by some amount of gravity. And then eventually, they're going to run out of vertical velocity. They're going to be at zero. And then they're going to start falling. So I'm going to give them negative one vertical velocity. And then each frame will be multiplying that velocity times gravity. So they'll go faster and faster and faster until they hit the ground, and then they can jump again. So maybe some code is a better way to explain that. So the implementation of that, just in the update function, if the user presses the space bar, we're going to call this jump function. It just does nothing if the user's already in the air. Otherwise, it'll give them a vertical velocity of 30 pixels per frame. The other change to the update function is we're going to call this handle jump function, which is going to do all that math we just described, and updating the hero's position, and updating vertical velocity. The implementation of that, we have this gravity constant of 1.75. Just has to be greater than one. I just fiddled around until I found a good feeling number. You will see that a lot in my game. Most of these numbers are just made up. The ground level, I can talk about that in a second. It's just the y-coordinate where the hero is standing on the ground, because that is currently how we're going to find out that we've landed. The next bit, just subtracting the vertical velocity from the hero's y-coordinate. Because remember, the top of the screen is y equals 0. So as you go up, your vertical velocity goes down. And then when you have negative vertical velocity, your y-coordinate will start going up again. The next paragraph thing there is just changing the vertical velocity. So if the vertical velocity is 0, that means the hero is at the top of the jump, and we need to start falling. So we'll give them a vertical velocity of negative 1. So on the next frame, they'll start falling. If their vertical velocity is negative, that means they are falling. So we're going to multiply their vertical velocity times the gravity constant. So they're falling a little faster on the next frame. And if they're going up, then we want to divide the vertical velocity by the gravity constant. So they go up a little less on the next frame. The last bit there, like I said, so when we get back on the ground, we need to know that we're back on the ground. Otherwise, we're just going to fall forever. So how we're doing that is our ground is a straight line. So the hero is always at the same y-coordinate when he's standing on the ground. So we could just look at the y-coordinate. If it's at the ground level, then we can stop jumping. Since this is a platform game, eventually you're going to get to the point where you have platforms to jump on, that kind of thing. The logic will have to change. You'll have to use something like collision detection to know if you're on the ground or not. But that's actually all there is to jumping and to walking. So now our guy, he can walk and he can jump. But he does look a little silly because he cannot look to the left, which is sad. And he also kind of just moves like a cut out paper doll and not like a real hero. It doesn't feel like a game yet. It feels like a toy. So in this chunk, we're going to animate the guy. We're going to add a few poses to him and make him look way better. So to make the guy be able to look to the left, we're going to need to know which way he's facing. And that can change as the game changes, so we need a new piece of state. So we're going to store the direction he's facing. We're going to update that direction in our move function because that's the only way currently that our player can be turned around to face the other way is that they start walking the other way. And then we're going to do this little trick in the draw function. So one way you could do this is that you have a left facing image for every image in the game. But that tends to get a little unwieldy and it's a little hacky. So a better way to do it is that the draw function on the image class has a scale x and a scale y parameter. And if you set those things to negative numbers, it actually will mirror the image and make it face the other way on that axis. So if you set scale x to negative 1, it's going to flip the hero over back the other way. And he's going to be facing the other direction back to back with his original position. To compensate for the fact that we just moved the hero to the left by his width, we're going to add the width back to the x coordinate. So now our hero can be standing in the same spot, but he can turn around and can face the other way. So a pretty neat little trick there. So now our guy can look to the left. He can look to the right. But now we need to animate him. So animation in Gosu is very simple, and by simple, I mean you're on your own. So what you do is you get an array of images, and each frame you just decide, this is the one I'm going to draw this frame. So you load up all of your stuff into an array, and then you pick based on the state of the game, which of these am I going to draw? Fortunately, we have a really easy way to get a bunch of images into an array, and that's by using sprite sheets. So sprite sheets are a very, very common tool in games development. You just basically take all of your images that you want. You make them all the same size, and then you put them all in a bigger image. And then you load them into your game, and you say, all of my sub-images are this size, cut them out for me. And it'll do that for you. This is usually done for complicated graphics card reasons, but in Running Hero, it's done because it makes the code nicer. That the thing there behind the blue wall is our sprite sheet. So we have seven poses for our hero. We have the standing still and doing nothing pose. We have three frames of animation for walking. So if you play these in a loop, it looks like walking. It's called a walk cycle. The fifth image is the player with his arms out going up. I don't know why this is going up. I have never jumped like that. But that is what going up looks like for our hero. For going down, our hero is kind of looking down at the ground. And then the last image there is like our hero has been hurt, and he's stunned, or just very surprised. So to build all this animation, all these poses into our game, in the initialize function, remember we load assets there, we're going to use the loadTiles class function on the image class. We're going to give it our sprite sheet. We're going to say our sprites are 64 by 64. So load those all into memory. And this will just return that array for us, of regular images. We also need to keep track of which image we're going to show on this frame. So we have this piece of state called the current hero image, which update is going to set, and then draw is going to draw. So in update, we're going to look at the state of the game to figure out which frame to draw. So if the hero is jumping, that means they have a vertical velocity. And based on that vertical velocity, we're going to decide to use the going up or coming down image. If they have a positive vertical velocity, we'll use the going up image. If they have a negative vertical velocity, we'll use the going down image. Otherwise, if the hero is walking, one of the weird things about this is that the hero can be jumping and walking at the same time. So if the hero is walking, then we will just use one of these three images in the walk cycle. And you don't want to just loop through these one per frame because your game runs at 60 frames a second. And this animation looks ridiculous at 60 frames a second. So you want to slow that way down without slowing down your whole game. So how we do that is we can take the number of milliseconds since the game started, divide it by 100, and that gives us 10 frames a second. So we just take that number, module O3, and that gives us a loop 0, 1, 2, 0, 1, 2. We add one to that, and then we get the right offsets in the array for the hero image that we need to show this frame. Finally, if the hero is neither jumping nor walking, we're going to just set the current hero image to the resting hero image, the first hero image. And the only change we had to make in the draw function was that we want to use the current hero image instead of that old static hero. And shockingly, that is all that it took to animate our guy, and actually he looks a lot better right now, and he's feeling great about life. However, our game is missing a plot. It's missing drama, and to have drama, you need to have pain, and you need to have suffering. And in games, in order to have pain and to have suffering, you have to have collision detection. And so we want our guy to be able to slam into things. We want our guy to be able to get hurt. And so we're going to add a bad guy who currently doesn't do anything, so he's not really that bad. He's more of a painful obstacle. But it's going to let us start building collision detection to our game. So how does collision detection work? Well, in our game, we are going to put imaginary rectangles around our hero and around all the stuff that our hero can run into. And then we're going to look for intersections of those rectangles, and if there's an intersection, that means there's a collision. There are other ways to do this. There's so-called pixel perfect collision detection, which will tell you exactly which pixels overlapped between two things on the screen. But basically, every platformer I ever played as a kid used rectangles, and it feels great. It feels good. It's also very conceptually simple. You can run these in constant time, so it doesn't matter how big the things are that you want to detect collisions are. The collision detection is always just very easy math. And it's totally good enough for our game. So this is what we're going to do. So let's draw our enemy into place. So we just have a static enemy image for now. We are going to use this drawCollisionBodies method, which I'm going to talk about in a second. It just draws rectangles around the physics bodies in the game so you can see where do your rectangles need to be. We have a couple changes in the update method. So first of all, our hero can feel pain now. So we have this hurt until timer thing. So our hero can be hurt for a little bit, but then we want him to recover. So how this works is we're going to set, if the hero gets hurt, we're going to set hurt until to some time in the future, and then if that is set, we're going to say, OK, the hero's hurt. Once you get past that time, you can un-set it. The last bit there is we're just going to call this handle collisions function because we need something to detect and respond to collisions in our game. And how is it going to respond? So a couple different modes of collision detection that can happen in our game. First of all, the hero can run into the enemy from the side, like from the left or the right. And when that happens, that means the hero made a mistake. So he should be hurt. And in order to hurt the hero, we're going to knock him back 30 pixels, and then we're going to stun him for a little bit. However, if we collide with the bottom of the hero, that means the hero jumped on the enemy's head. And as we all know, if you jump on the enemy's head, you did a good thing and the enemy should die. So right now, we don't know how to kill an enemy. So we're not going to talk about that. But we are going to give the hero a little bounce, which is a very cheerful celebration of jumping on the enemy's head. So handle collisions is implemented like this. The first third of the code there is just defining the rectangle around the player and around the enemy. There are a billion ways to describe rectangles in code. I chose the worst one, which is to use a hash. But it looks good on slides. So that's why I went with that. In real life, I probably would have used a struct or even just an array with the two corners specified. It's probably a superior way to specify a rectangle. The next bit there is we're calling this check for collisions function, which for now all you need to know is it takes two rectangles. If there's a collision between those two rectangles, it's going to return the direction of that collision relative to the hero. So if the hero gets hit on the left side, it's going to return the symbol left. If the hero gets hit on the bottom side, we're going to return bottom. And then the logic for how to deal with those collisions. If you get hit on the left or the right, that means you want to get knocked back by 30 pixels. And we're going to set the herd until timer to the current time plus 200 milliseconds, which again, just a magic number I made up because it felt pretty good. If you collide with the bottom of the hero, however, you want that little bounce. So to bounce, all you need to do is set that jumping flag to true and give the hero a vertical velocity of 10 pixels per frame. The check for collisions function I just described. So first you need to get the intersection of the two rectangles you were given. So this is very simple math. If you had like three minutes and a piece of paper, you could figure out how to get the intersection of two rectangles. But I just got my answer off Stack Overflow. And if there is an intersection, we can use that intersection as a heuristic for figuring out the direction that the collision came from. The reason we can do this is because if the intersection is wider than it is tall, so if it's like a landscape style intersection, we can know with high probability that the intersection came from the top or the bottom of the hero rectangle. The reason for that is because the rules of the game don't allow you to intersect very deeply with another rectangle. Like it'll stop you. It'll knock you back 30 pixels or you'll bounce off the enemy's head. So if it's wider than it is tall, that means these rectangles aren't very thin. It's probably a top bottom collision. And otherwise it's a left right collision for the same reasons. So knowing that, we can look at the top of the intersection and the top of the hero rectangle and decide if it's a top collision by checking if they're the same. Because if they're the same, that means the top of the hero's rectangle is pushing through the bottom of some other rectangle. And if it's a wide intersection and it's not a top collision, then it must be a bottom collision. So the same kind of trick applies to the left right thing. If we know it's a left right collision, we can look at the left of the intersection and the left of the hero rectangle. And if they're the same, it's a left collision. Otherwise it must be a right collision. That was a lot of words and not very many diagrams. So this is that draw collision bodies function I talked about. You should definitely do this if you're going to write a game with collision detection. Because it's very tricky to dial these numbers in without having some visual aid. All it is is just drawing four lines, makes a rectangle, goes to as built-in drawing functions. Draw line is a neat one. So I set a debug flag whenever I want to draw the physics bodies in the game. And that's all there is to collision detection. So now our guy can get hurt. He can bounce off the enemy. And he can celebrate in triumph. So the very last bit of the game, so we want to make it flesh out a little more. We want to be able to kill the enemy. We want the enemy to be a little bit smarter. And we want to be able to keep score. So the first thing I did was I separated the enemy class out as its own entity. Because eventually your game is just going to get unwieldy if you leave it in one class forever. So this is one way you can start moving out various entities in your game to have their own logic. Also, maybe we want to make a bunch of enemies. And so this is a neat way to do it. So we implement a update, a draw, and a body method on the enemy class here. The update and draw methods are just called on all enemies in the appropriate method in the game. So every update call is going to call update on all the enemies. Every draw call is going to call draw on all the enemies. The enemies know how to draw themselves. The enemies know how to update their state as the game progresses. The body method just returns the rectangle for collision detection. Because the game itself is still going to be responsible for detecting those collisions. So here's how we plug that in. We're going to instantiate an enemy in our initialized function. We're giving it a sprite sheet now instead of just a static image. You could make many enemies. We're just going to make one for the purposes of this talk. In our update function, we're going to call update on all of our enemies. In our draw function, we're going to call draw on all of our enemies. And in our collision detection function, we're going to loop through all of our enemies, and we're going to check for collisions on all of them. Otherwise, the logic remains the same. And since we have that in place, we can now implement the draw and update functions on the enemy to handle those bits. So the draw function on the enemy, the enemy only has two states. He can either be alive, which means he's walking around, or dead, which means he's squashed. So in our sprite sheet for enemy, we have four frames of animation. So we just do the same kind of thing we did with the hero, with the milliseconds trick to slow down the animation. And if the enemy is dead, we have a little squashed enemy image there on the end. The call to draw just uses some ternary stuff to figure out if the enemy is facing the other way. And if he is, flip him over so he's facing the right direction. The update function is responsible for the brains of the enemy. So if the enemy is dead, the enemy's brain is no longer functioning, so it doesn't do anything. Otherwise, we want to move the enemy five pixels in the direction he's facing. And if the enemy walks off the screen, then we want to turn him around so he's facing the other way so he starts walking back. So besides AI and besides drawing the enemy, we want to be able to keep score. So we're going to have this new piece of score state. It starts at a zero, as one might expect. We're going to load a font into memory. This is just a TTF file, nothing too fancy. You give it a size. You say it's a 64 point font and load this file. Then in the draw function, we're just going to say, hey font, draw the score in the top left corner of the screen. And draw will happily oblige. Finally, to get points, we're just going to handle collisions functions. So when we have that bottom collision, we're going to set the enemies alive flag to false. And we're going to increment our score. And that is the final product of Running Hero. So you can see we could jump on the enemy's head. We can get points. We have drama. And you can imagine how going from here, OK, well, you could make this a full game. You could add an endless number of enemies coming at you that get smarter and better. You could add power ups and weapons and all kinds of stuff. You could make platforms and levels and goals. And hopefully, you have a starting point where you could start to think, OK, well, that's how I would do that in Gosu. Couple of resources for your game development career. Highly recommend. Open game art, huge resource. If you look at the game development community, this is like the one everybody says, use this. The hero image and the enemy image, both CC0 images I pulled off of open game art. You can go there and build a collection of images for a game idea you've got. You can just go there for inspiration. They have sound effects, music, tons of art, just a great resource. For tools, for image manipulation, you can use whatever you want. You don't need anything super fancy. But if you've got Photoshop, you might as well. I use paint and paint-like tools. I think I use Pinta on the Mac, but also occasionally PicoPixel, which is a dedicated pixel editor. All that stuff is free. I highly recommend the Tiled Map Editor. So when you're building a game, you're going to start building up all these chunks of games like, OK, well, I've built this kind of enemy thing. I built this kind of block. And you're going to want to lay out your levels, your maps. You can load all of your stuff into Tiled, and then it'll export JSON or XML. You can parse that in your game. And it makes building levels so much nicer. Highly recommend that tool. Of course, you can get Gosu, All the Docs for Gosu, a really great tutorial on Gosu from libgosu.org. Highly recommend it. Check it out. So I hope you all feel inspired to make your own games now. I hope you feel at least a little more equipped to go make games now. And thanks for coming to my talk. I think we have about a couple minutes, if anybody has any questions. So for testing. OK, so testing in Gosu, you can write unit tests that do things like send keystrokes to the game. Testing in games is really fidgety, because there's so much that can happen. There's a lot of state that has to be set up. And so TDD in game development doesn't seem like to be much of a thing. But certainly, sanity checks and tests like that are a lot more appropriate in game development. It's a lot more experimental. And like, OK, I did a thing. How does it look? How does it feel? Kind of iterative development. The most fun game I've made with Gosu? Man, all my games are terrible. OK, so the most terrible game I made with Gosu? I made a game that was really just kind of this stupid art game. But it has this little fly. And he starts buzzing around on the screen. And then you slap the fly. And then more flies appear. And then you kill more flies. And then eventually you run out of time and the game ends. And you just try to get as high a score as possible. I don't have it with me, sorry. But I'll put it on my GitHub so everybody can see how bad I am at this. My GitHub, it's on the first slide. GitHub.com slash Chamberlain. Yeah, there's the link to the GitHub. These slides and all the code for this talk will be on that GitHub. Do I think they're going to make a library to write VR games? And Ruby, I think it would be tough just because 3D is more computationally intensive. It might be possible. But I think it would be very constrained by the performance of Ruby. Is there a 3D library? I don't know of one. There probably is. But I'm not aware of one. Yeah, so the question is, why do you draw the same images in every frame? Does that sum it up? So my understanding of how it ends up working is that you sort of end up with a blank slate each frame for video memory reasons or whatever. And so you're actually just going to redraw everything every frame anyway. But if you think about games from the point of view of regular application development, it seems very weird that I have to keep drawing this thing all the time. But under the covers, your graphics card has to redraw things every frame. So this is just getting very explicit about that. No more questions? All right. Thanks, everybody. Thank you.
 So, hey everybody, my name is Corey Chamberlain.  I am a programmer.  I write back-end software at a company called PagerDuty.  But today I'm not going to talk about any of that stuff.  I'm going to talk about something way sweeter.  I'm going to talk about how to build video games in Ruby.  I don't do this for my job, but I like to write games.  And I think Ruby is a good language to do it in.  So the GitHub for the talk, github.com.  You can go there, download the slides,  download all the code for the talk.  So first, we're going to talk about the reasons why you  might want to make games, or why you might try to get into  this as a hobby, just something to do on the weekends.  We're going to talk about why Ruby is a pretty good choice  for getting started making games.  The second bit there, the main chunk of the talk, we're  going to talk about how games are structured in pretty much  every game framework out there.  We're going to talk about GoSue, which is an amazing little  library in Ruby for building your own games.  And we're going to talk about how to write pieces of games  that you might want to build by building a couple toy  games using GoSue.  And if we have a little bit of time at the end, we're going  to talk about a few resources for aspiring independent game  developers.  So if you're not an artist, you can go get some open source  artwork or some open source music, sound effects, and maybe  some tools that you might use in building your own games.  So first of all, why do I make games, and why should you  make games?  So I like to make games because it reminds me of when I very  first started programming.  Back in the day, we would get these giant books of basic  programs.  And they were games.  And you would spend your whole afternoon typing this stupid  program into your computer.  And you would get to the end.  And you would type run.  And if you were very lucky and you didn't make any mistakes,  you would blow yourself away with a really bad game.  No, but it was exciting in the sense that I typed this thing  in and something happened on my computer.  And that is a real visceral, kind of creative feeling that I  think all professional programmers need to find  some way to get that.  The first time I hooked up an Arduino to my computer and I  made the little light blink on it, I wrote some code and do a  thing, and I pressed play.  And a thing that was not my computer did something else.  That was a similar feeling for me.  So I think you should try to write games if you don't  already have an outlet like this.  See if games are that for you.  And if not, I highly encourage you to go find some way to  creatively express yourself with your craft.  Because it's very important to grow as an engineer, as a  programmer, that you can express yourself creatively.  So what about Ruby?  Nobody considers Ruby a high performance programming language.  So is Ruby the right tool for the job?  Is it the right tool to build games in?  So when I first became a professional programmer, I  first had some interest in writing games.  Because a lot of people get into programming because of  writing games.  And I think games are sweet.  So I was like, OK, well, I can write stuff.  I have a computer science degree.  So what do I need to know to write games?  And I thought, man, OK, well, I need to go learn C++.  I need to go learn half a dozen graphics libraries.  I need to know what SDL stands for.  Like I felt like there was so much I had to learn to get  out there to write games.  But none of that is actually true.  So almost every language out there, every language out  there has some library where you can write really nice 2D  independent style games, very commercially popular.  These are the games I love to write.  I love to play.  And that's a ton of the kind of games.  Ruby is no exception.  We have Gosu and Ruby.  And there's plenty of power in Ruby to write these simple  games that are very full and very satisfying to write.  Photographers often say that the best camera that you can have  is the one you have with you.  So if you want to make games, don't feel like, OK, well,  there's just so much I need to know to do that.  You can make games today.  If you know how to write Ruby programs, you can write games.  So go make your games with Ruby.  If you feel like later on, OK, well, I'm feeling kind of  hamstrung.  I need a little more performance.  The lessons you learn writing games in Ruby, the lessons you  learn writing games in Gosu, ports very well to the more  commercial tools.  I poked around in Unity.  I wrote a couple games using Unity, just having background  writing games in Gosu.  And the vocabulary, the semantics of writing games,  the structure of games is very similar across the board.  So you can kind of get your sea legs writing games in Ruby.  And if you want to go do this professionally, totally room to  grow into those higher end tools.  But for me, I found out, hey, I love Ruby.  Ruby's a great language.  And I'm not making a ton of money writing games, so I'm  going to have fun doing it.  I'm going to write my games in Ruby.  So let's get into the kind of meat of the talk here.  Gosu games can just be one class.  So we inherit from a window class in Gosu.  And you only have to implement three methods, generally.  And these three methods, you're going to see them all over in  game development.  So every game framework literally I've ever used has  had these three methods be the primitives for building games.  The first thing is the constructor of your game.  So this is responsible for doing things like setting up the  window details, like what's the size of the window?  What's the title on top of it?  Is the game full screen?  What's the resolution, if so?  Kind of the graphics setup of your game.  It's responsible for loading assets from disks.  So things like your image files, your sounds, and your  music files, for reasons we'll talk about in a second, you  don't want to be reading those things off disk in the middle  of the game.  You want quick access to them.  So the constructor is responsible for loading all  those things into memory.  Finally, it's responsible for setting up the  initial game state.  So things like, well, I have three lives.  So initialize says, lives equals three.  It's not very complicated.  Level is one.  Very simple stuff.  The state of the game, as the game begins, it's  responsible for that.  So this is called, when your game first runs, this is called  the setup method in most game frameworks.  So after this gets done running, the game enters into an  infinite loop.  And that infinite loop is called the game loop.  And then we call the other two methods in sequence in that  game loop over and over again.  The first of those methods is the update method.  And it's responsible for detecting and  responding to user input.  So did the user press the B button?  OK, what do I do?  Did the user press the right arrow?  What do I do?  It's also responsible for updating all of the  state of the game.  So if the user pressed the B button, I'm shooting a fireball.  If the user pressed the right arrow, I'm adjusting the  player's position five pixels to the right.  That kind of stuff.  Finally, the draw method.  It's responsible for drawing the current state of the game  to the screen.  So it doesn't change anything about the game.  It just goes through, say, all of our image assets and says,  OK, well, based on the state of the game right now, I'm  going to draw the hero right here.  I'm going to put three hearts in the top right corner, all  this kind of stuff.  And so these are called in a loop, the game loop.  And each time we go through that loop, that's called a frame.  And however fast we're going through that loop, that's  called the frame rate.  And Ghost2Tries2 run games at 60 frames a second.  But obviously, if you have a bunch of stuff in these two  methods, that can slow it down.  This is why we load images into memory.  Because if you had to load all of your image files into  memory on each frame, if you try to do that 60 times a  second, your game is going to run really, really slowly.  And you're going to turn your hard disk into a pile of  slag.  So that helps us keep up a frame rate.  And that's basically, at a very high level, how games work.  So to demonstrate these concepts, I wrote a really  amazing game called Winner, Winner, Chicken Spinner.  And how this game works is the chicken there, he spins  until you press the spacebar.  And then when you press the spacebar, the game ends.  It's over.  And if he stops in the highlighted area, you win.  If he stops outside the highlighted area, you lose.  The game is over.  So the implementation of that game, it goes like this.  So first, we're inheriting from Ghost2 Window.  At the bottom there, we're instantiating our game, and  we're calling the show method on it.  I don't know why I named this game Window.  That's a terrible name.  It should have been Winner, Winner, Chicken Spinner Window.  So anyway, here's the implementation of Initialize.  We have the three responsibilities of Initialize.  And I kind of made different code paragraphs for the  responsibilities.  First, it's responsible for setting up the window.  It's saying, OK, super class, make the window 480 by 480.  It's setting the title of the window to Winner, Winner,  Chicken Spinner.  If the game were full screen, we would set that up.  Second, it's loading all of our assets into memory.  So the way we do that is we just instantiate the image  class.  We give it a path.  These are just PNG files.  There's no fancy games-only image format here, just PNG  files.  We have the chicken plate disk thing.  We have the arrow.  And then we have two different images at the end of the game.  If the player wins, we're going to draw that fantastic win  logo.  And if they lose, the sad lose logo on top of everything.  The last bit there is setting up the  initial state of the game.  So Winner, Winner, Chicken Spinner has really three pieces  of state.  It has the current rotation of the chicken, because each  frame we're going to rotate that chicken a little bit so  he starts spinning.  It has a game over flag, so we know to stop spinning the  chicken when the user presses the space bar.  And it has a one flag.  So we can tell, are we showing the win image or the lose  image on the game over screen?  The update function, remember, is responsible for checking  for user input.  And it's responsible for updating the state of the game.  So if the game is over, like the user's press space bar,  this function does nothing, because the user can't press  space bar again and they can't cheat.  However, if the game is not over, we want to check to see  if the user has pressed the space bar in this frame.  So if they did, we can use the button down method.  And it has a little constant called kb space.  All the keys on the keyboard have these constants.  They're all in the go-sue docs.  But if they press the space bar, we're going to set that  game over flag.  We're going to set the one flag to whether or not we won.  And that just looks at the current angle of the chicken  to figure that out.  And it was a lot of trial and error to come up with those  numbers.  There was no trick there.  And if neither of those things are true, we're just going to  rotate the chicken by 10 degrees by adding 10 to the  chicken angle.  And that is all update does.  The draw method, remember, all it's doing is it's saying,  I'm going to render the state of the game to the screen.  Whatever the game looks like right now, I'm  just going to draw that.  So the little blue arrow that you saw, we're always going to  draw that to the screen.  That's at x equals 320, y equals 200.  It's important to note in go-sue that the origin, 0, 0,  is on the top left-hand corner of the game window, not the  bottom left-hand corner.  So as you go down, your y coordinate goes up.  Some game frameworks like to do bottom left.  Some like to do top left.  I don't really know why, but go-sue is a top left kind of  framework.  The last number there is the z-index.  And if you do any web development, you're probably  familiar with that.  It's just the draw order of the images.  The arrow will be on top of anything with the z-index less  than 1 and under anything with the z-index greater than 1.  The next bit is drawing the chicken plate.  So we're going to use the drawROT method because we want to  rotate it.  It's the same x, y, z stuff there, but we have the angle  and degrees that we want to rotate the chicken.  You'll note we're not setting it or changing the angle.  We're simply drawing the angle that the update method has  set.  Finally, the game over state.  So if the game is over, we want to draw the game over screen.  And the game over screen depends on whether or not we  won the game.  And if we won the game, we want to draw the win image.  Otherwise, we're going to draw the lose image.  And we're going to draw it at z-index of 2, so it's on top  of all the other stuff on the screen.  And that's actually all the code there is in Winner, Winner,  Chicken Spinner.  But Winner, Winner, Chicken Spinner is a very, very  simple game.  And if you came to a talk about making games, you  probably have ideas in your brain about games you want to  build, and they don't look anything like that.  So we're going to make another very simple toy game.  But it's a little platform game.  And that has a lot of important kind of mechanics, pieces of  games that you will map to pretty much any kind of game  you want to build.  So you don't have to make that huge mental leap from Winner,  Chicken Spinner to what I actually want to do.  So let's jump right into the code on this guy.  So first, the idea is we have a little guy, and we want to be  able to move him around on the ground just like a real  platform game.  And we wanted to be able to jump.  So we're going to make a new game.  This one's called Running Hero.  And we have two images right now, the Hero image and the  background image.  We're going to load those into memory, and then we're just  going to draw them statically on the screen.  You know how to do this from Winner, Winner, Chicken  Spinner.  The next bit is we're going to make our guy be able to walk,  or at least be able to slide along the ground in an  orderly fashion.  So in a platform game, the rules of movement are that the  player can move to the left or the right, but he can't move  up and down.  And if you want to build like your top down, like Zelda kind  of game, you just change the perspective, and then the player  can walk up and down too.  The same principles apply though.  So all we've got to do to walk, we're going to store the  current position of the hero in an array with the x and the  y coordinate.  And in the update function, remember, update is going to  check for that user input.  It's also going to change the state of the game.  So when they press the right arrow, we're going to want to  update that position by changing the x coordinate.  And when they press the left arrow, we're going to want to  subtract from that x coordinate.  So we wrote this little move function that does that.  It just adds or subtracts five to the x coordinate based on  the speed thing, which you can set and change to make your  hero a little faster.  The only change I had to make to the draw function was just  quit using hard coded numbers and start using our position  when we're rendering our hero.  And that's all there is to drawing the hero and making  him be able to slide across the screen.  Jumping is a little trickier.  And you kind of have to think about how jumping works in  real life, and then you sort of have to dumb that down for  games.  And I kind of just made this up for this talk, so there's  probably some way better way to do this.  But this is a simple way, and it made sense to me.  So how jumping works in running hero is you can  only jump if you're on the ground, so there's no double  jumping antics.  But when you jump, you get a certain amount of vertical  velocity.  And then each frame, the game's going to look, does the  hero have vertical velocity?  If so, I'm going to move their y coordinate up by that  number, and then I'm going to divide their vertical velocity  by some amount of gravity.  And then eventually, they're going to run out of vertical  velocity.  They're going to be at zero.  And then they're going to start falling.  So I'm going to give them negative one vertical velocity.  And then each frame will be multiplying that velocity times  gravity.  So they'll go faster and faster and faster until they hit  the ground, and then they can jump again.  So maybe some code is a better way to explain that.  So the implementation of that, just in the update function,  if the user presses the space bar, we're going to call this  jump function.  It just does nothing if the user's already in the air.  Otherwise, it'll give them a vertical velocity of 30  pixels per frame.  The other change to the update function is we're going to  call this handle jump function, which is going to do all  that math we just described, and updating the hero's  position, and updating vertical velocity.  The implementation of that, we have this gravity constant of  1.75.  Just has to be greater than one.  I just fiddled around until I found a good feeling number.  You will see that a lot in my game.  Most of these numbers are just made up.  The ground level, I can talk about that in a second.  It's just the y-coordinate where the hero is standing on  the ground, because that is currently how we're going to  find out that we've landed.  The next bit, just subtracting the vertical velocity from  the hero's y-coordinate.  Because remember, the top of the screen is y equals 0.  So as you go up, your vertical velocity goes down.  And then when you have negative vertical velocity, your y-coordinate  will start going up again.  The next paragraph thing there is just changing the  vertical velocity.  So if the vertical velocity is 0, that means the hero is at  the top of the jump, and we need to start falling.  So we'll give them a vertical velocity of negative 1.  So on the next frame, they'll start falling.  If their vertical velocity is negative, that means they are  falling.  So we're going to multiply their vertical velocity times  the gravity constant.  So they're falling a little faster on the next frame.  And if they're going up, then we want to divide the vertical  velocity by the gravity constant.  So they go up a little less on the next frame.  The last bit there, like I said, so when we get back on  the ground, we need to know that we're back on the ground.  Otherwise, we're just going to fall forever.  So how we're doing that is our ground is a straight line.  So the hero is always at the same y-coordinate when he's  standing on the ground.  So we could just look at the y-coordinate.  If it's at the ground level, then we can stop jumping.  Since this is a platform game, eventually you're going to  get to the point where you have platforms to jump on, that  kind of thing.  The logic will have to change.  You'll have to use something like collision detection to  know if you're on the ground or not.  But that's actually all there is to jumping and to walking.  So now our guy, he can walk and he can jump.  But he does look a little silly because he cannot look to  the left, which is sad.  And he also kind of just moves like a cut out paper doll  and not like a real hero.  It doesn't feel like a game yet.  It feels like a toy.  So in this chunk, we're going to animate the guy.  We're going to add a few poses to him and make him look  way better.  So to make the guy be able to look to the left, we're going  to need to know which way he's facing.  And that can change as the game changes, so we need a new  piece of state.  So we're going to store the direction he's facing.  We're going to update that direction in our move function  because that's the only way currently that our player can  be turned around to face the other way is that they start  walking the other way.  And then we're going to do this little trick in the draw  function.  So one way you could do this is that you have a left facing  image for every image in the game.  But that tends to get a little unwieldy and  it's a little hacky.  So a better way to do it is that the draw function on the  image class has a scale x and a scale y parameter.  And if you set those things to negative numbers, it actually  will mirror the image and make it face the other way on that  axis.  So if you set scale x to negative 1, it's going to flip  the hero over back the other way.  And he's going to be facing the other direction back to back  with his original position.  To compensate for the fact that we just moved the hero to  the left by his width, we're going to add the width back  to the x coordinate.  So now our hero can be standing in the same spot, but he can  turn around and can face the other way.  So a pretty neat little trick there.  So now our guy can look to the left.  He can look to the right.  But now we need to animate him.  So animation in Gosu is very simple, and by simple, I mean  you're on your own.  So what you do is you get an array of images, and each frame  you just decide, this is the one I'm going to draw this  frame.  So you load up all of your stuff into an array, and then  you pick based on the state of the game, which of these am I  going to draw?  Fortunately, we have a really easy way to get a bunch of  images into an array, and that's by using sprite sheets.  So sprite sheets are a very, very common tool in games  development.  You just basically take all of your images that you want.  You make them all the same size, and then you put them all  in a bigger image.  And then you load them into your game, and you say, all of  my sub-images are this size, cut them out for me.  And it'll do that for you.  This is usually done for complicated graphics card  reasons, but in Running Hero, it's done because it makes the  code nicer.  That the thing there behind the blue wall is our sprite  sheet.  So we have seven poses for our hero.  We have the standing still and doing nothing pose.  We have three frames of animation for walking.  So if you play these in a loop, it looks like walking.  It's called a walk cycle.  The fifth image is the player with his arms out going up.  I don't know why this is going up.  I have never jumped like that.  But that is what going up looks like for our hero.  For going down, our hero is kind of looking down at the  ground.  And then the last image there is like our hero has been  hurt, and he's stunned, or just very surprised.  So to build all this animation, all these poses into our  game, in the initialize function, remember we load  assets there, we're going to use the loadTiles class  function on the image class.  We're going to give it our sprite sheet.  We're going to say our sprites are 64 by 64.  So load those all into memory.  And this will just return that array for us, of regular  images.  We also need to keep track of which image we're going to  show on this frame.  So we have this piece of state called the current hero  image, which update is going to set, and then draw is going  to draw.  So in update, we're going to look at the state of the game  to figure out which frame to draw.  So if the hero is jumping, that means they have a vertical  velocity.  And based on that vertical velocity, we're going to  decide to use the going up or coming down image.  If they have a positive vertical velocity, we'll use  the going up image.  If they have a negative vertical velocity, we'll use  the going down image.  Otherwise, if the hero is walking, one of the weird  things about this is that the hero can be jumping and  walking at the same time.  So if the hero is walking, then we will just use one of  these three images in the walk cycle.  And you don't want to just loop through these one per  frame because your game runs at 60 frames a second.  And this animation looks ridiculous at 60 frames a  second.  So you want to slow that way down without slowing down  your whole game.  So how we do that is we can take the number of milliseconds  since the game started, divide it by 100, and that gives us  10 frames a second.  So we just take that number, module O3, and that gives us  a loop 0, 1, 2, 0, 1, 2.  We add one to that, and then we get the right offsets in the  array for the hero image that we need to show this frame.  Finally, if the hero is neither jumping nor walking,  we're going to just set the current hero image to the  resting hero image, the first hero image.  And the only change we had to make in the draw function was  that we want to use the current hero image instead of that  old static hero.  And shockingly, that is all that it took to animate our  guy, and actually he looks a lot better right now, and he's  feeling great about life.  However, our game is missing a plot.  It's missing drama, and to have drama, you need to have  pain, and you need to have suffering.  And in games, in order to have pain and to have suffering,  you have to have collision detection.  And so we want our guy to be able to slam into things.  We want our guy to be able to get hurt.  And so we're going to add a bad guy who currently doesn't do  anything, so he's not really that bad.  He's more of a painful obstacle.  But it's going to let us start building collision  detection to our game.  So how does collision detection work?  Well, in our game, we are going to put imaginary  rectangles around our hero and around all the stuff that our  hero can run into.  And then we're going to look for intersections of those  rectangles, and if there's an intersection, that means  there's a collision.  There are other ways to do this.  There's so-called pixel perfect collision detection, which  will tell you exactly which pixels overlapped between two  things on the screen.  But basically, every platformer I ever played as a kid  used rectangles, and it feels great.  It feels good.  It's also very conceptually simple.  You can run these in constant time, so it doesn't matter  how big the things are that you want to detect collisions are.  The collision detection is always just very easy math.  And it's totally good enough for our game.  So this is what we're going to do.  So let's draw our enemy into place.  So we just have a static enemy image for now.  We are going to use this drawCollisionBodies method,  which I'm going to talk about in a second.  It just draws rectangles around the physics bodies in the  game so you can see where do your rectangles need to be.  We have a couple changes in the update method.  So first of all, our hero can feel pain now.  So we have this hurt until timer thing.  So our hero can be hurt for a little bit, but then we want  him to recover.  So how this works is we're going to set, if the hero gets  hurt, we're going to set hurt until to some time in the  future, and then if that is set, we're going to say, OK, the  hero's hurt.  Once you get past that time, you can un-set it.  The last bit there is we're just going to call this handle  collisions function because we need something to detect and  respond to collisions in our game.  And how is it going to respond?  So a couple different modes of collision detection that can  happen in our game.  First of all, the hero can run into the enemy from the side,  like from the left or the right.  And when that happens, that means the hero made a mistake.  So he should be hurt.  And in order to hurt the hero, we're going to knock him back  30 pixels, and then we're going to stun him for a little  bit.  However, if we collide with the bottom of the hero, that  means the hero jumped on the enemy's head.  And as we all know, if you jump on the enemy's head, you did  a good thing and the enemy should die.  So right now, we don't know how to kill an enemy.  So we're not going to talk about that.  But we are going to give the hero a little bounce, which is  a very cheerful celebration of jumping on the enemy's head.  So handle collisions is implemented like this.  The first third of the code there is just defining the  rectangle around the player and around the enemy.  There are a billion ways to describe rectangles in code.  I chose the worst one, which is to use a hash.  But it looks good on slides.  So that's why I went with that.  In real life, I probably would have used a struct or even  just an array with the two corners specified.  It's probably a superior way to specify a rectangle.  The next bit there is we're calling this check for  collisions function, which for now all you need to know is  it takes two rectangles.  If there's a collision between those two rectangles, it's  going to return the direction of that collision  relative to the hero.  So if the hero gets hit on the left side, it's going to  return the symbol left.  If the hero gets hit on the bottom side, we're going to  return bottom.  And then the logic for how to deal with those collisions.  If you get hit on the left or the right, that means you  want to get knocked back by 30 pixels.  And we're going to set the herd until timer to the current  time plus 200 milliseconds, which again, just a magic  number I made up because it felt pretty good.  If you collide with the bottom of the hero, however, you  want that little bounce.  So to bounce, all you need to do is set that jumping flag  to true and give the hero a vertical velocity of 10  pixels per frame.  The check for collisions function I just described.  So first you need to get the intersection of the two  rectangles you were given.  So this is very simple math.  If you had like three minutes and a piece of paper, you  could figure out how to get the intersection of two  rectangles.  But I just got my answer off Stack Overflow.  And if there is an intersection, we can use that  intersection as a heuristic for figuring out the direction  that the collision came from.  The reason we can do this is because if the intersection  is wider than it is tall, so if it's like a landscape style  intersection, we can know with high probability that the  intersection came from the top or the bottom of the hero  rectangle.  The reason for that is because the rules of the game don't  allow you to intersect very deeply with another rectangle.  Like it'll stop you.  It'll knock you back 30 pixels or you'll bounce off the  enemy's head.  So if it's wider than it is tall, that means these  rectangles aren't very thin.  It's probably a top bottom collision.  And otherwise it's a left right collision for the same  reasons.  So knowing that, we can look at the top of the intersection  and the top of the hero rectangle and decide if it's a  top collision by checking if they're the same.  Because if they're the same, that means the top of the  hero's rectangle is pushing through the bottom of some  other rectangle.  And if it's a wide intersection and it's not a top  collision, then it must be a bottom collision.  So the same kind of trick applies to the left right  thing. If we know it's a left right collision, we can look  at the left of the intersection and the left of the hero  rectangle.  And if they're the same, it's a left collision.  Otherwise it must be a right collision.  That was a lot of words and not very many diagrams.  So this is that draw collision bodies function I talked  about.  You should definitely do this if you're going to write a  game with collision detection.  Because it's very tricky to dial these numbers in without  having some visual aid.  All it is is just drawing four lines, makes a rectangle, goes  to as built-in drawing functions.  Draw line is a neat one.  So I set a debug flag whenever I want to draw the physics  bodies in the game.  And that's all there is to collision detection.  So now our guy can get hurt.  He can bounce off the enemy.  And he can celebrate in triumph.  So the very last bit of the game, so we want to make it  flesh out a little more.  We want to be able to kill the enemy.  We want the enemy to be a little bit smarter.  And we want to be able to keep score.  So the first thing I did was I separated the enemy class out  as its own entity.  Because eventually your game is just going to get unwieldy if  you leave it in one class forever.  So this is one way you can start moving out various  entities in your game to have their own logic.  Also, maybe we want to make a bunch of enemies.  And so this is a neat way to do it.  So we implement a update, a draw, and a body method on the  enemy class here.  The update and draw methods are just called on all enemies in  the appropriate method in the game.  So every update call is going to call update on all the  enemies.  Every draw call is going to call draw on all the enemies.  The enemies know how to draw themselves.  The enemies know how to update their state as the game  progresses.  The body method just returns the rectangle for collision  detection.  Because the game itself is still going to be responsible for  detecting those collisions.  So here's how we plug that in.  We're going to instantiate an enemy in our  initialized function.  We're giving it a sprite sheet now instead of just a static  image.  You could make many enemies.  We're just going to make one for the purposes of this talk.  In our update function, we're going to call update on all  of our enemies.  In our draw function, we're going to call draw on all of  our enemies.  And in our collision detection function, we're going to loop  through all of our enemies, and we're going to check for  collisions on all of them.  Otherwise, the logic remains the same.  And since we have that in place, we can now implement the  draw and update functions on the enemy to handle those bits.  So the draw function on the enemy, the enemy only has two  states.  He can either be alive, which means he's walking around, or  dead, which means he's squashed.  So in our sprite sheet for enemy, we have four frames of  animation.  So we just do the same kind of thing we did with the hero,  with the milliseconds trick to slow down the animation.  And if the enemy is dead, we have a little squashed enemy  image there on the end.  The call to draw just uses some ternary stuff to figure out  if the enemy is facing the other way.  And if he is, flip him over so he's facing the right  direction.  The update function is responsible for the brains of  the enemy.  So if the enemy is dead, the enemy's brain is no longer  functioning, so it doesn't do anything.  Otherwise, we want to move the enemy five pixels in the  direction he's facing.  And if the enemy walks off the screen, then we want to turn  him around so he's facing the other way so he starts  walking back.  So besides AI and besides drawing the enemy, we want to  be able to keep score.  So we're going to have this new piece of score state.  It starts at a zero, as one might expect.  We're going to load a font into memory.  This is just a TTF file, nothing too fancy.  You give it a size.  You say it's a 64 point font and load this file.  Then in the draw function, we're just going to say, hey  font, draw the score in the top left corner of the screen.  And draw will happily oblige.  Finally, to get points, we're just going to handle  collisions functions.  So when we have that bottom collision, we're going to set  the enemies alive flag to false.  And we're going to increment our score.  And that is the final product of Running Hero.  So you can see we could jump on the enemy's head.  We can get points.  We have drama.  And you can imagine how going from here, OK, well, you  could make this a full game.  You could add an endless number of enemies coming at you  that get smarter and better.  You could add power ups and weapons and all kinds of  stuff.  You could make platforms and levels and goals.  And hopefully, you have a starting point where you  could start to think, OK, well, that's how I would do that  in Gosu.  Couple of resources for your game development career.  Highly recommend.  Open game art, huge resource.  If you look at the game development community, this  is like the one everybody says, use this.  The hero image and the enemy image, both CC0 images I  pulled off of open game art.  You can go there and build a collection of images for a  game idea you've got.  You can just go there for inspiration.  They have sound effects, music, tons of art, just a great  resource.  For tools, for image manipulation, you can use  whatever you want.  You don't need anything super fancy.  But if you've got Photoshop, you might as well.  I use paint and paint-like tools.  I think I use Pinta on the Mac, but also occasionally  PicoPixel, which is a dedicated pixel editor.  All that stuff is free.  I highly recommend the Tiled Map Editor.  So when you're building a game, you're going to start  building up all these chunks of games like, OK, well, I've  built this kind of enemy thing.  I built this kind of block.  And you're going to want to lay out your levels, your maps.  You can load all of your stuff into Tiled, and then it'll  export JSON or XML.  You can parse that in your game.  And it makes building levels so much nicer.  Highly recommend that tool.  Of course, you can get Gosu, All the Docs for Gosu, a  really great tutorial on Gosu from libgosu.org.  Highly recommend it.  Check it out.  So I hope you all feel inspired to make your own games now.  I hope you feel at least a little more equipped to go  make games now.  And thanks for coming to my talk.  I think we have about a couple minutes, if anybody has  any questions.  So for testing.  OK, so testing in Gosu, you can write unit tests that do  things like send keystrokes to the game.  Testing in games is really fidgety, because there's so  much that can happen.  There's a lot of state that has to be set up.  And so TDD in game development doesn't seem like to be much  of a thing.  But certainly, sanity checks and tests like that are a lot  more appropriate in game development.  It's a lot more experimental.  And like, OK, I did a thing.  How does it look?  How does it feel?  Kind of iterative development.  The most fun game I've made with Gosu?  Man, all my games are terrible.  OK, so the most terrible game I made with Gosu?  I made a game that was really just kind of this stupid art  game.  But it has this little fly.  And he starts buzzing around on the screen.  And then you slap the fly.  And then more flies appear.  And then you kill more flies.  And then eventually you run out of time and the game ends.  And you just try to get as high a score as possible.  I don't have it with me, sorry.  But I'll put it on my GitHub so everybody can see how bad I  am at this.  My GitHub, it's on the first slide.  GitHub.com slash Chamberlain.  Yeah, there's the link to the GitHub.  These slides and all the code for this talk  will be on that GitHub.  Do I think they're going to make a library to write VR games?  And Ruby, I think it would be tough just because 3D is more  computationally intensive.  It might be possible.  But I think it would be very constrained by the performance  of Ruby.  Is there a 3D library?  I don't know of one.  There probably is.  But I'm not aware of one.  Yeah, so the question is, why do you draw the same images  in every frame?  Does that sum it up?  So my understanding of how it ends up working  is that you sort of end up with a blank slate each frame  for video memory reasons or whatever.  And so you're actually just going to redraw everything  every frame anyway.  But if you think about games from the point of view  of regular application development,  it seems very weird that I have to keep  drawing this thing all the time.  But under the covers, your graphics card  has to redraw things every frame.  So this is just getting very explicit about that.  No more questions?  All right.  Thanks, everybody.  Thank you.
Attention_Rubyists_0_395.png Attention_Rubyists_396_1235.png Attention_Rubyists_1236_2723.png Attention_Rubyists_2724_2915.png Attention_Rubyists_2916_4931.png Attention_Rubyists_4932_6215.png Attention_Rubyists_6216_8831.png Attention_Rubyists_8832_11507.png Attention_Rubyists_11508_12347.png Attention_Rubyists_12348_14087.png Attention_Rubyists_14088_14195.png Attention_Rubyists_14196_14483.png Attention_Rubyists_14484_14735.png Attention_Rubyists_14736_17759.png Attention_Rubyists_17760_19463.png Attention_Rubyists_19464_22271.png Attention_Rubyists_22272_22331.png Attention_Rubyists_22332_22391.png Attention_Rubyists_22392_22643.png Attention_Rubyists_22644_22799.png Attention_Rubyists_22800_23483.png Attention_Rubyists_23484_24287.png Attention_Rubyists_24288_24959.png Attention_Rubyists_24960_25667.png Attention_Rubyists_25668_26435.png Attention_Rubyists_26436_26903.png Attention_Rubyists_26904_28055.png Attention_Rubyists_28056_28931.png Attention_Rubyists_28932_32183.png Attention_Rubyists_32184_33107.png Attention_Rubyists_33108_33707.png Attention_Rubyists_33708_35303.png Attention_Rubyists_35304_38171.png Attention_Rubyists_38172_41819.png Attention_Rubyists_41820_42959.png Attention_Rubyists_42960_44459.png Attention_Rubyists_44460_45995.png Attention_Rubyists_45996_47267.png Attention_Rubyists_47268_49679.png Attention_Rubyists_49680_49979.png Attention_Rubyists_49980_52823.png Attention_Rubyists_52824_53639.png Attention_Rubyists_53640_54239.png Attention_Rubyists_54240_55643.png Attention_Rubyists_55644_56699.png Attention_Rubyists_56700_57815.png Attention_Rubyists_57816_58451.png Attention_Rubyists_58452_59351.png Attention_Rubyists_59352_59675.png Attention_Rubyists_59676_60539.png Attention_Rubyists_60540_61379.png Attention_Rubyists_61380_62963.png Attention_Rubyists_62964_63167.png Attention_Rubyists_63168_65555.png Attention_Rubyists_65556_65663.png Attention_Rubyists_65664_66443.png Attention_Rubyists_66444_66491.png Attention_Rubyists_66492_66671.png Attention_Rubyists_66672_66731.png Attention_Rubyists_66732_66971.png Attention_Rubyists_66972_67019.png Attention_Rubyists_67020_67235.png Attention_Rubyists_67236_67295.png Attention_Rubyists_67296_67475.png Attention_Rubyists_67476_67535.png Attention_Rubyists_67536_67643.png Attention_Rubyists_67644_67691.png Attention_Rubyists_67692_67739.png Attention_Rubyists_67740_67883.png Attention_Rubyists_67884_67923.png