Commit 089580e2db13ea269da85db86055292bf550938d

Authored by Marius Hanne
1 parent f6382e233a
Exists in master

fix collision handling

Showing 7 changed files with 93 additions and 68 deletions Side-by-side Diff

... ... @@ -58,31 +58,23 @@
58 58 @opts = opts
59 59 @steps = 0
60 60 Music.autoload_dirs = [ File.join(data_dir, "sound") ]
  61 + @collisions = {ships: [], bullets: []}
61 62 end
62 63  
63 64 def setup_collisions
64   - @space.add_collision_func(:ship, :ship) do |ship1_shape, ship2_shape|
65   - ship1 = self.objects.find {|o| o.shape == ship1_shape }
66   - ship2 = self.objects.find {|o| o.shape == ship2_shape }
67   - next unless ship1 && ship2
68   - power1 = ship1.power
69   - ship1.take_damage(ship2.power / 10)
70   - ship2.take_damage(power1 / 10)
  65 +
  66 + @space.add_collision_handler(:ship, :ship) do |ship1_shape, ship2_shape|
  67 + @space.add_post_step_callback([ship1_shape, ship2_shape]) do |space, ships|
  68 + @collisions[:ships] << ships
  69 + end
71 70 end
72   - @space.add_collision_func(:ship, :bullet) do |ship_shape, bullet_shape|
73   - bullet = self.objects.find {|o| o.shape == bullet_shape }
74   - ship = self.objects.find {|o| o.shape == ship_shape }
75   - next unless bullet && ship
76   - next if bullet.options[:shooter] == ship
77   - next if bullet.options[:shooter].is_a?(Enemy) && ship.is_a?(Enemy)
78   - bullet.hit(ship)
79   - @space.remove_body(bullet_shape.body)
80   - @space.remove_shape(bullet_shape)
  71 +
  72 + @space.add_collision_handler(:ship, :bullet) do |ship, bullet|
  73 + @space.add_post_step_callback([ship, bullet]) do |space, objects|
  74 + @collisions[:bullets] << objects
  75 + end
81 76 end
82   - @space.add_collision_func(:bullet, :bullet, &nil)
83   - @space.add_collision_func(:explosion, :explosion, &nil)
84   - @space.add_collision_func(:explosion, :bullet, &nil)
85   - @space.add_collision_func(:explosion, :ship, &nil)
  77 +
86 78 end
87 79  
88 80 def start_game
... ... @@ -99,6 +91,52 @@
99 91 def run
100 92 start_game
101 93 loop do
  94 +
  95 + if @state != :pause
  96 + @player.body.v += CP::Vec2.new(*BMA180.read) if options[:bma180]
  97 +
  98 + @player.fire if @keys_pressed.include?(:space) && @state == :running
  99 + @space.add_post_step_callback(self) do |space, game|
  100 + Enemy.generate(game) if game.enemies.size < game.level
  101 + end
  102 + [:up, :down, :left, :right].each {|d|
  103 + @player.move(d) if @keys_pressed.include?(d) }
  104 +
  105 + draw
  106 +
  107 + # TODO
  108 + if @bg_pos.abs >= (@background.size[1] * 2 * @level) && @state == :running
  109 + @level += 1
  110 + flash_text(text: "Level #{@level}")
  111 + @bg_pos = @background.size[1] / 2
  112 + end
  113 + end
  114 +
  115 + @collisions[:ships].each do |ship1_shape, ship2_shape|
  116 + ship1 = @objects.find {|o| o.shape == ship1_shape }
  117 + ship2 = @objects.find {|o| o.shape == ship2_shape }
  118 + next unless ship1 && ship2
  119 + ship1.destroy if ship1.take_damage(ship2.power / 10)
  120 + ship2.destroy if ship2.take_damage(ship1.power / 10)
  121 + end
  122 +
  123 + @collisions[:bullets].each do |ship_shape, bullet_shape|
  124 + bullet = self.objects.find {|o| o.shape == bullet_shape }
  125 + ship = self.objects.find {|o| o.shape == ship_shape }
  126 + next unless bullet && ship
  127 + next if bullet.options[:shooter] == ship
  128 + next if bullet.options[:shooter].is_a?(Enemy) && ship.is_a?(Enemy)
  129 + if bullet.hit(ship)
  130 + ship.destroy
  131 + end
  132 + space.remove_body(bullet_shape.body)
  133 + space.remove_shape(bullet_shape)
  134 + end
  135 +
  136 + @collisions[:ships] = []
  137 +
  138 + @enemies.each(&:fire)
  139 +
102 140 passed = @clock.tick
103 141 if @state != :pause
104 142 @steps += passed
105 143  
106 144  
107 145  
... ... @@ -132,34 +170,16 @@
132 170 end
133 171 end
134 172  
135   - if @state == :lost && @keys_pressed.include?(:return)
  173 + if @state == :lost && (@keys_pressed & [:return, :r]).any?
136 174 start_game
137 175 end
138 176  
139   - if @state != :pause
140   - @player.body.v += CP::Vec2.new(*BMA180.read) if options[:bma180]
141   -
142   - @player.fire if @keys_pressed.include?(:space) && @state == :running
143   -
144   - Enemy.generate(self) if @enemies.size < @level
145   -
146   - [:up, :down, :left, :right].each {|d|
147   - @player.move(d) if @keys_pressed.include?(d) }
148 177  
149   - draw
150   -
151   - # TODO
152   - if @bg_pos.abs >= (@background.size[1] * 2 * @level) && @state == :running
153   - @level += 1
154   - flash_text(text: "Level #{@level}")
155   - @bg_pos = @background.size[1] / 2
156   - end
157   - end
158 178 end
159 179 end
160 180  
161 181 def draw
162   - @bg_pos -= 1
  182 + @bg_pos -= 10
163 183 @background.blit(@screen, [0, 0], [0, @bg_pos % @background.size[1] / 2, *@screen.size])
164 184  
165 185 @objects.each {|o| o.draw(@screen) }
... ... @@ -13,10 +13,10 @@
13 13  
14 14 def hit ship
15 15 p = pos; @options[:dir] == :up ? p[1] -= @shape.r : p[1] += @shape.r
16   - Explosion.new(@game, p: p).show
17   - ship.take_damage(options[:damage])
  16 + Explosion.new(@game, p: p, group: 3).show
18 17 @options[:shooter].hit(ship, options[:damage])
19 18 hide
  19 + ship.take_damage(options[:damage])
20 20 end
21 21 end
... ... @@ -27,14 +27,19 @@
27 27 @shape = CP::Shape::Circle.new(@body, @surface.size[0] / 2, CP::Vec2.new(0.0, 0.0))
28 28 @shape.collision_type = options[:ctype]
29 29  
  30 + @shape.layers = options[:layers] if options[:layers]
  31 + @shape.group = options[:group] if options[:group]
  32 +
30 33 @body.p = CP::Vec2.new(*options[:p])
31 34 @body.v = CP::Vec2.new(*options[:v])
32 35 @body.v_limit = options[:v_limit]
33 36 @body.a = options[:a]
34 37 @body.w = options[:w]
35 38  
36   - game.space.add_body(@body)
37   - game.space.add_shape(@shape)
  39 + game.space.add_post_step_callback([body, shape]) do |space, data|
  40 + space.add_body(data[0])
  41 + space.add_shape(data[1])
  42 + end
38 43  
39 44 @alive = true
40 45 end
1 1 class Enemy < Ship
2 2  
3 3 def initialize game, options
  4 + options[:layers] = 0x101 # 1, 3
