road_to_rubykaigi 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +11 -0
  3. data/CHANGELOG.md +10 -0
  4. data/README.md +15 -1
  5. data/Rakefile +1 -0
  6. data/lib/road_to_rubykaigi/audio/audio_engine.rb +70 -0
  7. data/lib/road_to_rubykaigi/audio/oscillator.rb +159 -0
  8. data/lib/road_to_rubykaigi/audio/sequencer.rb +263 -0
  9. data/lib/road_to_rubykaigi/audio/wav/attack_01.wav +0 -0
  10. data/lib/road_to_rubykaigi/audio/wav/attack_02.wav +0 -0
  11. data/lib/road_to_rubykaigi/audio/wav/attack_03.wav +0 -0
  12. data/lib/road_to_rubykaigi/audio/wav/attack_04.wav +0 -0
  13. data/lib/road_to_rubykaigi/audio/wav/attack_05.wav +0 -0
  14. data/lib/road_to_rubykaigi/audio/wav/bonus.wav +0 -0
  15. data/lib/road_to_rubykaigi/audio/wav/crouch.wav +0 -0
  16. data/lib/road_to_rubykaigi/audio/wav/defeat.wav +0 -0
  17. data/lib/road_to_rubykaigi/audio/wav/game_over.wav +0 -0
  18. data/lib/road_to_rubykaigi/audio/wav/jump.wav +0 -0
  19. data/lib/road_to_rubykaigi/audio/wav/laptop.wav +0 -0
  20. data/lib/road_to_rubykaigi/audio/wav/stun.wav +0 -0
  21. data/lib/road_to_rubykaigi/audio/wav/walk_01.wav +0 -0
  22. data/lib/road_to_rubykaigi/audio/wav/walk_02.wav +0 -0
  23. data/lib/road_to_rubykaigi/audio/wav_source.rb +55 -0
  24. data/lib/road_to_rubykaigi/event_dispatcher.rb +122 -0
  25. data/lib/road_to_rubykaigi/fireworks.rb +4 -4
  26. data/lib/road_to_rubykaigi/game.rb +49 -60
  27. data/lib/road_to_rubykaigi/graphics/demo-map.txt +30 -0
  28. data/lib/road_to_rubykaigi/graphics/demo-mask.txt +30 -0
  29. data/lib/road_to_rubykaigi/graphics/map.rb +6 -1
  30. data/lib/road_to_rubykaigi/graphics/mask.rb +7 -1
  31. data/lib/road_to_rubykaigi/graphics/player.rb +56 -65
  32. data/lib/road_to_rubykaigi/graphics/player.txt +26 -0
  33. data/lib/road_to_rubykaigi/manager/audio_manager.rb +82 -0
  34. data/lib/road_to_rubykaigi/manager/collision_manager.rb +23 -108
  35. data/lib/road_to_rubykaigi/manager/drawing_manager.rb +7 -6
  36. data/lib/road_to_rubykaigi/manager/game_manager.rb +50 -13
  37. data/lib/road_to_rubykaigi/manager/physics_engine.rb +21 -0
  38. data/lib/road_to_rubykaigi/manager/update_manager.rb +15 -12
  39. data/lib/road_to_rubykaigi/map.rb +1 -15
  40. data/lib/road_to_rubykaigi/score_board.rb +18 -1
  41. data/lib/road_to_rubykaigi/sprite/attack.rb +13 -6
  42. data/lib/road_to_rubykaigi/sprite/bonus.rb +16 -0
  43. data/lib/road_to_rubykaigi/sprite/deadline.rb +3 -3
  44. data/lib/road_to_rubykaigi/sprite/enemy.rb +12 -8
  45. data/lib/road_to_rubykaigi/sprite/player.rb +110 -29
  46. data/lib/road_to_rubykaigi/version.rb +1 -1
  47. data/lib/road_to_rubykaigi.rb +20 -4
  48. metadata +55 -3
