# デバッグ用情報表示の設定
game.draw_collision_mode false
game.draw_fps_mode true
IS_VIEW_FPS = true
# 画像のローディング
game.loading do |loader|
loader.add :window, :system => "window"
loader.add :gui_item, :system => "gui_item"
loader.add :circle_white, 205
loader.add :bomb, 17
loader.add :majitai, 206
loader.add :bg, 204
end
# ゲームの初期化
game.on_init do
set_window_image :window
set_gui_image :gui_item
# オープニングのシーンへ
scene_change :start_scene
end
#
scene :start_scene do |scene|
scene.loading do |loader|
end
scene.on_init do
speak "3 match pazzle"
speak "円をドラッグして、3つ以上そろえると円が消えます。"
speak "このゲームに終わりはありません。やめる時は自分の意志の力でやめてください。"
scene_change :stage_scene
end
end
scene :stage_scene do |scene|
scene.loading do |loader|
end
scene.on_init do
start_stage scene
end
end
# ベクトルの長さを求めます。
def vector_len a
Math.sqrt a[0] * a[0] + a[1] * a[1]
end
# ベクトルの足し算
# ここではベクトルを、長さ2の配列で扱っています。
def vector_add a, b
[a[0] + b[0], a[1] + b[1]]
end
# ベクトルの引き算
def vector_sub a, b
[a[0] - b[0], a[1] - b[1]]
end
# ベクトルとスカラーの掛け算
def vector_mul a, b
[a[0] * b, a[1] * b]
end
# ベクトルの正規化、つまりベクトルの長さを1にする。
def vector_normal v
l = vector_len v
if l > 0
vector_mul v, 1.0 / l
else
[0, 1]
end
end
#
class Lazy_call
def initialize
@funcs = []
end
def add fiber
@funcs.push fiber
end
def poll
i = 0
while i < @funcs.length
fiber = @funcs[i]
if fiber.resume
#game.logger.debug "delete fiber"
@funcs.delete_at i
else
#game.logger.debug "next fiber"
i += 1
end
end
end
end
class Drop
attr_accessor :game_stage, :cell_x, :cell_y, :sprite, :index, :color_code, :need_delete
def initialize game_stage, color_code
self.game_stage = game_stage
self.color_code = color_code
if color_code != 6
self.sprite = game_stage.scene.sprite :template => :sprite_template_drop, :texture => :circle_white
else
self.sprite = game_stage.scene.sprite :template => :sprite_template_majitai, :texture => :majitai
self.sprite.change_animation :animate
end
self.sprite.set_data :drop, self
self.need_delete = false
end
def delete
self.game_stage.scene.sprite :template => :sprite_template_bomb,
:center_position => self.sprite.get_center_position
self.game_stage.remove_drop self
self.game_stage.set_active_count self.game_stage.active_count - 1
self.sprite.delete
end
# 爆発用のスプライトテンプレート
sprite_template :sprite_template_bomb do |sprite_template|
sprite_template.texture :bomb # テクスチャーの設定
sprite_template.scale 2.0, 2.0 # 拡大量
sprite_template.src_size 96, 96 # アニメーション時のコピー元の矩形の大きさ
sprite_template.dest_size 96, 96 # アニメーション時のコピー先の矩形の設定
sprite_template.copy_rect 0, 0 # src_sizeのグリッドで区切られたテクスチャーの
# どの位置の画像を表示するかの設定
sprite_template.center_offset 48, 48 # スプライトの中心位置の設定
sprite_template.layer_order 40 # 描画順の設定
sprite_template.collision :rect, :position => [0, 0], :width => [0, 0],
:group_id => 0 # 当たり判定はいらないが、デフォルトだとデバッグ時に見づらいため適当な値を設定している
sprite_template.render_way_type :add
# 爆発のアニメーションの設定(コマンドリスト)
sprite_template.animation :bomb do |commands|
(0...16).each do |i|
# 1行に8パターンならんでいるのでこのような書き方
# 1フレームアニメーションを表示します。
commands.copy_rect :frame => 1, :src => [i % 8, (i / 8).floor]
# アニメーションの待ち
commands.wait_animation
end
# アニメーションが終わったら消します。
commands.proc_call do |s| s.delete end
end
# スプライトが生成された時の処理
sprite_template.event :on_add do |event|
# 起動時のアニメーションを設定します
event.target.change_animation :bomb
end
end
def self.sprite_tamplate_drop_init sprite_template, type
sprite_template.position 230, 600
sprite_template.rotation 0.0
sprite_template.scale 1.0, 1.0
sprite_template.texture :circle_white
sprite_template.src_size 64, 64
sprite_template.dest_size 64, 64
sprite_template.copy_rect 0, 0
sprite_template.center_offset 32, 32
sprite_template.collision :circle, :mode => :center, :position => [0, 0],
:radius => 31.5, :group_id => 1
sprite_template.layer_order 10
sprite_template.motion :fall do |commands|
commands.loop false
(0...80).each do |i|
commands.velocity 0, i / 4
commands.wait_frame 1
end
commands.velocity 0, 20
commands.wait_frame 1024
end
sprite_template.motion :up do |commands|
commands.loop true
commands.velocity(0, lambda do |s|
drop = s.get_data :drop
cp = s.get_center_position
pt = drop.game_stage.snaped_cell_world_pos *cp
d = vector_sub pt, cp
#if drop.index == 0
# game.logger.debug "pt #{pt}, cp #{cp}, vector_len(d) #{vector_len(d)}"
#end
if vector_len(d) < 2
s.change_motion :stay
drop.game_stage.insert_drop drop
d[1]
else
d[1] / 2
end
end)
commands.wait_frame 1
end
sprite_template.motion :stay do |commands|
commands.loop false
commands.velocity 0, 0
commands.wait_frame 1
end
sprite_template.motion :swap do |commands|
commands.loop false
commands.proc_call do |s|
drop = s.get_data :drop
drop.game_stage.remove_drop drop
s.set_data :moving, true
end
commands.move_to(lambda do |s|
s.get_data :next_x
end,
lambda do |s|
s.get_data :next_y
end, 15)
commands.wait_motion
commands.proc_call do |s|
drop = s.get_data :drop
s.set_data :moving, false
drop.game_stage.insert_drop drop
end
commands.wait_frame 1
commands.proc_call do |s|
drop = s.get_data :drop
if drop.game_stage.need_revert
s.change_motion :swap_back
else
drop.game_stage.moving = false
end
end
end
sprite_template.motion :swap_back do |commands|
commands.loop false
commands.proc_call do |s|
drop = s.get_data :drop
drop.game_stage.remove_drop drop
s.set_data :moving, true
end
commands.move_to(lambda do |s|
s.get_data :back_x
end,
lambda do |s|
s.get_data :back_y
end, 15)
commands.wait_motion
commands.proc_call do |s|
drop = s.get_data :drop
drop.game_stage.insert_drop drop
s.set_data :moving, false
drop.game_stage.moving = false
end
end
sprite_template.event :on_add do |event|
event.target.change_motion :fall
end
sprite_template.event :on_collision do |event|
source = event.pair[0]
another = event.pair[1]
next unless another
next unless source
next if source.get_data :moving
drop = source.get_data :drop
#game.logger.debug "source #{source}"
#game.logger.debug "another #{another}"
#game.logger.debug "source.destroyed #{source.destroyed}"
#game.logger.debug "another.destroyed #{another.destroyed}"
#game.logger.debug "source.get_center_position #{source.get_center_position}"
#game.logger.debug "another.get_center_position #{another.get_center_position}"
if source.get_center_position[1] < another.get_center_position[1]
drop.game_stage.remove_drop drop
drop.sprite.change_motion :up
end
end
#sprite_template.event :on_step do |event|
#
#end
if type == :majitai
sprite_template.texture :majitai
sprite_template.layer_order 20
sprite_template.animation :animate do |commands|
commands.loop true
(0...59).each do |i|
# 1行に8パターンならんでいるのでこのような書き方
# 1フレームアニメーションを表示します。
commands.copy_rect :frame => 1, :src => [i % 8, (i / 8).floor]
# アニメーションの待ち
commands.wait_animation
end
end
end
end
sprite_template :sprite_template_drop do |sprite_template|
self.sprite_tamplate_drop_init sprite_template, :drop
end
sprite_template :sprite_template_majitai do |sprite_template|
self.sprite_tamplate_drop_init sprite_template, :majitai
end
end
class GameStage
attr_accessor :touch_down, :scene, :sprite, :floor_y, :cells,
:floor, :left_top_x, :left_top_y, :cell_w, :cell_h, :cell_sw, :cell_sh,
:lazy_call, :index_count, :need_revert, :need_decide,
:next_position, :back_position, :moving,
:score, :majitai_count, :score_label, :score_text,
:majitai_count_label, :majitai_count_text
def initialize scene
self.touch_down = false
self.scene = scene
self.sprite = scene.sprite :template => :sprite_template_screen
self.sprite.set_data :game_stage, self
GameStage.instance = self
self.floor = scene.collision :template => :sprite_template_floor
self.lazy_call = Lazy_call.new
self.index_count = 0
self.need_revert = false
self.need_decide = true
self.moving = false
self.score = 0
self.majitai_count = 0
text = scene.text :position => [self.screen_width - 200, self.screen_height - 24],
:layer_order => 200
text.set_text_area_size 200, 24
text.set_font_point_size 18
text.set_text "スタートメニュー"
text.event :on_click do |event|
# スタートメニューに戻る
game.change_project "start_menu"
end
if IS_VIEW_FPS
fps_sp = scene.text :position => [10, self.screen_height - 24]
fps_sp.set_text_area_size 200, 24
fps_sp.set_font_point_size 18
tm = Time.now.to_f
count = 0
fps_sp.event :on_step do
now = Time.now.to_f
count += 1
if now - tm > 1
fps_sp.set_text "fps = #{count}"
count = 0
tm = now
end
end
end
#
self.score_label = scene.text :position => [10, self.screen_height - 64 - 32],
:layer_order => 200
self.score_label.set_text_area_size 200, 32
self.score_label.set_font_point_size 20
self.score_label.set_text "Score"
self.score_text = scene.text :position => [self.screen_width - 200, self.screen_height - 64 - 32],
:layer_order => 200
self.score_text.set_text_area_size 200, 32
self.score_text.set_font_point_size 20
self.score_text.set_text self.score.to_s
#
self.majitai_count_label = scene.text :position => [10, self.screen_height - 64],
:layer_order => 200
self.majitai_count_label.set_text_area_size 200, 32
self.majitai_count_label.set_font_point_size 12
self.majitai_count_label.set_text "生き残っている\nmajitai"
self.majitai_count_text = scene.text :position => [self.screen_width - 200, self.screen_height - 64],
:layer_order => 200
self.majitai_count_text.set_text_area_size 200, 32
self.majitai_count_text.set_font_point_size 20
self.majitai_count_text.set_text self.majitai_count.to_s
#
@active_count = 0
@on_disactive = nil
end
def self.instance
@instance
end
def self.instance= o
@instance = o
end
def screen_width
game.get_screen_size[0]
end
def screen_height
game.get_screen_size[1]
end
def active_count
@active_count
end
def active_count= v
@active_count = v
#game.logger.debug "@active_count #{@active_count}"
if @active_count == 0
@on_disactive.call
end
end
def set_active_count v
@active_count = v
end
def on_disactive &f
@on_disactive = f
end
def can_move
!self.moving && self.active_count <= 0
end
def new_drop i, j
w = self.cell_w
h = self.cell_h
sw = self.cell_sw
sh = self.cell_sh
r = rand(2)
g = rand(2)
b = rand(2)
#if j == 2
# r = 1
# g = 0
# b = 0
#end
if r == 1 && g == 1 && b == 1
r, g, b = [1, 0, 0]
elsif r == 0 && g == 0 && b == 0
r, g, b = [0, 0, 1]
end
color_code = r + g * 2 + b * 4
drop = Drop.new self, color_code
drop.sprite.set_center_position j * sw + (screen_width - sw * w) / 2 + sw / 2,
(h - i - 1) * sh - sh * h
drop.index = self.index_count
r *= 255
g *= 255
b *= 255
if color_code != 6
drop.sprite.set_color r, g, b, 255
end
self.active_count += 1
self.index_count += 1
end
def make_drops
w = 7
h = 7
sw = 64
sh = 64
self.cell_w = w
self.cell_h = h
self.cell_sw = sw
self.cell_sh = sh
self.left_top_x = (screen_width - sw * w) / 2 - sw / 2
self.left_top_y = (screen_height - sh * h) / 2 - sh / 2
self.floor_y = (screen_height - sh * h) / 2 + sh * h + 2
self.cells = Array.new h, []
self.floor.set_center_position screen_width / 2, self.floor_y
#
bg = scene.sprite :texture => :bg, :position => [self.left_top_x, self.left_top_y], :layer_order => 0
dest_rects = []
colors = []
src_rects = []
(-5...h).each do |i|
(-5...w).each do |j|
dest_rects.concat [j * sw - sw * 1.5, i * sh - sh * 1.5, sw, sh]
colors.concat [63, 63, 63, 255]
src_rects.concat [0, 0, sw, sh]
end
end
(0...w).each do |j|
dest_rects.concat [j * sw - sw * 1.5, h * sh - sh * 1.5, sw, sh]
colors.concat [63, 63, 63, 255]
src_rects.concat [sw, 0, sw, sh]
end
bg.set_rects dest_rects, colors, src_rects
#
(0...h).each do |i|
self.cells[i] = Array.new w, nil
end
fiber = Fiber.new do
(0...h).each do |i|
(0...w).each do |j|
self.new_drop i, j
Fiber.yield false
end
end
true
end
self.lazy_call.add fiber
self.on_disactive do
self.decision
end
end
def snaped_cell_pos x, y
[((x - self.left_top_x) / self.cell_sw).floor - 1, ((y - self.left_top_y) / self.cell_sh).floor - 1]
end
def snaped_cell_world_pos x, y
ix, iy = snaped_cell_pos x, y
[(ix + 1) * self.cell_sw + self.left_top_x,
(iy + 1) * self.cell_sh + self.left_top_y]
end
def decision
w = self.cell_w
h = self.cell_h
self.need_revert = true
#game.logger.debug "decision #{w} #{h}"
#pt_info = "pt_info\n"
cluster_array = []
#table = Array h, [] # crush?
table = []
(0...h).each do |i|
table.push []
(0...w).each do |j|
table[i].push({ :cluster_id => nil, :counts => [], :parent => nil, :evaled => false,
:color_code => 0 })
current_cell = self.cells[i][j]
table[i][j][:color_code] = current_cell.color_code
table[i][j][:parent] = table[i][j]
end
end
cluster_count = 0
(0...h).each do |i|
j = 0
while j < w
jst = j
start_cell = self.cells[i][j]
c = start_cell.color_code
cell_connect_count = 0
#game.logger.debug "check #{i} #{j} #{cell_connect_count} #{c}"
cell_connect_count = 1
jed = j
j += 1
while j < w + 1
if j < w
current_cell = self.cells[i][j]
else
current_cell = nil
end
#game.logger.debug "check #{i} #{j} #{cell_connect_count} #{current_cell ? current_cell.color_code : "_"}"
if cell_connect_count >= 3 && (current_cell == nil || current_cell.color_code != c)
#game.logger.debug "cell_connect_count #{cell_connect_count}"
cluster = table[i][jst][:parent]
(jst..jed).each do |k|
table[i][k][:parent] = cluster
self.cells[i][k].need_delete = true
end
cluster[:counts].push cell_connect_count
cluster_count += 1
break
elsif current_cell && current_cell.color_code == c
jed = j
cell_connect_count += 1
j += 1
else
break
end
end
end
end
(0...w).each do |i|
j = 0
while j < h
jst = j
start_cell = self.cells[j][i]
c = start_cell.color_code
cell_connect_count = 0
#game.logger.debug "check #{j} #{i} #{cell_connect_count} #{c}"
cell_connect_count = 1
jed = j
j += 1
while j < h + 1
if j < h
current_cell = self.cells[j][i]
else
current_cell = nil
end
#game.logger.debug "check #{i} #{j} #{cell_connect_count} #{current_cell ? current_cell.color_code : "_"}"
if cell_connect_count >= 3 && (current_cell == nil || current_cell.color_code != c)
#game.logger.debug "cell_connect_count #{cell_connect_count}"
cluster = table[i][jst][:parent]
(jst..jed).each do |k|
table[k][i][:parent] = cluster
self.cells[k][i].need_delete = true
end
cluster[:counts].push cell_connect_count
cluster_count += 1
break
elsif current_cell && current_cell.color_code == c
jed = j
cell_connect_count += 1
j += 1
else
break
end
end
end
end
if false
(0...h).each do |i|
(0...w).each do |j|
down_cells = []
right_cells = []
current_cell = self.cells[i][j]
#game.logger.debug "current_cell #{current_cell}"
unless current_cell
#pt_info += "null, "
next
end
c = self.cells[i][j].color_code
#game.logger.debug "c #{c}"
#pt_info += "#{current_cell.index} (#{current_cell.cell_x}, #{current_cell.cell_y}), "
(0...2).each do |k|
#game.logger.debug "self.cells[i + k + 1][j].color_code #{self.cells[i + k + 1][j].color_code}"
if i + k + 1 < h && c == self.cells[i + k + 1][j].color_code
down_cells.push self.cells[i + k + 1][j]
#game.logger.debug "down_cells.push"
end
#game.logger.debug "self.cells[i][j + k + 1].color_code #{self.cells[i][j + k + 1].color_code}"
if j + k + 1 < w && c == self.cells[i][j + k + 1].color_code
right_cells.push self.cells[i][j + k + 1]
#game.logger.debug "right_cells.push"
end
end
if down_cells.length >= 2 ||
right_cells.length >= 2
current_cell.need_delete = true
#game.logger.debug "current_cell.need_delete = true"
end
if down_cells.length >= 2
down_cells.each do |cell|
cell.need_delete = true
end
end
if right_cells.length >= 2
right_cells.each do |cell|
cell.need_delete = true
end
end
end
#pt_info += "\n"
end
end
#game.logger.debug pt_info
a = Array.new w, 0
deleted = false
(0...h).each do |i|
(0...w).each do |j|
cell = self.cells[i][j]
next unless cell
if cell.need_delete
a[cell.cell_x] += 1
#game.logger.debug "cell.delete"
cell.delete
deleted = true
end
end
end
fiber = Fiber.new do
l = true
ct = 0
while l
l = false
(0...w).each do |i|
if a[i] > 0
self.new_drop ct, i
l = true
a[i] -= 1
Fiber.yield false
end
end
ct += 1
end
true
end
self.lazy_call.add fiber
#
(0...h).each do |i|
(0...w).each do |j|
cluster = table[i][j][:parent]
next if cluster[:evaled]
if cluster[:counts].length > 0
s = 1
cluster[:counts].each do |k|
s = ((k - 2) ** 2) * 10 * s
end
self.score += s
end
cluster[:evaled] = true
end
end
self.majitai_count = 0
(0...h).each do |i|
(0...w).each do |j|
cluster = table[i][j][:parent]
next if cluster[:counts].length > 0
cluster = table[i][j]
c = cluster[:color_code]
self.majitai_count += 1 if c == 6
end
end
self.score_text.set_text self.score.to_s
self.majitai_count_text.set_text self.majitai_count.to_s
#
if deleted
fall_fiber = Fiber.new do
(0...h).each do |i|
(0...w).each do |j|
#game.logger.debug "start fall!"
cell = self.cells[h - i - 1][j]
if cell
self.remove_drop cell
cell.sprite.change_motion :fall
end
end
Fiber.yield false
end
true
end
self.lazy_call.add fall_fiber
self.need_revert = false
end
self.need_decide = false
end
def insert_drop drop
cp = drop.sprite.get_center_position
cell = snaped_cell_pos *cp
drop.cell_x = cell[0]
drop.cell_y = cell[1]
#game.logger.debug "insert_drop drop.index #{drop.index}, drop.cell_x #{drop.cell_x}, drop.cell_y #{drop.cell_y}"
self.cells[drop.cell_y][drop.cell_x] = drop
#game.logger.debug "insert_drop #{drop.index}"
self.active_count -= 1
end
def remove_drop drop
return unless drop.cell_y
#game.logger.debug "remove_drop drop.cell_x #{drop.cell_x}, drop.cell_y #{drop.cell_y}"
self.cells[drop.cell_y][drop.cell_x] = nil
drop.cell_x = nil
drop.cell_y = nil
self.active_count += 1
end
sprite_template :sprite_template_floor do |sprite_template|
sprite_template.collision :rect, :position => [-1024, 0], :width => [2048, 32],
:group_id => 1
sprite_template.layer_order 0
end
sprite_template :sprite_template_screen do |sprite_template|
sprite_template.layer_order 100
sprite_template.align :fullscreen # 画面全体に広げます
sprite_template.no_image true
sprite_template.event :on_add do |event|
end
# タッチダウン時の処理
sprite_template.event :on_touch_down do |event|
gs = event.target.get_data :game_stage
if gs.can_move
gs.back_position = gs.snaped_cell_pos event.screen_x + gs.cell_sw / 2, event.screen_y + gs.cell_sh / 2
end
end
# タッチムーブ時の処理
sprite_template.event :on_touch_move do |event|
gs = event.target.get_data :game_stage
gs.next_position = gs.snaped_cell_pos event.screen_x + gs.cell_sw / 2, event.screen_y + gs.cell_sh / 2
if gs.back_position &&
((gs.next_position[0] == gs.back_position[0] && (gs.next_position[1] - gs.back_position[1]).abs == 1) ||
(gs.next_position[1] == gs.back_position[1] && (gs.next_position[0] - gs.back_position[0]).abs == 1))
next if gs.next_position[0] < 0
next if gs.next_position[1] < 0
next if gs.next_position[0] >= gs.cell_w
next if gs.next_position[1] >= gs.cell_h
next if gs.back_position[0] < 0
next if gs.back_position[1] < 0
next if gs.back_position[0] >= gs.cell_w
next if gs.back_position[1] >= gs.cell_h
#game.logger.debug "gs.snaped_cell_pos on_touch_move"
#game.logger.debug "gs.back_position #{gs.back_position}, gs.next_position #{gs.next_position}"
cell0 = gs.cells[gs.back_position[1]][gs.back_position[0]]
cell1 = gs.cells[gs.next_position[1]][gs.next_position[0]]
next unless cell0
next unless cell1
#game.logger.debug "cell0 #{cell0}, cell1 #{cell1}"
cp0 = cell0.sprite.get_position
cp1 = cell1.sprite.get_position
#game.logger.debug "cp0 #{cp0}, cp1 #{cp1}"
cell0.sprite.set_data :back_x, cp0[0]
cell0.sprite.set_data :back_y, cp0[1]
cell0.sprite.set_data :next_x, cp1[0]
cell0.sprite.set_data :next_y, cp1[1]
cell1.sprite.set_data :back_x, cp1[0]
cell1.sprite.set_data :back_y, cp1[1]
cell1.sprite.set_data :next_x, cp0[0]
cell1.sprite.set_data :next_y, cp0[1]
#game.logger.debug "pre chamge_motion"
cell0.sprite.change_motion :swap
cell1.sprite.change_motion :swap
#game.logger.debug "post chamge_motion"
gs.back_position = nil
#game.logger.debug "gs.snaped_cell_pos on_touch_move end"
end
end
# タッチアップ時の処理
sprite_template.event :on_touch_up do |event|
end
sprite_template.event :on_step do |event|
gs = event.target.get_data :game_stage
gs.lazy_call.poll
end
end
end
def start_stage scene, options = {}
game.collision_group_pair 1, 1, true
gs = GameStage.new scene
gs.make_drops
end