
マイケル
餅おいしいな〜〜〜
カタカタカタカタ

エレキベア
やっぱり正月はお餅クマね

マイケル
いくらでも食べれるぜ
カタカタカタカタ

エレキベア
・・・何カタカタしてるクマ?

マイケル
これかい?
これはね・・・
これはね・・・


マイケル
Pythonで作ったあけおめパズル さ!!!

エレキベア
そうなのクマね

マイケル
・・・。

エレキベア
・・・。

エレキベア
お餅おいしいクマね

マイケル
どんなゲームか聞いてよ!!!
あけおめゲームの設計

マイケル
あけおめゲームは一言でいうなら
ぷよぷよ風の文字連結ゲームだ!
ぷよぷよ風の文字連結ゲームだ!

エレキベア
(勝手に解説が始まったクマ・・・)

マイケル
ルールはこんな感じ!


マイケル
落ちてくる「あけおめ」の文字を探してなぞって消すゲーム!
一つ揃えると最下段3段も消す仕様にしました!
一つ揃えると最下段3段も消す仕様にしました!

エレキベア
よく見るタイプの落ち物パズルクマね

マイケル
テトリスやぷよぷよ系のゲームだね。
参考書は前回と同じくこちらを使用しました!
参考書は前回と同じくこちらを使用しました!

マイケル
この本の落ち物パズルゲームをカスタマイズして今回の動きにしています!
実装方法についてもっと詳しく知りたい方はぜひ読んでみてね!
実装方法についてもっと詳しく知りたい方はぜひ読んでみてね!

エレキベア
これは良本クマ〜〜〜〜
あけおめゲームの開発

マイケル
それでは開発方法について説明します!

エレキベア
(聞いてないクマけどなぁ)
画像の作成

マイケル
まずは使用する画像素材を作成します!
・背景
・落ちてくるボールの画像
・揃った時に帰るボール
・カーソル
の画像を用意しましょう!
・背景
・落ちてくるボールの画像
・揃った時に帰るボール
・カーソル
の画像を用意しましょう!

↑ball_bg.png(背景)




↑ball_1.png 〜 ball_4.png(あけおめボール)





↑ball_gold_1.png 〜 ball_gold_5.png(謹賀新年ボール、祝ボール)

↑ball_cursor.png(カーソル)

マイケル
背景については、今回は枠の大きさを24px、
正方形一つあたりの大きさを72pxにしておきます!
また、正方形の数は横8列 * 縦10列で作成します!
正方形一つあたりの大きさを72pxにしておきます!
また、正方形の数は横8列 * 縦10列で作成します!

↑ステージの大きさ

エレキベア
素材準備完了クマ〜〜〜
処理の実装

