[[目次へもどる>PuyoPuyo]]
* ピボットブロックの細かいとこ [#vabf0ef7]
#contents

** 上るような回転 [#e4079def]
ピボットブロックを回転させたとき,上に乗るような回転をするときがある.
 b
 p B
   B
   B
 ---
pはピボット,bはピボット周囲のブロック,Bはステーブルブロックである.
上記の様な場合,現在の使用では右に回転させると以下のようになる.
 p b B
     B
     B
 -----
ピボットの右側にブロックがあるのでシフトが発生し,ピボットが左にずれて回転が行われる.

しかし,実際のぷよぷよでは以下のようになる.
 p b
   B
   B
   B
 ---
この仕様を実現するにはどうすればよいか?

答えは''ピボットの斜め上も空いているときはシフト無しに回転OKとする''である.
どういうことかというと,上の例では,ピボットの右にはブロックがあるが,右上にはブロックがない.この場合もシフト0のまま回転を許可してしまう.
すると,ピボットの上のブロックが回転しようとしてステーブルブロックにめり込むが,
落下処理時のめり込み処理によってコントロールブロック全体が上に移動する.
結果として望みの状態となるわけである.
#sh(ruby){{
class PivotControlBlock < ControlBlock
  def can_rotate?(ir, table, row_s)
    return false if ir == 0 # no rotate
    r = @pivot.row; l = @pivot.line
    max_cond = r < row_s - 1
    min_cond = r > 0
    @belongs.each.with_index do |block,i|
      next if !block || i % 2 != 0
      row_dir = ir * (1 - i)
      if (row_dir > 0 ? max_cond : min_cond) && (!table[r+row_dir][l] || !table[r+row_dir][l+1]) # OK
        return {:dir => ir, :shift => 0}
      elsif (row_dir > 0 ? min_cond : max_cond) && !table[r-row_dir][l] # OK: shift
        return {:dir => ir, :shift => -row_dir}
      end
      # NG
      return false
    end
    return {:dir => ir, :shift => 0}
  end
end
}}
マスが空いているかの条件に一つ上のマスもチェック対象になっている.

*** 実行 [#e10d6a6c]
おお上った上った.
#media(PuyoPuyoChap18/PuyoPuyoChap18.flv);

** 狭いとこの回転 [#q6031dc0]
両側がステーブルブロックで挟まれた状態では,現状の仕様では回転できない.
しかしぷよぷよでは回転ボタンを二回押す度に180度ブロックが回転する.
 B b B          B b B          B   B
 B p B          B p B          B p B
 B   B =input=> B   B =input=> B b B
 B   B          B   B          B   B
 -----          -----          -----
これを満たすために次の機能を実装した.
- 回転できないと判断された時に回転ボタンが押された回数をカウントする@turn_counter
- ブロックを180度回転させるrotate_turnメソッド
- @turn_counterは1で初期化される
- 回転できないと判断された時に@turn_counterが0より大きければ1減らす
- 0ならばターンを表す特殊な戻り値をセットし@turn_counterを初期化

#sh(ruby){{
class PivotControlBlock < ControlBlock
  def clear_turn_counter
    @turn_counter = 1
  end
  def can_rotate?(ir, table, row_s)
    return false if ir == 0 # no rotate
    r = @pivot.row; l = @pivot.line
    max_cond = r < row_s - 1
    min_cond = r > 0
    @belongs.each.with_index do |block,i|
      next if !block || i % 2 != 0
      row_dir = ir * (1 - i)
      if (row_dir > 0 ? max_cond : min_cond) && (!table[r+row_dir][l] || !table[r+row_dir][l+1]) # OK
        return {:dir => ir, :shift => 0}
      elsif (row_dir > 0 ? min_cond : max_cond) && !table[r-row_dir][l] # OK: shift
        return {:dir => ir, :shift => -row_dir}
      end
      # turn
      if @turn_counter == 0
        clear_turn_counter
        return {:dir => ir, :shift => 0, :turn => true}
      end
      @turn_counter -= 1
      # NG
      return false
    end
    return {:dir => ir, :shift => 0}
  end
  def rotate_turn(dir, time)
    @belongs.rotate!(2)
    @belongs.each.with_index do |block, i|
      next unless block
      # set animation
      to = 90 * (1 - i)
      block.set_rotate(to + 180 * dir, to, time, 0, 0)
    end
  end
  def rotate(rotate, time)
    if rotate[:turn] # turn
      rotate_turn(rotate[:dir], time)
      return
    end
    (略:以下以前の定義と同様)
  end
end
}}
can_rotate?メソッドではターンを行う時のみハッシュに:turnというキーを追加している.
そして,rotateメソッドでもし:turnが有効ならrotate_turnをセットしている.

rotate_turnメソッドはまず通常のターンと同様に配列を循環させて配列上での位置を更新している.
そして次に回転アニメーションをセットしているが,右回転の時と左回転の時で結果として同じ位置にくるがアニメーションは異なることに注意.

*** 実行 [#ia2b62aa]
おお回った回った.
#media(PuyoPuyoChap18/PuyoPuyoChap18_2.flv);


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