前回ではゴーストの表示を行った
今回は消去ライン数やミノのカウント等の文字の表示を行う
文字の表示
pygameで文字を表示するにはfontの準備が必要みたい。
フォントはデフォルトのものを使用し、サイズは適当に指定
そして準備したfontを使って文字を描画するメソッドを追加する。
見栄えを考え、draw_txt_b
とdraw_txt
の2種類の描画メソッドを作成している。
-
draw_txt_b
はテキストを1文字ずつブロック状に分割してそれぞれを個別に描画する -
draw_txt
は単一のテキスト文字列を指定された位置に一つのブロックとして描画する
前者はHOLD
,NEXT
の固定した看板的な表示に用い、後者はライン消去数表示等の詳細的な情報に使用する。
class Game:
# ---中略---
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1024, 768))
+ self.font = pygame.font.Font(None, 32)
+ def draw_txt_b(self,txt,p,color=(255,255,255)):
+ for i, t in enumerate(txt):
+ self.draw_txt(t, (p.x + i) * TILE_SIZE + 8, p.y * TILE_SIZE + 8, *color)
+ def draw_txt(self,text, x, y, r=255,g=255,b=255):
+ text_surface = self.font.render(text, True, (r, g, b))
+ # 文字列を画面に描画
+ self.screen.blit(text_surface, (x, y))
ラインカウント
定数・変数を追加してライン消去時に値を増やす。
+line_cnt_pos = Pos(3,8)
class Game:
# ---中略---
+ line_cnt = 0
def line_check(self):
flg = False
for y in range(19, -2, -1):
n = 0
for x in range(10):
if self.matrix[y][x]!=0:
n += 1
if n != 10:
continue
else:
flg = True
+ self.line_cnt += 1
for x in range(10):
self.matrix[y][x] = OtherTiles.Vanish.tile
メインループで表示
消去ライン数をカウントするようにしたので表示する。
さっき実装したメソッドを実行すればOK。
class Game:
# ---中略---
def run(self):
# ---中略---
while running:
# ---中略---
# 21段目チラ見せ
self.screen.fill((0, 0, 0),
(10*TILE_SIZE,0,12*TILE_SIZE,TILE_SIZE*2//3)
)
# 文字の表示
+ self.draw_txt_b("HOLD",hold_pos)
+ self.draw_txt_b("NEXT",next_pos_lst[0])
+ p = line_cnt_pos
+ self.draw_txt(f"Line: {self.line_cnt:03d}", p.x * TILE_SIZE + 8, p.y * TILE_SIZE + 8)
+ self.draw_txt(f"mino: {self.put_mino_cnt:03d}", p.x * TILE_SIZE + 8, (p.y+1) * TILE_SIZE + 8)
pygame.display.flip()
clock.tick(60)
動作確認
実行してみた結果を録画
動画では適当に40ライン程度消しているが
画面にHOLD、NEXT、Line、minoと表示されるようになり、
ミノを設置するたびにminoの数が加算され、
ライン消去するたびにLineの数が加算されることを確認できる。
まとめ
今回は文字の表示を実装した
pygameのfontインスタンスを生成して、描画する処理を行うメソッドを作成。
ライン消去数を数える変数を追加して、
HOLD
,NEXT
や消去ライン数等の文字の表示を実装した。
付録
main.py
main.py
from collections import deque
from copy import deepcopy
from random import choice
import pygame
from pytmx import util_pygame as tmx_util
from mino import Mino7Bag, RandomBag, Mino, OtherTiles
from key import Key
from pos import Pos
TILE_SIZE = 32
MATRIX_SIZE = (20,10)
MATRIX_POS = (11,1)
WAIT = 60
GRAVITY = 60
SOFT_DROP_SPEED = 20
start_pos = Pos(3,-2)
hold_pos = Pos(4, 3)
next_pos_lst = tuple(Pos(24,3+3*i) for i in range(6))
line_cnt_pos = Pos(3,8)
class Game:
# 番兵用に連想配列でラクする
matrix = {
i:{
j:0 for j in range(-1,11)
} for i in range(-20,21)
}
is_debug = False
key = Key()
mino : Mino
put_mino_cnt = 0
lockdown_cnt = 0
gameover = False
bag = Mino7Bag()
nexts = deque()
hold_mino = None
is_hold_used = False
g_cnt = 0
wait_count = WAIT
is_draw_mino = True
line_cnt = 0
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1024, 768))
self.font = pygame.font.Font(None, 32)
def draw_tile(self, tmx, x, y, t):
tile_image = tmx.get_tile_image_by_gid(t)
if tile_image:
self.screen.blit(tile_image, (x * TILE_SIZE, y * TILE_SIZE))
def for_debug(self):
pass
def pop_bag(self):
p = self.nexts.popleft()
self.nexts.append(self.bag.get_mino())
return p
def mino_setup(self):
self.wait_count = WAIT
self.lockdown_cnt = 0
self.is_draw_mino = True
self.is_hold_used = False
t = self.pop_bag()
self.put_mino_cnt+=1
self.mino = Mino(t,start_pos,0)
if not self.can_mino_move(self.mino,Pos(0,0),0):
for i in range(3):
if self.can_mino_move(self.mino,Pos(0,-i),0):
self.mino.move(Pos(0,-i))
break
else:
self.gameover = True
def draw_txt_b(self,txt,p,color=(255,255,255)):
for i, t in enumerate(txt):
self.draw_txt(t, (p.x + i) * TILE_SIZE + 8, p.y * TILE_SIZE + 8, *color)
def draw_txt(self,text, x, y, r=255,g=255,b=255):
text_surface = self.font.render(text, True, (r, g, b))
# 文字列を画面に描画
self.screen.blit(text_surface, (x, y))
def put_mino(self):
self.is_draw_mino = False
m = self.mino
flg = True
for p in m.get_shape():
self.matrix[p.y][p.x] = m.m.tile
if p.y >= 0:
flg = False
if flg:
self.gameover = True
def line_check(self):
flg = False
for y in range(19, -2, -1):
n = 0
for x in range(10):
if self.matrix[y][x]!=0:
n += 1
if n != 10:
continue
else:
flg = True
self.line_cnt += 1
for x in range(10):
self.matrix[y][x] = OtherTiles.Vanish.tile
return flg
def clear_line(self):
for y in range(19,-3,-1):
while self.matrix[y][0] == OtherTiles.Vanish.tile:
self.wait_count = WAIT // 2 - 2
for i in range(y, -20, -1):
for x in range(10):
t = self.matrix[i - 1][x]
self.matrix[i][x] = t
for x in range(10):
self.matrix[-20][x] = 0
def wait(self) -> None:
if self.wait_count == 0:
self.mino_setup()
else:
self.wait_count -=1
if self.wait_count == WAIT // 2 - 1:
self.put_mino()
self.line_check()
if self.wait_count == 1:
self.clear_line()
def hold(self) -> None:
if self.hold_mino is None:
self.hold_mino = self.mino.m
self.mino_setup()
else:
self.g_cnt = 0
self.wait_count = WAIT
self.mino.r = 0
self.hold_mino, t = self.mino.m, self.hold_mino
self.mino = Mino(t,start_pos,0)
if not self.can_mino_move(self.mino,Pos(0,0),0):
for i in range(3):
if self.can_mino_move(self.mino,Pos(0,-i),0):
self.mino.move(Pos(0,-i))
break
self.is_hold_used = True
def lock_down(self):
if self.wait_count == WAIT:
return
if self.lockdown_cnt < 15:
self.lockdown_cnt += 1
self.g_cnt = 0
self.wait_count = WAIT
def can_mino_move(self,mino:Mino,pos:Pos,r:int):
for p in mino.get_moved_mino(pos,r):
if p.x < 0 or p.x > 10 or self.matrix[p.y][p.x] != 0:
return False
return True
def move_and_rotate_mino(self):
# 待機時間が設定の半分以下の場合は待機
if self.wait_count <= WAIT // 2:
self.wait()
return
# ホールドの処理
if self.key.flags["is_hold"] and not self.is_hold_used:
self.hold()
return
vpos = Pos()
vr = 0
# 床に着地しているかのチェック
is_floor_landed = not self.can_mino_move(self.mino, Pos(0, 1), 0)
# 回転と移動のフラグをチェックして状態を更新
if self.key.flags["is_left_rot"]:
vr += 3
elif self.key.flags["is_right_rot"]:
vr += 1
elif self.key.flags["is_left_move"]:
vpos += Pos(-1, 0)
elif self.key.flags["is_right_move"]:
vpos += Pos(1, 0)
# ソフトドロップと重力の処理
if not is_floor_landed and self.key.flags["is_soft_drop"]:
self.g_cnt += SOFT_DROP_SPEED
elif self.g_cnt < GRAVITY:
self.g_cnt += 1
if is_floor_landed:
self.wait_count -= 1
# 重力によるミノの移動
while self.g_cnt >= GRAVITY:
if self.can_mino_move(self.mino, Pos(0, 1), 0):
self.mino.move(Pos(0, 1))
self.g_cnt -= GRAVITY
self.wait_count = WAIT
else:
is_floor_landed = True
self.g_cnt = 0
break
# ハードドロップの処理
if self.key.flags["is_hard_drop"]:
while self.can_mino_move(self.mino, Pos(0, 1), 0):
self.mino.move(Pos(0, 1))
self.put_mino()
if self.line_check():
self.wait_count = WAIT // 2 - 2
else:
self.wait_count = 2
# 可能であればミノを移動または回転
if vpos.x == 0 and vpos.y == 0 and vr == 0:
pass
elif self.can_mino_move(self.mino, vpos, vr):
self.mino.move(vpos, vr)
if vpos.y > 0:
self.lockdown_cnt = 0
else:
self.lock_down()
elif vr != 0:
# SRSの実行
rot1 = self.mino.r % 4
rot2 = (self.mino.r + vr) % 4
for p in self.mino.m.srs.get_rotate_offsets(rot1, rot2):
if self.can_mino_move(self.mino, vpos + p, vr):
self.mino.move(vpos + p, vr)
self.lock_down()
break
def run(self):
clock = pygame.time.Clock()
running = True
tmx_data = tmx_util.load_pygame('field.tmx')
mino_block = tmx_util.load_pygame('mino_block.tmx')
for _ in range(len(next_pos_lst)):
self.nexts.append(self.bag.get_mino())
self.mino_setup()
gameover_cnt = 0
gameover_cnt_sub = 0
# 番兵を設置
for i in range(-20,21):
for j in range(-2,12):
if j < 0 or j >= 10:
self.matrix[i][j] = 1
else:
self.matrix[i][j] = 0
for i in range(-2,12):
self.matrix[20][i] = 1
if self.is_debug :self.for_debug()
while running:
if not self.gameover:
self.key.process_key_input() # キー入力の処理
self.move_and_rotate_mino()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
self.screen.fill((0, 0, 0))
for layer in tmx_data.layers:
if layer.name == "mainfield":
for x, y, gid in layer.iter_data():
self.draw_tile(tmx_data, x, y, gid)
if self.gameover:
# ゲームオーバーの演出、下から灰色になっていく
for i in range(20,19-gameover_cnt,-1):
for j in range(10):
if self.matrix[i][j] != 0:
self.matrix[i][j] = OtherTiles.Vanish.tile
if gameover_cnt_sub == 2:
gameover_cnt = min(22, gameover_cnt+1)
gameover_cnt_sub = 0
else: gameover_cnt_sub += 1
x, y = MATRIX_POS
for i in range(-1, MATRIX_SIZE[0]):
for j in range(MATRIX_SIZE[1]):
self.draw_tile(mino_block, x+j, y+i, self.matrix[i][j])
if not self.gameover:
if self.is_draw_mino:
#ghost
gst = deepcopy(self.mino)
while self.can_mino_move(gst,Pos(0,1),0):
gst.move(Pos(0,1))
for p in gst.get_shape():
self.draw_tile(mino_block, x+p.x, y+p.y, gst.m.ghost)
# current
for p in self.mino.get_shape():
self.draw_tile(mino_block, x+p.x, y+p.y, self.mino.m.tile)
# NEXT
for i, (t, p) in enumerate(zip(self.nexts,next_pos_lst)):
for pt in t.shape_pos[0]:
np = pt + p
self.draw_tile(mino_block, np.x, np.y, t.tile)
# HOLD
if self.hold_mino is not None:
for p in self.hold_mino.shape_pos[0]:
hp = p + hold_pos
self.draw_tile(mino_block, hp.x, hp.y, self.hold_mino.tile)
# 21段目チラ見せ
self.screen.fill((0, 0, 0),
(10*TILE_SIZE,0,12*TILE_SIZE,TILE_SIZE*2//3)
)
# 文字の表示
self.draw_txt_b("HOLD",hold_pos)
self.draw_txt_b("NEXT",next_pos_lst[0])
p = line_cnt_pos
self.draw_txt(f"Line: {self.line_cnt:03d}", p.x * TILE_SIZE + 8, p.y * TILE_SIZE + 8)
self.draw_txt(f"mino: {self.put_mino_cnt:03d}", p.x * TILE_SIZE + 8, (p.y+1) * TILE_SIZE + 8)
pygame.display.flip()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
game = Game()
game.run()