[[目次へもどる>PuyoPuyo]]
* 着地アニメーション [#pcc48360]
#contents

次は着地アニメーションを考えてみよう.
ぷよぷよフィーバー以降ではなんかブロックが着地したときに,
下にあるブロックとかも一緒にぐにゃって沈む.

これをy座標を変化させることで簡易的に表現してみよう.

** landアニメーション [#ad457116]
このアニメーションもステーブルブロックのアニメなので((コントロールブロックはフリーブロックだが,着地した瞬間ステーブルブロックに変換される))StableBlockクラスにインスタンス変数@landを追加した.
#sh(ruby){{
class StableBlock < Block
  def init_animation
    @move_x = nil
    @move_y = nil
    @move_y_wait = nil
    @collapse = nil
    @land = nil
    @draw_pos = [0,0]
  end
  def set_land(block, depth, time)
    time_divide = Math::PI / time
    rad = 0
    shifts = []
    time.times do
      rad += time_divide
      shifts.push(-Math.sin(rad) * depth)
    end
    @land = {
      :fallblock => block,
      :shifts => shifts
    }
  end
  def set_dummy_flag block; set_land(block, 0, 2); end
  def update_land
    return nil unless @land
    return nil if @land[:fallblock].move_y?
    y_shift = @land[:shifts].shift
    @land = nil if @land[:shifts].empty?
    return y_shift
  end
  def update
    # update move_y_wait
    y_wait = update_move_y_wait
    # update move_x move_y
    x = update_move(@move_x){@move_x = nil}
    y = y_wait ? @draw_pos[1] : update_move(@move_y){@move_y = nil}
    @draw_pos[0] = x ? x : @row * @block_s
    @draw_pos[1] = y ? y : @line * @block_s
    # update land
    y_shift = update_land unless move_y?
    @draw_pos[1] += y_shift if y_shift
    # update collapse
    update_collapse
  end
end
}}
地味に[[前章>PuyoPuyoChap19]]で定義したウェイトアニメーション全般の名前がmove_y_waitになっている,ということはウェイトの影響する部分がアニメーション全体ではなくmove_yのみになっているんだろう.
そして実際そうなっている.

