Guest User

current progress

a guest
Jul 30th, 2013
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 8.54 KB | None | 0 0
  1. require "gosu"
  2. require "singleton"
  3.  
  4. SCREEN_WIDTH    = 640
  5. SCREEN_HEIGHT   = 480
  6. MAP_WIDTH       = 640
  7. MAP_HEIGHT      = 3838
  8. SHIP_SPEED      = 12
  9.  
  10. # TODO:
  11. #   Use TexPlay to fix performance issues regarding printing the map
  12. #       This might be solved using the to_blob method on a loaded image of the map
  13. #       And then splicing that image every time we need to draw it
  14. #       Instead of reloading the same image every game tick, which causes lagg
  15. #   Handle Game-Over and Game-Win scenarios
  16. #   Fix bullet movement function so it can damage the player
  17. #   Create additional enemies and add them to the fated enemy queue
  18. #   Find a way to distribute this game without the host computer having Ruby
  19.  
  20. class Box
  21.  
  22.     attr_accessor :x, :y
  23.     attr_reader :w, :h
  24.     def initialize(x,y,w,h)
  25.         @x, @y, @w, @h = x, y, w, h
  26.     end
  27.  
  28.     # AABB collision detection with other boxes.
  29.     def collidesWith?(o)
  30.         overlap = lambda { |ap, ad, bp, bd|
  31.             ((ap + ad >= bp) and (bp >= ap)) or ((bp + bd >= ap) and (ap >= bp))
  32.         }
  33.         overlap.(x, w, o.x, o.w) and overlap.(y, h, o.y, o.h)
  34.     end
  35.  
  36.     # AABB swept collision detection with another box with velocities vx and vy
  37.     def inPathOf?(o, vx, vy)
  38.         # Collision detection for one-dimensional paths
  39.         return collidesWith?(Box.new(o.x, o.y, o.w, o.h + vy)) if vx.zero?
  40.         return collidesWith?(Box.new(o.x, o.y, o.w + vx, o.h)) if vy.zero?
  41.  
  42.         # Collision detection for beginning and end of path
  43.         return true if collidesWith?(o)
  44.         return true if collidesWith?(Box.new(o.x + vx, o.y + vy, o.w, o.h))
  45.  
  46.         # Distances to enter and exit a collision on an axis
  47.         # Entry = Distance between closest points, Exit = between farthest
  48.         xInvEn = ((vx > 0) ? (@x - (o.x + o.w)) : ((@x + @w) - o.x))
  49.         xInvEx = ((vx > 0) ? ((@x + @w) - o.x) : (@x - (o.x + o.w)))
  50.         yInvEn = ((vy > 0) ? (@y - (o.y + o.h)) : ((@y + @h) - o.y))
  51.         yInvEx = ((vy > 0) ? ((@y + @h) - o.y) : (@y - (o.y + o.h)))
  52.  
  53.         # Collision times relative to entry and exit
  54.         # 0 = collision at start of movement, 1 = collision at end, 0.5 = halfway, etc...
  55.         xEntry      = xInvEntry / vx.to_f
  56.         xExit       = xInvExit  / vx.to_f
  57.         yEntry      = yInvEntry / vx.to_f
  58.         yExit       = yInvExit  / vx.to_f
  59.  
  60.         entryTime   = [xEntry, yEntry].max
  61.         exitTime    = [xExit, yExit].min
  62.  
  63.         return !((entryTime > exitTime) or ((xEntry < 0.0) and (yEntry < 0.0)) or (xEntry > 1.0) or (yEntry > 1.0))
  64.     end
  65. end
  66.  
  67. class Bullet
  68.  
  69.     attr_reader :hitbox
  70.     def initialize(img, dbox, hbox, vx, vy, enemy=true)
  71.         @image      = img
  72.         @drawbox    = dbox
  73.         @hitbox     = hbox
  74.         @vx         = vx
  75.         @vy         = vy
  76.         @isEnemy    = enemy
  77.     end
  78.  
  79.     def destroy
  80.         $bulletQueue.remove(self)
  81.     end
  82.  
  83.     def draw
  84.         @image.draw(@drawbox.x, @drawbox.y, 0)
  85.     end
  86.  
  87.     def update
  88.         shouldDie   = false
  89.         moved       = true
  90.  
  91.         if @isEnemy then
  92.             puts "Enemy bullet fired" # Temporary
  93.         else
  94.             $liveEnemyQueue.each do |enemy|
  95.                 if enemy.hitbox.inPathOf?(@hitbox, @vx, @vy) then
  96.                     moved       = false
  97.                     shouldDie   = true
  98.                     enemy.takeHit
  99.                 end
  100.             end
  101.         end
  102.  
  103.         if moved then
  104.             @hitbox.x   += @vx
  105.             @hitbox.y   += @vy
  106.             @drawbox.x  += @vx
  107.             @drawbox.y  += @vy
  108.         end
  109.  
  110.         destroy if ((@drawbox.x < 0) or (@drawbox.y < 0) or (@drawbox.x > SCREEN_WIDTH) or (@drawbox.y > SCREEN_HEIGHT)) or shouldDie
  111.     end
  112. end
  113.  
  114. class Enemy
  115.  
  116.     attr_reader :hitbox
  117.     def initialize(img, dbox, hbox, hp)
  118.         @image      = img
  119.         @drawbox    = dbox
  120.         @hitbox     = hbox
  121.         @hitpoints  = hp
  122.     end
  123.  
  124.     def draw
  125.         @image.draw(@drawbox.x, @drawbox.y, 0)
  126.     end
  127.  
  128.     def move(distX, distY)
  129.         # Bounds checking
  130.         distX = -(@drawbox.x) if (@drawbox.x + distX < 0)
  131.         distX = (SCREEN_WIDTH - (@drawbox.x + @drawbox.w)) if (@drawbox.x + @drawbox.w + distX > SCREEN_WIDTH)
  132.         distY = -(@drawbox.y) if (@drawbox.y + distY < 0)
  133.         distY = (SCREEN_HEIGHT - (@drawbox.y + @drawbox.h)) if (@drawbox.y + @drawbox.h + distY > SCREEN_HEIGHT)
  134.  
  135.         # Moving the unit
  136.         @hitbox.x   += distX
  137.         @hitbox.y   += distY
  138.         @drawbox.x  += distX
  139.         @drawbox.y  += distY
  140.  
  141.         # Collision detection may happen here
  142.  
  143.     end
  144.  
  145.     def takeHit
  146.         @hitpoints -= 1
  147.         if @hitpoints <= 0 then
  148.             $deadEnemyQueue.add(self)
  149.             $liveEnemyQueue.remove(self)
  150.         end
  151.     end
  152. end
  153.  
  154. class Player
  155.  
  156.     include Singleton
  157.     attr_reader :hitbox
  158.     def initialize
  159.         @drawbox    = Box.new(272, 399, 96, 80)
  160.         @hitbox     = Box.new(304, 399, 30, 80)
  161.         @lives      = 3
  162.     end
  163.  
  164.     def draw
  165.         @image.draw(@drawbox.x, @drawbox.y, 0) unless @image.nil?
  166.     end
  167.  
  168.     def fireBullet
  169.  
  170.         $bulletQueue.add(Bullet.new(@pBullet, Box.new(@drawbox.x + 28, @drawbox.y + 12, @bWidth, @bHeight),
  171.             Box.new(@drawbox.x + 28, @drawbox.y + 12, @bWidth, @bHeight), 0, -20, false))
  172.         $bulletQueue.add(Bullet.new(@pBullet, Box.new(@drawbox.x + 58, @drawbox.y + 12, @bWidth, @bHeight),
  173.             Box.new(@drawbox.x + 58, @drawbox.y + 12, @bWidth, @bHeight), 0, -20, false))
  174.     end
  175.  
  176.     def giveWindow(window)
  177.         @window     = window
  178.         @image      = Gosu::Image.new(@window, "./Ship.png", false, 0, 0, 96, 80)
  179.         @bWidth     = 8
  180.         @bHeight    = 14
  181.         @pBullet    = Gosu::Image.new(@window, "./PlayerBullet.png", false, 0, 0, @bWidth, @bHeight)
  182.     end
  183.  
  184.     def takeHit
  185.         @lives -= 1
  186.         # game over effects here
  187.     end
  188.  
  189.     def update
  190.         distX = 0
  191.         distY = 0
  192.         moved = false
  193.  
  194.         $actionQueue.each do |action|
  195.             case action
  196.             when Gosu::KbLeft
  197.                 distX -= SHIP_SPEED
  198.                 moved = true
  199.             when Gosu::KbUp
  200.                 distY -= SHIP_SPEED
  201.                 moved = true
  202.             when Gosu::KbRight
  203.                 distX += SHIP_SPEED
  204.                 moved = true
  205.             when Gosu::KbDown
  206.                 distY += SHIP_SPEED
  207.                 moved = true
  208.             when Gosu::KbSpace
  209.                 fireBullet
  210.             end
  211.         end
  212.  
  213.         if moved
  214.             # Bounds checking
  215.             distX = -(@drawbox.x) if (@drawbox.x + distX < 0)
  216.             distX = (SCREEN_WIDTH   - (@drawbox.x + @drawbox.w)) if (@drawbox.x + @drawbox.w + distX > SCREEN_WIDTH)
  217.             distY = -(@drawbox.y) if (@drawbox.y + distY < 0)
  218.             distY = (SCREEN_HEIGHT  - (@drawbox.y + @drawbox.h)) if (@drawbox.y + @drawbox.h + distY > SCREEN_HEIGHT)
  219.         end
  220.  
  221.         @hitbox.x   += distX
  222.         @hitbox.y   += distY
  223.         @drawbox.x  += distX
  224.         @drawbox.y  += distY
  225.         # Handle any coollision detection here maybe?
  226.     end
  227.  
  228. end
  229.  
  230. class SetQueue
  231.  
  232.     def initialize(*elements)
  233.         @ary = elements.uniq
  234.     end
  235.  
  236.     def add(element)
  237.         @ary.push(element) unless @ary.include?(element)
  238.     end
  239.  
  240.     def remove(element)
  241.         @ary.delete(element)
  242.     end
  243.  
  244.     def top
  245.         @ary.first
  246.     end
  247.  
  248.     def each
  249.         if block_given? then
  250.             @ary.each { |o| yield o }
  251.         else
  252.             @ary.each
  253.         end
  254.     end
  255.  
  256.     def empty?
  257.         @ary.empty?
  258.     end
  259. end
  260.  
  261. class GameWindow < Gosu::Window
  262.     def initialize
  263.         super(640, 480, false)
  264.         self.caption = "I made a terrible shooter game!"
  265.         @player = Player.instance
  266.         @player.giveWindow(self)
  267.     end
  268.  
  269.     def update
  270.         [Gosu::KbLeft, Gosu::KbUp, Gosu::KbRight, Gosu::KbDown, Gosu::KbSpace].each do |btn|
  271.             if button_down?(btn)
  272.                 $actionQueue.add(btn)
  273.             else
  274.                 $actionQueue.remove(btn)
  275.             end
  276.         end
  277.         $gameTicks += 1
  278.         nextEnemy = $fatedEnemyQueue.top
  279.         unless nextEnemy.nil?
  280.             if nextEnemy[0] < $gameTicks then
  281.                 $liveEnemyQueue.add(nextEnemy[1])
  282.                 $fatedEnemyQueue.remove(nextEnemy)
  283.             end
  284.         end
  285.         @player.update
  286.         $bulletQueue.each       { |bullet| bullet.update    }
  287.         $liveEnemyQueue.each    { |enemy| enemy.update      }
  288.     end
  289.  
  290.     def draw
  291.         Gosu::Image.new(self, "./map.png", false, 0,
  292.             MAP_HEIGHT - SCREEN_HEIGHT - [$gameTicks, 3358].min,
  293.             SCREEN_WIDTH, SCREEN_HEIGHT).draw(0, 0, 0)
  294.         @player.draw
  295.         $bulletQueue.each       { |bullet| bullet.draw  }
  296.         $liveEnemyQueue.each    { |enemy| enemy.draw    }
  297.     end
  298. end
  299.  
  300. # Most global variables will be here
  301. $gameTicks      = 0
  302. $actionQueue    = SetQueue.new
  303. $bulletQueue    = SetQueue.new
  304. $liveEnemyQueue = SetQueue.new
  305. $deadEnemyQueue = SetQueue.new
  306.  
  307. # List of enemies
  308.  
  309. class TinyShip < Enemy
  310.     def initialize(window, x, y)
  311.         @window = window
  312.         super(Gosu::Image.new(window, "./TinyShip.png", false, 0, 0, 23, 27),
  313.             Box.new(x, y, 23, 27), Box.new(x + 3, y, 17, 24), 1)
  314.     end
  315.  
  316.     def fireBullet
  317.         # Target hitbox, center-x and center-y
  318.         thb = Player.instance.hitbox
  319.         tcx = ((thb.x + thb.w) / 2.0).to_i
  320.         tcy = ((thb.y + thb.h) / 2.0).to_i
  321.  
  322.         # center-x and center-y for self
  323.         cx  = ((@hitbox.x + @hitbox.w) / 2.0).to_i
  324.         cy  = ((@hitbox.y + @hitbox.h) / 2.0).to_i
  325.  
  326.         # Distance between central points
  327.         dst = Math.sqrt(((tcx - cx) ** 2) + ((tcy - cy) ** 2))
  328.  
  329.         vx  = (10 * (tcx - cx) / dst).to_i
  330.         vy  = (10 * (tcy - cy) / dst).to_i
  331.  
  332.         img = Gosu::Image.new(@window, "./TinyShipBullet.png", false, 0, 0, 9, 9)
  333.         box = Box.new(@drawbox.x + 5, @drawbox.y + 9, 9, 9)
  334.  
  335.         $bulletQueue.add(Bullet.new(img, box, box, vx, vy))
  336.     end
  337.  
  338.     def update
  339.         if ($gameTicks % 5).zero? then
  340.             fireBullet
  341.         end
  342.     end
  343. end
  344.  
  345. gamewindow = GameWindow.new
  346.  
  347. $fatedEnemyQueue = SetQueue.new(
  348.     [30, TinyShip.new(gamewindow, 250, 300)]
  349. )
  350.  
  351. gamewindow.show
Advertisement
Add Comment
Please, Sign In to add comment