@@ -0,0 +1,82 @@
1
+ require 'singleton'
2
+
3
+ module RoadToRubykaigi
4
+ module Manager
5
+ class AudioManager
6
+ include Singleton
7
+ SOUND_FILES = {
8
+ attack: %w[
9
+ lib/road_to_rubykaigi/audio/wav/attack_03.wav
10
+ lib/road_to_rubykaigi/audio/wav/attack_04.wav
11
+ lib/road_to_rubykaigi/audio/wav/attack_05.wav
12
+ ],
13
+ bonus: %w[lib/road_to_rubykaigi/audio/wav/bonus.wav],
14
+ crouch: %w[lib/road_to_rubykaigi/audio/wav/crouch.wav],
15
+ defeat: %w[lib/road_to_rubykaigi/audio/wav/defeat.wav],
16
+ game_over: %w[lib/road_to_rubykaigi/audio/wav/game_over.wav],
17
+ jump: %w[lib/road_to_rubykaigi/audio/wav/jump.wav],
18
+ laptop: %w[lib/road_to_rubykaigi/audio/wav/laptop.wav],
19
+ stun: %w[lib/road_to_rubykaigi/audio/wav/stun.wav],
20
+ walk: %w[
21
+ lib/road_to_rubykaigi/audio/wav/walk_01.wav
22
+ lib/road_to_rubykaigi/audio/wav/walk_02.wav
23
+ ],
24
+ }
25
+ WALK_SOUND_INTERVAL = 0.25
26
+
27
+ SOUND_FILES.keys.each do |action|
28
+ define_method(action) {
29
+ @audio_engine.add_source(@sources[action].sample)
30
+ }
31
+ end
32
+
33
+ def fanfare
34
+ @audio_engine.mute
35
+ @audio_engine.remove_source(@bass_sequencer)
36
+ @audio_engine.remove_source(@melody_sequencer)
37
+ @audio_engine.unmute
38
+ @audio_engine.add_source(@fanfare_sequencer)
39
+ end
40
+
41
+ def fanfare_finished?
42
+ @fanfare_sequencer.finished?
43
+ end
44
+
45
+ def game_over
46
+ @sources[:game_over].first.tap do |source|
47
+ @audio_engine.remove_source(@bass_sequencer)
48
+ @audio_engine.remove_source(@melody_sequencer)
49
+ @audio_engine.add_source(source)
50
+ until source.finished?
51
+ sleep 0.1
52
+ end
53
+ end
54
+ end
55
+
56
+ def walk
57
+ now = Time.now
58
+ if (now - @last_walk_time) >= WALK_SOUND_INTERVAL
59
+ @audio_engine.add_source(@sources[:walk][@walk_index])
60
+ @last_walk_time = now
61
+ @walk_index = (@walk_index + 1) % @sources[:walk].size
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def initialize
68
+ @bass_sequencer = Audio::BassSequencer.new
69
+ @melody_sequencer = Audio::MelodySequencer.new
70
+ @fanfare_sequencer = Audio::FanfareSequencer.new
71
+ @audio_engine = Audio::AudioEngine.new(@bass_sequencer, @melody_sequencer)
72
+ @sources = SOUND_FILES
73
+ dir = __dir__.sub("lib/road_to_rubykaigi/manager", "")
74
+ @sources.each do |action, file_paths|
75
+ @sources[action] = file_paths.map { |file_path| Audio::WavSource.new(dir + file_path) }
76
+ end
77
+ @walk_index = 0
78
+ @last_walk_time = Time.now - 1
79
+ end
80
+ end
81
+ end
82
+ end
@@ -2,122 +2,37 @@ module RoadToRubykaigi
2
2
  module Manager
3
3
  class CollisionManager
4
4
  def process
5
- event = [player_meet_enemy] # player must hit enemy before land
6
- player_fall
7
- player_land
8
-
9
- event += [
10
- player_meet_deadline,
11
- player_meet_bonus,
12
- attack_hit_bonus,
13
- attack_hit_enemy,
14
- ]
15
- if event.include?(:game_over)
16
- :game_over
17
- elsif event.include?(:bonus)
18
- :bonus
19
- end
20
- end
21
-
22
- private
23
-
24
- def initialize(background, foreground)
25
- @map = background
26
- @player, @deadline, @bonuses, @enemies, @attacks, @effects = foreground.layers
27
- end
28
-
29
- def player_fall
30
- bounding_box = @player.bounding_box
31
- foot_y = bounding_box[:y] + bounding_box[:height]
32
- center_x = bounding_box[:x] + bounding_box[:width] / 2.0
33
- if @map.passable_at?(center_x, foot_y + 1)
34
- @player.fall
35
- end
36
- end
37
-
38
- def player_land
39
- bounding_box = @player.bounding_box
40
- foot_y = bounding_box[:y] + bounding_box[:height]
41
- foot_y = foot_y.clamp(bounding_box[:height], RoadToRubykaigi::Sprite::Player::BASE_Y)
42
- (bounding_box[:x]...(bounding_box[:x] + bounding_box[:width])).each do |col|
43
- unless @map.passable_at?(col, foot_y)
44
- break @player.land(foot_y)
5
+ {
6
+ attack_bonus: [@attacks, @bonuses],
7
+ attack_enemy: [@attacks, @enemies],
8
+ player_bonus: [[@player], @bonuses],
9
+ player_deadline: [[@player], [@deadline]],
10
+ player_enemy: [[@player], @enemies],
11
+ }.each do |type, pair|
12
+ collided_pair = find_collided_pair(*pair)
13
+ unless collided_pair.empty?
14
+ EventDispatcher.publish(:collision, { type: type, pair: collided_pair })
45
15
  end