*** set_land [#p07aaa6f]
引数にblock,depth,timeをとっている.
depthはぐにゃってなる深さで,timeはアニメーション時間だろう.
一番怪しいのはblockである.
こいつはこのメソッド内ではなにもされずに:fallblockの値としてセットされているので気にしないことにしよう.

もう一つの重要な値shiftsについて見てみると,どうも構造が回転アニメーションのセットと似ている気がする.そしてその通りである.

回転アニメーションと同様に毎フレームのシフト量を配列にしている.
ここで気をつけなければいけないのが,ステーブルブロックはフリーブロックと異なり,
x,y方向共にrow,lineの値を基準にして毎フレーム描画座標がセットされるので,
y方向に関するshiftsでも毎フレームごとの移動量ではなくシフト量を計算する必要がある.

*** set_dummy_flag [#pdccfeae]
なんだこれ.set_land(block,0,2).
深さ0でアニメーション時間が2フレームのアニメをセットしている.

まあ名前的にダミーの何か特別な状態を表すフラグとして着地アニメーションをセットしているんだろう.

*** update_land [#q994f7b0]
ここでいよいよさっきのblockを使っている.
まず,着地アニメーションがセットされていなければもちろん何もしない.

次に,block.move_y?なら何もしない.
なにやらブロックがy方向に動いてなければ着地アニメを更新するらしい.

着地アニメーションの更新は回転アニメーションと同様に先頭のシフト量を取り出して返すだけ.

:fallblockの正体を確かめるにはフィールド側の着地アニメーションの処理を見る必要がありそうだ.

** フィールド側の処理 [#d63c8b83]
*** control_block_land [#i33626d0]
#sh(ruby){{
class Field
  def control_block_land
    stable_blocks = @cbm.ctrl_block.blocks.map{|block| block.convert_stable}
    @active_blocks.concat stable_blocks
    stable_blocks.each do |block|
      @table[block.row][block.line] = block
      block.set_dummy_flag(block) # set dummy fall flag
    end
    @cbm.ctrl_block.clear
  end
end
}}
はぁん,なるほど.コントロールブロックから変換されたステーブルブロックに対して,ダミーフラグをセットしている.
つまりこのダミーフラグはコントロールブロックから変換されたステーブルブロックを表すものらしい.

*** falldown_line [#u42daad6]
#sh(ruby){{
class Field
  def falldown_line(r)
    @fallen = @table[r].compact! ? true : false
    stable,fall = @table[r].partition.with_index{|block, l|
      block.line == l && !block.land? # dummy flag
    }
    return if fall.empty?
    # fall list animation
    impact = 16
    wait = 0
    base = stable.size
    fall.each.with_index do |block, i|
      l = base + i
      imp = l < 2 ? l * 6 : impact
      block.set_land(block, imp, 16)
      if block.line != l
        block.set_move_y_wait(wait) if wait != 0
        block.set_move_y(block.line * @block_s, l * @block_s, -6)
        block.line = l
        wait += 3
      end
    end
    # stable list animation
    fallen_block = fall.first
    stable.reverse.each.with_index do |block, i|
      break if impact <= 0
      l = base - (i + 1)
      impact = l * 6 if impact > l * 6
      block.set_land(fallen_block, impact, 16)
      impact /= 2
    end
  end
end
}}
このメソッドは大幅に変更されている.

まずは今までと同様にcompact!メソッドで列にスキマスイッチ((覚えているだろうか,スキマスイッチとはブロックの間にできたスキマのことである))の処理をさせている.

次の部分では処理を施した列に対してパーティションをかけている.
Rubyのpartitionメソッドはある配列をブロックで与えられた条件を満たす部分と満たさない部分の二つの配列に分けるメソッドである.
ここではblock.lineがテーブル上の行と同じ,かつブロックに着地アニメーションがセットされていないことが条件となっている.
すなわち,''落下しないブロックで,元コントロールブロックでないもの''がstableで,''落下するブロックか元コントロールブロック''がfallに入ることになる.

さらに言うならばfallによってぐにゃってなる着地アニメーションが発生するのである.
fallが空ならこれ以上やることはないので終了する.

まず先にfallを処理している.
こいつらはほとんど今までの落下処理を行っているにすぎない.
ただし,新しく着地アニメーションがセットされている.

とりあえずimpactが着地アニメの深さのマックスの値なんだろう.
そして落下後の行に応じて実際にセットする深さを変えている.
これはすなわち,一番下あたりの行の場合を考慮している.
たとえば一番下に落下した場合,それ以上沈みようが無いので深さは0だし,一つ上の行でも底がすぐ下にあるのでそれほど沈まないだろう.

そしてset_landの最初の引数に自分自身を指定している.
うん,これで謎が解けたはずだ.
着地アニメーションでは:fallblockのy方向の移動がセットされている限り着地アニメーションは進行しない.
つまり,ブロックの落下が終わったら着地アニメーションを進めるのだ.よく考えたら当たり前.

ちなみに元コントロールブロックにセットされたダミーフラグはここで新しい着地アニメーションがセットされるため,ダミーフラグのせいで変な挙動をすることはない((そもそも深さ0なので影響はないはずだが))((さらにダミーフラグのアニメーション時間が2フレームなのは,コントロールブロックが着地してから落下フェーズに移行しfalldownメソッドが呼ばれるまでにアニメーションが1フレーム進行してしまうためである)).

fallが終わったらstableの処理.
こいつらはfallによって衝撃をあたえられて沈む.
なので直に衝撃を与えてくるブロック,すなわちfallのうち一番下にあるブロックをfallen_blockとしておく.

そして衝撃は上から順番に伝搬してくるので,stableは逆順((上から順に))に見ていくのが理にかなっている.

まず,fallの時と同じように行の位置によっては深さを調節しなければならないのでそれを考慮する.
そして着地アニメーションをセットするのだが,今回は落下してくるブロックの落下が終わったら着地アニメーションを実行したいので,set_landの最初の引数はfallen_blockを指定する.

最後にimpactを半分にしているが,これは衝撃は下に行くにつれて弱くなっていくためである.

** 実行 [#u429a0c8]
ずいぶんリッチな感じになって来た.
#media(PuyoPuyoChap20/PuyoPuyoChap20.flv);
調子のって本気で連鎖くんでみたらブランクを感じざるを得ない結果に・・・


トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS