着地アニメーション †次は着地アニメーションを考えてみよう. ぷよぷよフィーバー以降ではなんかブロックが着地したときに, 下にあるブロックとかも一緒にぐにゃって沈む. これをy座標を変化させることで簡易的に表現してみよう. landアニメーション †このアニメーションもステーブルブロックのアニメなので*1StableBlock?クラスにインスタンス変数@landを追加した. 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 地味に前章で定義したウェイトアニメーション全般の名前がmove_y_waitになっている,ということはウェイトの影響する部分がアニメーション全体ではなくmove_yのみになっているんだろう. そして実際そうなっている. set_land †引数にblock,depth,timeをとっている. depthはぐにゃってなる深さで,timeはアニメーション時間だろう. 一番怪しいのはblockである. こいつはこのメソッド内ではなにもされずに:fallblockの値としてセットされているので気にしないことにしよう. もう一つの重要な値shiftsについて見てみると,どうも構造が回転アニメーションのセットと似ている気がする.そしてその通りである. 回転アニメーションと同様に毎フレームのシフト量を配列にしている. ここで気をつけなければいけないのが,ステーブルブロックはフリーブロックと異なり, x,y方向共にrow,lineの値を基準にして毎フレーム描画座標がセットされるので, y方向に関するshiftsでも毎フレームごとの移動量ではなくシフト量を計算する必要がある. set_dummy_flag †なんだこれ.set_land(block,0,2). 深さ0でアニメーション時間が2フレームのアニメをセットしている. まあ名前的にダミーの何か特別な状態を表すフラグとして着地アニメーションをセットしているんだろう. update_land †ここでいよいよさっきのblockを使っている. まず,着地アニメーションがセットされていなければもちろん何もしない. 次に,block.move_y?なら何もしない. なにやらブロックがy方向に動いてなければ着地アニメを更新するらしい. 着地アニメーションの更新は回転アニメーションと同様に先頭のシフト量を取り出して返すだけ. :fallblockの正体を確かめるにはフィールド側の着地アニメーションの処理を見る必要がありそうだ. フィールド側の処理 †control_block_land †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 †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!メソッドで列にスキマスイッチ*2の処理をさせている. 次の部分では処理を施した列に対してパーティションをかけている. Rubyのpartitionメソッドはある配列をブロックで与えられた条件を満たす部分と満たさない部分の二つの配列に分けるメソッドである. ここではblock.lineがテーブル上の行と同じ,かつブロックに着地アニメーションがセットされていないことが条件となっている. すなわち,落下しないブロックで,元コントロールブロックでないものがstableで,落下するブロックか元コントロールブロックがfallに入ることになる. さらに言うならばfallによってぐにゃってなる着地アニメーションが発生するのである. fallが空ならこれ以上やることはないので終了する. まず先にfallを処理している. こいつらはほとんど今までの落下処理を行っているにすぎない. ただし,新しく着地アニメーションがセットされている. とりあえずimpactが着地アニメの深さのマックスの値なんだろう. そして落下後の行に応じて実際にセットする深さを変えている. これはすなわち,一番下あたりの行の場合を考慮している. たとえば一番下に落下した場合,それ以上沈みようが無いので深さは0だし,一つ上の行でも底がすぐ下にあるのでそれほど沈まないだろう. そしてset_landの最初の引数に自分自身を指定している. うん,これで謎が解けたはずだ. 着地アニメーションでは:fallblockのy方向の移動がセットされている限り着地アニメーションは進行しない. つまり,ブロックの落下が終わったら着地アニメーションを進めるのだ.よく考えたら当たり前. ちなみに元コントロールブロックにセットされたダミーフラグはここで新しい着地アニメーションがセットされるため,ダミーフラグのせいで変な挙動をすることはない*3*4. fallが終わったらstableの処理. こいつらはfallによって衝撃をあたえられて沈む. なので直に衝撃を与えてくるブロック,すなわちfallのうち一番下にあるブロックをfallen_blockとしておく. そして衝撃は上から順番に伝搬してくるので,stableは逆順*5に見ていくのが理にかなっている. まず,fallの時と同じように行の位置によっては深さを調節しなければならないのでそれを考慮する. そして着地アニメーションをセットするのだが,今回は落下してくるブロックの落下が終わったら着地アニメーションを実行したいので,set_landの最初の引数はfallen_blockを指定する. 最後にimpactを半分にしているが,これは衝撃は下に行くにつれて弱くなっていくためである. 実行 †ずいぶんリッチな感じになって来た. Loading the player ...
調子のって本気で連鎖くんでみたらブランクを感じざるを得ない結果に・・・ |