Index: lang/ruby/starframe/samples/stg/main.rb
===================================================================
--- lang/ruby/starframe/samples/stg/main.rb (revision 27370)
+++ lang/ruby/starframe/samples/stg/main.rb (revision 27370)
@@ -0,0 +1,479 @@
+$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib"))
+
+require "starruby"
+require "starframe"
+
+# スプライト定義
+#   キャラクタ（自機および敵機）
+class Character < StarFrame::Sprite
+  include StarFrame::Sprite::Animatable
+  include StarFrame::Sprite::Collidable
+  
+  def init options
+    @scene            = options[:scene]
+    @shot_collection  = options[:shot_collection]
+    @smoke_collection = options[:smoke_collection]
+  end
+  
+  # 発射
+  # 指定された弾を追加
+  def shot shot_sprite
+    @shot_collection << shot_sprite
+  end
+  
+  # 撃墜
+  # 爆煙を追加して消滅
+  def crash
+    @smoke_collection << Smoke.new(@x, @y)
+    StarRuby::Audio.play_se @crash_se
+    @scene.score += @score
+    vanish
+  end
+end
+#     自機
+class Player < Character
+  @texture = StarRuby::Texture.load("player")
+  @center_x = 16
+  @center_y = 16+6
+  
+  attr_accessor :stelth_ready, :readying, :stelth
+  
+  animation :burner do
+    frames [0, 32], 2 do |c|
+      # このアニメーション方式は定型に？
+      self.src_y = c
+    end
+  end
+  animation :shot, :loop => false, :start => false do
+    frames 3, 2 do
+      shot Shot::Player.new(@x-8, @y-16)
+      shot Shot::Player.new(@x+8, @y-16)
+    end
+  end
+  animation :ready, :loop => false, :start => false do
+    frame do
+      @x = 160
+      @y = 520
+      @readying = true
+      @stelth = true
+      self.alpha = 0xFF
+      animation(:stelth).stop
+    end
+    frames 40.decelerate do |c|
+      @y = 520-c*2
+    end
+    frame do
+      @y = 440
+      @readying = false
+      self.stelth = @stelth_ready
+    end
+    wait 60
+    frame do
+      self.stelth = false
+    end
+  end
+  animation :stelth, :start => false do
+    frames [0x00, 0xFF] do |c|
+      self.alpha = c
+    end
+  end
+  
+  collision :damage, StarFrame::Sprite::Collision::Rect, -2, -2, 4, 4 do |other|
+    crash
+  end
+  collision :graze, StarFrame::Sprite::Collision::Rect, -8, -8, 16, 16 do |other|
+    @scene.score += 5
+    # カスリアニメーションを動的に生成できるようにしたい
+    @scene.foreground.render_rect other.x-3, other.y-3, 7, 7, StarRuby::Color.new(0xFF, 0xFF, 0xFF)
+    StarRuby::Audio.play_se "tm2_shoot000" if StarRuby::Audio.playing_se_count < 4
+  end
+  
+  def init *args
+    self.src_height = 32
+    @crash_se = "tm2r_bom34"
+    @score = -1000
+    super
+    ready false
+  end
+  
+  def update
+    if @x < 0
+      @x = 0
+    elsif @x > 320
+      @x = 320
+    end
+    if @y < 0
+      @y = 0
+    elsif @y > 480
+      @y = 480
+    end
+  end
+  
+  # 無敵モードON/OFF
+  def stelth= value
+    if value
+      animation(:stelth).restart
+    else
+      animation(:stelth).stop
+      self.alpha = 0xFF
+    end
+    @stelth = value
+  end
+  
+  # 発射
+  def fire
+    animation(:shot).start
+    StarRuby::Audio.play_se "tm2_gun001"
+  end
+  
+  # 準備
+  def ready stelth_ready = true
+    @stelth_ready = stelth_ready
+    animation(:ready).restart
+  end
+  
+  # 撃墜
+  def crash
+    super
+    ready
+  end
+end
+#     敵機
+class Enemy < Character
+  attr_accessor :score
+  
+  def init options
+    @target = options[:target]
+    @crash_se = "tm2_bom001"
+    
+    @shot_point_x =  0
+    @shot_point_y = 24
+    super
+  end
+  
+  def update
+    @to_target_angle = nil
+  end
+  
+  # 自機との角度
+  def to_target_angle
+    unless @to_player_angle
+      @to_target_angle = Math.atan((shot_y - @target.y).quo(shot_x - @target.x))
+      @to_target_angle += 180.degrees if @target.x < shot_x
+    end
+    @to_target_angle
+  end
+  def shot_x
+    @x + @shot_point_x
+  end
+  def shot_y
+    @y + @shot_point_y
+  end
+  
+  # 自機狙い弾
+  def fire_to_player speed = 3, add_angle = 0
+    enemy_shot = Shot::Enemy.new(shot_x, shot_y)
+    shot_angle = to_target_angle + add_angle
+    enemy_shot.movement_x = Math.cos(shot_angle) * speed
+    enemy_shot.movement_y = Math.sin(shot_angle) * speed
+    shot enemy_shot
+  end
+  
+  # 敵機名：ディスク
+  class Disc < Enemy
+    @texture = StarRuby::Texture.load("enemy")
+    @center_x = 16
+    @center_y = 16
+    
+    animation :burner do
+      frames [0, 32], 2 do |c|
+        self.src_y = c
+      end
+    end
+    animation :move, :loop => false do
+      frames 128.decelerate do |c|
+        @y = -32+c
+      end
+      frame do
+        @y = 96
+      end
+      
+      3.times do |i|
+        wait 18 unless i.zero?
+        frames 3, 4 do
+          fire
+        end
+      end
+      
+      parallel do
+        frame do
+          @score = 200
+        end
+        frames 32.accelerate do |c|
+          @y = 96-c*4
+        end
+        frames 32 do
+          @x += 1
+        end
+      end
+      frame do
+        vanish
+      end
+    end
+    
+    collision :body, StarFrame::Sprite::Collision::Rect, -16, -16, 32, 32 do |other|
+      # @scene.render_number @scene.foreground, @x-32, @y-32, score
+      crash
+    end
+    
+    def fire
+      # fire_to_player 3,  10.degrees
+      fire_to_player 3
+      # fire_to_player 3, -10.degrees
+    end
+    
+    def init *args
+      @score = 100
+      @shot_speed = 3
+      self.src_height = 32
+      self.tone_red  =  0x33
+      self.tone_blue = -0x33
+      super
+    end
+  end
+end
+
+class Smoke < StarFrame::Sprite
+  @texture = StarRuby::Texture.load("smoke")
+  @center_x = 16
+  @center_y = 16
+  
+  include StarFrame::Sprite::Animatable
+  animation :smoke do
+    frames Array.new(18){ |i| i*32 }, 2 do |c|
+      self.src_x = c
+    end
+    frame do
+      vanish
+    end
+  end
+  
+  include StarFrame::Sprite::Collidable
+  collision :attack, StarFrame::Sprite::Collision::Rect, -16, -16, 32, 32
+  
+  def init *args
+    self.src_width = 32
+  end
+end
+
+class Shot < StarFrame::Sprite
+  @texture = StarRuby::Texture.load("shot")
+  @center_x = 4
+  @center_y = 4
+  
+  include StarFrame::Sprite::Animatable
+  animation :rolling do
+    frames (0...8), 2 do |c|
+      self.src_x = c*8
+    end
+  end
+  
+  include StarFrame::Sprite::Collidable
+  collision :attack, StarFrame::Sprite::Collision::Rect, -4, -4, 8, 8 do |other|
+    vanish
+  end
+  collision :graze, StarFrame::Sprite::Collision::Rect, -4, -4, 8, 8
+  
+  def init *args
+    self.src_width = 8
+  end
+  
+  def update
+    if @x < -64 || @x > 384 ||
+       @y < -64 || @y > 544
+      vanish
+    end
+  end
+  
+  class Player < Shot
+    def init *args
+      self.movement_y = -4
+      super
+    end
+  end
+  
+  class Enemy < Shot
+    def init *args
+      self.tone_blue =  0x99
+      self.tone_red  = -0x66
+      super
+    end
+  end
+end
+
+# シーン定義
+class MainScene < StarFrame::Scene
+  attr_reader :foreground
+  
+  include StarFrame::Sprite::Animatable
+  animation :scroll do
+    frames (0...544) do |c|
+      @bg_scroll = c
+    end
+  end
+  animation :enemy do
+    frame 30 do
+      @enemies << Enemy::Disc.new(16+rand(320-32), -64, @enemy_options)
+    end
+  end
+  
+  def init *args
+    srand 0
+    
+    @shots       = StarFrame::Sprite::Collection.new
+    @smokes      = StarFrame::Sprite::Collection.new
+    @enemies     = StarFrame::Sprite::Collection.new
+    @enemy_shots = StarFrame::Sprite::Collection.new
+    
+    @player = Player.new(-64, -64,        :scene => self, :shot_collection => @shots,       :smoke_collection => @smokes)
+    @enemy_options = {:target => @player, :scene => self, :shot_collection => @enemy_shots, :smoke_collection => @smokes}
+    
+    @score = 0
+    @frame = 0
+    @pause = false
+    
+    @numbers = StarRuby::Texture.load("numbers")
+    @background = StarRuby::Texture.load("bg")
+    @foreground = StarRuby::Texture.new(@screen.width, @screen.height)
+    
+    @pause_screen = StarRuby::Texture.new(@screen.width, @screen.height)
+    @pause_screen.fill StarRuby::Color.new(0, 0, 0)
+    pause_logo = StarRuby::Texture.load("pause")
+    @pause_screen.render_texture pause_logo, (@screen.width-pause_logo.width)/2, (@screen.height-pause_logo.height)/2
+  end
+  
+  def update
+    # ゲーム終了・ポーズ・無敵モードON/OFF
+    exit_scene if StarRuby::Input.keys(:keyboard).include? :escape
+    input_special_keys = StarRuby::Input.keys(:gamepad, :duration => 1)
+    if input_special_keys.include? 9
+      @pause = !@pause
+      if @pause
+        animation(:scroll).stop
+        animation(:enemy).stop
+      else
+        animation(:scroll).start
+        animation(:enemy).start
+      end
+    end
+    @player.stelth = !@player.stelth if input_special_keys.include? 10
+    return if @pause
+    
+    @foreground.clear
+    
+    # 自弾と敵機（敵機に二つの自弾が当たってしまわないように）
+    @shots.collide @enemies, :attack, :body
+    # 爆煙と敵機
+    @smokes.collide @enemies, :attack, :body
+    # 自機と
+    unless @player.stelth
+      catch :vanished do
+        # 敵機
+        @enemies.each do |enemy|
+          throw :vanished if enemy.collide @player, :body, :damage
+        end
+        # 敵弾
+        @enemy_shots.each do |enemy_shot|
+          # 命中
+          throw :vanished if @player.collide enemy_shot, :damage, :attack
+          # カスり
+          @player.collide enemy_shot, :graze, :graze
+        end
+      end
+    end
+    
+    # 自機移動
+    movement_x = 0
+    movement_y = 0
+    angle = 0
+    scale_x = 1
+    unless @player.readying
+      input_move_keys = StarRuby::Input.keys(:gamepad)
+      # 高速／低速移動
+      if (input_move_keys & [5, 6, 7, 8]).empty?
+        speed = 3
+      else
+        speed = 1
+      end
+      # Up Down
+      if input_move_keys.include? :up
+        movement_y = -speed
+      elsif input_move_keys.include? :down
+        movement_y = speed
+      end
+      # Left Right
+      if input_move_keys.include? :left
+        movement_x = -speed
+        angle = -4.degrees
+        scale_x = 0.90
+      elsif input_move_keys.include? :right
+        movement_x = speed
+        angle = 4.degrees
+        scale_x = 0.90
+      end
+      # Shot::Player
+      input_shot_keys = StarRuby::Input.keys(:gamepad, :duration => 1, :delay => 8, :interval => 8)
+      @player.fire unless (input_shot_keys & [1, 2, 3, 4]).empty?
+    end
+    @player.movement_x = movement_x
+    @player.movement_y = movement_y
+    @player.angle = angle
+    @player.scale_x = scale_x
+    
+    # 各種更新
+    @player.call
+    @shots.call
+    @enemy_shots.call
+    @enemies.call
+    @smokes.call
+  end
+  
+  def render
+    @screen.render_texture @background, 0, @bg_scroll
+    @screen.render_texture @background, 0, @bg_scroll-544
+    
+    @enemies.render_to @screen
+    @player.render_to @screen
+    
+    @shots.render_to @screen
+    @enemy_shots.render_to @screen
+    @smokes.render_to @screen
+    
+    render_number @screen, 96, 8, @score
+    render_number @screen, 304, 8, @game.real_fps.to_i
+    
+    @screen.render_texture @foreground, 0, 0
+    
+    @screen.render_texture @pause_screen, 0, 0, :alpha => 0x7F if @pause
+  end
+  
+  def render_number screen, x, y, number
+    digit = 0
+    loop do
+      score_digit = number%10
+      screen.render_texture @numbers, x-(8*digit), y, :src_x => (8*score_digit), :src_width => 8
+      digit += 1
+      number /= 10
+      break if number == 0
+    end
+  end
+  
+  attr_accessor :score
+  def score= score
+    @score = score
+    @score = 0 if @score < 0
+  end
+end
+
+StarFrame.start :width => 320, :height => 480,
+                :title => "STG", :fps => 60, :fullscreen => false
Index: lang/ruby/starframe/samples/stg/readme.txt
===================================================================
--- lang/ruby/starframe/samples/stg/readme.txt (revision 27370)
+++ lang/ruby/starframe/samples/stg/readme.txt (revision 27370)
@@ -0,0 +1,32 @@
+���T�v
+  �G�@����ƃX�R�A�������܂��B
+    �e�𔭎˂����̃X�R�A�͒Ⴍ�A���˂����X�R�A�������Ȃ����B
+  �e�ɓ������X�R�A���������񌸂����B
+  ���Ԑ�����@�ݒ��ǂ͓�ɂȂ��̂œK���ɏI�����Ă��������B
+
+�������@
+  �Q�[���p�b�h 1P
+    ���[ - �ړ�
+    1�`4�{�^�� - �V���b�g
+    5�`8�{�^�� - �ᑬ�ړ�
+    9�{�^�� - �|�[�Y
+  �L�[�{�[�h
+    esc - �Q�[���I��
+
+����
+  �ȉ��̕�����nimatable���W���[����p���Ă��܂��B
+  �E���@�A�G�@�A�e�A�����̃A�j���[�V����
+  �E���@�̏o������  �E���@�̖��G���Ԃ̊Ǘ�
+  �E���@�̒e���ˁi3�A�ˁj
+  �E�G�@�̓����o�����e���ˁ��ދ�����Łj
+  �E�����̏��
+  �E�w�i�̃X�N���[��
+
+���R�s�[���C�g
+���̃T���v���͈ȉ��̃t���[�f�ނ𗘗p���Ă��܂��B
+�E�摜
+    HamCorossam
+    http://homepage2.nifty.com/hamcorossam
+�E����
+    �U�E�}�b�`���C�J�@�Y
+    http://osabisi.sakura.ne.jp/m2/index.html
