Statistics
17
Views
0
Downloads
0
Donations
Uploader

高宏飞

Shared on 2025-12-21
Support
Share

AuthorTomas Varaneckas

At day you have to wrangle with legacy code, fix bugs, struggle with APIs, deploy services and integrate things. Yet you wish you could create worlds, animate dragons, break laws of physics and design artificial intelligence. You can.

Tags
ruby
Publish Year: 2015
Language: 英文
File Format: PDF
File Size: 11.8 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
Developing Games With Ruby For those who write code for living Tomas Varaneckas This book is for sale at http://leanpub.com/developing-games-with-ruby This version was published on 2014-12-16 * * * * * This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do. * * * * * © 2014 Tomas Varaneckas
Table of Contents A Boy Who Wanted To Create Worlds Why Ruby? What You Should Know Before Reading This Book What Are We Going To Build? Graphics Game Development Library Theme And Mechanics Preparing The Tools Getting Gosu to run on Mac Os X Getting The Sample Code Other Tools Gosu Basics Hello World Screen Coordinates And Depth Main Loop Moving Things With Keyboard Images And Animation Music And Sound Warming Up Using Tilesets Integrating With Texture Packer Combining Tiles Into A Map Using Tiled To Create Maps Loading Tiled Maps With Gosu Generating Random Map With Perlin Noise Player Movement With Keyboard And Mouse Game Coordinate System Prototyping The Game Switching Between Game States Implementing Menu State Implementing Play State Implementing World Map Implementing Floating Camera Implementing The Tank Implementing Bullets And Explosions
Running The Prototype Optimizing Game Performance Profiling Ruby Code To Find Bottlenecks Advanced Profiling Techniques Optimizing Inefficient Code Profiling On Demand Adjusting Game Speed For Variable Performance Frame Skipping Refactoring The Prototype Game Programming Patterns What Is Wrong With Current Design Decoupling Using Component Pattern Simulating Physics Adding Enemy Objects Adding Bounding Boxes And Detecting Collisions Catching Bullets Implementing Turn Speed Penalties Implementing Terrain Speed Penalties Implementing Health And Damage Adding Health Component Inflicting Damage With Bullets Creating Artificial Intelligence Designing AI Using Finite State Machine Implementing AI Vision Controlling Tank Gun Implementing AI Input Implementing Tank Motion States Wiring Tank Motion States Into Finite State Machine Making The Prototype Playable Drawing Water Beyond Map Boundaries Generating Tree Clusters Generating Random Objects Implementing A Radar Dynamic Sound Volume And Panning Giving Enemies Identity Respawning Tanks And Removing Dead Ones Displaying Explosion Damage Trails Debugging Bullet Physics Making Camera Look Ahead Reviewing The Changes
Dealing With Thousands Of Game Objects Spatial Partitioning Implementing A Quadtree Integrating ObjectPool With QuadTree Moving Objects In QuadTree Implementing Powerups Implementing Base Powerup Implementing Powerup Graphics Implementing Powerup Sounds Implementing Repair Damage Powerup Implementing Health Boost Implementing Fire Rate Boost Implementing Tank Speed Boost Spawning Powerups On Map Respawning Powerups After Pickup Implementing Heads Up Display Design Considerations Rendering Text With Custom Font Implementing HUD Class Implementing Game Statistics Tracking Kills, Deaths and Damage Making Damage Personal Tracking Damage From Chain Reactions Displaying Game Score Building Advanced AI Improving Tank Navigation Implementing Demo State To Observe AI Visual AI Debugging Making AI Collect Powerups Seeking Health Powerups After Heavy Damage Evading Collisions And Getting Unstuck Wrapping It Up Lessons Learned Special Thanks
A Boy Who Wanted To Create Worlds Once there was a boy who fell in love with this magical device that could bring things to life inside a glaring screen. He spent endless hours exploring imaginary worlds, fighting strange creatures, shooting pixelated spaceships, racing boxy cars. The boy kept pondering. “How is this made? I want to create my own worlds…”. Then he discovered programming. “I can finally do it!” - he thought. And he tried. And failed. Then he tried harder. He failed again and again. He was too naive to realize that those worlds he was trying to create were too sophisticated, and his knowledge was too limited. He gave up creating those worlds. What he didn’t give up is writing code for this magical device. He realized he isn’t smart enough to create worlds, yet he found out he could create simpler things like small applications - web, desktop, server side or whatnot. Few years later he found himself getting paid to make those. Applications got increasingly bigger, they spanned across multiple servers, integrated with each other, became pats of huge infrastructures. The boy, now a grown man, was all into it. It was fun and challenging enough to spend over 10000 hours learning and building what others wanted him to build. Some of these things were useful, some where boring and pointless. Some were never finished. There were things he was proud of, there were others that he wouldn’t want to talk about, nonetheless everything he built made him a better builder. Yet he never found the time, courage or reason to build what he really wanted to build since he was a little boy - his own worlds. Until one day he realized that no one can stop him from following his dream. He felt that equipped with his current knowledge and experience he will be able to learn to create worlds of his own. And he went for it. This boy must live in many software developers, who dream about creating games, but instead sell their software craftsmanship skills to those who need something else. This boy is me, and you. And it’s time to set him free. Welcome to the world of game development that was waiting for you all these years.
Why Ruby? When it comes to game development, everyone will tell you that you should go with C++ or some other statically typed language that compiles down to bare metal instructions. Or that you should go with full blown game development platform like Unity. Slow, dynamic languages like Ruby seem like the last choice any sane game developer would go for. A friend of mine said “There’s little reason to develop a desktop game with Ruby”, and he was absolutely right. Perhaps this is the reason why there are no books about it. All the casual game action happens in mobile devices, and desktop games are for seasoned gamers who demand fast and detailed 3D graphics, motion-captured animations and sophisticated game mechanics - things we know we are not going to be able to build on our own, without millions from VC pockets and Hollywood grade equipment. Now, bear with me. Your game will not be a 3D MMORPG set in huge, photo realistic representation of Middle-earth. Let’s leave those things to Bethesda, Ubisoft and Rockstar Games. After all, everyone has to start somewhere, and you have to be smart enough to understand, that even though that little boy in you wants to create an improved version of Grand Theft Auto V, we will have to go for something that resembles lesser known Super Nintendo titles instead. Why not go mobile then? Those devices seem perfect for simpler games. If you are a true gamer at heart, you will agree that touch screen games you find in modern phones and tablets are only good for killing 10 minutes of your time while taking a dump. You have to feel the resistance when you click a button! Screen size also does matter. Playing anything on mobile phone is a torture for those who know what playing real games should feel like. So, your game will have to be small enough for you to be able to complete it, it will have to have simple 2D graphics, and would not require the latest GeForce with at least 512MB of RAM. This fact gives you the benefit of choice. You don’t have to worry about performance that much. You can choose a friendly and productive language that is designed for programmer happiness. And this is where Ruby starts to shine. It’s beautiful, simple and elegant. It is close to poetry.
What You Should Know Before Reading This Book As you can read on the cover, this book is “for those who write code for living”. It’s not a requirement, and you will most likely be able to understand everything even if you are a student or hobbyist, but this book will not teach you how to be a good programmer. If you want to learn that, start with timeless classic: The Pragmatic Programmer: From Journeyman to Master. You should understand Ruby at least to some extent. There are plenty of books and resources covering that subject. Try Why’s Poignant Guide To Ruby or Eloquent Ruby. You can also learn it while reading this book. It shouldn’t be too hard, especially if you already write code for living. After all programming language is merely a tool, and when you learn one, others are relatively easy to switch to. You should know how to use the command line. Basic knowledge of Git can also be handy. You don’t have to know how to draw or compose music. We will use media that is available for free. However, knowledge of graphics and audio editing software won’t hurt.
What Are We Going To Build? This question is of paramount importance. The answer will usually determine if you will likely to succeed. If you want to overstep your boundaries, you will fail. It shouldn’t be too easy either. If you know something about programming already, I bet you can implement Tic Tac Toe, but will you feel proud about it? Will you be able to say “I’ve built a world!”. I wouldn’t. Graphics To begin with, we need to know what kind of graphics we are aiming for. We will instantly rule out 3D for several reasons: We don’t want to increase the scope and complexity Ruby may not be fast enough for 3D games Learning proper 3D graphics programming requires reading a separate book that is several times thicker than this one. Now, we have to swallow our pride and accept the fact that the game will have simple 2D graphics. There are three choices to go for: Parallel Projection Top Down Side-Scrolling Parallel Projection (think Fallout 1 & 2) is pretty close to 3D graphics, it requires detailed art if you want it to look decent, so we would have a rough start if we went for it. Top Down view (old titles of Legend of Zelda) offers plenty of freedom to explore the environment in all directions and requires less graphical detail, since things look simpler from above. Side Scrolling games (Super Mario Bros.) usually involve some physics related to jumping and require more effort to look good. Feeling of exploration is limited, since you usually move from left to right most of the time. Going with Top Down view will give us a chance to create our game world as open for exploration as possible, while having simple graphics and movement mechanics. Sounds like the best choice for us. If you are as bad at drawing things as I am, you could still wonder how we are going to get our graphics. Thankfully, there is this opengameart.org. It’s like GitHub of game media, we will surely find something there. It also contains audio samples and tracks. Game Development Library
Implement it all yourself or harness the power of some game development library that offers you boilerplates and convenient access to common functions? If you’re like me, you would definitely want to implement it all yourself, but that may be the reason why I failed to make a decent game so many times. If you will try to implement it all yourself, you will most likely end up reimplementing some existing game library, poorly. It won’t take long while you reach a point where you need to interface with underlying operating system libraries to get graphics. And guess if those bindings will work in a different operating system? So, swallow your pride again, because we are going to use an existing game development library. Good news is that you will be able to actually finish the game, and it will be portable to Windows, Mac and Linux. We will still have to build our own game engine for ourselves on top of it, so don’t think it won’t be fun. There are several game libraries available for Ruby, but it’s a simple choice, because Gosu is head and shoulders above others. It’s very mature, has a large and active community, and it is mainly written in C++ but has first class Ruby support, so it will be both fast and convenient to use. Many of other Ruby game libraries are built on top of Gosu, so it’s a solid choice. Theme And Mechanics Choosing the right theme is undoubtedly important. It should be something that appeals to you, something you will want to play, and it should not imply difficult game mechanics. I love MMORPGs, and I always dreamed of making an open world game where you can roam around, meet other players, fight monsters and level up. Guess how many times I started building such a game? Even if I wouldn’t have lost the count, I wouldn’t be proud to say the number. This time, equipped with logic and sanity, I’ve picked something challenging enough, yet still pretty simple to build. Are you ready? Drumroll… We will be building a multi directional shooter arcade game where you control a tank, roam around an island, shoot enemy tanks and try not to get destroyed by others. If you have played Battle City or Tank Force, you should easily get the idea. I believe that implementing such a game (with several twists) would expose us to perfect level of difficulty and provide substantial amount of experience. We will use a subset of these gorgeous graphics which are available on opengameart.org, generously provided by Csaba Felvegi.
Preparing The Tools While writing this book, I will be using Mac OS X (10.9), but it should be possible to run all the examples on other operating systems too. Gosu Wiki has “Getting Started” pages for Mac, Linux and Windows, so I will not be going into much detail here. Getting Gosu to run on Mac Os X If you haven’t set up your Mac for development, first install Xcode using App Store. System Ruby should work just fine, but you may want to use Rbenv or RVM to avoid polluting system Ruby. I’ve had trouble installing Gosu with RVM, but your experience may vary. To install the gem, simply run: $ gem install gosu You may need to prefix it with sudo if you are using system Ruby. To test if gem was installed correctly, you should be able to run this to produce an empty black window: $ irb irb(main):001:0> require 'gosu' => true irb(main):002:0> Gosu::Window.new(320, 240, false).show => nil Most developers who use Mac every day will also recommend installing Homebrew package manager, replace Terminal app with iTerm2 and use Oh-My-Zsh to manage ZSH configuration.
Getting The Sample Code You can find sample code at GitHub: https://github.com/spajus/ruby-gamedev-book- examples. Clone it to a convenient location: $ cd ~/gamedev $ git clone git@github.com:spajus/ruby-gamedev-book-examples.git The source code of final product can be found at https://github.com/spajus/tank_island
Other Tools All you need for this adventure is a good text editor, terminal and probably some graphics editor. Try GIMP if you want a free one. I’m using Pixelmator, it’s wonderful, but for Mac only. A noteworthy fact is that Pixelmator was built by fellow Lithuanians. When it comes to editors, I don’t leave home without Vim, but as long as what you use makes you productive, it doesn’t make any difference. Vim, Emacs or Sublime are all good enough to write code, just have some good plugins that support Ruby, and you’re set. If you really feel you need an IDE, which may be the case if you are coming from a static language, you can’t go wrong with RubyMine.
Gosu Basics By now Gosu should be installed and ready for a spin. But before we rush into building our game, we have to get acquainted with our library. We will go through several simple examples, familiarize ourselves with Gosu architecture and core principles, and take a couple of baby steps towards understanding how to put everything together. To make this chapter easier to read and understand, I recommend watching Writing Games With Ruby talk given by Mike Moore at LA Ruby Conference 2014. In fact, this talk pushed me towards rethinking this crazy idea of using Ruby for game development, so this book wouldn’t exist without it. Thank you, Mike. Hello World To honor the traditions, we will start by writing “Hello World” to get a taste of what Gosu feels like. It is based on Ruby Tutorial that you can find in Gosu Wiki. 01-hello/hello_world.rb 1 require 'gosu' 2 3 class GameWindow < Gosu::Window 4 def initialize(width=320, height=240, fullscreen=false) 5 super 6 self.caption = 'Hello' 7 @message = Gosu::Image.from_text( 8 self, 'Hello, World!', Gosu.default_font_name, 30) 9 end 10 11 def draw 12 @message.draw(10, 10, 0) 13 end 14 end 15 16 window = GameWindow.new 17 window.show Run the code: $ ruby 01-hello/hello_world.rb You should see a neat small window with your message:
Hello World See how easy that was? Now let’s try to understand what just happened here. We have extended Gosu::Window with our own GameWindow class, initializing it as 320x240 window. super passed width, height and fullscreen initialization parameters from GameWindow to Gosu::Window. Then we defined our window’s caption, and created @message instance variable with an image generated from text "Hello, World!" using Gosu::Image.from_text. We have overridden Gosu::Window#draw instance method that gets called every time Gosu wants to redraw our game window. In that method we call draw on our @message variable, providing x and y screen coordinates both equal to 10, and z (depth) value equal to 0. Screen Coordinates And Depth Just like most conventional computer graphics libraries, Gosu treats x as horizontal axis (left to right), y as vertical axis (top to bottom), and z as order.
Screen coordinates and depth x and y are measured in pixels, and value of z is a relative number that doesn’t mean anything on it’s own. The pixel in top-left corner of the screen has coordinates of 0:0. z order in Gosu is just like z-index in CSS. It does not define zoom level, but in case two shapes overlap, one with higher z value will be drawn on top. Main Loop The heart of Gosu library is the main loop that happens in Gosu::Window. It is explained fairly well in Gosu wiki, so we will not be discussing it here. Moving Things With Keyboard We will modify our “Hello, World!” example to learn how to move things on screen. The following code will print coordinates of the message along with number of times screen was redrawn. It also allows exiting the program by hitting Esc button. 01-hello/hello_movement.rb 1 require 'gosu' 2 3 class GameWindow < Gosu::Window 4 def initialize(width=320, height=240, fullscreen=false) 5 super 6 self.caption = 'Hello Movement' 7 @x = @y = 10 8 @draws = 0 9 @buttons_down = 0 10 end 11 12 def update 13 @x -= 1 if button_down?(Gosu::KbLeft) 14 @x += 1 if button_down?(Gosu::KbRight)
15 @y -= 1 if button_down?(Gosu::KbUp) 16 @y += 1 if button_down?(Gosu::KbDown) 17 end 18 19 def button_down(id) 20 close if id == Gosu::KbEscape 21 @buttons_down += 1 22 end 23 24 def button_up(id) 25 @buttons_down -= 1 26 end 27 28 def needs_redraw? 29 @draws == 0 || @buttons_down > 0 30 end 31 32 def draw 33 @draws += 1 34 @message = Gosu::Image.from_text( 35 self, info, Gosu.default_font_name, 30) 36 @message.draw(@x, @y, 0) 37 end 38 39 private 40 41 def info 42 "[x:#{@x};y:#{@y};draws:#{@draws}]" 43 end 44 end 45 46 window = GameWindow.new 47 window.show Run the program and try pressing arrow keys: $ ruby 01-hello/hello_movement.rb The message will move around as long as you keep arrow keys pressed.
Use arrow keys to move the message around We could write a shorter version, but the point here is that if we wouldn’t override needs_redraw? this program would be slower by order of magnitude, because it would create @message object every time it wants to redraw the window, even though nothing would change. Here is a screenshot of top displaying two versions of this program. Second screen has needs_redraw? method removed. See the difference?
Redrawing only when necessary VS redrawing every time Ruby is slow, so you have to use it wisely. Images And Animation It’s time to make something more exciting. Our game will have to have explosions, therefore we need to learn to animate them. We will set up a background scene and trigger explosions on top of it with our mouse. 01-hello/hello_animation.rb 1 require 'gosu' 2 3 def media_path(file) 4 File.join(File.dirname(File.dirname( 5 __FILE__)), 'media', file) 6 end 7 8 class Explosion 9 FRAME_DELAY = 10 # ms 10 SPRITE = media_path('explosion.png') 11 12 def self.load_animation(window) 13 Gosu::Image.load_tiles( 14 window, SPRITE, 128, 128, false) 15 end 16 17 def initialize(animation, x, y) 18 @animation = animation 19 @x, @y = x, y 20 @current_frame = 0 21 end 22 23 def update 24 @current_frame += 1 if frame_expired? 25 end 26 27 def draw 28 return if done? 29 image = current_frame 30 image.draw( 31 @x - image.width / 2.0, 32 @y - image.height / 2.0, 33 0) 34 end 35 36 def done? 37 @done ||= @current_frame == @animation.size 38 end
39 40 private 41 42 def current_frame 43 @animation[@current_frame % @animation.size] 44 end 45 46 def frame_expired? 47 now = Gosu.milliseconds 48 @last_frame ||= now 49 if (now - @last_frame) > FRAME_DELAY 50 @last_frame = now 51 end 52 end 53 end 54 55 class GameWindow < Gosu::Window 56 BACKGROUND = media_path('country_field.png') 57 58 def initialize(width=800, height=600, fullscreen=false) 59 super 60 self.caption = 'Hello Animation' 61 @background = Gosu::Image.new( 62 self, BACKGROUND, false) 63 @animation = Explosion.load_animation(self) 64 @explosions = [] 65 end 66 67 def update 68 @explosions.reject!(&:done?) 69 @explosions.map(&:update) 70 end 71 72 def button_down(id) 73 close if id == Gosu::KbEscape 74 if id == Gosu::MsLeft 75 @explosions.push( 76 Explosion.new( 77 @animation, mouse_x, mouse_y)) 78 end 79 end 80 81 def needs_cursor? 82 true 83 end 84 85 def needs_redraw? 86 !@scene_ready || @explosions.any? 87 end 88 89 def draw 90 @scene_ready ||= true 91 @background.draw(0, 0, 0) 92 @explosions.map(&:draw) 93 end 94 end 95 96 window = GameWindow.new 97 window.show Run it and click around to enjoy those beautiful special effects: $ ruby 01-hello/hello_animation.rb
The above is a preview of the first 20 pages. Register to read the complete e-book.