46
16
  end
47
17
  end
48
18
 
49
- # @returns [:game_over, Nil]
50
- def player_meet_deadline
51
- find_collision_item(@player, @deadline) && :game_over
52
- end
53
-
54
- def player_meet_bonus
55
- if (collided_item = find_collision_item(@player, @bonuses))
56
- @effects.heart(
57
- @player.x + @player.width - 1,
58
- @player.y,
59
- )
60
- @bonuses.delete(collided_item)
61
- :bonus
62
- end
63
- end
64
-
65
- # @returns [:bonus, false]
66
- def attack_hit_bonus
67
- collided = !@attacks.dup.select do |attack|
68
- if (collided_item = find_collision_item(attack, @bonuses))
69
- @effects.heart(
70
- @player.x + @player.width - 1,
71
- @player.y,
72
- )
73
- @bonuses.delete(collided_item)
74
- @attacks.delete(attack)
75
- end
76
- end.empty?
77
- collided && :bonus
78
- end
19
+ private
79
20
 
80
- # @returns [:bonus, Nil]
81
- def attack_hit_enemy
82
- collided = !@attacks.dup.select do |attack|
83
- if (collided_item = find_collision_item(attack, @enemies))
84
- @effects.note(
85
- @player.x + @player.width - 1,
86
- @player.y,
87
- )
88
- @enemies.delete(collided_item)
89
- @attacks.delete(attack)
90
- end
91
- end.empty?
92
- collided && :bonus
21
+ def initialize(attacks:, bonuses:, deadline:, enemies:, player:)
22
+ @attacks = attacks
23
+ @bonuses = bonuses
24
+ @deadline = deadline
25
+ @enemies = enemies
26
+ @player = player
93
27
  end
94
28
 
95
- # @returns [:bonus, Nil]
96
- def player_meet_enemy
97
- if (collided_item = find_collision_item(@player, @enemies))
98
- if @player.stompable?
99
- @effects.note(
100
- @player.x + @player.width - 1,
101
- @player.y,
102
- )
103
- @enemies.delete(collided_item)
104
- @player.vy = @player.class::JUMP_INITIAL_VELOCITY
105
- :bonus
106
- else
107
- @effects.lightning(
108
- @player.x + @player.width - 1,
109
- @player.y,
110
- )
111
- @enemies.delete(collided_item)
112
- @player.stun
29
+ def find_collided_pair(entities, others)
30
+ entities.map do |entity|
31
+ found = others.find do |other|
32
+ collided?(entity.bounding_box, other.bounding_box)
113
33
  end
114
- end
115
- end
116
-
117
- def find_collision_item(entity, others)
118
- others.find do |other|
119
- collided?(entity.bounding_box, other.bounding_box)
120
- end
34
+ break [entity, found] if found
35
+ end.compact
121
36
  end
122
37
 
123
38
  def collided?(box1, box2)
@@ -13,7 +13,7 @@ module RoadToRubykaigi
13
13
  ANSI.home
14
14
  ANSI.background_color
15
15
  ANSI.default_text_color
16
- print @score_board.render
16
+ print @game_manager.render_score_board
17
17
  @viewport_height.times do |row|
18
18
  @viewport_width.times do |col|
19
19
  unless buffer[row][col] == @preview_buffer[row][col]
@@ -26,14 +26,15 @@ module RoadToRubykaigi
26
26
 
27
27
  private
28
28
 
29
- def initialize(score_board, background, foreground, fireworks)
29
+ def initialize(map:, attacks:, bonuses:, deadline:, effects:, enemies:, player:, game_manager:)
30
30
  @viewport_width = Map::VIEWPORT_WIDTH
31
31
  @viewport_height = Map::VIEWPORT_HEIGHT
