Hunter Loftis: We Will All Be Game Programmers

Slides Transcript
Hunter Loftis

Games in the 80s and 90s redefined the relationship between a user and a computer. Realtime, offline-first networking, fluid graphics, and physics-based animations posed incredible development challenges. Overcoming these challenges introduced a whole class of elegant techniques for immersive user experiences - that most JavaScript developers have never heard of!

My talk will show that normal JS apps will soon rely on game programming techniques every day. You’ll be amazed at how many libraries you’re using are already based on basic game-loop architecture. Finally, you’ll leave ready to push your own apps forward with a little bit of gaming magic.

Video Video Video

Transcript

Guten morgen. I am here with good news, because we will all be game programmers very soon. I'm sure a lot of you -- like myself -- kind of got into programming in the first place because you wanted to build the next Mario. In the decade since then it turns out I build a lot more banking apps than Mario, but I'm excited that JavaScript is heading in a direction where we're all going to be able to integrate a couple of things from video games into all of our apps very soon. Not only do I think these techniques that we can get from game development patterns are useful, I actually think they're inevitable. No matter what you do, you're going to end up integrating at least a few of these things in the next couple of years. This might make a little more sense if I explain my double life. During the day, I'm "the Node Guy" at Heroku. Whenever any of you file a Node ticket at Heroku, it goes to me. And whenever the build pack doesn't work, I get yelled at. And whenever people are confused about the Dev Center documentation, that's also my fault. So I'm sorry. This is my serious work. This is the work that I do that people's livelihoods depend on. So it's the scary, slow development work that I do nine-to-five. But it's also really interesting, because it's the first role that I've had where there are literally thousands of production apps that people are earning a living with that I can look at and I can compare. Like how people are following different patterns and how some companies are successful while some aren't. There's actually a really interesting talk later today about how Toyota Europe is running right now on Heroku. That's by Ray McDermott: you should check it out. I've gotten a unique insight into how people are building JavaScript apps these days, because I see so many of them every day. That's my day job. At night, I work in a less-serious way. I work on something I like to call "Playful JS". It's JS that doesn't really have a purpose or a point. It's JS techniques that aren't going to help you pass questions when you're interviewing for a job. It's nothing people teach at a university. But it's stuff I've been interested since I was a kid: games, graphics, audio... I play with stuff like procedural generation. I play with stuff like old-school 3D rendering techniques. And very occasionally, some crazy company will pay me to build a game in HTML5. This is becoming a bigger and bigger thing. A couple years ago this would never have happened. Nobody would ever reach and say "Hey, can you build me a game in JavaScript?" I'm really excited about this and I think this is going to keep on accelerating. I wanted to highlight that because this is the basis for the talk, and I'm not an expert. I just play with things at night. I am an expert at Node and running production Node apps, but this is my hobby. I'm not a game developer by trade, but I love this stuff and I think more of us are going to become game developers by trade over the next few years. And even if not, it's fun to learn and it's something I think everybody should try at least once. So first, why should JS developers even care about what game developers do? It seems like we're worlds apart. They write things in C++ while we write in an interpreted language, they're writing on bare metal while we're trapped in a browser, they're concerned with "players" while we're concerned with "users", so it doesn't seem like there's a whole lot of overlap here. To answer that question, let me take you way, way back to 2008. That was when jQuery was also still new and popular with everybody. And this is Google Maps in 2008. As usual, Google was pushing the limits of JS, so this was a cutting-edge app. But from our current perspective you can see that it was just a little bit better than MapQuest. It's still two-dimensional, it's still very simple, there's not a lot of interactivity here. Now here's 2008's Grand Theft Auto. It's the exact same year, but a very different piece of software. Instead of a 2D, top-down camera, you've got a 3D moving camera, you have full textures and full shadows and even the geometric detail on the buildings is sort of incredible. It's a far cry from the Google Maps of the same era. But 4 years later, Google released an update to Google Maps. In this new version, you have the 3D textures, the geometric details, the dynamic shadows. And that brings me to the question: if you had to put money on which of these three apps are most similar -- which two of them have the most-similar architectures -- which would you bet on? My money would be on [the last] two. Even thought it's the same app, the Google Maps of 2013 is a lot closer to the video game Grand Theft Auto of 2008 than it is to its predecessor. Similarly, in 2004 Nintendo's DSTouch was a big hit. Early developers for the platform pioneered what touch could do on as a core interaction element. Then in 2007, Apple's iPod Touch was also a big hit. Apple is clearly responsible for the fact that nobody younger than me even knows how to use a screen that you can't touch and drag things on. But even though Apple is responsible for making it ubiquitous, touch was really pioneered in the games industry. Do any of you remember Moon Patrol? Really? All right! Congratulations, because I was one year old when this game came out, so I don't remember Moon Patrol. But according to Wikipedia it was the game that pioneered parallax. As you moved, the backgrounds scrolled at different speeds relative to each other to give you the illusion of depth. This was the first game -- at least according to Wikipedia -- to use that, and now you can't go anywhere without seeing parallax on some website. You can't throw a stone without hitting somebody who stuck parallax on their site. Sometimes for good, sometimes for bad... You know who you are. I think it's incredible that something that became prevalent decades ago in games is just now gaining popularity in the web. And this is a trend that we see over and over and over: games lead all other technologies and push boundaries that we then come in and get to fill, once it's easier to do. Today's game experience leads to tomorrow's app experience. And this is important, because if you want to know how to build the apps of tomorrow, you should be looking at what games of today are doing, and how they're working. What kinds of technologies they're employing and how they're training users to expect user interfaces to behave. You can see a lot of other examples. I've given several. But every time you use Google Docs, you're now sharing an interactive, virtual environment with other users, and this is something games have been doing for decades. Now document services are starting to do it and it's a big deal. It's important for everybody to learn. I'm not the first person to realize that we can learn a lot from game developers. If any of you have been getting spammed by Famo.us like I have, you know all about it. It's basically a game engine that allows you to run apps on top of it. They just chucked all the regular MVC framework stuff out the window and said "We're going to build a--" I think they called it a "crappy game engine". And that's what people are running really interesting, interactive apps on top of with great success. This is my favorite example of how games and apps are colliding, at least in JS. This is the architecture for Doom 3. The important part is actually the 2nd and 3rd step. The 2nd step is where Doom actually builds a description of the scene. It figures out what you're looking at, what you can see, what's in the frame, what's local to your character. And then the 4th step is where it actually renders it. And this is the expensive step: rendering something in 3D costs a lot. The operation order and which operations you choose to render are actually really, really important. They can make your frame rate fast or they can make it slow. So in Doom 3, they figured out exactly which operations they needed to pass to the renderer before they rendered anything, to make the application run as quickly as possible. And this is what I think is really fun and cool. React.js -- the library that you guys might have seen from Facebook -- works exactly the same way. The architecture is identical. Instead of OpenGL, their bottleneck is the DOM. So they build sort of a scene -- like a full scene-builder -- before they write to the DOM. That way their scene-builder can compute exactly which DOM operations would be fastest to render. So they're pulling ideas straight out of the world of game development. It's inevitable. You're probably already using some of this stuff today, and if you're not then I guarantee you will be. So I think that this is a great opportunity to become familiar with some of the patterns that game developers have been using for decades. When you're building your next app, you can at least have them in your toolset. That's the argument for using game techniques inside of apps, but of course the other reason you might want to know about game dev is because you just want to build a game in JS. And I've got great news for you: there's never been a better time to build games with JS. Finally we have all the basic primitives we need in the form of web APIs in order to start competing toe-to-toe with real native experiences. The gap between JS and native capabilities is closing rapidly. I'm really excited by this. So that begs the question: why isn't everybody doing this? Some people are trying. Some early pioneers are getting out there. This is a game called Polycraft. It's a really polished, 3D game built in JS. And you've probably seen Cut the Rope, which is probably the most well-known HTML5 game to date. There's not a whole lot of examples and there's not a whole lot of success stories. The reason is that we lack familiarity, tooling and a marketplace. Game developers have a huge toolset and they have a familiarity with how games are built. They also have marketplaces with which to ship games. We don't have any of these things. Certainly I can't address all of these today. What I'm going to try to do is provide a little bit of familiarity to JS developers that might be interested in building a game someday. So with that, we're going to start looking at a couple of very basic game dev techniques. These are things that you're going to use any time you're going to build a game in any language. We're going to look at it specifically in JS. All of these techniques can also be applied to apps these days, which is really neat. It usually takes a little more creativity to do that, but they can push things forward. The first pattern to look at whenever you're getting into game development is the game loop. This is the basic, fundamental, core idea behind any game that's ever been written since videogames were a thing. This is the sort of code that you probably wouldn't get through code review at most shops. It's just a while loop that never stops and it's got some conditionals in it and you're doing everything in a waterfall pattern. But this is what's powering every game that you've ever used. The reason for that is because it makes time explicit. The issue with traditional, event-based architectures that we're all using in our apps now, where you have an event like a click event and that event triggers some sort of reaction like maybe you close a window or you move to the next screen, is that time is implicit in that. An event happened, then you gave a response. So you know that the response happened after the event, but you never elevate time to a first-class citizen. You never measure time to the point where you could put it into a variable. That's what a game loop gives you. It lets you measure time between different iterations of your loop and store time in a variable. The other thing that it lets you do is keep moving without user input. If you don't have a game loop, if you're just responding to events, then you're dependent entirely on the user to push your game forward and to push any elements on the screen forward. With a game loop, you can respond to time instead of to the user. You don't just have to have an action-and-reaction sort of pattern. I have two demos that I hope will work to illustrate this. This is a version of a small, simple game -- and I hope it doesn't blow anybody's ears, let's go a little quieter -- a small, simple game without an event loop. This is a game that only runs -- there's no loop here -- it's just listening to my input and it's responding to the input. So if I start pressing keys, then it'll start doing things. I can move around and I can jump and whatever. But then as soon as I stop, nothing happens. So I could be in the middle of a jump and it's just there. If you tried to build a game using MVC -- if you tried to use Backbone to build a game -- this is basically what would happen. You're entirely dependent on the user for everything in this environment. So as soon as the user stops doing anything, your game is sort of pointless. This is not just true with games. This is also true with anything that requires physics and motion beyond something that you're responding to: anytime you see a bounce effect or a nice physical scroller. When you flick an icon and it moves with momentum, it's got the same problem. That's without an event loop. If you go with an event loop -- this is the exact same game -- it's going to keep running no matter what. I can stop doing anything and everything still works. I'll still fall, everything still behaves, the environment's still going to be alive. And this is 100% due to the event loop: there is literally no other difference between these two games. I copied and pasted. That's how important an event loop is to rich, interactive, dynamic applications. Let me kill that so the music stops... Cool. Once you've got a game loop, you quickly run into a problem. You're starting to pack all the logic for your game into this one loop. You can imagine how that gets really hairy. Once you have more than one or two entities that you're trying to update with time, they start to get conflated. You've got this enormous, thousand-line loop and you don't know where anything is. In almost every game that's ever been created, there's an update pattern. This is where you make your game entities -- or even just your graphical entities -- autonomous. You teach them how to handle their own changes through time. You can see it's just an organizational pattern. You're not really doing anything differently, you're just organizing it in a sane way. This is important because that way your player can know how to walk and how far he needs to walk depending on how much time needs to pass. Your clouds can move with the wind. When you flick an icon, that icon knows how to keep sliding across the screen without any sort of direction from the main game loop. We all know that decoupling is a good idea, and this is one of the primary ways decoupling is done in games that have to respond to time and not just to the user. The other cool thing about it is that it's sort of a light form of dependency injection because you don't have to just pass time to these things, you can also pass other arguments that they may depend on. The final thing that you need to know if you're going to ship a game tomorrow is you need to understand what a time buffer is. A time buffer is an extension to the traditional game loop and update cycles. Instead of treating time in each loop as an individual entity that you respond to, every time you get a new instance of the loop you add time to a buffer. Whenever that buffer overflows by a certain amount -- which is your frame time -- you render a frame. You keep on adding to the buffer and whenever the buffer's bigger than a certain set amount that you decided on initially, you render a frame and you clear that time out of the buffer. This sounds like you might be over-complicating things, because you've already got a game loop that will let you process time. But this is important for a couple key reasons. The first is that it keeps physics models stable. If you don't do this, most physical integration methods -- from the ones you see on your iPhone when you pull down the drawer to the ones that you see in a 3D video game -- will start to feel weird and wonky because they're rendering at different time intervals. Sometimes you're moving a character really far and sometimes you're not moving it far enough. It's really important for anything that has physics involved. It's also important because it decouples simulating and rendering. This is critical for JS developers, because you're going to want to use requestAnimationFrame any time you're building anything you want to be responsive on the web. With requestAnimationFrame, you know that the browser's ready to re-render the DOM, but that might happen at 30 frames per second on one machine and 60 on another. You want those machines to simulate the exact same thing at the exact same speed. A time buffer will allow you to do this. A time buffer will make sure that both of those machines call the update method with the same amount of time, over and over and over. The output of both machines will be the same, even if they render a different number of frames. This is kind of hard to explain, so I've put together a little demo of it. One of the stupid side-projects I work on is called "Newton" and it's a physics engine that noone should ever use, because I wrote it to learn how physics engines work. One of the things that I discovered was that it's really cool when you can replicate time-based behavior. That's what a time buffer allows you to do. No matter how many times I re-run the same physical simulation, it'll happen the exact same way every time, so you can write automated tests about it. This is really important when you're trying to use game techniques in an app, because you obviously want to write automated tests for your app. Let me open up this example... This is Newton. It's a little embarrassing, because what I was trying to do in this particular version was just to make a stable box. That was the whole goal. Literally just make a box that doesn't implode on itself. This was like version 500 of that attempt because I really suck at math. What sucks but is also cool is that when it first starts, the box seems to be stable, but then something happens to it. It's my favorite bug that I've ever created in any software ever. Here we go. I'm going to refresh it and you'll see what happens. If the Internet works. OK, watch... [audience laughter] I'm in a cafe thinking "what the hell?" I couldn't build this if you asked me to. [applause] That's the crazy part. It becomes a box. It stabilizes. It will never do that again, ever. You can run it as long as you want: trust me, I've tried. Back and forth, back and forth. What's really cool about this is the because it's using a time buffer I can refresh this as many times as I want and every single time it will behave exactly the same way. There will be no difference. You can compare the different frames and they would be identical. It doesn't matter what computer I ran it on. You can run this on an iPhone and you can put it beside a computer and it'll run at the exact same speed. This is due to the combination of game loop and the time buffer. In this case it lets me reproduce a bug, but it does other more useful things. I just love this bug. Let's put a little of this together. That game that I showed you earlier -- the one that looks like a really shitty version of Minecraft -- this is the event loop for that game. I copied and pasted it straight out. It's not complicated. That's a full, published game. You can finish it, it has music and audio and upgrades and everything, but this is the full event loop. It can keep things tight and simple. One of the nicest things about an event loop is after building all sorts of Backbone spaghetti apps over the years, I love that an event loop lexically in code shows you what you're doing over and over and over. It really highlights the hotspots in your code for you. Usually if something's going wrong or going slow it's because of something deep inside of your event loop. Here you can see the game loop, the time buffer and the update method all working together. The whole function is the loop. This frame is just called over and over and over with a requestAnimationFrame. That time buffer is the bit at the top. I wrote a little clock system that implements a time buffer and does what I said: adds time to it, and whenever it overflows a certain amount, it extracts that amount out, over and over and over. Nothing special. Then you see a bunch of update methods. It's updating controls, it's updating the player, it's updating the music. That's the whole thing! That all you really need to make a game. Where do you go next? I showed you the very, very basics of building a game, but I hope some of you guys will go home and join one of these 48-hour competitions and try throwing something together. These are some really good resources. If I was going to put together any game right now with JS, these are the things that are simple, short, mature, fun, well-documented that you can rely on to build 2D graphics, 3D graphics and physics. You can use my company -- Heroku -- and the ws module in Node to build really awesome multi-player stuff. If you ever want to talk about any of this nerdy videogame stuff with me, I'm @hunterloftis on Twitter, I'm hunter@heroku.com and I really appreciate everybody getting up this morning to come listen to all this game-nerd stuff. Thank you. Edit transcript via pull request.