Share Pastebin
Guest
Public paste!

Andy Brown

By: a guest | Mar 22nd, 2009 | Syntax: Ruby | Size: 9.90 KB | Hits: 435 | Expires: Never
Copy text to clipboard
  1. #!/usr/bin/env ruby
  2.  
  3. ## gosu_chipmunk_demo.rb
  4. ##
  5. ## I'm posting this in the hope that it will be useful to someone, but I make no
  6. ## guarantee that it works and won't fail your system up. (BSD 4 life)
  7. ##
  8. ## Andy B - Tasty Sandwiches - http://www.neorab.com
  9.  
  10.  
  11. # I tend to distribute the compiled libraries with any game that I write that
  12. # uses gosu or chipmunk.  Or rather I would if I ever finished one and managed
  13. # to get it out to anyone.  This script expects to find the libraries on the
  14. # search path.  If you installed from rubygems, be sure to require that first.
  15. require 'gosu'
  16. require 'chipmunk'
  17.  
  18.  
  19. # Define the size in an easy to see place. Here.
  20. SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480
  21.  
  22.  
  23. # Here we define the number of physics steps that will be done during each call
  24. # to the update method.  If you want to see how this can affect performance and
  25. # collision detection, fiddle with it.  DTIME is basically how long each substep
  26. # is.  Lower values will increates the accuracy while higher help performance.
  27. SUBSTEPS, DTIME = 6, (1.0 / 60.0)
  28.  
  29.  
  30. # This defines the shape of the Box that we draw later.
  31. BOX_SHAPE_ARRAY = [
  32.   CP::Vec2.new(-8.0, -8.0),
  33.   CP::Vec2.new(-8.0, 8.0),
  34.   CP::Vec2.new(8.0, 8.0),
  35.   CP::Vec2.new(8.0, -8.0)
  36. ]
  37.  
  38.  
  39. # All of the groups of boxes is set up at the begining of the scene, we are just
  40. # starting them all off the screen and dropping them from different heights. So
  41. # instead of having magic numbers all over the place, we'll set them here. For
  42. # some of them, it's not a specific point, but a rand range.  Play around with
  43. # these, it's a bucket of little box fun. Whooo!
  44. def first_bread_height
  45.   -rand(600)
  46. end
  47. MEAT_HEIGHT = [-900, -1300, -1700]
  48. def meat_height(layer)
  49.   MEAT_HEIGHT[layer]
  50. end
  51. CHEESE_HEIGHT = [-2200, -2300]
  52. def cheese_height(layer)
  53.   CHEESE_HEIGHT[layer]
  54. end
  55. def lettuce_height
  56.   -3000
  57. end
  58. def top_bread_height
  59.   -rand(400) - 3500
  60. end
  61.  
  62. # Convenience method for converting from radians to a Vec2 vector.
  63. class Numeric
  64.   def radians_to_vec2
  65.     CP::Vec2.new(Math::cos(self), Math::sin(self))
  66.   end
  67. end
  68.  
  69.  
  70. # This is a Container for a Gosu Image and a Chupmunk Body/Shape pair.  It is
  71. # largely just for a coninient method to create this item and a wrapper around
  72. # it's draw routine.
  73. class GosuMunk_Box
  74.   attr_reader :body, :shape
  75.  
  76.   def initialize(space, image, x, y, color)
  77.     # Create a small body and the shape around it.
  78.     @body  = CP::Body.new(1.0, 1.0)
  79.     @shape = CP::Shape::Poly.new(@body, BOX_SHAPE_ARRAY, CP::Vec2.new(0,0))
  80.    
  81.     @image = image
  82.     @color = color
  83.    
  84.     @shape.body.p = CP::Vec2.new(x, y)      # position
  85.     @shape.body.v = CP::Vec2.new(0.0, 0.0)  # velocity
  86.     @shape.body.a = (3 * Math::PI / 2.0)    # angle in radians
  87.    
  88.     space.add_body(@body)
  89.     space.add_shape(@shape)
  90.   end
  91.  
  92.  
  93.   def draw
  94.     @image.draw_rot(@shape.body.p.x, @shape.body.p.y, 1, @shape.body.a.radians_to_gosu, 0.5, 0.5, 1, 1, @color)
  95.   end
  96. end
  97.  
  98.  
  99. # Gosu::Window is exactly that, a window.  It's the top level container for our
  100. # scene environment.  Normally I only use this for a basic state holder and pass
  101. # the scene building, updating and drawing to another class for readability but
  102. # for a single scene demo, this will work just as well.
  103. class GameWindow < Gosu::Window
  104.  
  105.  
  106.   # When we create the window, we need to set all of the options for Gosu and
  107.   # the physics environment.  While we don't strictly have to build our scene
  108.   # here, it makes a great deal more sense than doing it anywhere else.
  109.   def initialize
  110.     super(SCREEN_WIDTH, SCREEN_HEIGHT, false)
  111.     self.caption = "Tasty Sandwich Software"
  112.    
  113.     # Some values for keeping track of FPS
  114.     @prev_frames = @frames = 0
  115.     @yester_second = Time.now.sec
  116.    
  117.     # Font for easily drawing text
  118.     @font = Gosu::Font.new(self, "monospace", 40)
  119.    
  120.     setup_scene
  121.   end
  122.  
  123.  
  124.   # Instead of filling up the initialize with method with all of this, I like to
  125.   # pull it out into a method by itself.  For one, it makes the initialize much
  126.   # easier to read.  Also, I am working on an external level format for a game
  127.   # and have found that a function that does the set up from a data file is much
  128.   # easier to write and maintain that filling everything into a single method.
  129.   def setup_scene
  130.     # We're going to be using a single image for all of the boxes.
  131.     # It's a 16x16 png of a white box with a black border.
  132.     @box_image = Gosu::Image.new(self, "crumb.png", true)
  133.    
  134.     # Collection of Boxes
  135.     @boxes = Array.new
  136.    
  137.     # Create our Space with a bit of resistance and simple gravity.
  138.     @space = CP::Space.new
  139.     @space.damping = 0.8
  140.     @space.gravity = CP::Vec2.new(0.0, 25.0)
  141.    
  142.     # A static body with an infinite mass and inertia is handy for pinning
  143.     # objects to the window.  We are not going to allow this body to move, so
  144.     # if we need something to stay put, we can attach it to this.  Also, we can
  145.     # attach some shapes that can give us a non-mobile object to collide with.
  146.     static_body = CP::Body.new((1.0 / 0.0), (1.0 / 0.0));
  147.    
  148.     # Here we are going to use line segments to create a "cup" of sorts to hold
  149.     # all of the little boxes we are going to drop down from the sky.  When
  150.     # creating line segments, the vectors that are passed to the creation method
  151.     # are really just points on the screen.  So first we create a line left to
  152.     # right across the window near the middle.  Then on the left we create a
  153.     # line that slopes into the center just slightly.  The we create a similar
  154.     # one on the right.  All of these shapes are attached to the static_body.
  155.     @space.add_shape CP::Shape::Segment.new(static_body, CP::Vec2.new(0.0,   350.0), CP::Vec2.new(640.0, 350.0), 1.0)
  156.     @space.add_shape CP::Shape::Segment.new(static_body, CP::Vec2.new(25.0,  0.0),   CP::Vec2.new(30.0,  350.0), 1.0)
  157.     @space.add_shape CP::Shape::Segment.new(static_body, CP::Vec2.new(615.0, 0.0),   CP::Vec2.new(610.0, 350.0), 1.0)
  158.    
  159.    
  160.     # First Layer of Bread
  161.     color = Gosu::Color.new(0xffffffff)
  162.     (0..155).each do |i|
  163.       box = GosuMunk_Box.new(@space, @box_image, rand(20) + 300, first_bread_height, color)
  164.       @boxes.push box
  165.     end
  166.    
  167.    
  168.     # A few slices of "Meat" substance
  169.     color = Gosu::Color.new(0x33ffffff)
  170.     min, max = 10.0, 25.0
  171.     (0..2).each do |i|
  172.      
  173.       # Create the starter box
  174.       last_box = GosuMunk_Box.new(@space, @box_image, 40 + rand(20), meat_height(i), color)
  175.       @boxes.push last_box
  176.      
  177.       # String the boxes together with Slide Joints
  178.       (0..45).each do |j|
  179.         box = GosuMunk_Box.new(@space, @box_image, 40 + (j * 12) + rand(20), meat_height(i), color)
  180.         @boxes.push box
  181.         joint = CP::Joint::Slide.new(last_box.body, box.body, CP::Vec2.new(0, 0), CP::Vec2.new(0, 0), min, max)
  182.         @space.add_joint(joint)
  183.         last_box = box
  184.       end
  185.     end
  186.    
  187.    
  188.     # Double Cheese for Me, Please!
  189.     color = Gosu::Color.new(0xddff3311)
  190.     (0..1).each do |i|
  191.      
  192.       # Create the starter box
  193.       last_box = GosuMunk_Box.new(@space, @box_image, 45 + (i * 50), cheese_height(i), color)
  194.       @boxes.push last_box
  195.      
  196.       # String the boxes together with Pin Joints
  197.       (0..50).each do |j|
  198.         box = GosuMunk_Box.new(@space, @box_image, 55.0 + (j * 10.0) + (i * 50), cheese_height(i), color)
  199.         @boxes.push box
  200.         joint = CP::Joint::Pin.new(last_box.body, box.body, CP::Vec2.new(0, 0), CP::Vec2.new(0, 0))
  201.         @space.add_joint(joint)
  202.         last_box = box
  203.       end
  204.     end
  205.    
  206.    
  207.     # Lettuce... Or big green booger worm, whatever...
  208.     color = Gosu::Color.new(0xff00ff00)
  209.    
  210.     # Create the starter box
  211.     last_box = GosuMunk_Box.new(@space, @box_image, 60.0, lettuce_height, color)
  212.     @boxes.push last_box
  213.    
  214.     # String the boxes together with Groove Joints
  215.     (0..60).each do |i|
  216.       box = GosuMunk_Box.new(@space, @box_image, 68.0 + (i * 8.0), lettuce_height, color)
  217.       @boxes.push box
  218.       joint = CP::Joint::Groove.new(last_box.body, box.body, CP::Vec2.new(-8, 1), CP::Vec2.new(8, -1), CP::Vec2.new(-8, 0))
  219.       @space.add_joint(joint)
  220.       last_box = box
  221.     end
  222.    
  223.    
  224.     # Top Layer of Bread
  225.     color = Gosu::Color.new(0xffffffff)
  226.     (0..155).each do |i|
  227.       box = GosuMunk_Box.new(@space, @box_image, rand(580) + 40, top_bread_height, color)
  228.       @boxes.push box
  229.     end
  230.   end
  231.  
  232.  
  233.   # By default this is called 60 times per second.  This is kind of broken into
  234.   # two sections, the Gosu section and the Chipmunk section.  We need to handle
  235.   # our physics faster than our game logic.  In fact, this scene has no game
  236.   # logic, but you should fiddle with a SUBSTEPS = 1 and understand why.
  237.   def update
  238.     # Perform the physics steps first
  239.     SUBSTEPS.times do
  240.       ## Do Physics tasks here.  Collisions, forces from the environment, etc.
  241.      
  242.       # This tells chipmunk to calculate everything in the space for DTIME time.
  243.       @space.step(DTIME)
  244.     end
  245.    
  246.     ## Do Game Logic tasks here. See the Gosu Wiki (RubyChipmunkIntegration) for
  247.     ## more details on what you might want to put here instead of above.
  248.   end
  249.  
  250.  
  251.   # Very Simple Draw Routine
  252.   def draw
  253.     @boxes.each { |box| box.draw }
  254.    
  255.     # Write our Little Strings and the FPS indicator.
  256.     @font.draw("Tasty Sandwich Software", 50, 350, 2, 1.0, 1.0, 0xffffffff)
  257.     @font.draw("Ruby + Gosu + Chipmunk = Awesome", 50, 400, 2, 0.75, 0.75, 0xff00ffff)
  258.     @font.draw("fps: #{@prev_frames}", 0, 460, 2, 0.5, 0.5, 0xffff00ff)
  259.    
  260.     # Update the FPS counters
  261.     if Time.now.sec != @yester_second
  262.       @prev_frames = @frames
  263.       @frames = 0
  264.       @yester_second = Time.new.sec
  265.     else
  266.       @frames += 1
  267.     end
  268.   end
  269.  
  270.  
  271.   # Give us a way out of this demo
  272.   def button_down(id)
  273.     if id == Gosu::Button::KbEscape
  274.       close
  275.     end
  276.   end
  277. end
  278.  
  279.  
  280. # Ztart zee zimulation!
  281. window = GameWindow.new
  282. window.show