マイケル
ソース全体は以下のようになります!
import tkinter
import random
###############
# マウス操作用
###############
cursor_x = 0
cursor_y = 0
mouse_x = 0
mouse_y = 0
mouse_c = 0
# マウス移動処理
def mouse_move(e):
global mouse_x, mouse_y
mouse_x = e.x
mouse_y = e.y
# マウスクリック処理
def mouse_press(e):
global mouse_c
mouse_c = 1
###############
# ゲーム内変数
###############
index = 0
chain_list = []
generate_timer = 0
delete_timer = 0
ball = []
year = 2020
# ステージの定義
def init_stage():
global ball
ball = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
]
# ボール描画処理
def draw_ball():
for y in range(10):
for x in range(8):
if ball[y][x] > 0:
cvs.create_image(x*72+60, y*72+60, image=img_ball[ball[y][x]], tag="BALL")
# ボール落下処理
def drop_ball():
# 下から確認、最下段は除く
for y in range(8, -1, -1):
for x in range(8):
if ball[y][x] != 0 and ball[y+1][x] == 0:
ball[y+1][x] = ball[y][x]
ball[y][x] = 0
# テキスト描画処理
def draw_txt(txt, x, y, siz, col, tg):
fnt = ("sans-serif", siz, "bold")
# 影も追加
cvs.create_text(x, y, text=txt, fill=col, font=fnt, tag=tg)
cvs.create_text(x + 2, y + 2, text=txt, fill="black", font=fnt, tag=tg)
# ゲームメイン処理
def game_main():
global cursor_x, cursor_y, mouse_c, index, chain_list, generate_timer, delete_timer, year
###############
# タイトル
###############
if index == 0:
draw_txt("スタート", 312, 560, 50, "light green", "TITLE")
# マウスボタン押下
if mouse_c == 1:
mouse_c = 0
cvs.delete("TITLE")
index = 1
# カーソルの描画(非表示)
cvs.delete("CURSOR")
# ボールの描画
cvs.delete("BALL")
draw_ball()
###############
# ゲーム中
###############
elif index == 1:
# ボール生成処理
if generate_timer > 0:
generate_timer -= 1
elif generate_timer == 0:
# 生成時間0秒:ボール生成
for x in range(8):
# 生成箇所にボールがあった場合ゲームオーバー
if not ball[0][x] == 0:
index = 2
break
if random.randint(1, 2) == 1:
ball[0][x] = random.randint(1, 4)
# 次の生成時間を設定(25秒)
generate_timer = 20
# ボール削除処理
if delete_timer > 0:
delete_timer -= 1
# 削除時間5秒:「祝」に変換
if delete_timer == 5:
# 最下段3段を全て「祝」に変換
for y in range(9, 6, -1):
for x in range(8):
if ball[y][x] in (1, 2, 3, 4):
ball[y][x] = 9
elif delete_timer == 0:
# 削除時間0秒:ボール削除
for y in range(10):
for x in range(8):
# 「謹賀新年」「祝」を削除
if ball[y][x] in (5, 6, 7, 8, 9):
ball[y][x] = 0
# マウスがステージ内の場合
if 24 <= mouse_x < 24+72*8 and 24 <= mouse_y < 24+72*10:
cursor_x = int((mouse_x-24)/72)
cursor_y = int((mouse_y-24)/72)
# マウスボタン押下時
if mouse_c == 1:
# ボールを繋いでいない時、または次のボールに繋いだ時
max_chain = len(chain_list)
if max_chain == 0 or chain_list[max_chain-1][0] != cursor_y or chain_list[max_chain-1][1] != cursor_x:
# 繋いでる文字の次の文字の場合
if ball[cursor_y][cursor_x] == max_chain + 1:
if ball[cursor_y][cursor_x] == 4:
# あけおめを繋いだら「謹賀新年」に変換
chain_list.append([cursor_y, cursor_x])
for item in chain_list:
ball[item[0]][item[1]] = ball[item[0]][item[1]] + 4
# チェインリストクリア
chain_list = []
mouse_c = 0
# 削除までの時間を設定(15秒)
delete_timer = 15
# 年度を加算
year += 1
else:
# あけおめを繋げる
chain_list.append([cursor_y, cursor_x])
else:
# あけおめの文字以外に繋いだ場合
# チェインリストクリア
chain_list = []
mouse_c = 0
else:
# マウスボタン離した場合
# チェインリストクリア
chain_list = []
# カーソルの描画
cvs.delete("CURSOR")
cvs.create_image(cursor_x*72+60, cursor_y*72+60, image=cursor, tag="CURSOR")
# ボールの描画
drop_ball()
cvs.delete("BALL")
draw_ball()
###############
# ゲームオーバー
###############
elif index == 2:
draw_txt("ゲームオーバー", 312, 560, 50, "purple", "TITLE")
# マウスボタン押下
if mouse_c == 1:
mouse_c = 0
cvs.delete("TITLE")
index = 0
# ステージとスコアを初期化
year = 2020
init_stage()
# 年度の描画
cvs.delete("YEAR")
draw_txt("年度:" + str(year), 135, 60, 32, "blue", "YEAR")
# 0.1秒後に再実行
root.after(100, game_main)
root = tkinter.Tk()
root.title("あけおめパズル")
root.resizable(False, False)
# マウスイベントの登録
root.bind("<Motion>", mouse_move)
root.bind("<ButtonPress>", mouse_press)
# Canvasの描画
cvs = tkinter.Canvas(root, width=912, height=768)
cvs.pack()
# 画像の取得
bg = tkinter.PhotoImage(file="ball_bg.png")
cursor = tkinter.PhotoImage(file="ball_cursor.png")
img_ball = [
None,
tkinter.PhotoImage(file="ball_1.png"),
tkinter.PhotoImage(file="ball_2.png"),
tkinter.PhotoImage(file="ball_3.png"),
tkinter.PhotoImage(file="ball_4.png"),
tkinter.PhotoImage(file="ball_gold_1.png"),
tkinter.PhotoImage(file="ball_gold_2.png"),
tkinter.PhotoImage(file="ball_gold_3.png"),
tkinter.PhotoImage(file="ball_gold_4.png"),
tkinter.PhotoImage(file="ball_gold_5.png")
]
cvs.create_image(456, 384, image=bg)
# ステージ生成
init_stage()
# メイン処理
game_main()
root.mainloop()

エレキベア
長くて分からないクマ

マイケル
細かくみていきます!
マウス操作の検知について

マイケル
マウスの操作については、下記のように
「<Motion>」「<ButtonPress>」をbind することによって検知しています。
検知後は各変数に値を設定します。
「<Motion>」「<ButtonPress>」をbind することによって検知しています。
検知後は各変数に値を設定します。
###############
# マウス操作用
###############
cursor_x = 0
cursor_y = 0
mouse_x = 0
mouse_y = 0
mouse_c = 0
# マウス移動処理
def mouse_move(e):
global mouse_x, mouse_y
mouse_x = e.x
mouse_y = e.y
# マウスクリック処理
def mouse_press(e):
global mouse_c
mouse_c = 1
・・・略・・・
# マウスイベントの登録
root.bind("<Motion>", mouse_move)
root.bind("<ButtonPress>", mouse_press)
・・・略・・・
↑マウス操作の検知ゲームメイン処理について

マイケル
ゲームのメイン処理についてはroot.after()メソッドに時間を設定することによってループ処理を行なっています。
下記の例では0.1秒毎にgame_main()メソッドを実行しています。
下記の例では0.1秒毎にgame_main()メソッドを実行しています。
# ゲームメイン処理
def game_main():
・・・略・・・
# 0.1秒後に再実行
root.after(100, game_main)
↑ゲームのメイン処理
エレキベア
UnityでいうUpdateメソッドの代わりクマね
画面の状態切り替えについて

マイケル
画面の状態については、index変数に「タイトル画面」「ゲーム中」「ゲームオーバー」の状態を設定することで処理を分けています。
###############
# ゲーム内変数
###############
index = 0
・・・略・・・
# ゲームメイン処理
def game_main():
global cursor_x, cursor_y, mouse_c, index, chain_list, generate_timer, delete_timer, year
###############
# タイトル
###############
if index == 0:
・・・略・・・
###############
# ゲーム中
###############
elif index == 1:
・・・略・・・
###############
# ゲームオーバー
###############
elif index == 2:
・・・略・・・

エレキベア
ループ処理の中で各処理を分岐しているクマね
ボールの描画と落下処理

マイケル
ここからゲームの処理内容に入っていきます。
ステージは 横8列 * 縦10列の配列 として定義し、
draw_ball()メソッド で描画しています。
ステージは 横8列 * 縦10列の配列 として定義し、
draw_ball()メソッド で描画しています。
# ステージの定義
def init_stage():
global ball
ball = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
]
# ボール描画処理
def draw_ball():
for y in range(10):
for x in range(8):
if ball[y][x] > 0:
cvs.create_image(x*72+60, y*72+60, image=img_ball[ball[y][x]], tag="BALL")
# ボール落下処理
def drop_ball():
# 下から確認、最下段は除く
for y in range(8, -1, -1):
for x in range(8):
if ball[y][x] != 0 and ball[y+1][x] == 0:
ball[y+1][x] = ball[y][x]
ball[y][x] = 0
・・・略・・・
# ゲームメイン処理
def game_main():
・・・略・・・
###############
# ゲーム中
###############
elif index == 1:
・・・略・・・
# ボールの描画
drop_ball()
cvs.delete("BALL")
draw_ball()
・・・略・・・
img_ball = [
None,
tkinter.PhotoImage(file="ball_1.png"),
tkinter.PhotoImage(file="ball_2.png"),
tkinter.PhotoImage(file="ball_3.png"),
tkinter.PhotoImage(file="ball_4.png"),
tkinter.PhotoImage(file="ball_gold_1.png"),
tkinter.PhotoImage(file="ball_gold_2.png"),
tkinter.PhotoImage(file="ball_gold_3.png"),
tkinter.PhotoImage(file="ball_gold_4.png"),
tkinter.PhotoImage(file="ball_gold_5.png")
]
・・・略・・・

マイケル
各ボールの画像は img_ball変数 に定義していて、
配列0番目はNone(何も表示しない)にしています。
配列0番目はNone(何も表示しない)にしています。
【img_ball配列の設定値】
1〜4:「あ」「け」「お」「め」のボール
5〜8:「謹」「賀」「新」「年」のボール
9: 「祝」のボール

エレキベア
ステージ配列内の数字を変えることで各ボールが表示されるクマね

マイケル
そういうことだね!
そして描画したボールは drop_ball()メソッド で徐々に落下するよう実装しています。
そして描画したボールは drop_ball()メソッド で徐々に落下するよう実装しています。
ボール生成処理

マイケル
ボールの生成処理は下記箇所で行なっています!
ループ時間とは別に generate_timer変数 を用意し、一定時間ごとにランダムで生成するよう実装しています。
ループ時間とは別に generate_timer変数 を用意し、一定時間ごとにランダムで生成するよう実装しています。
generate_timer = 0
・・・略・・・
###############
# ゲーム中
###############
elif index == 1:
# ボール生成処理
if generate_timer > 0:
generate_timer -= 1
elif generate_timer == 0:
# 生成時間0秒:ボール生成
for x in range(8):
# 生成箇所にボールがあった場合ゲームオーバー
if not ball[0][x] == 0:
index = 2
break
if random.randint(1, 2) == 1:
ball[0][x] = random.randint(1, 4)
# 次の生成時間を設定(25秒)
generate_timer = 20
・・・略・・・
↑ボール生成処理
エレキベア
ここで「あ」「け」「お」「め」のボールを生成するクマね
ボールを繋げる処理

