CoRサンプル集: テトリスのような落ちものパズルゲームのサンプル
テトリスのような落ちものパズルゲームのサンプル。
音楽・画像などの素材は一切使用していないので、コピペだけで移植出来ます。
操作方法
横移動:←→
ソフトドロップ:↓
ハードドロップ:↑
左回転:Z
右回転:X
ホールド:C
音楽・画像などの素材は一切使用していないので、コピペだけで移植出来ます。
操作方法
横移動:←→
ソフトドロップ:↓
ハードドロップ:↑
左回転:Z
右回転:X
ホールド:C
プレー:3
(人数:2)
クリア:2
評価: 10 (1回)
タグが設定されていません
#==============================================================================
# 落下ピースのロジッククラス
#==============================================================================
class Piece
# 読み取り専用のメソッドを定義
# @param piece_type [Symbol] ピース形状を表すシンボル
# @param blocks [Array<Array<Symbol|Integer>>] ピースの形状とブロックIDを含む2次元配列
# @param x [Integer] ピースのx座標
# @angle angle [Integer] ピース向き
attr_reader :piece_type, :blocks, :x, :angle
# 読み取り・書き込みメソッドの両方を定義
# @return [Integer] ピースのy座標
attr_accessor :y
# 初期化メソッド
# @param piece_type [Symbol] 使用するピース形状を表すシンボル(:I, :O, :Tなど)
# @param block_types [Array<Symbol>] ピースを構成するブロックの種類
def initialize(piece_type, block_types)
@piece_type = piece_type
@angle = 0
@blocks = assign_random_ids(PIECE_BLOCKS[piece_type], block_types)
@x = nil
@y = nil
end
# ピースを初期配置にセット
def initialize_position
@x = (CONFIG[:grid_width] / 2 - @blocks[0].length / 2).floor # 初期位置(中央)
@y = 0
# 最初のブロック行の位置までyを補正(ピースの先頭が空白なら上に移動)
@blocks.length.times do |y|
if @blocks[y].count { |n| n != 0 } > 0
@y -= y
break
end
end
end
# ピースのブロック(値1)を、指定されたタイプ一覧からランダムに選んで置き換える
# @param blocks [Array<Array<Integer>>] ブロック形状 (0,1)
# @param types [Array<Symbol>] 使用可能なブロックタイプの一覧
# @return [Array<Array<Symbol|Integer>>] ブロックタイプを反映したピースデータ
def assign_random_ids(blocks, types)
return blocks.map do |row|
row.map { |cell| cell == 1 ? types.sample : 0 }
end
end
# ピースを移動させる(可能であれば)
# @param dx [Integer] x方向移動量(-1:左, 1:右)
# @param dy [Integer] y方向移動量(-1:上, 1:)
# @param grid [Array<Array>] 参照するグリッド
def move(dx, dy, grid)
if !colliding?(dx, dy, @blocks, grid)
@x += dx
@y += dy
end
end
# ピースを一番下まで落とす(ハードドロップ)
# @param grid [Array<Array>] グリッド
def hard_drop(grid)
dy = 0
# 衝突しない範囲まで落とす
while !colliding?(0, dy + 1, @blocks, grid)
dy += 1
end
move(0, dy, grid)
end
# ピースを右回りに90度回転(衝突がなければ反映)
# @param grid [Array<Array>] 参照するグリッド
def right_rotate(grid)
rotated = Extension.rotate_right(@blocks)
unless colliding?(0, 0, rotated, grid, false)
@blocks = rotated
@angle = (@angle + 1) % 4
end
end
# ピースを左回りに90度回転(衝突がなければ反映)
# @param grid [Array<Array>] 参照するグリッド
def left_rotate(grid)
rotated = Extension.rotate_left(@blocks)
unless colliding?(0, 0, rotated, grid, false)
@blocks = rotated
@angle = (@angle - 1) % 4
end
end
# ピースを180度回転(衝突がなければ反映)
# @param grid [Array<Array>] 参照するグリッド
def rotate_180(grid)
rotated = Extension.rotate_180(@blocks)
unless colliding?(0, 0, rotated, grid, false)
@blocks = rotated
@angle = (@angle + 2) % 4
end
end
# ピースの向きをリセット
# 当たり判定は無し
def reset_rotate
case @angle
when 1
@blocks = Extension.rotate_left(@blocks)
when 2
@blocks = Extension.rotate_180(@blocks)
when 3
@blocks = Extension.rotate_right(@blocks)
end
@angle = 0
end
# 移動・回転後に衝突するかどうかを判定する
#
# @param dx [Integer] x方向移動量
# @param dy [Integer] y方向移動量
# @param test_blocks [Array<Array>] 試験的に使用するピースの形状
# @param grid [Array<Array>] 参照するグリッド
# @param up_check [Boolean] 上方向(y<0)の範囲チェックをするか
# @return [Boolean] 衝突する場合はtrue
def colliding?(dx, dy, test_blocks, grid, up_check = true)
test_blocks.each_with_index do |row, iy|
row.each_with_index do |block, ix|
next if block == 0
new_x = @x + ix + dx
new_y = @y + iy + dy
if up_check && new_y < 0
return true
end
if new_x < 0 || new_x >= CONFIG[:grid_width]
return true
end
if new_y >= CONFIG[:grid_height]
return true
end
if new_y >= 0 && grid[new_y][new_x] != 0
return true
end
end
end
return false
end
# 現在のピースをグリッドに固定する
# @param grid [Array<Array>] 参照するグリッド
# @return [Boolean] 正常に設置できた場合はtrue。配置場所が範囲外の場合はfalse
def place(grid)
is_ok = true
@blocks.each_with_index do |row, iy|
row.each_with_index do |block, ix|
next if block == 0
place_y = @y + iy
place_x = @x + ix
if place_x.between?(0, CONFIG[:grid_width] - 1) && place_y.between?(0, CONFIG[:grid_height] - 1)
grid[place_y][place_x] = block
else
is_ok = false
end
end
end
return is_ok
end
end
#==============================================================================
# 描画関連ユーティリティ関数
#==============================================================================
# ピース用スプライトを生成する(最大サイズに合わせて生成)
#
# @param sprite_name [String] ピース描画用のスプライト名
# @param generation_count [Integer] スプライト生成数
# @return [Array<Sprite>] ピース描画用スプライト配列
def make_piece_sprites(sprite_name, generation_count = nil)
# 必要な枚数
generation_count ||= PIECE_BLOCKS.values.map { |shape| shape.flatten.count(1) }.max # 各ピースのブロック数を数える(配列を1次元にして1の数を数える)
# ピース描画用スプライトの配置
return Array.new(generation_count) do
put_text sprite_name do
position *INVISIBLE_PIECE_SPRITE_POSITION
text ''
end
end
end
# ブロック描画のためにスプライトプロパティを設定する
#
# @param sprite [Sprite] ブロック描画用のスプライト
# @param block_pattern [Symbol] ブロック描画パターン
# @param pos_x [Integer] X座標
# @param pos_y [Integer] Y座標
# @return [Sprite] スプライト
def set_block_sprite_property(sprite, block_pattern, pos_x = nil, pos_y = nil)
pos_x ||= INVISIBLE_PIECE_SPRITE_POSITION[0]
pos_y ||= INVISIBLE_PIECE_SPRITE_POSITION[1]
sprite.text '■'
sprite.position pos_x, pos_y
sprite.color BLOCK_PATTERNS.fetch(block_pattern, ERROR_BLOCK_COLOR)
sprite.scale 1.0, 1.0
return sprite
end
# 操作中のピースを描画する
#
# @param piece [Piece, nil] 描画対象のピース(nil可)
# @param piece_sprites [Array<Sprite>] ピース描画用スプライト配列
def draw_piece(piece, piece_sprites)
if piece.nil?
y_len = 0
x_len = 0
else
blocks = piece.blocks
y_len = blocks.length
x_len = blocks[0].length
end
count = 0
y_len.times do |y|
x_len.times do |x|
id = blocks[y][x]
next if id == 0
px, py = get_grid_position(piece.x + x, piece.y + y)
set_block_sprite_property(piece_sprites[count], id, px, py)
count += 1
end
end
# 未使用のスプライトは非表示に
piece_sprites[count...piece_sprites.length].each do |sprite|
sprite.text ''
sprite.position *INVISIBLE_PIECE_SPRITE_POSITION
sprite.color FREE_CELL_COLOR
end
end
# ネクストピースを描画する
#
# @param next_pieces [Array<Piece>] ネクストピース一覧
# @param next_piece_sprites [Array<Sprite>] ネクストピース描画用スプライト配列
def draw_next_piece(next_pieces, next_piece_sprites)
next_piece_count = CONFIG[:next_piece_count]
block_size = NEXT_PIECE_CONFIG[:block_size]
next_piece_sprite_len = next_piece_sprites[0]&.length || 0
origin_y = NEXT_PIECE_CONFIG[:y]
next_piece_count.times do |index|
piece_obj = next_pieces[index]
next unless piece_obj
blocks = piece_obj.blocks
origin_x = NEXT_PIECE_CONFIG[:x]
block_count = 0
blocks.each_with_index do |row, y|
next if row.count { |n| n != 0 } == 0 # ブロックが無い行は飛ばす
origin_y += block_size
row.each_with_index do |val, x|
next if val == 0
px = origin_x + grid_center_offset(x, blocks[0].length, block_size)
set_block_sprite_property(next_piece_sprites[index][block_count], val, px, origin_y)
block_count += 1
end
end
origin_y += NEXT_PIECE_CONFIG[:vertical_spacing]
# 未使用スプライトは非表示に
next_piece_sprites[index][block_count...next_piece_sprite_len].each do |sprite|
sprite.text ''
sprite.position *INVISIBLE_PIECE_SPRITE_POSITION
sprite.color FREE_CELL_COLOR
end
end
end
# ホールドしたピースを描画する
#
# @param piece [Piece, nil] 描画対象のピース(nil可)
# @param piece_sprites [Array<Sprite>] ホールドピース描画用スプライト配列
def draw_hold_piece(piece, piece_sprites)
return unless CONFIG[:enable_hold]
if piece.nil?
y_len = 0
x_len = 0
else
# 空行を除いたブロック配列を作る
blocks = piece.blocks.find_all do |row|
row.count { |n| n != 0 } > 0
end
y_len = blocks.length
x_len = blocks[0].length
end
block_size = HOLD_CONFIG[:block_size]
count = 0
y_len.times do |y|
x_len.times do |x|
id = blocks[y][x]
next if id == 0
px = HOLD_CONFIG[:x] + grid_center_offset(x, x_len, block_size)
py = HOLD_CONFIG[:y] + grid_center_offset(y, y_len, block_size)
set_block_sprite_property(piece_sprites[count], id, px, py)
count += 1
end
end
# 未使用のスプライトは非表示に
piece_sprites[count...piece_sprites.length].each do |sprite|
sprite.text ''
sprite.position *INVISIBLE_PIECE_SPRITE_POSITION
sprite.color FREE_CELL_COLOR
end
end
#==============================================================================
# バッグシステムユーティリティ関数
#==============================================================================
# バッグを生成する
# 全ピースをシャッフル
# バッグシステムが無効ならランダムで1つ取得
#
# @param types [Array<Symbol>] バッグに格納するピースタイプ一覧
# @return [Array<Symbol>] バッグ
def next_bag(types)
return CONFIG[:enable_bags] ? types.shuffle : [types.sample]
end
プレー内容を公開する