対戦しよう †ゲームモードが作れるようになったので早速新しいゲームモードを作ろう. ここはやはりプレイヤー同士の対戦が作りたいところだ. 幸い2プレイヤーのためのフィールドコントローラーはすでに作っておいたので*1, 今回作るべきはフィールドコントローラー間のやり取りである. 新しいゲームモード †まずは新しいゲームモードのためのシーンを作ってしまおう. SceneTwoPlayer?*2は二人のプレイヤーが 対戦するゲームモードである. とはいっても多人数プレイを意識した設計になっているので特に気を張る必要はない. ひとつシングルモードと異なるのはrivalsだろう. ここに敵となるフィールドコントローラーの配列をセットすることで, 自動的にジャマーが相手に発生するようになる予定だ. class SceneTwoPlayer < ScenePuyoPuyo def start super player1 = Player1.new(16,16,6,12,16) player2 = Player2.new(32 + 16 *6,16,6,12,16) player1.rivals = [player2] player2.rivals = [player1] @players.push(player1) @players.push(player2) end def gameover GameMain.scene_change SceneTwoPlayer end end ジャマーの発生と相殺 †フィールドコントローラーでセットするライバルの配列は フィールドクラスを経てジャマーマネージャーの@rivalsにセットされる. このとき登録される配列の中身はライバルのジャマーマネージャーに変換される. ここで再びジャマーに関する考察を行おう.
まずあくまで個人的意見なのだが初代ぷよぷよの相殺なしルールは
ぷよぷよと認めていないので相殺は必須だろう.
また,フィーバーのように相殺している限りジャマーは降らないというルールは
フィーバーモードがあるから活きるわけである.
よって今回は素直にぷよぷよ通のルールを採用しよう.
class JammerManager
attr_accessor :rivals_buf
def initialize(row_s, line_s)
(略)
@rivals_buf = {}
@rivals = []
init_order
end
def rivals=(rivals)
@rivals = rivals.map{|rival| rival.field.jm }
@rivals_buf = {}
@rivals.each do |rival|
@rivals_buf[rival] = 0
end
end
def total_jammers
self.jammers + self.total_rivals_buf
end
def total_rivals_buf
@rivals_buf.inject(0){|sum,(rival,num)| sum + num}
end
def offset_rivals_buf(num)
return num if @rivals.empty?
offset_per = [num / @rivals.size, 1].max
rest = num
@rivals.shuffle.each do |rival|
return 0 if rest == 0
if rest >= offset_per && @rivals_buf[rival] >= offset_per
@rivals_buf[rival] -= offset_per
rest -= offset_per
elsif @rivals_buf[rival] >= rest
@rivals_buf[rival] -= rest
rest = 0
else
rest -= @rivals_buf[rival]
@rivals_buf[rival] = 0
end
end
rest = offset_rivals_buf(num) if rest < num
rest
end
def store_buf(num)
# offset certain jammers
rest = num - self.jammers
self.jammers -= num
return if rest <= 0
# offset buffered jammers
rest = offset_rivals_buf(rest)
# attack
@rivals.each do |rival|
next if rival.rivals_buf[self].nil?
rival.rivals_buf[self] += rest
end
end
def establish_rival_buf(rival)
return if @rivals_buf[rival].nil?
@jammers += @rivals_buf[rival]
@rivals_buf[rival] = 0
end
def establish_jammers
@rivals.each do |rival|
rival.establish_rival_buf(self)
end
end
end
ジャマーマネージャーが新しく持つ要素として@rivalsと@rivals_bufがある.
@rivalsは先程説明した通り敵となるフィールドコントローラーのジャマーマネージャーである. この処理により,あとは今までの設計のままでうまくジャマーが落下するようになっている. 相殺については,まずは確定ジャマーが存在すれば優先的に数を減らし,
ライバルのバッファにジャマーが存在すればバッファから数を減らす.
これを実装したのがoffset_rivals_bufメソッドである. そしてこれら相殺を行った後,それでも余ったジャマーがライバルのジャマーバッファ
に加算される. class Field attr_reader :jm def rivals=(rivals) @jm.rivals = rivals end def eliminate (略) unless @connect_table.empty? jammers = @jm.calc_jammer(@sm.chain_score, @jammer_rate, scene.margin_time, scene.playtime) @jm.store_buf(jammers) end (略) end end class FieldController attr_reader :field def rivals=(rivals) @field.rivals = rivals end def init_phase (略) @phase.add_end_handler(:eliminate, :fall_jammer, method(:end_eliminate_to_fall_jammer)) (略) end def end_eliminate_to_fall_jammer # establish jammers @field.jm.establish_jammers end end 実行 †デバッグ表示の右側がジャマーに関する情報である. 合計ジャマー = 確定ジャマー + ジャマーバッファ という形式になっている. プレイヤー2が連鎖中はジャマーバッファに蓄積されてジャマーは降らない. その後プレイヤー1が相殺し連鎖が終わるとプレイヤー2の確定ジャマーに移動し, ジャマーが降っている様子がわかる. Loading the player ...
一人二役でいい相殺の例はとても無理なので,久しぶりにset_tableを使った. ゲームオーバーの処理 †これでほぼ対戦のゲームモードは完成したわけだが,
今のままではどちらかがゲームオーバーになってももう一方がゲームオーバーにならない
限りゲームが終了しない.負けたら暇である. この機能のためには何が必要かといえばフェーズが終了しているかどうかだろう. この場フィールドコントローラーに関して
class FieldController
def initialize(x,y,row_s, line_s, block_s)
(略)
@rivals = []
(略)
end
def rivals=(rivals)
@rivals = rivals
@field.rivals = rivals
end
def init_phase
(略)
# added :win handler
@phase.add_condition_handler(:win, method(:win_cond))
@phase.change :control_block
end
def gameover?
@phase.phase == :gameover
end
def rivals_gameover?
return false if @rivals.empty?
@rivals.all? {|rival| rival.dead? || rival.gameover? }
end
def update_control_block
inputs = [input_move_row?,input_rotate?,
input_fastfall?,input_momentfall?]
active = @field.update_control_block(*inputs)
if active
@phase.change :win if rivals_gameover?
else
@phase.change :falldown
end
end
def update_eliminate
eliminated = @field.eliminate
if eliminated
@phase.change :falldown
else
@phase.change rivals_gameover? ? :win : :fall_jammer
end
end
def update_win
@phase.wait(60)
@phase.change :term
end
def win_cond
@phase.kill
end
end
rivalsのセットメソッドにフィールドへライバルを伝達する前に自分自身もライバルのフィールドコントローラーを保持するようにした.これでライバルの状態がわかる. あとは次の状態のときライバル全員がゲームオーバーであれば winフェーズへと移行すれば完成である.
連鎖中はwinフェーズへ移行しない仕様になっている.これは一度発火した連鎖は最後まで見たいという要望から生まれた仕様だろう*6. winフェーズはgameoverフェーズと全く同じなので説明は不要だろう. 実行 †これでどちらかがゲームオーバーになればゲームが終了するようになった. Loading the player ...
|