32
32
  @preview_buffer = Array.new(@viewport_height) { Array.new(@viewport_width) { "" } }
33
- @score_board = score_board
34
- @background = background
35
- @foreground = foreground
36
- @layers = [background, *foreground.layers, fireworks]
33
+ @game_manager = game_manager
34
+ @layers = [
35
+ # From bottom to top
36
+ map, player, deadline, bonuses, enemies, attacks, effects, game_manager.fireworks,
37
+ ]
37
38
  end
38
39
 
39
40
  def merge_buffer(buffer, layer, offset_x:)
@@ -1,7 +1,10 @@
1
1
  module RoadToRubykaigi
2
2
  module Manager
3
3
  class GameManager
4
+ UPDATE_RATE = 1.0 / 10
5
+ FRAME_RATE = 1.0 / 60
4
6
  GOAL_X = 650
7
+ DEMO_GOAL_X = 540
5
8
  STATE = {
6
9
  playing: 0,
7
10
  pause: 1,
@@ -11,29 +14,68 @@ module RoadToRubykaigi
11
14
  }
12
15
  attr_reader :fireworks
13
16
 
17
+ def self.goal_x
18
+ @goal_x ||= RoadToRubykaigi.demo? ? DEMO_GOAL_X : GOAL_X
19
+ end
20
+
21
+ def offset_x
22
+ (@player.x - Map::VIEWPORT_WIDTH / 2).clamp(0, @map.width - Map::VIEWPORT_WIDTH).to_i
23
+ end
24
+
14
25
  def update
15
26
  @deadline.activate(player_x: @player.x)
16
27
  @enemies.activate if player_moved?
17
- if @player.x >= GOAL_X && playing?
18
- game_clear
28
+ if @player.x >= GameManager.goal_x && playing?
29
+ EventDispatcher.publish(:ending)
19
30
  end
20
31
  end
21
32
 
22
- def finish
23
- @state = STATE[:finished]
33
+ def increment_score
34
+ @score_board.increment
35
+ end
36
+
37
+ def render_score_board
38
+ @score_board.render_score_board
39
+ end
40
+
41
+ def render_result
42
+ if game_over?
43
+ @score_board.render_game_over_result
44
+ else
45
+ @score_board.render_clear_result
46
+ end
24
47
  end
25
48
 
26
- def finished?
27
- @state == STATE[:finished]
49
+ def result?
50
+ @state == STATE[:game_over] || @state == STATE[:finished]
51
+ end
52
+
53
+ def game_over
54
+ @state = STATE[:game_over]
55
+ end
56
+
57
+ def game_over?
58
+ @state == STATE[:game_over]
59
+ end
60
+
61
+ def ending
62
+ @state = STATE[:ending]
63
+ @fireworks.shoot
64
+ end
65
+
66
+ def finish
67
+ @state = STATE[:finished]
28
68
  end
29
69
 
30
70
  private
31
71
 
32
- def initialize(player, deadline, enemies)
72
+ def initialize(map:, deadline:, enemies:, player:)
73
+ @map = map
33
74
  @player = player
34
75
  @deadline = deadline
35
76
  @enemies = enemies
36
- @fireworks = RoadToRubykaigi::Fireworks.new(self)
77
+ @score_board = ScoreBoard.new
78
+ @fireworks = RoadToRubykaigi::Fireworks.new
37
79
  @state = STATE[:playing]
38
80
  end
39
81
 
@@ -45,11 +87,6 @@ module RoadToRubykaigi
45
87
  def playing?
46
88
  @state == STATE[:playing]
47
89
  end
48
-
49
- def game_clear
50
- @state = STATE[:ending]
51
- @fireworks.shoot
52
- end
53
90
  end
54
91
  end
55
92
  end
@@ -0,0 +1,21 @@
1
+ module RoadToRubykaigi
2
+ module Manager
3
+ class PhysicsEngine
4
+ def simulate
5
+ @attacks.simulate_physics
6
+ @deadline.simulate_physics
7
+ @enemies.simulate_physics
8
+ @player.simulate_physics
9
+ end
10
+
11
+ private
12
+
13
+ def initialize(attacks:, deadline:, enemies:, player:)
14
+ @attacks = attacks
15
+ @deadline = deadline
16
+ @enemies = enemies
17
+ @player = player
18
+ end
19
+ end
20
+ end
21
+ end
@@ -2,25 +2,28 @@ module RoadToRubykaigi
2
2
  module Manager
3
3
  class UpdateManager
4
4
  def update(offset_x:)
