2010年3月7日

MiyakoでTetris


先日のエントリのとおり、Miyakoを使って、Tetrisを作ってみた。
ずいぶん時間がかかった。
Miyakoはあんまリアルタイムゲーム向きのライブラリじゃないのかな?
動きがぎくしゃく&キー入力が微妙。

これをもう少し遊べるように改造するのはありかもしれないけど、とりあえず、「できた!」ってことで、終わりにしておこうかな。

ちなみに所要時間は、4日ぐらいかな。
スコアをつける、落下スピードを変える(早くなる)、ブロック消すときのイフェクト、次のブロックを表示、などの改善が考えられそう。
というか、こういうのを改善しないと、ゲームとしてはおもしろくないですね。

ソースは、以下に貼り付けておきます。


# -*- coding: utf-8 -*-
# Miyako Tetris
#
#
# 2010 mole

require 'miyako'

include Miyako

$sprite_size=16
$depth=2
Width = 10
Height = 20
Color[:gray] = [200,200,200,255]
Color[:orange] = [255, 160, 64]
Color[:dark_orange] = [80, 50, 20]
Color[:half_orange] = [128, 80, 32]
# 画面描画自動化のために、Sprite.[]=メソッドで登録
# Sprite[:blocks] = @blocks
# Sprite[:red_ruby] = @red_ruby
# Sprite[:blue_dia] = @blue_dia




$blockcolors = Array.new(7)
$blockcolors[0] = "black"
$blockcolors[1] = "green"
$blockcolors[2] = "cyan"
$blockcolors[3] = "blue"
$blockcolors[4] = "orange"
$blockcolors[5] = "purple"
$blockcolors[6] = "yellow"
$blockcolors[7] = "red"
$blockcolors[8] = "gray"

Blockparts = Array.new(7)
Blockparts[0] = []
Blockparts[1] = [[-1,0],[0,0],[0,1],[1,1]] # tetris
Blockparts[2] = [[0,0],[1,0],[-1,1],[0,1]] # invers tetris
Blockparts[3] = [[0,0],[0,1],[1,1],[2,1]] # L
Blockparts[4] = [[-1,0],[0,0],[1,0],[2,0]] # bar
Blockparts[5] = [[0,0],[0,1],[1,0],[2,0]] # invers L
Blockparts[6] = [[-1,0],[0,0],[1,0],[0,1]] # T
Blockparts[7] = [[0,0],[1,0],[0,1],[1,1]] # square


class Blocks
def initialize(tp = 1, x = 5, y = 1)
@tp = tp
@x = x
@y = y
@parts = Marshal.load(Marshal.dump(Blockparts[@tp]))
end
def move(nx, ny)
if check_move(nx,ny) then
@x=nx
@y=ny
end
end
def turn()
@parts.each {|bp|
bx = bp[0]
by = bp[1]
bp[0] = -by
bp[1] = bx
}
if !check_move() then
@parts.each {|bp|
bx = bp[0]
by = bp[1]
bp[0] = by
bp[1] = -bx
}
end
end
def check_move(nx = @x, ny = @y)
@parts.each {|bp|
new_pos = nx+bp[0]+(ny+bp[1])*(Width+2)
if !((new_pos>0) and (new_pos < (Width+2)*(Height+1)) and $stage[new_pos] == 0) then
return false
end
}
return true
end

def show()
@parts.each {|bp|
draw_block(@x+bp[0],@y+bp[1],$blockcolors[@tp])
}
end
def fix()
@parts.each {|bp|
$stage[@x+bp[0]+(@y+bp[1])*(Width+2)]=@tp
}
end
attr_accessor :x,:y,:parts, :tp
end

def erase_line (auto_erase = true)
filled_line = Array.new
for i in 1..19 do
check_line = 0
for j in 1..Width do
check_line = check_line+1 if $stage[j+i*(Width+2)]>0
end
if check_line == Width then
filled_line << i
end
end
filled_line.each{ |l|
for i in 0..l-1 do
for j in 1..Width do
$stage[j+(l-i)*(Width+2)]=$stage[j+(l-i-1)*(Width+2)]
end
end
}

end

def new_game()
@bl1=Blocks.new(1)
@tet_timer = WaitCounter.new(300)
$start = 1
$gameover = false
@tet_timer.start
clear_stage()
end

def s_pos(x,y)
return x+y*(Width+2)
end

def draw_stage()
for i in 0..Width+1 do
for j in 0..Height do
draw_block(i,j,$blockcolors[$stage[s_pos(i,j)]])
end
end
@bl1.show()
end

def clear_stage()
$stage = Array.new((Width+2)*(Height+1),0)
for i in 0..Height do
$stage[s_pos(0,i)] = 8
$stage[s_pos(Width+1,i)] = 8
end
for i in 0..(Width+2) do
$stage[s_pos(i, Height)] = 8
end
end

def draw_block(pos_x,pos_y,col)
x=pos_x*$sprite_size
y=pos_y*$sprite_size
if col != "black" then
Drawing.rect(Screen, [x,y,$sprite_size,$sprite_size],eval(':'+col),true)
Drawing.rect(Screen, [x+$depth,y+$depth,$sprite_size-$depth*2,$sprite_size-$depth*2],eval(':dark_'+col),true)
Drawing.rect(Screen, [x+$sprite_size-$depth,y,$depth,$sprite_size],eval(':half_'+col),true)
Drawing.rect(Screen, [x,y+$sprite_size-$depth,$sprite_size,$depth],eval(':half_'+col),true)
end
end


def player_move()
input_key = Input.pushed_amount
if (input_key[1]==1) then
@bl1.move(@bl1.x, @bl1.y+1)
elsif (input_key[1]==-1) then
@bl1.turn()
elsif !(input_key[0]==0) then
@bl1.move(@bl1.x+input_key[0], @bl1.y)
end
end

def block_down()
if ((@tet_timer.now % 3 ) == 0)
if @bl1.check_move(@bl1.x, @bl1.y+1) then
@bl1.y = @bl1.y+1
else
block_new()
end
end
end

def block_new()
@bl1.fix()
erase_line(true)
@bl1=Blocks.new(rand(max = 7) + 1)
@tet_timer.reset
if !@bl1.check_move() then
$gameover = true
end
end

$start = 0
$gameover = false
Miyako.main_loop do
break if Input.quit_or_escape?
if ($start==0) then
new_game()
elsif $gameover then
if Input.pushed_any?(:btn1) then
$start = 0
end
else
player_move()
block_down()
end
draw_stage()
end