4 5 super game, options
5 6  
6 7 @pos = [rand(game.screen.size[0] - size[0]), 0 - size[1]]
7 8 @body.p = CP::Vec2.new(*@pos)
8 9 @body.v += CP::Vec2.new(0.0, @params[:speed] / 10.0)
9   - @thread = Thread.start do
10   - loop do
11   - break unless @visible
12   - begin
13   - fire
14   - rescue
15   - p $!
16   - puts *$@
17   - end
18   - sleep rand(0.5)
19   - end
20   - end
21 10 end
22 11  
23 12 def draw screen
24 13  
... ... @@ -32,14 +21,12 @@
32 21 @game.player.score -= @power * @game.level
33 22 @game.flash_hud :red
34 23 @game.player.destroy if @game.player.score < 0
35   - @thread.kill
36 24 end
37 25  
38 26 def destroy
39 27 super
40 28 @game.player.score += @params[:power]
41 29 @game.flash_hud :green
42   - @thread.kill
43 30 end
44 31  
45 32 def self.generate game
... ... @@ -5,6 +5,7 @@
5 5 def initialize game, options
6 6 options[:type] = :fighter
7 7 options[:a] = 180.0
  8 + options[:layers] = 0x11 # 1, 2
8 9 super game, options
9 10 @score = 0
10 11 end
... ... @@ -38,16 +38,17 @@
38 38 end
39 39  
40 40 def fire
41   - @weapons.each(&:fire)
  41 + @weapons.each.with_index do |weapon, index|
  42 + pos = index - (@weapons.size / 2)
  43 + weapon.fire(pos * 20)
  44 + end
42 45 end
43 46  
  47 + # decrease power by +damage+, returns true if ship has been destroyed
44 48 def take_damage damage
45 49 @power -= damage
46   - if @power <= 0
47   - @power = 1
48   - destroy
49   - end
50 50 @debug = nil
  51 + @power < 0
51 52 end
52 53  
53 54 def size
... ... @@ -62,8 +63,10 @@
62 63 def destroy
63 64 return unless @alive
64 65 @game.enemies.delete(self)
65   - Explosion.new(@game, p: pos, image: "explosion.png",
66   - zoom: options[:zoom] * 1.25, volume: options[:zoom]) { hide }.show
  66 + @game.space.remove_body(@body)
  67 + @game.space.remove_shape(@shape)
  68 + Explosion.new(@game, @params[:explosion].merge(p: pos, layer: 0x1000, image: "explosion.png",
  69 + group: 3)) { hide }.show
67 70 @alive = false
68 71 end
69 72  
... ... @@ -11,19 +11,28 @@
11 11  
12 12 attr_reader :ship, :game, :type, :params
13 13 def initialize ship, type
14   - @ship, @game, @type, @params = ship, ship.game, type, WEAPONS[type]
  14 + @ship, @game, @type, @params = ship, ship.game, type, WEAPONS[type].dup
15 15 @lock = 0
16 16 @last_shot = 0
17 17 end
18 18  
19   - def fire
  19 + def fire offset = 0
20 20 if game.steps - @last_shot >= (1000 - params[:freq])
21 21 v = [0.0, 1.0]
22 22 a = 0 - ((@ship.a * Math::PI / 180 ) % 360)
23 23 cos, sin = Math.cos(a), Math.sin(a)
24 24 v = [v[0] * cos - v[1] * sin, v[0] * sin + v[1] * cos]
25   - opts = params[:image].merge(p: @ship.p, a: @ship.a + 180.0,
  25 + p = @ship.p
  26 + p[0] += offset
  27 + opts = params[:image].merge(p: p, a: @ship.a + 180.0,
26 28 v: v, shooter: @ship, damage: params[:damage], m: params[:damage])
  29 + if @ship.is_a?(Player)
  30 + opts[:layers] = 0x100 # 3
  31 + opts[:group] = 1
  32 + else # Enemy
  33 + opts[:layers] = 0x010 # 2
  34 + opts[:group] = 2
  35 + end
27 36 Bullet.new(game, opts).show
28 37 @last_shot = game.steps
29 38 end