このページでは、Python で tkinter を用いた「神経衰弱ゲーム」の作り方について解説していきます!
作成できる「神経衰弱ゲーム」は下記のようなものになります。
一人用のゲームなのでゲームとしては物足りないかもしれないですが、tkinter やプログラミングに慣れる目的としてはちょうど良い難易度の題材だと思います!
Contents
作成する「神経衰弱」ゲーム
今回作成する「神経衰弱」はマウスのクリックで操作を行うゲームとなります。
アプリを起動すると、下の図のように52枚のカード('2'
〜 '10'
と 'A'
, 'J'
, 'Q'
, 'K'
の13枚のカードをそれぞれ4枚ずつ用意)が裏返しされた状態で並べられます。
これらのカードは tkinter のキャンバス上に長方形を描画することで表現しています。
いずれかのカードをマウスでクリックすれば、そのカードが選択されてカードがめくられ、数字が表示されます。
さらに、カードを1枚選択した状態で他のカードをマウスでクリックすれば、そのカードも選択されてカードがめくられ、数字が表示されます。
もしこの時に、マウスのクリックにより選択された2枚のカードの数字が異なる場合、約1秒経過後に再びそれらのカードが裏返しされます。それらの裏返しされたカードは再びマウスでクリックして選択することが可能です。
それに対し、マウスでクリックして選択された2枚のカードの数字が同じ場合、それらの選択したカードの色がグレーに変化し、プレイヤーがその2枚のカードを獲得したことになります。
あとは、全てのカードを獲得するためにカードを選択する操作を繰り返していくだけです。
全てのカードを獲得した際には、下の図のように画面に "GAME CLEAR!!!"
が表示されます。
以上が今回作成する神経衰弱ゲームの概要となります。
神経衰弱ゲームを作成するポイント
続いて、神経衰弱ゲームを作成する上でポイントになる点について説明しておきます。
神経衰弱ゲームの作成は簡単そうにも思えますが、この神経衰弱を GUI アプリで作成することになると結構難しいです。
特に難しいのが、キャンバス上に描画した長方形を操作して「カードの数字の表示 / 非表示」を切り替える必要があるところです。
この辺りに焦点を当てながら、神経衰弱を作成する上でのポイントについて解説していきます。
スポンサーリンク
カードを選択する
まずは「カードを選択する」動作を実現するためのポイントについて解説していきます。
選択されたカードの数字を見えるようにする
前述の通り、アプリ起動時にはカードは全て裏返しされた状態なので数字が見えない状態になっています。
ただし、マウスのクリックによりカードが選択された際にはカードがめくられることになるため、カードの数字を見えるようにする必要があります。
今回は、このような動作を、キャンバス上に描画した長方形の「塗りつぶし色を変更する」ことで実現したいと思います。
具体的には、まずトランプのカードは「数字(テキスト)」と「長方形」の2つの図形を描画することで表現していきます。
アプリ起動時にはキャンバス上に数字を描画し、さらにその数字を上書きする形で長方形を描画します。この時、描画する長方形を特定の色で塗りつぶすことで、数字が長方形に覆われて見えないようにすることができます(見えなくなりますが、実際には数字はキャンバス上に存在します)。
さらにマウスのクリックによりカードが選択された際には、そのカードの長方形を「塗りつぶしなし」に設定します。
これにより、長方形の背面側に隠れていた数字が見えるようになり、選択したカードをめくる動作を表現することができます。
このような図形の塗りつぶし色の変更は、キャンバスの itemconfig
メソッドにより実現することができます。
# canvasはtkinter.Canvasクラスのインスタンス
canvas.itemconfig(図形ID or タグ名, fill="色名など")
上記の "色名など"
のところを ""
と空文字列にすれば、itemconfig
の第1引数で指定した 図形ID
or タグ名
の図形を「塗りつぶしなし」にすることができます。
また、"色名など"
のところを例えば "blue"
にすれば、itemconfig
の第1引数で指定した 図形ID
or タグ名
の図形を青色で塗りつぶすことができます。要は fill
オプションを指定することにより図形の塗りつぶし色を変更する事ができます。
itemconfig
のようなキャンバスに描画した図形を操作するメソッドや、メソッドに指定する 図形ID
や タグ名
については下記ページにまとめていますので、ご存知ない方は是非読んでみてください。これらのメソッドや 図形ID
・タグ名
を理解していれば、今回紹介する神経衰弱だけでなく色んなゲームを作成することができるようになります。
選択された図形 ID を取得する
ただし、上記の itemconfig
の使用例でも示しているように、itemconfig
メソッドを実行するためには 図形ID
or タグ名
を引数に指定する必要があります。
で、itemconfig
メソッドにより実現したいことは「マウスのクリックにより選択されたカードの長方形の塗りつぶし色の変更」ですので、itemconfig
メソッド実行時にはクリックされた長方形の 図形ID
or タグ名
が必要になることになります。
このようなマウスでクリックされた図形の 図形ID
の取得は、キャンバスのメソッドである find_closest
により実現することができます。
# canvasはtkinter.Canvasクラスのインスタンス
canvas.find_closest(マウスカーソルのx座標, マウスカーソルのy座標)
具体的には、事前にマウスクリック時にイベント処理が行われるように設定しておき、そのマウスクリック時に実行されるメソッドの中で find_closest
を実行してクリックされた長方形の 図形ID
を取得します。
さらに itemconfig
メソッドにより取得した 図形ID
の長方形を「塗りつぶしなし」に設定することで、カードをめくる動作を実現していきます。
カードを元に戻す
また、選択された2枚のカードの数字が異なる場合は、再びカードを裏返して元に戻す必要があります。
前述の通り、カードをめくる動作はカードの長方形を「塗りつぶしなし」に設定することで実現しているのですから、カードを裏返す操作は逆にその長方形を特定の色で塗りつぶすことで実現することができます。
ただし、このカードを裏返して元に戻す際の「タイミング」には注意が必要です。
選択された2枚のカードの数字が異なるからといってマウスで2枚目のカードがクリックされた瞬間にカードを裏返してしまうと、ユーザーがカードの数字を確認する前にカードが裏返されてしまいます。
神経衰弱は、めくられたカードの数字をいかにして記憶するかが勝敗を分けるゲームです。めくったカードの数字が確認できないと当然カードの数字の記憶をする事ができず、神経衰弱ゲームとしての面白さが損なわれてしまいます。
そのため、今回は2枚目のカードがめくられた時に2枚のカードの数字が異なる場合、ユーザーがカードの数字が確認できるように1秒経過した後にカードを裏返すようにしていきたいと思います。
このように、特定の時間が経過した後に処理を開始したいような場合、tkinter では after
メソッドを利用することになります。
after
メソッドについては下記ページでまとめていますので、after
メソッドをご存知ない方は是非読んでみてください。
今回は1秒経過後に裏返すようにしていきますが、もちろんマウスクリックされた際や OK ボタンを用意してボタンが押された時にカードを裏返して元に戻すようにするのでも良いと思います。
カードを獲得する
また神経衰弱ゲームでは、選択した2枚のカードが同じ数字の場合はプレイヤーがそれらの2枚のカードを獲得することになります。
この時にポイントになるのが、獲得済みのカードが「以降のゲームプレイ時に選択されないようにする」ことです。
これを実現するために、今回は獲得済みのカードに対して下記の処理を行うようにしていきます。
- 獲得済みのカードはクリックされても反応しないようにする
- 獲得済みのカードの色を変更して選択できないことをユーザーに示す
今回は上記のように処理を行いますが、もちろん獲得済みのカードをキャンバスから削除するようにすることでも「以降のゲームプレイ時に選択されないようにする」を実現することは可能です(こっちの方が簡単かも)。
獲得済みのカードはクリックされても反応しないようにする
獲得済みのカードが以降のゲームプレイ時に選択されないようにするための処理の1つ目は、獲得済みのカードがクリックされても反応しないようにするための処理です。
このような処理は、マウスでカードがクリックされた際に、そのクリックされたカードが獲得済みのものであれば「何もしない」ようにするだけで実現することができます。
ただ、そのためには、クリックされたカードが獲得済みであるか未獲得であるかを判断できるようにしておく必要があります。
今回は、未獲得のカードの長方形の図形 ID をリストで管理することで、この判断を行えるようにしていきたいと思います。
具体的には、アプリ起動時に全てのカードの図形 ID をリストに格納し、
獲得されたカードをそのリストから削除するようにすることで、未獲得のカードのみをリストで管理されるようにします。
さらにカードがクリックされた際には、そのカードに対応する長方形の図形 ID がそのリスト内に存在しない場合は「何もしない」ようにすることで、獲得済みのカードがクリックされてもアプリが反応しないようにしていきます。
獲得済みのカードの色を変更して選択できないことをユーザーに示す
また、どのカードが獲得済みであるかどうかがユーザーが視覚的に判断できるよう、獲得済みのカードの色を変更するようにもしていきます。
前述の通り、カードは長方形により表現されていますので、カードが獲得されたタイミングでその長方形の塗りつぶし色を itemconfig
メソッドで変更することで、この獲得済みのカードの色の変更は実現することができます。
ただ、単に塗りつぶし色を変更するだけだと獲得済みのカードの数字が見えなくなってしまいます。これは、長方形の方が数字よりも前面に描画されているからです。
もちろん、数字が見えなくてもカードの色さえ変更すればユーザーに獲得済みのカードであることを視覚的に伝えることはできます。ただ、数字が見えた方がより親切かなぁと思いますので、今回は獲得済みのカードの数字も見えるようにしていきたいと思います。
前述の通り、数字が見えなくなるのは長方形よりも数字が背面側に描画されていることが理由ですので、カードが獲得済みになった際に長方形を塗りつぶした後、さらに長方形を数字よりも背面側に移動させるようにすれば、長方形を塗りつぶしても数字が見えるようにすることができます。
このような、キャンバスに描画した図形を前面や背面に移動する動作は、キャンバスの lift
メソッドや lower
メソッドにより実現することができます。
特に図形を背面に移動する際には lower
メソッドを利用します。このメソッドでは、第1引数で指定した 図形ID
or タグ名
の図形を、第2引数で指定した 図形ID
or タグ名
の図形の背面側に移動することができます。
# canvasはtkinter.Canvasクラスのインスタンス
canvas.lower(図形ID or タグ名, 図形ID or タグ名)
ですので、獲得済みとなったカードの長方形の 図形ID
を第1引数に指定し、数字の 図形ID
or タグ名
を第2引数に指定してやることで、長方形を数字の背面側に移動することができます。
スポンサーリンク
神経衰弱ゲームのサンプルスクリプト
ここまで説明してきたポイントを踏まえて作成した神経衰弱ゲームのサンプルスクリプトは下記のようになります。
import tkinter
import random
CARD_WIDTH = 50
CARD_HEIGHT = 70
class Concentration:
def __init__(self, master):
self.master = master
# 選択中のカード
self.first_card_id = None
self.second_card_id = None
# キャンバスのサイズ
self.width = CARD_WIDTH*13 # 横に13枚並べるためのサイズ
self.height = CARD_HEIGHT*4 # 縦に4枚並べるためのサイズ
# 未獲得のカードを管理するリスト
self.remain_card_ids = []
self.createWidgets() # キャンバス作成
self.createCards() # カードを作成
self.layoutCards() # カードを並べる
self.setEvents() # イベントの受付設定
def createWidgets(self):
'''アプリに必要なウィジェットを作成する'''
self.canvas = tkinter.Canvas(
self.master,
width=self.width,
height=self.height,
bg="white",
highlightthickness=0
)
self.canvas.pack()
def createCards(self):
'''神経衰弱に使用するカードを作成する'''
numbers = [
"A", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K"
]
# 13枚*4のカードをcardsに保持
self.cards = [number for _ in range(4) for number in numbers]
# カードの並びをシャッフルする
random.shuffle(self.cards)
def layoutCards(self):
'''カードをキャンバス上に並べる'''
for i, number in enumerate(self.cards):
# 水平方向と垂直方向の位置
h = i % 13
v = i // 13
# カードを表現する長方形の座標を計算
x1 = h * CARD_WIDTH
x2 = (h + 1) * CARD_WIDTH
y1 = v * CARD_HEIGHT
y2 = (v + 1) * CARD_HEIGHT
# カードの中心に数字を描画
self.canvas.create_text(
x1 + CARD_WIDTH / 2, y1 + CARD_HEIGHT / 2,
text=number,
font=("", 40)
)
# 長方形を数字の上に描画して数字を隠す
fig_id = self.canvas.create_rectangle(
x1, y1, x2, y2,
fill="blue",
tag=number
)
# 未獲得のカードとしてリストに追加
self.remain_card_ids.append(fig_id)
def setEvents(self):
'''アプリに必要なイベントの設定を行う'''
# クリック時にfaceupCardが実行されるように設定
self.canvas.bind("<ButtonPress>", self.selectCard)
def selectCard(self, event):
'''選択されたカードに対する処理'''
# 3枚同時に選択された場合は何もしない
if self.first_card_id is not None and self.second_card_id is not None:
return
# クリックされたカードに対応する図形IDを取得
card_fig_ids = self.canvas.find_closest(event.x, event.y)
card_fig_id = card_fig_ids[0]
# クリックされた図形が未獲得カードでない場合は何もしない
if not card_fig_id in self.remain_card_ids:
return
# 同じ図形がクリックされた場合は何もしない
if card_fig_id == self.first_card_id:
return
# 取得したIDの図形を塗りつぶし無しにする(数字が見えるようになる)
self.canvas.itemconfig(card_fig_id, fill="")
if self.first_card_id is None:
# 1枚目に選択したカードとして覚えておく
self.first_card_id = card_fig_id
else:
# 2枚目に選択したカードとして覚えておく
self.second_card_id = card_fig_id
# 図形IDから表向きにされたカードの数字を取得する
first_number = self.canvas.gettags(self.first_card_id)[0]
second_number = self.canvas.gettags(self.second_card_id)[0]
if first_number == second_number:
# 選んだカードが同じ数字だった場合
self.earnCards()
else:
# 選んだカードが異なる数字だった場合
# 一時的にクリックを無効にする
self.canvas.unbind("")
# 1000ミリ秒後にカードを裏向きにする
self.master.after(1000, self.reverseCards)
def reverseCards(self):
'''めくったカードを元に戻す'''
# カードを表す長方形に色をつける(数字が隠れる)
self.canvas.itemconfig(self.first_card_id, fill="blue")
self.canvas.itemconfig(self.second_card_id, fill="blue")
# 選択中のカードの図形IDをNoneに設定
self.first_card_id = None
self.second_card_id = None
# 再度クリック時にselectCardが実行されるように設定
self.canvas.bind("<ButtonPress>", self.selectCard)
def earnCards(self):
'''めくったカードを獲得済みにする'''
# 揃ったカードの色をグレーにする
self.canvas.itemconfig(self.first_card_id, fill="gray")
self.canvas.itemconfig(self.second_card_id, fill="gray")
# カードの長方形を最背面に移動する(数字が見えるようになる)
self.canvas.lower(self.first_card_id, "all")
self.canvas.lower(self.second_card_id, "all")
# 未獲得カードのリストから獲得されたカードを削除する
self.remain_card_ids.remove(self.first_card_id)
self.remain_card_ids.remove(self.second_card_id)
# 選択中のカードの図形IDをNoneに設定
self.first_card_id = None
self.second_card_id = None
# 未獲得カードのリストに要素がなくなったらゲームクリア
if len(self.remain_card_ids) == 0:
self.canvas.create_text(
self.width / 2, self.height / 2,
font=("", 80),
text="GAME CLEAR!!!",
fill="red"
)
app = tkinter.Tk()
Concentration(app)
app.mainloop()
スクリプトを起動すれば、下の図のような画面が表示され、カードをクリックすることで神経衰弱ゲームがプレイできることが確認できると思います。
カードを全て獲得した場合は、下の図のように "GAME CLEAR!!!"
が表示されます。
サンプルスクリプトの解説
最後に 神経衰弱ゲームのサンプルスクリプト で紹介したスクリプトについて解説していきます。
このスクリプトでは Concentration
クラスを用意し、この Concentration
クラスが神経衰弱ゲームを実現するクラスとなります。
ここからは、Concentration
クラスの各メソッドがどのような処理を行なっているのかについて解説していきます。
__init__
Concentration
クラスのコンストラクタである __init__
では、神経衰弱を実現するために必要になる各データ属性の初期化とメソッドの実行を行なっています。
特に神経衰弱を実現していく上でポイントになる下記の3つのデータ属性の役割について解説しておきます。
first_card_id
second_card_id
remain_card_ids
他のデータ属性の意味合いはスクリプト中のコメントに記載しておりますので、そちらを参照していただければと思います。
first_card_id
と second_card_id
first_card_id
と second_card_id
はそれぞれ1枚目に選択されたカードの長方形の図形 ID および2枚目に選択されたカードの長方形の図形 ID を記憶しておくために使用するデータ属性となります。
さらに、カードが選択されていない際には first_card_id
や second_card_id
に None
を設定しておくようにすることで、これらのデータ属性は単純に図形 ID を知りたいときだけでなく、選択中のカードの枚数を知る目的でも利用できるデータ属性とする事ができます。
例えば、first_card_id
と second_card_id
の両方が None
以外の場合、既にカードは2枚とも選択中であることになります。この場合、カードがクリックされても何もしないようにする必要があります。
また、「first_card_id
と second_card_id
の両方が None
以外」で無い場合、選択中のカードは2枚未満であり、second_card_id
は必ず None
であることになります(先にクリックされた方のカードが1枚目のカードとなるため)。
そのため、first_card_id
から選択中のカードの枚数を下記のように判断できます。
first_card_id
がNone
:選択中のカードは0枚first_card_id
がNone
以外:選択中のカードは1枚
つまり、first_card_id
が None
の状態でカードがクリックされた場合、そのクリックは1枚目のカードを選択するために行われたと判断できますし、None
以外の状態でカードがクリックされた場合、そのクリックは2枚目のカードを選択するために行われたと判断する事ができます。
神経衰弱ゲームを実現するためには、上記のように first_card_id
と second_card_id
から選択中のカードの枚数を判断し、その枚数に応じて行う動作を切り替えるような処理が必要になります。
この辺りの処理は、後述の selectCard メソッドの中で行っています。
remain_card_ids
remain_card_ids
は未獲得のカードの長方形の図形 ID を管理するために使用するリストになります。
具体的には、キャンバスに長方形を描画した際に全ての長方形の図形 ID を remain_card_ids
に格納しておき、カードが獲得されるたびに獲得されたカードの長方形の図形 ID を remain_card_ids
から削除するようにします。これにより未獲得のカードの長方形の図形 ID のみをリストで管理する事ができます。
このようなリストを保持しておくことで、下記のような判断が行えるようになります。
remain_card_ids
の要素数が0
:全てのカードが獲得された(ゲームクリア)- クリックされたカードの長方形の図形 ID が
remain_card_ids
内に存在しない:すでにそのカードは獲得済み
スポンサーリンク
createWidgets
createWidgets
では神経衰弱を実現するために必要になるウィジェットの作成と配置を行います。
といっても、今回用意するウィジェットはキャンバスのみとなります。
createCards
createCards
では神経衰弱プレイ時に使用するカードの準備を行います。
要はジョーカーを除くトランプの52枚のカード('2'
〜 '10'
と 'A'
, 'J'
, 'Q'
, 'K'
の13枚のカードを4セット)の数字を格納したリスト cards
の作成を行なっています。
ただし、単にリストを作成を行するだけだと数字の並びが固定されてしまいますので、最後に random.shuffle(self.cards)
によりリスト cards
の並びがランダムになるようにしています。
今回作成する神経衰弱ではカードの数字を利用しますが、カードにマークを表示したいような場合は数字とマーク両方を格納したリストを作成することになります。
layoutCards
layoutCards
では、createCards
メソッドで作成したカードをキャンバス上に並べて描画する処理を行います。
今回は52枚のカードを横方向に13枚・縦方向に4枚並べられるように座標の計算を行い、さらにキャンバスの create_text
メソッドによってカードの「数字」を描画し、create_rectangle
メソッドによってカードの「長方形」の描画を行なっています。
今回は簡単にするために長方形を敷き詰める形で描画していますが、スペースを空けて描画した方がもうちょっと見た目は良くなると思います
create_text
メソッドで描画する数字を決めるときに、createCards
メソッドで作成したカードのリスト cards
を利用し、このリスト cards
の先頭から順番に数字を取得しながら数字の描画を行なっています。
また、create_rectangle
メソッド実行時にはオプション fill="blue"
を指定しているため、描画される長方形は青色で塗りつぶしされることになります。
create_text
メソッドや create_rectangle
メソッドのような図形を描画するメソッドや指定可能なオプションについては下記ページでまとめていますので、興味のある方は是非読んでみてください。
また、この layoutCards
のポイントは2つあります。
create_text
と create_rectangle
の実行順序
1つ目のポイントは create_text
と create_rectangle
の実行順序です。
tkinter のキャンバスでは、後から描画した図形の方が前面側に描画されることになります。layoutCards
メソッドでは create_text
の後に create_rectangle
を実行するようにしているため、数字よりも長方形の方が前面側に描画され、数字は前面側に存在する長方形で隠れることになります(長方形が塗りつぶされているため)。
数字を取得するための長方形へのタグ付け
2つ目のポイントは長方形へのタグ付けです。
神経衰弱ゲームを実現するためには、クリックして選択された2枚のカードの数字が一致するかどうかの判断を行いながら処理を進めていく必要があります。
で、この処理を行うためにはクリックして選択されたカードの数字を何らかの方法で取得できるようにしておく必要があります。
そのために、create_rectangle
実行時にはオプション tag=number
を指定するようにしています。number
は長方形の背面側に描画された数字となりますので、このオプション指定により描画される長方形の背面に存在する数字がタグ付けされることになります。
さらに、キャンバスのメソッドである gettags
メソッドを実行することで、引数に指定した ID の図形に付けられているタグを取得することができます。
したがって、クリックされたカードの長方形の図形 ID を引数に指定して gettags
メソッドを実行すれば、その図形に付けられたタグ、すなわち長方形の背面に存在する数字を取得することができることになります。
この gettags
メソッドを利用して数字を取得する処理については、後述の selectCard
メソッドで行っています。
今回はタグを利用して数字の取得を行なっていますが、辞書などで図形 ID と数字を関連づけて管理しておき、その辞書から図形 ID をキーとして数字を取得するようにするような方法も考えられます
スポンサーリンク
setEvents
setEvents
では神経衰弱ゲームを実現する上で必要になるイベント処理の設定を行なっています。
今回はマウスのクリックのイベントのみを受け付けるようにし、クリックが実行された際には次に解説する selectCard
が実行されるように bind
メソッドを実行しています。
イベント処理について詳しく知りたい方は、下記ページで解説していますので別途参照していただければと思います。
Tkinterの使い方:イベント処理を行うselectCard
ここから紹介していく selectCard
・earnCards
・reverseCards
は 神経衰弱ゲームを作成するポイント で解説した内容を実現しているメソッドであり、これらのメソッドは神経衰弱を実現する上で重要なメソッドなので詳しく解説していきたいと思います。
まず selectCard
はカードを選択する処理を実現するメソッドになります。前述の通り、このメソッドはマウスでクリックされたときに実行されます。
selectCard
メソッドの引数 event
の意味合いについては、先ほども紹介した下記のイベント処理の解説ページで説明していますので、詳しく知りたい方は下記ページをご参照いただければと思います。
2枚選択中の場合は何もしない
selectCard
メソッドでは、まず最初に下記部分で、既にカードが2枚とも選択中であるかどうかの判断を行なっています。
# 3枚同時に選択された場合は何もしない
if self.first_card_id is not None and self.second_card_id is not None:
return
__init__ でも解説したように、first_card_id
と second_card_id
の両方が None
以外の場合はカードが2枚とも選択中であると判断することができます。この場合、クリックによってカードが余分に選択されたことになりますので、return
を実行して selectCard
メソッドを直ちに終了し、カードの選択を行わないようにしています。
図形 ID の取得
続いて selectCard
メソッドでは、下記でクリックされた図形の ID の取得を行っています。
# クリックされたカードに対応する図形IDを取得
card_fig_ids = self.canvas.find_closest(event.x, event.y)
card_fig_id = card_fig_ids[0]
選択された図形 ID を取得する でも解説したように、指定した座標に一番近い図形の ID はキャンバスの find_closest
メソッドにより取得することができます。
find_closest
メソッドの引数にクリックされた時のマウスの座標 (event.x
, event.y
) を指定することで、クリックされた座標に一番近い図形の ID の取得を実現しています。
ただ、find_closest
メソッドで取得できるのは単なる図形 ID ではなく、図形 ID のタプルであることに注意してください。このため selectCard
では、find_closest
メソッドの返却値 card_fig_ids
ではなく、タプルの先頭要素 card_fig_ids[0]
を図形 ID として使用して処理を行うようにしています。
また、もし取得した図形 ID がリスト remain_card_ids
に存在しない場合は、その図形はカードを表現する長方形の図形 ID ではない or そのカードはすでに獲得済みということになりますので、その場合は return
のみ行なってメソッドを終了するようにしています。
# クリックされた図形が未獲得カードでない場合は何もしない
if not card_fig_id in self.remain_card_ids:
return
さらに、取得した図形 ID が first_card_id
と一致する場合は、そのカードは1枚目に選択されたカードと同じカードですので、この場合も return
のみ行なってメソッドを終了するようにしています。
# 同じ図形がクリックされた場合は何もしない
if card_fig_id == self.first_card_id:
return
カードをめくる(数字を表示する)
図形 ID 取得後は、下記の処理によりクリックされたカードの長方形を「塗りつぶしなし」に設定しています。
# 取得したIDの図形を塗りつぶし無しにする(数字が見えるようになる)
self.canvas.itemconfig(card_fig_id, fill="")
選択されたカードの数字を見えるようにする で説明したように、キャンバスに描画した図形の塗りつぶし色は、fill
オプションを指定して itemconfig
メソッドを実行することにより変更することができます。
ここでカードを「塗りつぶしなし」に設定することで、カードの背面に存在する数字が見えるようになり、カードをめくったような動作を実現することができます。
何枚目のカードが選択されたかを判断
続いて、マウスのクリックにより選択されたカードが何枚目のものであるかの判断を行います。
まず、2枚選択中の場合は何もしない で解説したように、カードが既に2枚選択されている場合は selectCard
メソッドは直ちに終了するようになっています。
また、__init__ でも解説したように、カードの選択枚数が2枚未満であるとき、現在何枚のカードが選択されているかはデータ属性 first_card_id
が None
or None
以外のどちらであるかを調べることで判断することができます。
具体的には、selectCard
メソッドの下記部分で現在選択中のカードが0枚 or 1枚のどちらであるか判断し、0枚である場合はクリックされたカードを「1枚目に選択されたカード」として長方形の図形 ID を first_card_id
に、1枚である場合はクリックされたカードを「2枚目に選択されたカード」として長方形の図形 ID を second_card_id
に設定するようにしています。
if self.first_card_id is None:
# 1枚目に選択したカードとして覚えておく
self.first_card_id = card_fig_id
else:
# 2枚目に選択したカードとして覚えておく
self.second_card_id = card_fig_id
2枚のカードの数字の取得
さらに、選択されたカードが2枚目の場合は、選択中の2枚のカードの数字が同じであるかどうかの判断を行なっています。
layoutCards で解説したように、カードの長方形にはそのカードの数字がタグ付けされていますので、そのタグを取得すればカードの数字を取得することができることになります。
具体的には、このカードの数字の取得は selectCard
メソッドの下記部分で行っています。
# 図形IDから表向きにされたカードの数字を取得する
first_number = self.canvas.gettags(self.first_card_id)[0]
second_number = self.canvas.gettags(self.second_card_id)[0]
gettags
も返却値がタグそのものではなく、タグのタプルであることに注意してください。
2枚のカードの数字に応じた処理
さらに、取得したカードの数字が同じであるかどうかを判断し、同じである場合は後述の earnCards
メソッドによりカードを獲得する処理を行います。
その一方で、取得したカードの数字が異なる場合は、一旦マウスクリックイベントの受付を停止し、1秒後に reveseCards
メソッドを実行して選択してめくられたカードを再び裏返して元に戻す処理を行っています。
これらの処理は selectCards
メソッドの下記部分で行っています。
if first_number == second_number:
# 選んだカードが同じ数字だった場合
self.earnCards()
else:
# 選んだカードが異なる数字だった場合
# 一時的にクリックを無効にする
self.canvas.unbind("ButtonPress")
# 1000ミリ秒後にカードを裏向きにする
self.master.after(1000, self.reverseCards)
わざわざ after
メソッドを利用して1秒後に reveseCards
メソッドを実行するようにしている理由は カードを元に戻す で解説した通りですし、カードを元に戻すまでは次のカードが選択されないよう、マウスクリックのイベントの受付を停止するようにしています。
マウスクリックのイベントの受付の再開は、reveseCards
メソッドでカードを再度裏返す際に行います。
earnCards
earnCards
はカードを獲得する動作を実現するためのメソッドで、選択した2枚のカードの数字が同じだった場合に selectCards
から呼び出されるメソッドになります。主に、神経衰弱ゲームを作成するポイント の カードを獲得する で解説した内容の動作を行います。
カードを獲得する動作といっても、今回はポイント加算などは行わず、単に獲得されたカードが今後選択できないようにするための処理と、カードが「獲得済み」であることが分かるようにカードの色の変更のみを行います。
earnCards
メソッドでは、まずカードが獲得済みであることが分かるように、獲得された2枚のカードの長方形の塗りつぶし色を下記で "gray"
に変更します。
# 揃ったカードの色をグレーにする
self.canvas.itemconfig(self.first_card_id, fill="gray")
self.canvas.itemconfig(self.second_card_id, fill="gray")
ただし、カードを獲得する で解説したように単に塗りつぶし色を変更するだけだと長方形の背面側にある数字が見えなくなってしまいますので、下記で長方形を最背面に移動する処理を行なっています。
# 数字の裏側にカードの長方形を移動する
self.canvas.lower(self.first_card_id, "all")
self.canvas.lower(self.second_card_id, "all")
上記により長方形が数字の背面に移動し、数字が見えるようになります。
さらに、下記で未獲得のカードの長方形の ID を管理するリスト remain_card_ids
から獲得されたカードの長方形の図形 ID を削除しています。
# 未獲得カードのリストから獲得されたカードを削除する
self.remain_card_ids.remove(self.first_card_id)
self.remain_card_ids.remove(self.second_card_id)
selectCard で解説した通り、selectCard
メソッドはマウスでクリックされたカードの長方形の図形 ID が remain_card_ids
に存在しない場合、カードを選択する処理が行われないようになっています。
そのため、上記の処理実行以降は獲得済みのカードの長方形がクリックされても何も処理が行われないようになり、獲得済みのカードが再度選択されてしまうことを防ぐことができます。
また、カード獲得によってプレイヤーのカードの選択が解除されるよう、下記のように選択中のカードを None
に設定するようにしています。
# 選択中のカードの図形IDをNoneに設定
self.first_card_id = None
self.second_card_id = None
最後に、もし未獲得のカードがなくなった場合、すなわちリスト remain_card_ids
の要素数が 0
になった場合はゲームクリアしたことになりますので、この場合には下記のようにキャンバスの create_text
メソッドで "GAME CLEAR!!!"
の文字列を描画するようにしています。
# 未獲得カードのリストに要素がなくなったらゲームクリア
if len(self.remain_card_ids) == 0:
self.canvas.create_text(
self.width / 2, self.height / 2,
font=("", 80),
text="GAME CLEAR!!!",
fill="red"
)
スポンサーリンク
reverseCards
reverseCards
は選択してめくられたカードを再び裏返して元に戻す動作を実現するためのメソッドで、選択された2枚のカードの数字が同じでなかった場合に selectCards
から 1000
ms 後に呼び出されるメソッドになります。
reverseCards
メソッドでは最初にカードの長方形を選択前の状態、すなわちカードの長方形の塗りつぶし色を "blue"
に設定しています。
# カードを表す長方形に色をつける(数字が隠れる)
self.canvas.itemconfig(self.first_card_id, fill="blue")
self.canvas.itemconfig(self.second_card_id, fill="blue")
さらに、カードを元に戻した事によってプレイヤーのカードの選択が解除されるよう、下記のように選択中のカードを None
に設定するようにしています。
# 選択中のカードの図形IDをNoneに設定
self.first_card_id = None
self.second_card_id = None
最後に、selectCards
で unbind
メソッドにより一旦停止されたマウスクリックイベントの受付を、下記で再度受付を行うよう bind
メソッドを実行しています。
# 再度クリック時にselectCardが実行されるように設定
self.canvas.bind("<ButtonPress>", self.selectCard)
以上が神経衰弱ゲームの作り方の解説となります。
今回作成した神経衰弱は完全に一人プレイ用のものでしたが、コンピュータと対戦できるように発展させることでより面白いゲームに仕立てていく事ができます。
おすすめ書籍(PR)
この時のコンピュータ側の動作の実現方法については、例えば下記の「Pythonで作って学べるゲームのアルゴリズム入門」が参考になると思います。
この参考書でも神経衰弱のゲームの作り方が解説されているのですが、この参考書では神経衰弱ゲームにおけるコンピュータ側の動作を実現するための方法(コンピュータの動作アルゴリズム)についても重点的に解説されています。
他にも三目並べやオセロゲームにおけるコンピュータ側の動作アルゴリズムについても解説されておりますので、一人プレイ用のゲームだけでなく、コンピュータと対戦可能なゲームを作成してみたい人にオススメの入門書になります(アルゴリズムの考え方の入門書としてもオススメです)。
まとめ
このページでは、Python で tkinter を用いた「神経衰弱ゲーム」の作り方について解説しました!
カードの数字を表示したり隠したりするあたりの処理がポイントであり、神経衰弱ゲームの作成で一番難しいところだと思います。
神経衰弱ゲームは簡単に作れそうにも見えますが、意外といろんなことを考えながら実装する必要があって驚いた方もおられるのでは無いでしょうか?
今回紹介した神経衰弱ゲーム同様に、アプリやゲームは簡単そうに見えても実際に作ってみると結構難易度が高いことも多いです。私も結構この神経衰弱を作る時に悩みました…。
何が言いたいかというと、簡単そうに思えるアプリやゲームでも実際に作ってみることで多くの事を学ぶ事ができます。
自身で実際に作りながら悩んだり調べたりしていくことでプログラミングの力を伸ばす事ができますので、是非色んなアプリやゲームを実際に作ってみることに挑戦してみてください!
特にトランプゲームを作成するためのヒントはこのページにもあると思いますので、神経衰弱の作り方を理解したら、次は違うトランプゲームの作成に挑戦してみましょう!
オススメ参考書(PR)
簡単なアプリやゲームを作りながら Python について学びたいという方には、下記の Pythonでつくる ゲーム開発 入門講座 がオススメです!ちなみに私が Python を始めるときに最初に買った書籍です!
下記ようなゲームを作成しながら Python の基本が楽しく学べます!素材もダウンロードして利用できるため、作成したゲームの見た目にも満足できると思います。
- すごろく
- おみくじ
- 迷路ゲーム
- 落ち物パズル
- RPG
また本書籍は下記のような構成になっているため、Python 初心者でも内容を理解しやすいです。
- プログラミング・Python の基礎から解説
- 絵を用いた解説が豊富
- ライブラリの使い方から解説(tkitner と Pygame)
- ソースコードの1行1行に注釈
ゲーム開発は楽しくプログラミングを学べるだけでなく、ゲームで学んだことは他の分野のプログラミングにも活かせるものが多いですし(キーボードの入力受付のイベントや定期的な処理・画像や座標を扱い方等)、逆に他の分野のプログラミングで学んだ知識を活かしやすいことも特徴だと思います(例えばコンピュータの動作に機械学習を取り入れるなど)。
プログラミングを学ぶのにゲーム開発は相性抜群だと思います。
Python の基礎や tkinter・Pygame の使い方をご存知なのであれば、下記の 実践編 をいきなり読むのもアリです。
実践編 では「シューティングゲーム」や「アクションゲーム」「3D カーレース」等のより難易度の高いゲームを作りながらプログラミングの力をつけていくことができます!
また、単にゲームを作るのではなく、対戦相手となるコンピュータの動作のアルゴリズムにも興味のある方は下記の「Pythonで作って学べるゲームのアルゴリズム入門」がオススメです。
この本はゲームのコンピュータ(AI)の動作アルゴリズム(思考ルーチン)に対する入門解説本になります。例えばオセロゲームにおけるコンピュータが、どのような思考によって石を置く場所を決めているか等の基本的な知識を得ることが出来ます。
プログラミングを挫折せずに続けていくためには楽しさを味わいながら学習することが大事ですので、特にゲームに興味のある方は、この辺りの参考書と一緒に Python を学んでいくのがオススメです!