5
- enemies = @entities[3]
6
- enemies.each do |enemy|
5
+ @enemies.each do |enemy|
7
6
  enemy.activate_with_offset(offset_x)
8
7
  end
9
- @entities.each(&:update)
10
- enforce_boundary(offset_x: offset_x)
8
+ @effects.update
9
+ @enemies.update
10
+ @player.update
11
+ @fireworks.update
12
+ @player.enforce_boundary(@map, offset_x: offset_x)
13
+ @attacks.enforce_boundary(@map, offset_x: offset_x)
14
+ @player.fall_if_ground_is_passable(@map)
15
+ @player.land_unless_ground_is_passable(@map)
11
16
  end
12
17
 
13
18
  private
14
19
 
15
- def initialize(map, foreground, fireworks)
20
+ def initialize(map:, attacks:, effects:, enemies:, player:, fireworks:)
16
21
  @map = map
17
- @entities = foreground.layers + [fireworks]
18
- end
19
-
20
- def enforce_boundary(offset_x:)
21
- @entities.each do |entity|
22
- entity.respond_to?(:enforce_boundary) && entity.enforce_boundary(@map, offset_x:)
23
- end
22
+ @attacks = attacks
23
+ @effects = effects
24
+ @enemies = enemies
25
+ @player = player
26
+ @fireworks = fireworks
24
27
  end
25
28
  end
26
29
  end
@@ -12,7 +12,7 @@ module RoadToRubykaigi
12
12
 
13
13
  def clamp_position(x:, y:, width:, height:, dx:, dy:)
14
14
  clamped_x = x.clamp(2, @width - width)
15
- clamped_y = y.clamp(2, @height - height)
15
+ clamped_y = y.clamp(2, @height - (Sprite::Player::BASE_HEIGHT - height))
16
16
  return [clamped_x, clamped_y] if box_passable?(clamped_x, clamped_y, width, height)
17
17
 
18
18
  attempt_count = 10
@@ -85,20 +85,6 @@ module RoadToRubykaigi
85
85
  end
86
86
  end
87
87
 
88
- class Layer
89
- attr_reader :layers
90
-
91
- def build_buffer(offset_x:)
92
- @layers.map { |layer| layer.build_buffer(offset_x: offset_x) }
93
- end
94
-
95
- private
96
-
97
- def initialize(player:, deadline:, bonuses:, enemies:, attacks:, effects:)
98
- @layers = [player, deadline, bonuses, enemies, attacks, effects]
99
- end
100
- end
101
-
102
88
  class Tile
103
89
  MASK_CHAR = "#"
104
90
 
@@ -4,14 +4,31 @@ module RoadToRubykaigi
4
4
  @score += 1
5
5
  end
6
6
 
7
- def render
7
+ def render_score_board
8
8
  "Score: #{@score}".ljust(10).rjust(Map::VIEWPORT_WIDTH)
9
9
  end
10
10
 
11
+ def render_clear_result
12
+ [ANSI::BLUE + "CLEAR!" + ANSI::DEFAULT_TEXT_COLOR, "Score: #{@score}", "Time: #{result_time} seconds"].map.with_index do |message, i|
13
+ ANSI::RESULT_DATA[i] + " #{message} "
14
+ end.join
15
+ end
16
+
17
+ def render_game_over_result
18
+ [ANSI::RED + "Game Over" + ANSI::DEFAULT_TEXT_COLOR, "Score: #{@score}", "Time: #{result_time} seconds"].map.with_index do |message, i|
19
+ ANSI::RESULT_DATA[i] + " #{message} "
20
+ end.join
21
+ end
22
+
11
23
  private
12
24
 
13
25
  def initialize
14
26
  @score = 0
27
+ @start_time = Time.now
28
+ end
29
+
30
+ def result_time
31
+ (Time.now - @start_time).round(2)
15
32
  end
16
33
  end
17
34
  end
@@ -4,13 +4,18 @@ module RoadToRubykaigi
4
4
  module Sprite
5
5
  class Attacks
6
6
  extend Forwardable
7
- def_delegators :@attacks, :each, :delete, :select
7
+ def_delegators :@attacks, :each, :map, :delete, :select
8
+ ATTACK_COUNT = 13
8
9
 
9
- def add(x, y)
10
- @attacks << Attack.new(x, y)
10
+ def remain_attack?
11
+ @attacks.size < ATTACK_COUNT
11
12
  end
12
13
 
