PuyoPuyoChap16
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[目次へもどる>PuyoPuyo]]
* 回転させよう(Pivot編) [#p5c2cbb9]
#contents
いよいよ操作ブロック最後の山,回転を作ろう.
** 考察 [#h79a6a7c]
まずこれまで,操作ブロックは回転の際,軸となるブロックが...
これは正しい.そしてそれを元にしてコントロールブロックは...
しかし,ぷよぷよフィーバーを考えるとそうも行かない.
ぷよぷよフィーバーでは通常の2個ブロックの他に3個のブロッ...
このうち前述の仕様が通用するのは2個,3個ブロックのみであ...
そのため,例によってControlBlockクラスはコントロールブロ...
#sh(ruby){{
class ControlBlock
def initialize
clear
end
def clear
@postpone = 0
end
def set(postpone)
@postpone = postpone
end
def start(row, line)
end
def can_move_row?(imr, table, row_s)
return false if imr == 0 # no move
blocks.group_by{|block| block.line}.each do |line, bl...
if imr > 0 # move right
row = blks.max_by{|blk| blk.row}.row
return false if row >= row_s - 1
return false if table[row + 1][line]
else # move left
row = blks.min_by{|blk| blk.row}.row
return false if row <= 0
return false if table[row - 1][line]
end
end
return true
end
def move_row(imr, speed, block_s)
blocks.each do |block|
x1 = block.row * block_s
x2 = x1 + imr * block_s
block.set_move_x(x1, x2, imr * speed)
block.row += imr
end
end
def can_falldown?(table, block_s)
fall_ys = blocks.group_by{|block| block.row}.map{|row...
min_y = blks.min_by{|blk| blk.draw_pos[1]}.draw_pos...
min_y - table[row].size * block_s
}
# fall_ys == 0 : can not fall
# > 0 : fall y
# < 0 : dent y
return fall_ys.min
end
def falldown(y)
blocks.each do |block|
block.draw_pos[1] -= y
end
end
def fix_dent(y)
blocks.each do |block|
block.draw_pos[1] += y
end
end
def update_postpone
@postpone -= 1
end
def blocks
return []
end
def move_x?
blocks.each do |block|
return true if block.move_x?
end
return false
end
def move_y?
blocks.each do |block|
return true if block.move_y?
end
return false
end
def move?
blocks.each do |block|
return true if block.move?
end
return false
end
def postpone?; @postpone > 0; end
end
}}
#sh(ruby){{
=begin
blocks = [b0,b1,b2,b3]
b1 b2
(b0) b3
(block) is base block
=end
class CycleControlBlock < ControlBlock
def clear
super
@blocks = []
end
def set(*blocks, postpone)
super(postpone)
@blocks = Array.new(4){|i| blocks[i] }
end
def start(row, line)
@blocks.each.with_index do |block, i|
next unless block
case i
when 0; row_shift = 0; line_shift = 0
when 1; row_shift = 0; line_shift = 1
when 2; row_shift = 1; line_shift = 1
when 3; row_shift = 1; line_shift = 0
end
block.row = row + row_shift
block.line = line + line_shift
end
end
def blocks
return @blocks.compact
end
end
}}
#sh(ruby){{
=begin
pivot = p
belongs = [b0, b1, b2, b3]
b0
b3 p b1
b2
=end
class PivotControlBlock < ControlBlock
def clear
super
@pivot = nil
@belongs = []
end
def set(pivot, *belongs, postpone)
super(postpone)
@pivot = pivot
@belongs = Array.new(4){|i| belongs[i] }
end
def start(row, line)
if @pivot
@pivot.row = row
@pivot.line = line
end
@belongs.each.with_index do |block, i|
next unless block
case i
when 0; row_shift = 0 ; line_shift = -1
when 1; row_shift = 1 ; line_shift = 0
when 2; row_shift = 0 ; line_shift = 1
when 3; row_shift = -1; line_shift = 0
end
block.row = row + row_shift
block.line = line + line_shift
end
end
def blocks
return [@pivot, *@belongs].compact
end
end
}}
サイクルブロックは四つ組みのブロックで,フィーバーの四個...
ピボットブロックはピボットを中心として最大4個その周囲にブ...
さて,このように違うタイプのコントロールクラスが存在しう...
これらの切り替えなどをまとめて管理してくれる人を作ろう.
#sh(ruby){{
class ControlBlockManager
def initialize
init_ctrl_blocks
end
def init_ctrl_blocks
@ctrl_blocks = {
PivotControlBlock => PivotControlBlock.new,
CycleControlBlock => CycleControlBlock.new
}
@type = PivotControlBlock
end
def set_type(type)
ctrl_block.clear unless @type.nil?
@type = type
end
def ctrl_block
@ctrl_blocks[@type]
end
end
}}
ControlBlockManagerクラスはその名の通りコントロールブロッ...
作ったコントロールブロックの種類だけ@ctrl_blocksに追加し...
** フィールドの処理 [#a335630d]
フィールド側の回転処理はコントロールブロックの種類によら...
#sh(ruby){{
class Field
def update_control_block(imr,ir,iff)
update_control_block_rotate(ir)
update_control_block_move_x(imr)
active = update_control_block_move_y(iff)
end
def update_control_block_rotate(ir)
return true if @cbm.ctrl_block.rotate?
rotate = @cbm.ctrl_block.can_rotate?(ir, @table, @row...
return false unless rotate
@cbm.ctrl_block.rotate(rotate, 8)
end
end
}}
流れは落下や横移動と全く同じ.回転可能かチェックするcan_r...
** 回転アニメーション [#h8d0bd75]
回転のアニメーションもブロックの種類によらず同様である((...
先に定義しよう.
#sh(ruby){{
class FreeBlock < Block
def init_animation
@move_x = nil
@rotate_x = nil
@rotate_y = nil
end
def set_rotate_x(from, to, time, push_shift)
# x : rotate distance list based on to
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
shift = Math.cos(to_rad) * @block_s
ps_divide = (push_shift * @block_s).quo time
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
end
def set_rotate_y(from, to, time)
# y : rotate speed list base on from
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
rotate = []
rad = from_rad
time.times do
old = rad
rad += time_divide
rotate.push((Math.sin(rad) - Math.sin(old)) * @bloc...
end
return rotate
end
def set_rotate(from, to, time, shift)
@rotate_x = set_rotate_x(from, to, time, shift)
@rotate_y = set_rotate_y(from, to, time)
end
def update_rotate(rotate)
return nil unless rotate
x_shift = rotate.shift
yield if rotate.empty?
return x_shift
end
def update
# update move_x
x = update_move(@move_x){@move_x = nil}
@draw_pos[0] = x ? x : @row * @block_s
# update rotate
x_shift = update_rotate(@rotate_x){@rotate_x = nil}
y_shift = update_rotate(@rotate_y){@rotate_y = nil}
@draw_pos[0] += x_shift if x_shift
@draw_pos[1] += y_shift if y_shift
# update line
@line = (@draw_pos[1].round + @block_s / 2) / @block_s
end
end
}}
回転アニメのセットはset_rotateメソッドで行う.
引数には初期角度,移動角度,移動時間,シフトを指定する.
例えば
set_rotate(0,90,60,1)
とすれば0度から90度まで60フレームでアニメーションする.シ...
ところで,0度から90度ってなんだよって思うだろう.
今回の定義では回転の原点は現在のブロックの描画位置となっ...
また,0度とは数学の時間によく見たように単位円の右側を表す...
初期角度 < 移動角度 なら左回転,逆なら右回転のアニメーシ...
コントロールブロックについて,xは現在のrowの値から決定さ...
これはすなわち以下を意味する.
+ x座標については毎回rowの値から算出されるため,アニメー...
+ y座標については描画位置そのものを保持しているため,アニ...
よって,x方向とy方向で計算の仕方が多少異なる.
それらを考慮した上で回転のアニメーションの仕様を以下のよ...
- インスタンス変数@rotate_xと@rotate_yを用意
- それぞれnilならアニメーションしていない
- それ以外の場合は配列が格納されている
- @rotate_xは毎フレームごとのx座標のシフト量が入っている
- @rotate_yは毎フレームごとのy座標の移動量が入っている
- アニメーションは配列の先頭から一つ要素を取り出してx,y座...
- 配列が空になったらそれぞれにnilをセットしてアニメーショ...
*** set_rotate_x [#fc96db05]
まずはx方向の回転のセットを見てみよう.
引数のpush_shiftは後で説明するとして,
何をしているのかざっと見てみる.
#sh(ruby){{
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
}}
まず角度をラジアンに変換している.ここはいいだろう.
#sh(ruby){{
time_divide = (to_rad - from_rad) / time
}}
次に1フレームあたりどのくらい角度が変化するのかを計算.
#sh(ruby){{
shift = Math.cos(to_rad) * @block_s
}}
最終的な角度はどのくらいシフト量があるのかを計算している.
#sh(ruby){{
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
}}
結果の配列rotateを作り,現在の角度radを初期角度にセットす...
そしてアニメーションのフレーム数だけループを回してシフト...
シフト量は数学の時間で習ったとおり,x方向についてはコサイ...
これにブロックサイズをかけて1ブロック分のシフト量にする.
前述した通り,x方向に関しては毎フレームrowに基づいた描画...
アニメーションが終了したときは正規の描画座標になるように...
すなわち,rotate_xの配列の最後は0になるはずである.
よって(Math.cos(rad) - shift)はrad=to_radの時0となり,仕...
*** set_rotate_y [#ye4348ef]
x方向との違いだけ説明する.
というか話は簡単で,まずy方向なのでサインでシフト量が求め...
あとはy方向に関しては速度が知りたいので,今回のシフト量か...
** ピボットブロックの回転 [#r0c71d7e]
いよいよピボットブロックの回転処理を考えよう.
ピボットブロックはピボットを中心に最大4個のブロックを周囲...
2個ブロック,3個ブロックの構成は以下のようになる.
b0 b0
p or p b1
すなわち,縦か横に3個並ぶことはない.
今回は簡単のためにこの制約の下での回転を考えることにする.
*** can_rotate? [#o21e7ce0]
回転可能かチェックするca_rotate?メソッドを以下のように定...
#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+...
return {:dir => ir, :shift => 0}
elsif (row_dir > 0 ? min_cond : max_cond) && !table...
return {:dir => ir, :shift => -row_dir}
end
# NG
return false
end
return {:dir => ir, :shift => 0}
end
end
}}
引数のirは右回転なら1,左なら-1,それ以外なら0が入ってい...
まずピボットを取り囲むブロックそれぞれについてループを回...
その位置にブロックが存在しないか位置がピボットの横の場合...
なぜなら横にあるブロックは回転するとピボットの上か下に来...
そして,回転できるかの判定部分だがなんかわかりにくいこと...
回転の結果としてピボットの右にブロックが来る場合はピボッ...
もしなければ回転できるので結果を返す.
ここで返す結果は回転方向とシフト量を持ったハッシュである.
また,ここですぐに結果を返せるのは先ほどの制約のおかげで...
もし制約がなければ左側にブロックが来るかどうかも考慮しな...
次に,すでにブロックがあった場合,ピボットを左にずらすこ...
このときのピボットのrowの移動量が今までさんざんスルーして...
なので,ピボットの右にもしすでにブロックがあった場合はピ...
それ以外は回転できない.
*** rotate [#v9075e66]
実際に回転するrotateメソッドは以下のように定義した.
#sh(ruby){{
class PivotControlBlock < ControlBlock
def rotate(rotate, time)
# pivot
@pivot.row += rotate[:shift]
@pivot.set_rotate(0, 0, time, rotate[:shift]) if rota...
# belongs
@belongs.rotate!(-rotate[:dir])
@belongs.each.with_index do |block, i|
next unless block
# set animation
to = 90 * (1 - i)
block.set_rotate(to + 90 * rotate[:dir], to, time, ...
case i
when 0; block.row += rotate[:dir]
when 1; block.row += 1
when 2; block.row += -rotate[:dir]
when 3; block.row += -1
end
# shift
block.row += rotate[:shift]
end
end
end
}}
引数のrotateはcan_rotate?の戻り値,すなわち回転方向とシフ...
メソッド内部ではまずピボットの処理を行っている.
ピボットはシフト以外の影響を受けない.
もしシフトがある場合は実質シフトの影響のみとなるが,回転...
次に周囲のブロックについての処理を行っている.
まずrotate!メソッドにより配列の中身をごっそり回転させてい...
どういうことかというとピボットブロックは配列の順番でピボ...
old = [b0, b1, b2, b3]
new = [b3, b0, b1, b2]
b0 b3
b3 p b1 => b2 p b0
b2 b2
配列の順番の入れは概念的な回転処理にすぎないので,あとは...
すでに配列的には回転させているので,ループ時に持つ配列の...
よって,まずは回転後の角度toを計算し,それを元にfromを求...
さらに,しつこいようだがx方向についてはrowが基準で描画座...
最後にシフトに基づいてさらにrowを更新すれば回転終了である.
*** set_rotate_xのシフトについて [#db40770f]
ここでシフトの概念が登場したので,少し戻ってset_rotate_x...
#sh(ruby){{
class FreeBlock < Block
def set_rotate_x(from, to, time, push_shift)
# x : rotate distance list based on to
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
shift = Math.cos(to_rad) * @block_s
ps_divide = (push_shift * @block_s).quo time
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
end
end
}}
このメソッド内ではピボット自体が移動するシフトをプッシュ...
プッシュシフトとはx方向に移動することであるから,ようする...
また,前述の通りx方向は移動後の位置を元にしてシフト配列を...
よってpsの初期値はプッシュシフト*ブロックサイズとなり,あ...
** 実行 [#dbe3c5d8]
いよいよそれっぽくなってきた.
#media(PuyoPuyoChap16/PuyoPuyoChap16.flv);
終了行:
[[目次へもどる>PuyoPuyo]]
* 回転させよう(Pivot編) [#p5c2cbb9]
#contents
いよいよ操作ブロック最後の山,回転を作ろう.
** 考察 [#h79a6a7c]
まずこれまで,操作ブロックは回転の際,軸となるブロックが...
これは正しい.そしてそれを元にしてコントロールブロックは...
しかし,ぷよぷよフィーバーを考えるとそうも行かない.
ぷよぷよフィーバーでは通常の2個ブロックの他に3個のブロッ...
このうち前述の仕様が通用するのは2個,3個ブロックのみであ...
そのため,例によってControlBlockクラスはコントロールブロ...
#sh(ruby){{
class ControlBlock
def initialize
clear
end
def clear
@postpone = 0
end
def set(postpone)
@postpone = postpone
end
def start(row, line)
end
def can_move_row?(imr, table, row_s)
return false if imr == 0 # no move
blocks.group_by{|block| block.line}.each do |line, bl...
if imr > 0 # move right
row = blks.max_by{|blk| blk.row}.row
return false if row >= row_s - 1
return false if table[row + 1][line]
else # move left
row = blks.min_by{|blk| blk.row}.row
return false if row <= 0
return false if table[row - 1][line]
end
end
return true
end
def move_row(imr, speed, block_s)
blocks.each do |block|
x1 = block.row * block_s
x2 = x1 + imr * block_s
block.set_move_x(x1, x2, imr * speed)
block.row += imr
end
end
def can_falldown?(table, block_s)
fall_ys = blocks.group_by{|block| block.row}.map{|row...
min_y = blks.min_by{|blk| blk.draw_pos[1]}.draw_pos...
min_y - table[row].size * block_s
}
# fall_ys == 0 : can not fall
# > 0 : fall y
# < 0 : dent y
return fall_ys.min
end
def falldown(y)
blocks.each do |block|
block.draw_pos[1] -= y
end
end
def fix_dent(y)
blocks.each do |block|
block.draw_pos[1] += y
end
end
def update_postpone
@postpone -= 1
end
def blocks
return []
end
def move_x?
blocks.each do |block|
return true if block.move_x?
end
return false
end
def move_y?
blocks.each do |block|
return true if block.move_y?
end
return false
end
def move?
blocks.each do |block|
return true if block.move?
end
return false
end
def postpone?; @postpone > 0; end
end
}}
#sh(ruby){{
=begin
blocks = [b0,b1,b2,b3]
b1 b2
(b0) b3
(block) is base block
=end
class CycleControlBlock < ControlBlock
def clear
super
@blocks = []
end
def set(*blocks, postpone)
super(postpone)
@blocks = Array.new(4){|i| blocks[i] }
end
def start(row, line)
@blocks.each.with_index do |block, i|
next unless block
case i
when 0; row_shift = 0; line_shift = 0
when 1; row_shift = 0; line_shift = 1
when 2; row_shift = 1; line_shift = 1
when 3; row_shift = 1; line_shift = 0
end
block.row = row + row_shift
block.line = line + line_shift
end
end
def blocks
return @blocks.compact
end
end
}}
#sh(ruby){{
=begin
pivot = p
belongs = [b0, b1, b2, b3]
b0
b3 p b1
b2
=end
class PivotControlBlock < ControlBlock
def clear
super
@pivot = nil
@belongs = []
end
def set(pivot, *belongs, postpone)
super(postpone)
@pivot = pivot
@belongs = Array.new(4){|i| belongs[i] }
end
def start(row, line)
if @pivot
@pivot.row = row
@pivot.line = line
end
@belongs.each.with_index do |block, i|
next unless block
case i
when 0; row_shift = 0 ; line_shift = -1
when 1; row_shift = 1 ; line_shift = 0
when 2; row_shift = 0 ; line_shift = 1
when 3; row_shift = -1; line_shift = 0
end
block.row = row + row_shift
block.line = line + line_shift
end
end
def blocks
return [@pivot, *@belongs].compact
end
end
}}
サイクルブロックは四つ組みのブロックで,フィーバーの四個...
ピボットブロックはピボットを中心として最大4個その周囲にブ...
さて,このように違うタイプのコントロールクラスが存在しう...
これらの切り替えなどをまとめて管理してくれる人を作ろう.
#sh(ruby){{
class ControlBlockManager
def initialize
init_ctrl_blocks
end
def init_ctrl_blocks
@ctrl_blocks = {
PivotControlBlock => PivotControlBlock.new,
CycleControlBlock => CycleControlBlock.new
}
@type = PivotControlBlock
end
def set_type(type)
ctrl_block.clear unless @type.nil?
@type = type
end
def ctrl_block
@ctrl_blocks[@type]
end
end
}}
ControlBlockManagerクラスはその名の通りコントロールブロッ...
作ったコントロールブロックの種類だけ@ctrl_blocksに追加し...
** フィールドの処理 [#a335630d]
フィールド側の回転処理はコントロールブロックの種類によら...
#sh(ruby){{
class Field
def update_control_block(imr,ir,iff)
update_control_block_rotate(ir)
update_control_block_move_x(imr)
active = update_control_block_move_y(iff)
end
def update_control_block_rotate(ir)
return true if @cbm.ctrl_block.rotate?
rotate = @cbm.ctrl_block.can_rotate?(ir, @table, @row...
return false unless rotate
@cbm.ctrl_block.rotate(rotate, 8)
end
end
}}
流れは落下や横移動と全く同じ.回転可能かチェックするcan_r...
** 回転アニメーション [#h8d0bd75]
回転のアニメーションもブロックの種類によらず同様である((...
先に定義しよう.
#sh(ruby){{
class FreeBlock < Block
def init_animation
@move_x = nil
@rotate_x = nil
@rotate_y = nil
end
def set_rotate_x(from, to, time, push_shift)
# x : rotate distance list based on to
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
shift = Math.cos(to_rad) * @block_s
ps_divide = (push_shift * @block_s).quo time
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
end
def set_rotate_y(from, to, time)
# y : rotate speed list base on from
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
rotate = []
rad = from_rad
time.times do
old = rad
rad += time_divide
rotate.push((Math.sin(rad) - Math.sin(old)) * @bloc...
end
return rotate
end
def set_rotate(from, to, time, shift)
@rotate_x = set_rotate_x(from, to, time, shift)
@rotate_y = set_rotate_y(from, to, time)
end
def update_rotate(rotate)
return nil unless rotate
x_shift = rotate.shift
yield if rotate.empty?
return x_shift
end
def update
# update move_x
x = update_move(@move_x){@move_x = nil}
@draw_pos[0] = x ? x : @row * @block_s
# update rotate
x_shift = update_rotate(@rotate_x){@rotate_x = nil}
y_shift = update_rotate(@rotate_y){@rotate_y = nil}
@draw_pos[0] += x_shift if x_shift
@draw_pos[1] += y_shift if y_shift
# update line
@line = (@draw_pos[1].round + @block_s / 2) / @block_s
end
end
}}
回転アニメのセットはset_rotateメソッドで行う.
引数には初期角度,移動角度,移動時間,シフトを指定する.
例えば
set_rotate(0,90,60,1)
とすれば0度から90度まで60フレームでアニメーションする.シ...
ところで,0度から90度ってなんだよって思うだろう.
今回の定義では回転の原点は現在のブロックの描画位置となっ...
また,0度とは数学の時間によく見たように単位円の右側を表す...
初期角度 < 移動角度 なら左回転,逆なら右回転のアニメーシ...
コントロールブロックについて,xは現在のrowの値から決定さ...
これはすなわち以下を意味する.
+ x座標については毎回rowの値から算出されるため,アニメー...
+ y座標については描画位置そのものを保持しているため,アニ...
よって,x方向とy方向で計算の仕方が多少異なる.
それらを考慮した上で回転のアニメーションの仕様を以下のよ...
- インスタンス変数@rotate_xと@rotate_yを用意
- それぞれnilならアニメーションしていない
- それ以外の場合は配列が格納されている
- @rotate_xは毎フレームごとのx座標のシフト量が入っている
- @rotate_yは毎フレームごとのy座標の移動量が入っている
- アニメーションは配列の先頭から一つ要素を取り出してx,y座...
- 配列が空になったらそれぞれにnilをセットしてアニメーショ...
*** set_rotate_x [#fc96db05]
まずはx方向の回転のセットを見てみよう.
引数のpush_shiftは後で説明するとして,
何をしているのかざっと見てみる.
#sh(ruby){{
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
}}
まず角度をラジアンに変換している.ここはいいだろう.
#sh(ruby){{
time_divide = (to_rad - from_rad) / time
}}
次に1フレームあたりどのくらい角度が変化するのかを計算.
#sh(ruby){{
shift = Math.cos(to_rad) * @block_s
}}
最終的な角度はどのくらいシフト量があるのかを計算している.
#sh(ruby){{
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
}}
結果の配列rotateを作り,現在の角度radを初期角度にセットす...
そしてアニメーションのフレーム数だけループを回してシフト...
シフト量は数学の時間で習ったとおり,x方向についてはコサイ...
これにブロックサイズをかけて1ブロック分のシフト量にする.
前述した通り,x方向に関しては毎フレームrowに基づいた描画...
アニメーションが終了したときは正規の描画座標になるように...
すなわち,rotate_xの配列の最後は0になるはずである.
よって(Math.cos(rad) - shift)はrad=to_radの時0となり,仕...
*** set_rotate_y [#ye4348ef]
x方向との違いだけ説明する.
というか話は簡単で,まずy方向なのでサインでシフト量が求め...
あとはy方向に関しては速度が知りたいので,今回のシフト量か...
** ピボットブロックの回転 [#r0c71d7e]
いよいよピボットブロックの回転処理を考えよう.
ピボットブロックはピボットを中心に最大4個のブロックを周囲...
2個ブロック,3個ブロックの構成は以下のようになる.
b0 b0
p or p b1
すなわち,縦か横に3個並ぶことはない.
今回は簡単のためにこの制約の下での回転を考えることにする.
*** can_rotate? [#o21e7ce0]
回転可能かチェックするca_rotate?メソッドを以下のように定...
#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+...
return {:dir => ir, :shift => 0}
elsif (row_dir > 0 ? min_cond : max_cond) && !table...
return {:dir => ir, :shift => -row_dir}
end
# NG
return false
end
return {:dir => ir, :shift => 0}
end
end
}}
引数のirは右回転なら1,左なら-1,それ以外なら0が入ってい...
まずピボットを取り囲むブロックそれぞれについてループを回...
その位置にブロックが存在しないか位置がピボットの横の場合...
なぜなら横にあるブロックは回転するとピボットの上か下に来...
そして,回転できるかの判定部分だがなんかわかりにくいこと...
回転の結果としてピボットの右にブロックが来る場合はピボッ...
もしなければ回転できるので結果を返す.
ここで返す結果は回転方向とシフト量を持ったハッシュである.
また,ここですぐに結果を返せるのは先ほどの制約のおかげで...
もし制約がなければ左側にブロックが来るかどうかも考慮しな...
次に,すでにブロックがあった場合,ピボットを左にずらすこ...
このときのピボットのrowの移動量が今までさんざんスルーして...
なので,ピボットの右にもしすでにブロックがあった場合はピ...
それ以外は回転できない.
*** rotate [#v9075e66]
実際に回転するrotateメソッドは以下のように定義した.
#sh(ruby){{
class PivotControlBlock < ControlBlock
def rotate(rotate, time)
# pivot
@pivot.row += rotate[:shift]
@pivot.set_rotate(0, 0, time, rotate[:shift]) if rota...
# belongs
@belongs.rotate!(-rotate[:dir])
@belongs.each.with_index do |block, i|
next unless block
# set animation
to = 90 * (1 - i)
block.set_rotate(to + 90 * rotate[:dir], to, time, ...
case i
when 0; block.row += rotate[:dir]
when 1; block.row += 1
when 2; block.row += -rotate[:dir]
when 3; block.row += -1
end
# shift
block.row += rotate[:shift]
end
end
end
}}
引数のrotateはcan_rotate?の戻り値,すなわち回転方向とシフ...
メソッド内部ではまずピボットの処理を行っている.
ピボットはシフト以外の影響を受けない.
もしシフトがある場合は実質シフトの影響のみとなるが,回転...
次に周囲のブロックについての処理を行っている.
まずrotate!メソッドにより配列の中身をごっそり回転させてい...
どういうことかというとピボットブロックは配列の順番でピボ...
old = [b0, b1, b2, b3]
new = [b3, b0, b1, b2]
b0 b3
b3 p b1 => b2 p b0
b2 b2
配列の順番の入れは概念的な回転処理にすぎないので,あとは...
すでに配列的には回転させているので,ループ時に持つ配列の...
よって,まずは回転後の角度toを計算し,それを元にfromを求...
さらに,しつこいようだがx方向についてはrowが基準で描画座...
最後にシフトに基づいてさらにrowを更新すれば回転終了である.
*** set_rotate_xのシフトについて [#db40770f]
ここでシフトの概念が登場したので,少し戻ってset_rotate_x...
#sh(ruby){{
class FreeBlock < Block
def set_rotate_x(from, to, time, push_shift)
# x : rotate distance list based on to
from_rad = from * Math::PI / 180
to_rad = to * Math::PI / 180
time_divide = (to_rad - from_rad) / time
shift = Math.cos(to_rad) * @block_s
ps_divide = (push_shift * @block_s).quo time
rotate = []
rad = from_rad
ps = push_shift * @block_s
time.times do
rad += time_divide
ps -= ps_divide
rotate.push(Math.cos(rad) * @block_s - shift - ps)
end
return rotate
end
end
}}
このメソッド内ではピボット自体が移動するシフトをプッシュ...
プッシュシフトとはx方向に移動することであるから,ようする...
また,前述の通りx方向は移動後の位置を元にしてシフト配列を...
よってpsの初期値はプッシュシフト*ブロックサイズとなり,あ...
** 実行 [#dbe3c5d8]
いよいよそれっぽくなってきた.
#media(PuyoPuyoChap16/PuyoPuyoChap16.flv);
ページ名: