前回作成?生成?した、CUIで遊べるカードゲームをGUI画面で遊べるようにする。
Pythonの知識がない私が頼るのはchatGPT様だ。
とりあえずわがまま言ってみる。
「このゲームをGUI画面で遊べるようにコードを作成してください」
回答:
以下は、Tkinterを使用して作成したカードバトルゲームのGUIの一部です。このコードを基にして、GUI画面で遊べるカードバトルゲームを構築することができます。
import tkinter as tk
import random
# カードデッキを作成
suits = ['ハート', 'ダイヤ', 'スペード', 'クラブ']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'ジャック', 'クイーン', 'キング', 'エース']
deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]
# プレイヤーを作成
player1 = {'名前': 'プレイヤー1', '手札': []}
player2 = {'名前': 'コンピュータ', '手札': []}
# ゲームを初期化
def initialize_game():
player1['手札'] = []
player2['手札'] = []
for _ in range(5):
player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
update_display()
# ゲームの進行
def play_round():
player1_card = player1['手札'].pop(random.randint(0, len(player1['手札']) - 1))
player2_card = player2['手札'].pop(random.randint(0, len(player2['手札']) - 1))
winner = battle(player1_card, player2_card)
if winner == player1:
result_label.config(text=f"{player1['名前']}の勝利!")
elif winner == player2:
result_label.config(text=f"{player2['名前']}の勝利!")
else:
result_label.config(text="引き分け")
update_display()
# カードのバトル
def battle(player1_card, player2_card):
rank_order = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'ジャック', 'クイーン', 'キング', 'エース']
if rank_order.index(player1_card['ランク']) > rank_order.index(player2_card['ランク']):
return player1
elif rank_order.index(player1_card['ランク']) < rank_order.index(player2_card['ランク']):
return player2
else:
return None
# 画面の更新
def update_display():
player1_label.config(text=f"{player1['名前']}の手札: {', '.join([card['ランク'] for card in player1['手札']])}")
player2_label.config(text=f"{player2['名前']}の手札: {', '.join([card['ランク'] for card in player2['手札']])}")
# Tkinterウィンドウを作成
root = tk.Tk()
root.title("カードバトルゲーム")
# ボタンとラベルを配置
start_button = tk.Button(root, text="ゲームを始める", command=initialize_game)
start_button.pack()
play_button = tk.Button(root, text="ラウンドをプレイ", command=play_round)
play_button.pack()
result_label = tk.Label(root, text="")
result_label.pack()
player1_label = tk.Label(root, text="")
player1_label.pack()
player2_label = tk.Label(root, text="")
player2_label.pack()
# ゲームを初期化
initialize_game()
# Tkinterウィンドウを表示
root.mainloop()
こりゃ楽ちんだぜ!と喜んだのも束の間、実行してみるとコレジャナイ感が。
でも、これをベースにカスタマイズして作り上げるってことだね。
どんどんchatGPTに要望や質問を投げてコードをカスタマイズしていく。
「手札のカードを横に並べて表示し、その中からプレイヤーが選んでバトルするようにしたい」
「プレイヤー1のカードをマウスでクリックして選択できるようにしたい」
「カードに画像を表示したい」
カード画像はStableDiffusionで生成しますが、サイズが小さいとコレジャナイ画像が生成されてしまうので、512 x 768で生成しました。
ゲームで表示するにはデカすぎるので縮小します。
画像変形ツールを使って縮小しておくのが常套手段ですが、
ここはあえてPythonでやります。
「カードの画像サイズを幅128、高さ192にしたい」
回答:
〜 Pillow(PIL)ライブラリを使用して画像をリサイズすることができます。
ここで、生成されたコードの動作には、追加のライブラリをインストールする作業が必要となりました。
Windowsならコマンドプロンプトから、Macならターミナルから、Pillowライブラリをインストールします。
pip install Pillow
うまくいかないときはググってみましょう。
まだまだchatGPT様のお世話になります。
「canvasの背景色を深緑にしたい」
「カード画像5枚横に並べて表示し、クリックされたカード画像に外枠を表示、前にクリックし表示していた枠は消すようにしてください」
「それぞれのカード画像の下にランクを表示したい」
・・・
そんなこんなで出来上がった「ドラゴンカードゲーム」
import tkinter as tk
from tkinter import messagebox
from tkinter import PhotoImage
from PIL import Image, ImageTk # Pillowライブラリをインポート
import random
# カードデッキを作成
suits = ['ハート', 'ダイヤ', 'スペード', 'クラブ']
ranks = ['ドラコ', 'リザードマン', 'ドレイク', 'ヒドラ', 'サーペント', 'ワイバーン', 'アジュールドラゴン', 'クリムゾンドラゴン', '西洋ドラゴン', '東洋ドラゴン']
deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]
card_width = 128
card_height = 192
card_interval = 20
card_p1_y = 310
card_p2_y = 40
# プレイヤーを作成
player1 = {'名前': 'あなた', '手札': []}
player2 = {'名前': 'コンピュータ', '手札': []}
#プレイヤー1カード(表示用)
player1_cards = {}
# 選択されたカードを格納する変数
selected_card = None
# 外枠の色を指定
frame_color = "red"
# 選択されたカードのインデックスを保持する変数
selected_card_index = None
# ゲームの設定
rounds = 5 # ラウンド数を指定
round_num = 0
player1_score = 0
player2_score = 0
# 各ランクに対応する画像ファイルを辞書に格納
original_images = {}
card_images = {}
text_objects = []
# ゲームを初期化
def initialize_game():
global round_num
global player1_score
global player2_score
global player1_cards
round_num = 0
player1_score = 0
player2_score = 0
player1_cards = []
player1['手札'] = []
player2['手札'] = []
deck = []
deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]
for _ in range(5):
player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
# 選択されたカードをクリア
global selected_card
selected_card = None
global selected_card_index
canvas.delete(selected_card_index)
selected_card_index = None
global text_objects
for text_object in text_objects:
canvas.delete(text_object)
text_objects = []
#update_display()
# プレイヤー1の手札を描画
canvas.delete("player1_hand")
x = card_interval
for card in player1['手札']:
draw_card(canvas, x, card_p1_y, card, 1)
x += (card_width + card_interval)
# プレイヤー2(コンピュータ)の手札を描画
canvas.delete("player2_hand")
x = card_interval
for card in player2['手札']:
draw_card(canvas, x, card_p2_y, card, 0)
x += (card_width + card_interval)
result_label1.config(text="")
result_label2.config(text="")
result_label3.config(text="")
# ゲームの進行
def play_round():
global player1_score
global player2_score
global round_num
if round_num > rounds:
messagebox.showinfo("エラー", "「ゲームを始める」をクリックしてください")
return
# プレイヤー1(人間)がカードを選ぶ
global selected_card # グローバル変数として選択されたカードを保持
if selected_card is None:
messagebox.showinfo("エラー", "カードを選択してください。")
return
player1_card = selected_card
round_num += 1
# プレイヤー2(コンピュータ)が一番強いカードを選ぶ
player2_card = select_strongest_card(player2['手札'])
result_label1.config(text=f"{player2['名前']}のカード : {player2_card['ランク']} " + "★" * (ranks.index(player2_card['ランク']) + 1))
result_label2.config(text=f"{player1['名前']}のカード : {player1_card['ランク']} " + "★" * (ranks.index(player1_card['ランク']) + 1))
winner = battle(player1_card, player2_card)
if winner == player1:
player1_score += 1
result_label3.config(text=f"{player1['名前']}の勝利!")
elif winner == player2:
player2_score += 1
result_label3.config(text=f"{player2['名前']}の勝利!")
else:
result_label3.config(text="引き分け")
if round_num >= rounds:
round_num += 1
# 最終結果を表示
if player1_score > player2_score:
messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n{player1['名前']}の勝利!")
elif player1_score < player2_score:
messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n{player2['名前']}の勝利!")
else:
messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n引き分け!")
return
# プレイヤー1の手札から選択されたカードを削除
player1['手札'].remove(selected_card)
# プレイヤー2の手札から選択されたカードを削除
player2['手札'].remove(player2_card)
# カードをデッキからランダムに選び、手札に加える
player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
# 選択されたカードをクリア
selected_card = None
global selected_card_index
canvas.delete(selected_card_index)
selected_card_index = None
global text_objects
for text_object in text_objects:
canvas.delete(text_object)
text_objects = []
# プレイヤー1の手札を描画
canvas.delete("player1_hand")
x = card_interval
for card in player1['手札']:
draw_card(canvas, x, card_p1_y, card, 1)
x += (card_width + card_interval)
# プレイヤー2(コンピュータ)の手札を描画
canvas.delete("player2_hand")
x = card_interval
for card in player2['手札']:
draw_card(canvas, x, card_p2_y, card, 0)
x += (card_width + card_interval)
def battle(player1_card, player2_card):
global ranks
if ranks.index(player1_card['ランク']) > ranks.index(player2_card['ランク']):
return player1
elif ranks.index(player1_card['ランク']) < ranks.index(player2_card['ランク']):
return player2
else:
return None
def select_strongest_card(player_hand):
global ranks
strongest_card = None
for card in player_hand:
if not strongest_card or ranks.index(card['ランク']) > ranks.index(strongest_card['ランク']):
strongest_card = card
return strongest_card
def draw_card(canvas, x, y, card, sw):
suit = card['スート']
rank = card['ランク']
rank_i = "★" * (ranks.index(rank) + 1)
card_text = f"{rank}\n{rank_i}"
# カード画像を配置
card_object = canvas.create_image(x, y, anchor=tk.NW, image=card_images[rank])
if sw > 0:
canvas.tag_bind(card_object, "", select_card)
# カードテキストを配置
text_object = canvas.create_text(x, (y + card_height + 5), text=card_text, anchor="nw", fill="white", font=("Helvetica", 12))
text_objects.append(text_object)
# クリックされたカードの処理
def select_card(event):
global selected_card_index
# 前に選択されたカードの外枠を削除
if selected_card_index is not None:
canvas.delete(selected_card_index)
global selected_card
x, y = event.x, event.y # クリックされた位置のx座標とy座標を取得
for card in player1['手札']:
card_x, card_y = get_card_position(player1['手札'].index(card))
if card_x <= x <= card_x + card_width and card_y <= y <= card_y + card_height:
selected_card = card
break
# クリックされたカードの外枠を表示
selected_card_index = canvas.create_rectangle((card_x - 2), (card_y - 2), (card_x + card_width + 2), (card_y + card_height + 2), outline=frame_color, width=2)
def get_card_position(index):
x = card_interval + index * (card_width + card_interval)
return x, card_p1_y
# Tkinterウィンドウを作成
root = tk.Tk()
root.title("カードバトルゲーム")
# Canvasを作成
canvas = tk.Canvas(root, width=800, height=550, bg="dark green")
canvas.pack()
# デフォルトカードに対応する画像ファイルを読み込む
default_card_image = Image.open('default.png')
# Stable Diffusion で生成した画像のサイズが大きいため縮小します
# 幅と高さ比2:3で512*768とかで生成(サイズ小さいとコレジャナイ画像になるため)
# 各ランクの画像ファイルを読み込み、Tkinter PhotoImageオブジェクトに変換して格納
for rank in ranks:
image_file = f"{rank}.png" # ランクごとの画像ファイル名を生成
original_images[rank] = Image.open(image_file)
# 対応するランクの画像を辞書から取得
original_image = original_images.get(rank)
if original_image:
# 画像を指定のサイズにリサイズ
resized_image = original_image.resize((card_width, card_height), Image.LANCZOS)
# リサイズした画像をTkinter PhotoImageオブジェクトに変換
card_images[rank] = ImageTk.PhotoImage(resized_image)
else:
# デフォルト画像を指定のサイズにリサイズ
resized_image = default_card_image.resize((card_width, card_height), Image.LANCZOS)
# リサイズした画像をTkinter PhotoImageオブジェクトに変換
card_images[rank] = ImageTk.PhotoImage(resized_image)
#テキスト配置
canvas.create_text(card_interval, card_p1_y - 20, text=f"{player1['名前']}のカード", anchor="w", fill="white", font=("Helvetica", 14))
canvas.create_text(card_interval, card_p2_y - 20, text=f"{player2['名前']}のカード", anchor="w", fill="white", font=("Helvetica", 14))
# ボタンとラベルを配置
play_button = tk.Button(root, text="選んだカードで対決!", command=play_round)
play_button.pack()
start_button = tk.Button(root, text="ゲームを始める", command=initialize_game)
start_button.pack()
result_label1 = tk.Label(root, text="")
result_label1.pack()
result_label2 = tk.Label(root, text="")
result_label2.pack()
result_label3 = tk.Label(root, text="")
result_label3.pack()
# ゲームを初期化
initialize_game()
# Tkinterウィンドウを表示
root.mainloop()
生成されたコードを付け加えたり変更したりで大変だった感じはあるが
これを知識が無いところから作成することを考えたら
もう、素晴らしいね。
出来上がったコードは、まだ変なところとかあったり、
コンピュータのカードを隠せるようにするなど、改良の余地はあるけど、
なんか、標題の目的を達成できた感がある。
今回はドラゴンで男の子向けだが、女の子向けの画像も用意したいなと思っている。
オトナ向けは・・・やめておこう。