マイケル
そしてあけおめの文字を繋げる判定処理は下記部分です!
ボタンが押されている状態でカーソルが移動した場合に
あけおめの順番で繋いでいるかチェックし、
正しい場合はchain_list変数に格納しています!
ボタンが押されている状態でカーソルが移動した場合に
あけおめの順番で繋いでいるかチェックし、
正しい場合はchain_list変数に格納しています!
###############
# ゲーム内変数
###############
index = 0
chain_list = []
generate_timer = 0
delete_timer = 0
ball = []
year = 2020
・・・略・・・
# マウスがステージ内の場合
if 24 <= mouse_x < 24+72*8 and 24 <= mouse_y < 24+72*10:
cursor_x = int((mouse_x-24)/72)
cursor_y = int((mouse_y-24)/72)
# マウスボタン押下時
if mouse_c == 1:
# ボールを繋いでいない時、または次のボールに繋いだ時
max_chain = len(chain_list)
if max_chain == 0 or chain_list[max_chain-1][0] != cursor_y or chain_list[max_chain-1][1] != cursor_x:
# 繋いでる文字の次の文字の場合
if ball[cursor_y][cursor_x] == max_chain + 1:
if ball[cursor_y][cursor_x] == 4:
# あけおめを繋いだら「謹賀新年」に変換
chain_list.append([cursor_y, cursor_x])
for item in chain_list:
ball[item[0]][item[1]] = ball[item[0]][item[1]] + 4
# チェインリストクリア
chain_list = []
mouse_c = 0
# 削除までの時間を設定(15秒)
delete_timer = 15
# 年度を加算
year += 1
else:
# あけおめを繋げる
chain_list.append([cursor_y, cursor_x])
else:
# あけおめの文字以外に繋いだ場合
# チェインリストクリア
chain_list = []
mouse_c = 0
else:
# マウスボタン離した場合
# チェインリストクリア
chain_list = []
↑ボールを繋げる処理
エレキベア
ここの処理が肝クマね
「あけおめ」まで繋げ終わったら「謹賀新年」に変える処理 も行なっているクマね
「あけおめ」まで繋げ終わったら「謹賀新年」に変える処理 も行なっているクマね
ボール削除処理

マイケル
そして最後にボール削除処理!
ボールを繋げる処理部分で設定された delete_timer変数をカウントダウンし、
削除の5秒前になったら最下段3段全て「祝」ボールに変えています。
ボールを繋げる処理部分で設定された delete_timer変数をカウントダウンし、
削除の5秒前になったら最下段3段全て「祝」ボールに変えています。

マイケル
あとは削除時間0秒になった時点で「謹賀新年」「祝」のボールを全て削除するよう実装すれば完了です!
delete_timer = 0
・・・略・・・
# ボール削除処理
if delete_timer > 0:
delete_timer -= 1
# 削除時間5秒:「祝」に変換
if delete_timer == 5:
# 最下段3段を全て「祝」に変換
for y in range(9, 6, -1):
for x in range(8):
if ball[y][x] in (1, 2, 3, 4):
ball[y][x] = 9
elif delete_timer == 0:
# 削除時間0秒:ボール削除
for y in range(10):
for x in range(8):
# 「謹賀新年」「祝」を削除
if ball[y][x] in (5, 6, 7, 8, 9):
ball[y][x] = 0
# マウスがステージ内の場合
if 24 <= mouse_x < 24+72*8 and 24 <= mouse_y < 24+72*10:
・・・略・・・
# 削除までの時間を設定(15秒)
delete_timer = 15

エレキベア
完成したクマ〜〜〜〜〜
おわりに

マイケル
駆け足でしたがざっと処理内容を解説しました!
どうだったかな??
どうだったかな??

エレキベア
簡単にそれらしいゲームができて楽しかったクマ〜〜

マイケル
Pythonは気軽に開発できるのがいいよね
今回作ったゲームは難易度が一定なので、
気が向いた方はぜひレベルデザインにも挑戦してみてね!
今回作ったゲームは難易度が一定なので、
気が向いた方はぜひレベルデザインにも挑戦してみてね!

エレキベア
作り込んでやるクマ〜〜〜〜

マイケル
それでは今日はこの辺で!
のんびりしたお正月をお過ごしくださいね!
のんびりしたお正月をお過ごしくださいね!

エレキベア
パズル作るクマ〜〜〜〜〜〜〜

マイケル
お餅食べよ〜〜〜〜
【Python】ぷよぷよ風の文字連結パズルゲームを作る【あけおめパズル】 〜完〜
コメント