13
- def update
14
+ def add(player)
15
+ @attacks << Attack.new(*player.attack_position, player.current_direction)
16
+ end
17
+
18
+ def simulate_physics
14
19
  @attacks.each(&:move)
15
20
  end
16
21
 
@@ -43,9 +48,10 @@ module RoadToRubykaigi
43
48
 
44
49
  class Attack < Sprite
45
50
  SYMBOL = ".˖"
51
+ SPEED = 3
46
52
 
47
53
  def move
48
- @x += 1
54
+ @x += @direction * SPEED
49
55
  end
50
56
 
51
57
  def characters
@@ -63,9 +69,10 @@ module RoadToRubykaigi
63
69
 
64
70
  private
65
71
 
66
- def initialize(x, y)
72
+ def initialize(x, y, direction)
67
73
  @x = x
68
74
  @y = y
75
+ @direction = direction
69
76
  end
70
77
  end
71
78
  end
@@ -98,6 +98,22 @@ module RoadToRubykaigi
98
98
  sake: "🍶",
99
99
  laptop: "💻",
100
100
  }
101
+ TYPE = {
102
+ ruby: :basic,
103
+ money: :basic,
104
+ coffee: :basic,
105
+ book: :basic,
106
+ sushi: :basic,
107
+ meat: :basic,
108
+ fish: :basic,
109
+ beer: :alcohol,
110
+ sake: :alcohol,
111
+ laptop: :laptop,
112
+ }
113
+
114
+ def type
115
+ TYPE[@character]
116
+ end
101
117
 
102
118
  def bounding_box
103
119
  { x: @x, y: @y, width: width, height: height }
@@ -10,7 +10,7 @@ module RoadToRubykaigi
10
10
  yield self || nil
11
11
  end
12
12
 
13
- def update
13
+ def simulate_physics
14
14
  return unless active?
15
15
  now = Time.now
16
16
  if (now - @last_update) > DEADLINE_SPEED
@@ -40,11 +40,11 @@ module RoadToRubykaigi
40
40
 
41
41
  private
42
42
 
43
- def initialize(map_height)
43
+ def initialize
44
44
  @x = 2
45
45
  @y = 1
46
46
  @width = 1
47
- @height = map_height
47
+ @height = Map::VIEWPORT_HEIGHT
48
48
  @last_update = Time.now
49
49
  @waiting = true
50
50
  end
@@ -43,10 +43,14 @@ module RoadToRubykaigi
43
43
  buffer
44
44
  end
45
45
 
46
- def update
46
+ def simulate_physics
47
47
  if activated?
48
- @enemies.each(&:update)
49
- else
48
+ @enemies.each(&:move)
49
+ end
50
+ end
51
+
52
+ def update
53
+ unless activated?
50
54
  @enemies.each(&:reset_last_update_time)
51
55
  end
52
56
  end
@@ -101,10 +105,10 @@ module RoadToRubykaigi
101
105
  super { [CHARACTER[@character]] }
102
106
  end
103
107
 
104
- def update
108
+ def move
105
109
  elapsed_time = Time.now - @last_update_time
106
110
  @last_update_time = Time.now
107
- @strategy.update(self, elapsed_time)
111
+ @strategy.move(self, elapsed_time)
108
112
  end
109
113
 
110
114
  def reset_last_update_time
@@ -153,7 +157,7 @@ module RoadToRubykaigi
153
157
  @speed = speed
154
158
  end
155
159
 
156
- def update(enemy, elapsed_time)
160
+ def move(enemy, elapsed_time)
157
161
  end
158
162
  end
159
163
 
@@ -161,7 +165,7 @@ module RoadToRubykaigi
161
165
  end
162
166
 
163
167
  class HorizontalPatrolStrategy < PatrolStrategy
164
- def update(enemy, elapsed_time)
168
+ def move(enemy, elapsed_time)
165
169
  enemy.x += @speed * elapsed_time * enemy.direction
166
170
  enemy.x = enemy.x.clamp(@left_bound, @right_bound)
167
171
  enemy.reverse_direction if enemy.x == @left_bound || enemy.x == @right_bound
@@ -169,7 +173,7 @@ module RoadToRubykaigi
169
173
  end
170
174
 
171
175
  class ScreenEntryPatrolStrategy < PatrolStrategy
172
- def update(enemy, elapsed_time)
176
+ def move(enemy, elapsed_time)
173
177
  return unless enemy.active?
174
178
  enemy.x += @speed * elapsed_time * enemy.direction
175
179
  end