miyako/sample/space/component.rb
module Miyako
class Unit
extend Forwardable
def initialize(name, ow, oh, dp, layout_x = :center, layout_y = :middle, amount = 8)
@spr = Sprite.new(name)
@spr.ow = ow
@spr.oh = oh
@spr.dp = dp
@spr.set_layout(layout_x, layout_y)
@move_amt = amount
@live = true
end
def dead
@live = false
hide
end
def live
@live = true
show
end
def live?
return @live
end
def update
end
def move(dx)
end
def_delegators(:@spr, :show, :hide)
end
class Shot < Unit
extend Forwardable
def initialize(fname, x = 0, y = 0, dir = -1)
super(fname, 4, 8, 80)
@spr.move_to(x, y - @spr.oh)
@dir = dir
@wait = 2
@cnt = @wait
@moving = false
@se = Audio::SE.new("shot.wav")
@se.setVolume(64)
end
def set_position(x, y)
@spr.move_to(x * @move_amt, y * @move_amt)
end
def post_shot(score)
stop
hide
return score
end
def update(enemys)
return 0 unless @moving
score = enemys.inject(0){|r, e| r += collision_check(e) }
return post_shot(score) unless score == 0
@cnt -= 1
return 0 unless @cnt == 0
return post_shot(0) if move(@move_amt * @dir) == -1
@cnt = @wait
return 0
end
def shot(x, y)
return if @moving
@spr.move_to(x, y - @spr.oh)
show
@moving = true
@cnt = @wait
@se.play
end
def stop?
return !(@moving)
end
def stop
@moving = false
end
def move(dy)
return @spr.round_y(@move_amt * dy, false)
end
def collision_check(enemy)
return 0 unless enemy.live?
if enemy.collision?(@spr)
enemy.dead
hide
@moving = false
return enemy.score
end
return 0
end
end
class MyShip < Unit
extend Forwardable
def initialize
super("myship.bmp", 32, 32, 100, :center, [:bottom, 32])
@shot_num = 4
@shots = Array.new(@shot_num){ Shot.new("shot.png", 0, @spr.y) }
end
def reset
@shots.each{|s| s.stop}
end
def update(score, enemys)
score += @shots.inject(0){|r, s| r += s.update(enemys)}
if Input.pushed_any?(:btn1)
sh = @shots.find{|s| s.stop? }
sh.shot(@spr.x + (@spr.ow >> 1), @spr.y) if sh
end
move(Input.triggerAmount[0])
return score
end
def move(dx)
@spr.round_x(@move_amt * dx)
end
def_delegators(:@spr, :x, :y, :ow, :oh)
end
class Enemy < Unit
extend Forwardable
attr_reader :score
def initialize(score = 100)
super("enemy.bmp", 32, 32, 150)
@pre_x = 0
@pre_y = 0
@score = score
@wait = 4
@cnt = @wait
@dx = 1
@se = Audio::SE.new("exp.wav")
@se.setVolume(64)
end
def set_position(x, y)
@spr.move_to(x * @move_amt, y * @move_amt)
@pre_x = x
@pre_y = y
end
def reset
@spr.move_to(@pre_x * @move_amt, @pre_y * @move_amt)
@dx = 1
self.show
end
def update
@cnt -= 1
return unless @cnt == 0
@dx, dy = move(@dx, 0)
@cnt = @wait
end
def move(dx, dy)
px, py = @spr.round(@move_amt * dx, @move_amt * dy)
return [px == 0 ? dx : -dx, py == 0 ? dy : -dy]
end
def collision?(spr)
return @spr.collision?(spr)
end
def show
super
@live = true
end
def hide
super
@live = false
end
def dead
super
@se.play
end
end
class ScoreBoard
extend Forwardable
def initialize
@fsize = 16
font = Font.sans_serif
font.size = @fsize
@spr = Sprite.new([(@fsize * 13) >> 1, font.line_skip])
@spr.font = font
@spr.font.setColor(Color::WHITE)
@spr.dp = 200
@spr.set_layout(:center, [:top, 16])
end
def update(score)
@spr.draw_rect(Rect.new(0,0,@spr.ow,@spr.oh), Color::BLACK, :fill)
@spr.draw_text(sprintf("SCORE %07d", score))
end
def_delegators(:@spr, :show, :hide)
end
class StageBoard
extend Forwardable
def initialize
@fsize = 16
font = Font.sans_serif
font.size = @fsize
@spr = Sprite.new([(@fsize * 9) >> 1, font.line_skip])
@spr.font = font
@spr.font.setColor(:green)
@spr.dp = 200
@spr.set_layout([:left, @fsize], [:top, @fsize])
@stage = 0
end
def update
@stage += 1
@spr.draw_rect(Rect.new(0,0,@spr.ow,@spr.oh), :black, :fill)
@spr.draw_text(sprintf("STAGE %03d", @stage))
end
def_delegators(:@spr, :show, :hide)
end
class MainComponent
include Singleton
def initialize
@space = Plane.new("space.bmp")
@space.dp = -100
@ship = MyShip.new
@score = 0
@score_board = ScoreBoard.new
@stage_board = StageBoard.new
@enemy_num = 20
@enemy_x = 4
@enemy_y = 8
@enemy_w = 5
@enemy_h = 4
@enemys = Array.new(@enemy_num){ Enemy.new }
@enemy_num.times{|e|
x = e % @enemy_w
y = e / @enemy_w
@enemys[e].set_position(@enemy_x + x * 8 , @enemy_y + y * 8)
}
end
def setup
@space.show
@score_board.show
@ship.show
@enemys.each{|e| e.live}
@stage_board.update
@stage_board.show
end
def reset
@ship.reset
@enemys.each{|e| e.reset}
end
def next_stage
reset
@stage_board.update
end
def update(now, prev, upper)
return "End" if Input.pushed_any?(:esc) or Input.quit?
@score = @ship.update(@score, @enemys)
return "Clear" if @enemys.inject(0){|r, i| r += i.live? ? 1 : 0 } == 0
# return "Clear" if @enemys.inject(0){|r, i| r += 1 if i.live } == 0 # error! why?
@enemys.each{|e| e.update}
@score_board.update(@score)
scroll
return now
end
def final
@enemys.each{|e| e.hide}
@ship.hide
@stage_board.hide
@score_board.hide
@space.hide
end
def scroll
@space.y = @space.y - 2
end
end
end