このページでは Python の tkinter を用いた迷路ゲームの開発方法について解説していきたいと思います。
迷路自体を作ったり、迷路を解く方法についてはそれぞれ下記で解説しています。
【C言語】穴掘り法で「迷路」を作成する 【C言語】「再帰呼び出しの動き・メリット・書き方」を迷路を解いて理解するプログラムはC言語になりますが、考え方に関しては Python ユーザーの方でも分かるように解説していますし、プログラム自体も読んでみると Python ユーザーにも処理や制御の流れは分かるのではないかと思います。
今回紹介するスクリプトでも上記ページで紹介しているプログラムを Python 向けに変更したものを利用しています。
このページでは迷路の作り方や迷路の解き方のアルゴリズムではなく、GUI アプリに仕立てて行く部分に重点を置いて解説していきたいと思います!
Contents
開発する迷路ゲーム
まずは開発するゲームについて簡単に紹介していきます。
迷路ゲームの画面構成
今回開発する迷路ゲームの画面構成は下図のようになります。
上側が迷路を表しています。
各マスの色はそれぞれ下記を表しています。
- 白:通路(通れるマス)
- 灰:壁(通れないマス)
- 緑:スタート
- 青:ゴール
- 赤:現在位置
この迷路はキャンバスウィジットを利用して画面に表示しています。
迷路の下にはボタンウィジットを用意しています。
スポンサーリンク
迷路ゲームで出来ること
次は開発する迷路ゲームで何が出来るのかについて解説していきます。
当然迷路ゲームですので迷路がプレイできるようにしています。
キーボードの上キー・下キー・左キー・右キーを押せば現在位置がその方向に移動し、通路を辿ってゴールを目指すという迷路ゲームをプレイできます。
またボタンを押すことでスタートからゴールまでの経路、つまり迷路の正解を表示することもできます。
迷路ゲームの作り方
次はここまで紹介した迷路ゲームの作り方について説明していきたいと思います。
今回は Maze
というクラスを作成し、このクラスに迷路ゲームを作るための機能を実装することで迷路ゲームを作成していきます。
この迷路ゲームでは大きく分けて下記の3つをプログラミングしていきます。
- 迷路ゲームの起動
- 迷路ゲームのプレイ
- 迷路の解答表示
迷路ゲームの起動
まずは迷路ゲームが起動できるようにします。
より具体的に言うと、迷路ゲームの起動を行うために下記の3つのことを行います。
- 壁と穴を設定する
- スタートとゴールを設定する
- 迷路を表示する
壁と穴を設定する
まずは迷路の基となるリストを作成していきます。
「迷路の基となるリスト」と言うと分かりにくいですが、要は、各要素に迷路の各マスが「壁」「通路」「スタート」「ゴール」「現在地」のどのマスであるかの情報を格納する2次元リストです(解答表示時は「経路」であるかどうかの情報も格納させます)。
例えばリストの名前が maze
であれば、maze[j][i]
に座標 (i
, j
) のマスが「壁」「通路」「スタート」「ゴール」「現在地」のどのマスであるかの情報を格納します。
迷路の各マスの座標は左上を (0, 0) とし、縦方向の正方向を下、横方向の正方向を右としています。
このリストを最初に作っておけば、後からこのリストを参照するだけで各マスがどのようなものであるかを確認することができます。
例えば迷路を画面に表示することを考えてみましょう。
この時はリストの全要素に対し、要素が「壁」を表していればその要素に対応するマスの位置に灰色の四角、「通路」を表していればその要素に対応する位置に白色の四角を描画してやれば迷路を図として画面に表示することができます。
迷路プレイ時においても、進行方向にあるマスが「壁」の場合はその方向に進めないように制御する必要があります。このような場合もリストを最初に作成して「通路」「壁」のどちらであるかどうかをリストから参照できるようにしておくと便利です。
迷路ゲームを作成するためには、まずはこのリストに各マスが「壁」もしくは「通路」のどちらであるかを設定していきます。
これは、作りたい迷路をまず考え、各マスに対応する要素に対して1つずつ「壁」もしくは「通路」のどちらであるかの情報を格納するようにプログラミングしてやれば実現することができます。
ただし、これだと各要素に1つずつ情報を格納する必要があるので手間がかかります。ですので、今回紹介するスクリプトでは「穴掘り法」と言われる方法で自動的にリストを作成するようにしています。
この「穴掘り法」で迷路を作る方法やプログラム(C言語ですが…)は下記ページで解説しています。
【C言語】穴掘り法で「迷路」を作成する上記ページで「穴掘り法」については詳しく解説していますので、アルゴリズムについてはこのページでの解説は省略します。
「穴掘り法」では最初に全てのマスを「壁」とし、そこからランダムに穴を掘ることで「通路」を作成していく手法です。
この「穴掘り法」を実行することで、ここまで説明してきた各マスが「壁」もしくは「通路」に設定されたリストを作成することができます。
最後に紹介する迷路ゲームのスクリプトでは、この辺りの処理を createMaze
メソッドにより行っています(実際に穴を掘って通路を設定しているのは dig
メソッドになります)。
ただし、これだけだと各マスが「壁」or「通路」のどちらか一方に設定されているだけですので、続いては「スタート」と「ゴール」の設定をしていきます。
スタートとゴールを設定する
続いて「迷路の基となるリスト」内の「通路」を表す要素の中から「スタート」と「ゴール」のマスを1つずつ設定します。
今回紹介するスクリプトでは、単純に「通路」の要素の中からランダムに1つずつ要素を選んで、それらのマスを「スタート」「ゴール」に設定しています。
最後に紹介する迷路ゲームのスクリプトでは、この辺りの処理を setStart
メソッドとsetGoal
メソッドにより行っています。
これで各マスに「通路」「壁」「スタート」「ゴール」のどれかが設定された「迷路の基となるリスト」が出来上がります。
迷路を表示する
次は、ここまで作成してきたリストを用いて画面に迷路を表示していきます。
キャンバスウィジェットを作成し、このキャンバスウィジェット上に迷路のマスの数だけ長方形を描画していきます。
この時に、マスの種類(通路・壁・スタート・ゴール)に応じて色を変えて長方形を描画してやれば、キャンバス上に迷路を描画することができます。
あとは解答表示用のボタンを表示するためにボタンウィジェットを作成して配置してやれば、迷路ゲームアプリの外観は完成です!
最後に紹介する迷路ゲームのスクリプトでは、この辺りの処理を createWidgets
メソッドにより行っています。
ここまでのことをプログラミングしてアプリを起動すれば、迷路ゲームが起動します。
ですが、ゲームがプレイできる状態ではありませんので、次は迷路ゲームのプレイができるようにプログラミングしていきます。
スポンサーリンク
迷路ゲームのプレイ
次は迷路をプレイできるようにプログラミングしていきます。
より具体的に言うと、迷路ゲームのプレイができるように下記の5つのことを行います。
- 現在地を設定する
- 上下左右キー入力を受け付ける
- 現在地を移動させる
- 移動後の現在地を画面に表示する
- ゴール到達時にゲームを終了する
現在地を設定する
まずは迷路のスタートの座標を「現在地」に設定します。
最後に紹介する迷路ゲームのスクリプトでは、この処理を play
メソッドの前半部分で行っています。
この「現在地」を上下左右キーで動かしながらゴールを目指せるようにすることで、迷路ゲームを実現していきます。
上下左右キー入力を受け付ける
次に先ほど設定した「現在地」を上下左右キーで移動できるようにするために、上下左右キーの入力受け付けを行います。
これは上下左右キーに対してイベント処理を設定することで実現することができます。
イベント処理については下記ページで詳しく解説していますので、イベント処理について詳しく知りたい方は是非こちらも読んでみてください。
Tkinterの使い方:イベント処理を行うこのイベント処理設定を行うことで、上下左右キー入力された時に、特定の関数(メソッド)が自動的に実行されるようになります。
最後に紹介する迷路ゲームのスクリプトでは、この処理を play
メソッドの後半部分により行っています。
次はこの上下左右キー入力された時に実行される関数(メソッド)に対して、現在地を移動させる処理を実装していきます。
現在地を移動させる
現在地を移動させるためには、上下左右キーが入力された時に「現在地」の座標を変更してやれば良いです。
より具体的には、各キーが押された時に、下記のように現在地の座標を変更します。
- 上キー:縦方向の座標を -1 する
- 下キー:縦方向の座標を +1 する
- 左キー:横方向の座標を -1 する
- 右キー:横方向の座標を +1 する
ただし、移動先のマスが「壁」の場合はその方向には移動できません。
ですので、最初に作成したリストを参照し、移動先のマスが「壁」の場合はその方向に移動しないように制御してやる必要があります。
最後に紹介する迷路ゲームのスクリプトでは、この辺りの処理を up_move
メソッド・down_move
メソッド・left_move
メソッド・right_move
メソッドが行っています。
移動後の現在地を画面に表示する
ただし単に「現在地」の座標を移動させただけでは、ユーザーに「現在地が移動したこと」が伝わりません。
ユーザーに「現在地が移動したこと」を伝えるために、移動後の現在地を画面に描画してやる必要があります。
これを実現するためには、移動後の現在地のマスの色を変更してやれば良いです。
ただし、移動前の現在地のマスの色をそのままにしてしまうと複数のマスが現在地になっているように見えてしまいます。ですので、移動後の現在地のマスの色を変更すると同時に、移動前の現在地のマスの色を元に戻してやる必要があります。
最後に紹介する迷路ゲームのスクリプトでは、この処理を update
メソッドが行っています。
ゴール到達時にゲームを終了する
ユーザーのキー入力により現在地が移動し、現在地がゴールに到達した時にはゲームクリアした事をユーザーに伝えてやる必要があります。
今回紹介するスクリプトでは迷路を表示しているキャンバスに「ゲームクリア!」というテキストを描画することでゲームクリアをユーザーに伝えるようにしています。
ゲームクリア後に現在地が移動できてしまうと、まだゲームが続いているとユーザーが勘違いしてしまう可能性があるため、キー入力受付の取り消しを行っておくとさらに良いと思います。
最後に紹介する迷路ゲームのスクリプトでは、この辺りの処理を game_clear
メソッドが行っています。
迷路の解答表示
ここまでで迷路ゲームとしては完成しています。
ですが、今回紹介するスクリプトではさらに「迷路の解答表示」できるようにしています。
迷路の解答とは、具体的にはスタートからゴールへの経路ですね。
この経路は下記ページで紹介しているように再帰処理により簡単に見つけることができます。
【C言語】「再帰呼び出しの動き・メリット・書き方」を迷路を解いて理解するどのマスが経路であるかをリストに覚えさせておき、キャンバスに描画しているマスの中から経路であるマスのみ色を変更してやることで、迷路の解答表示を行うことができます。
最後に紹介する迷路ゲームのスクリプトでは、この処理を show_answer
メソッドで行っています(実際に答えを導き出して表示しているのは resolve_maze
メソッドになります)。
迷路ゲームのスクリプト
続いては迷路ゲームのスクリプトを紹介していきます。
スポンサーリンク
スクリプト
ここまで解説してきた内容を実際にプログラミングしたスクリプトは下記のようになります。
import tkinter
import random
# キャンバスのサイズ設定
CANVAS_WIDTH = 600
CANVAS_HEIGHT = 400
# 迷路のサイズ設定
WIDTH = 31 # 5以上の奇数
HEIGHT = 21 # 5以上の奇数
# 色設定
PATH_COLOR = "white"
WALL_COLOR = "gray"
GOAL_COLOR = "blue"
START_COLOR = "green"
PASSED_COLOR = "orange"
NOW_COLOR = "red"
# 数値の定義
PATH = 0
WALL = 1
GOAL = 2
START = 3
PASSED = 4
NOW = 5
UP = 0
DOWN = 1
LEFT = 2
RIGHT = 3
class Maze():
def __init__(self, master):
'''迷路ゲームの起動'''
# ゲームを作成する親ウィジェット
self.master = master
# 迷路のサイズ
self.width = WIDTH
self.height = HEIGHT
# 迷路の元になるリスト
self.maze = None
# 現在位置
self.now = None
# 1つ前の位置
self.before = None
# スタートとゴールの位置
self.start = None
self.goal = None
# 解答を既に見つけたかどうかのフラグ
self.resolved = False
# 迷路の元になる2次元リストを作成
self.createMaze()
# ウィジェットを作成して迷路を表示
self.createWidgets()
def createMaze(self):
'''迷路の元になる2次元リストを作成'''
# 2次元リストを作成(全て壁)
self.maze = [[WALL] * self.width for j in range(self.height)]
# 開始点を決定
i = 2 * random.randint(0, self.width // 2 - 1) + 1
j = 2 * random.randint(0, self.height // 2 - 1) + 1
# (i, j) を通路に設定
self.maze[j][i] = PATH
# 穴掘り法でマス(i, j) を起点に穴を掘る
self.dig(i, j)
# ここまで穴掘り法
# スタートを設定
self.setStart()
# ゴールを決定
self.setGoal()
def setStart(self):
'''スタートの位置を設定'''
# 通路の数をカウント
num_path = 0
for j in range(self.height):
for i in range(self.width):
if self.maze[j][i] == PATH:
num_path += 1
# スタートの位置をランダムに決定
startPos = random.randint(0, num_path - 1)
# 左上からstartPos個目の通路のマスをスタートに設定
count = 0
for j in range(self.height):
for i in range(self.width):
if self.maze[j][i] == PATH:
if count == startPos:
self.maze[j][i] = START
self.start = (i, j)
return
else:
count += 1
def setGoal(self):
'''ゴールの位置を設定'''
# 通路の数をカウント
num_path = 0
for j in range(self.height):
for i in range(self.width):
if self.maze[j][i] == PATH:
num_path += 1
# ゴールの位置をランダムに決定
goalPos = random.randint(0, num_path - 1)
# 左上からgoalPos個目の通路のマスをゴールに設定
count = 0
for j in range(self.height):
for i in range(self.width):
if self.maze[j][i] == PATH:
if count == goalPos:
self.maze[j][i] = GOAL
self.goal = (i, j)
return
else:
count += 1
def dig(self, i, j):
'''(i,j)座標を起点に穴を掘る'''
# どの方向を掘ろうとしたかを覚えておく変数
up = True
down = True
left = True
right = True
# 全方向試すまでループ
while up or down or left or right:
# 0 - 3 の乱数を取得
d = random.randint(0, 3)
if d == UP:
# 上方向が掘れるなら掘る
if j - 2 >= 0 and j - 2 < self.height:
if self.maze[j - 2][i] == WALL:
self.maze[j - 2][i] = PATH
self.maze[j - 1][i] = PATH
self.dig(i, j - 2)
up = False
elif d == DOWN:
# 下方向が掘れるなら掘る
if j + 2 >= 0 and j + 2 < self.height:
if self.maze[j + 2][i] == WALL:
self.maze[j + 2][i] = PATH
self.maze[j + 1][i] = PATH
self.dig(i, j + 2)
down = False
elif d == LEFT:
# 左方向が掘れるなら掘る
if i - 2 >= 0 and i - 2 < self.width:
if self.maze[j][i - 2] == WALL:
self.maze[j][i - 2] = PATH
self.maze[j][i - 1] = PATH
self.dig(i - 2, j)
left = False
elif d == RIGHT:
# 右方向が掘れるなら掘る
if i + 2 >= 0 and i + 2 < self.width:
if self.maze[j][i + 2] == WALL:
self.maze[j][i + 2] = PATH
self.maze[j][i + 1] = PATH
self.dig(i + 2, j)
right = False
def change_color(self, i, j):
'''(i,j)座標に対応する長方形の色を変更'''
# mazeリストの値に応じて色を取得
if self.maze[j][i] == WALL:
color = WALL_COLOR
elif self.maze[j][i] == PATH:
color = PATH_COLOR
elif self.maze[j][i] == GOAL:
color = GOAL_COLOR
elif self.maze[j][i] == START:
color = START_COLOR
elif self.maze[j][i] == PASSED:
color = PASSED_COLOR
elif self.maze[j][i] == NOW:
color = NOW_COLOR
else:
print("そんなマスはあり得ません")
return
# (i,j)座標の長方形を特定するためにタグを作る
tag = "rectangle_" + str(i) + "_" + str(j)
# そのタグがつけられたfill設定を変更
self.canvas.itemconfig(
tag,
fill=color
)
def createWidgets(self):
'''ウィジェットを作成する'''
# キャンバスウィジェットの作成と配置
self.canvas = tkinter.Canvas(
self.master,
width=CANVAS_WIDTH,
height=CANVAS_HEIGHT,
)
self.canvas.pack()
for j in range(self.height):
for i in range(self.width):
# 後から操作できるように座標に基づいたタグを付ける
tag = "rectangle_" + str(i) + "_" + str(j)
# キャンバスへの長方形の描画(迷路の描画)
self.canvas.create_rectangle(
3 + i * CANVAS_WIDTH / self.width,
3 + j * CANVAS_HEIGHT / self.height,
3 + (i + 1) * CANVAS_WIDTH / self.width,
3 + (j + 1) * CANVAS_HEIGHT / self.height,
width=0, # 枠線なし
tag=tag # タグ
)
# 長方形に色をつける
self.change_color(i, j)
# ボタンの作成と配置
self.button = tkinter.Button(
self.master,
text="ボタン",
command=self.show_answer
)
self.button.pack()
def resolve_maze(self, i, j):
'''(i,j)マスから移動できる方向に1マス進む'''
# 迷路外 or 壁のマスが指定された場合はエラー
if i < 0 or i >= self.width or j < 0 or j >= self.height or self.maze[j][i] == WALL:
return
# 既に経路表示済みの場合は即終了
if self.resolved:
return
# このマスがゴールなら終了
if self.maze[j][i] == GOAL:
# ここまでの経路を表示
self.print_pass()
self.resolved = True
return
# このマスを通過したことを覚えておく
if self.maze[j][i] != START:
self.maze[j][i] = PASSED
# 上に1マス移動
ni = i
nj = j - 1 # 上に移動
if nj >= 0:
if self.maze[nj][ni] != WALL:
if self.maze[nj][ni] != PASSED and self.maze[nj][ni] != START:
# 次のマスからゴールまで移動させる
self.resolve_maze(ni, nj)
# 下に1マス移動
ni = i
nj = j + 1 # 下に移動
if nj < self.height:
if self.maze[nj][ni] != WALL:
if self.maze[nj][ni] != PASSED and self.maze[nj][ni] != START:
# 次のマスからゴールまで移動させる
self.resolve_maze(ni, nj)
# 左に1マス移動
ni = i - 1 # 左に移動
nj = j
if ni >= 0:
if self.maze[nj][ni] != WALL:
if self.maze[nj][ni] != PASSED and self.maze[nj][ni] != START:
# 次のマスからゴールまで移動させる
self.resolve_maze(ni, nj)
# 右に1マス移動
ni = i + 1 # 右に移動
nj = j
if ni < self.width:
if self.maze[nj][ni] != WALL:
if self.maze[nj][ni] != PASSED and self.maze[nj][ni] != START:
# 次のマスからゴールまで移動させる
self.resolve_maze(ni, nj)
# このマスを通過したことを忘れる
if self.maze[j][i] != START:
self.maze[j][i] = PATH
def print_pass(self):
'''答えを表示する'''
for j in range(self.height):
for i in range(self.width):
self.change_color(i, j)
def show_answer(self):
'''解答表示する'''
if self.playing:
# プレイ中フラグをFalseに設定
self.playing=False
# 答えを見つけ出して表示する
self.resolve_maze(self.start[0], self.start[1])
def play(self):
'''ゲームプレイを開始する'''
# ゲームプレイフラグをTrueにセット
self.playing = True
# 現在地をスタート値値に設定
self.now = self.start
# 上下左右キーに対してイベント受付設定
self.master.bind("<KeyPress-Up>", self.up_move)
self.master.bind("<KeyPress-Down>", self.down_move)
self.master.bind("<KeyPress-Left>", self.left_move)
self.master.bind("<KeyPress-Right>", self.right_move)
def update(self):
'''移動後の状態に迷路リストを更新'''
# 移動後の現在地を取得
i, j = self.now
# GOALであれば終了処理
if self.maze[j][i] == GOAL:
self.game_clear()
return
# 現在地を更新
self.maze[j][i] = NOW
# 色を更新
self.change_color(i, j)
# 移動前の現在地を取得
i, j = self.before
# 移動前の位置を更新
if self.before != self.start:
self.maze[j][i] = PATH
else:
self.maze[j][i] = START
# 色を更新
self.change_color(i, j)
def up_move(self, event):
''' 上に1マス移動する'''
# 現在地を取得
now = self.now
i, j = now
# 上に移動
j = j - 1
# 迷路外 or 壁のマスが指定された場合は移動しない
if i < 0 or i >= self.width or j < 0 or j >= self.height or self.maze[j][i] == WALL:
return
self.before = self.now
# 移動後の座標を現在位置に設定
self.now = i, j
self.update()
def down_move(self, event):
''' 下に1マス移動する'''
# 現在地を取得
now=self.now
i, j=now
# 下に移動
j=j + 1
# 迷路外 or 壁のマスが指定された場合は移動しない
if i < 0 or i >= self.width or j < 0 or j >= self.height or self.maze[j][i] == WALL:
return
self.before=self.now
# 移動後の座標を現在位置に設定
self.now=i, j
self.update()
def left_move(self, event):
''' 左に1マス移動する'''
# 現在地を取得
now=self.now
i, j=now
# 左に移動
i=i - 1
# 迷路外 or 壁のマスが指定された場合は移動しない
if i < 0 or i >= self.width or j < 0 or j >= self.height or self.maze[j][i] == WALL:
return
self.before=self.now
# 移動後の座標を現在位置に設定
self.now=i, j
self.update()
def right_move(self, event):
''' 右に1マス移動する'''
# 現在地を取得
now=self.now
i, j=now
# 右に移動
i=i + 1
# 迷路外 or 壁のマスが指定された場合は移動しない
if i < 0 or i >= self.width or j < 0 or j >= self.height or self.maze[j][i] == WALL:
return
self.before=self.now
# 移動後の座標を現在位置に設定
self.now=i, j
# 座標に移動する
self.update()
def game_clear(self):
self.playing=False
self.canvas.create_text(
CANVAS_WIDTH // 2,
CANVAS_HEIGHT // 2,
font=("", 80),
text="ゲームクリア!"
)
self.master.unbind("<KeyPress-Up>")
self.master.unbind("<KeyPress-Left>")
self.master.unbind("<KeyPress-Right>")
self.master.unbind("<KeyPress-Down>")
app=tkinter.Tk()
maze=Maze(app)
maze.play()
app.mainloop()
実行結果
スクリプトを実行すると下のような画面のアプリが起動します。
緑色のマスがスタート、青色のマスがゴールを示しています。
また、アプリ起動後に上下左右キーを押すことで赤マスが移動します。この赤マスをゴールまで移動できればゲームクリアになります。
またボタンをクリックすれば、スタートからゴールまでの経路がオレンジ色のマスで表示されます。
設定
スクリプトの先頭部分で設定を行うことができるようにしています。
キャンバスのサイズ設定
下記部分はキャンバスのサイズの設定箇所になります。
この CANVAS_WIDTH
と CANVAS_HEIGHT
を変更することで、画面に表示されるキャンバスのサイズを変更することができます。
# キャンバスのサイズ設定
CANVAS_WIDTH = 600
CANVAS_HEIGHT = 400
迷路のサイズ設定
下記部分は迷路のサイズ(マス数)の設定箇所になります。
この WIDTH
と HEIGHT
を変更することで、画面に表示される迷路のサイズ(マス数)を変更することができます。
# 迷路のサイズ設定
WIDTH = 31 # 5以上の奇数
HEIGHT = 21 # 5以上の奇数
ただし、スクリプトで再帰呼び出しを行っているため、大きい数を設定するとスタックオーバーフローでエラーが発生する可能性があるので注意してください。
マスの色設定
下記部分は迷路のマスの色の設定箇所になります。
これらを変更することで、画面に表示されるマスの色設定することができます。
# 色設定
PATH_COLOR = "white"
WALL_COLOR = "gray"
GOAL_COLOR = "blue"
START_COLOR = "green"
PASSED_COLOR = "orange"
NOW_COLOR = "red"
各設定の詳細は下記のようになります。
PATH_COLOR
:通路の色WALL_COLOR
:壁の色GOAL_COLOR
:ゴールの色START_COLOR
:スタートの色PASSED_COLOR
:解答表示時の経路の色NOW_COLOR
:現在地の色
スポンサーリンク
スクリプトのポイント
最後にスクリプト作成時のポイントをいくつか紹介しておきます。
リストの作成
スクリプトの一つ目はやはり「迷路の基となるリスト」です。
ざっと眺めていただければ確認できるように、このスクリプトでは、いろんな箇所でリスト self.maze
を使用しています。
このリストが迷路ゲームの作り方で説明した「迷路の基となるリスト」になります。
現在地等の情報をこのリストに格納しておくことで、これを参照するだけで各マスの描画や色の変更を簡単に行えるようにしています。
描画した図形の操作
迷路はキャンバスに描画した長方形を二次元的に配置することで画面に表示しています。
この長方形は、現在地の移動や解答の表示時に色の変更等を行っています。
この描画した図形に対して色を変更するためにポイントになるのがタグ(tag
)です。
描画した図形に対して色の変更等の操作を行うためにはタグ(もしくは図形 ID)を指定する必要があります。そのため、createWidgets
メソッドで図形描画する時(create_rectangle
実行時)に下記のようにタグを付け、
# 後から操作できるように座標に基づいたタグを付ける
tag = "rectangle_" + str(i) + "_" + str(j)
# キャンバスへの長方形の描画(迷路の描画)
self.canvas.create_rectangle(
3 + i * CANVAS_WIDTH / self.width,
3 + j * CANVAS_HEIGHT / self.height,
3 + (i + 1) * CANVAS_WIDTH / self.width,
3 + (j + 1) * CANVAS_HEIGHT / self.height,
width=0, # 枠線なし
tag=tag # タグ
)
そのタグを用いて change_color
メソッドで長方形の色の変更を行っています(色の変更は itemconfig
メソッドで行っています)。
# (i,j)座標の長方形を特定するためにタグを作る
tag = "rectangle_" + str(i) + "_" + str(j)
# そのタグがつけられたfill設定を変更
self.canvas.itemconfig(
tag,
fill=color
)
まとめ
このページでは Python で tkinter を利用して迷路ゲームを開発する方法について解説しました。
迷路自体はキャンバスウィジットを利用してアプリ上に表示していますので、キャンバスウィジットの使い方について学べますし、キー入力も必要なのでイベントについても学べます。
また迷路を作ったり迷路の解答を導き出す際に再帰呼び出しを利用すれば再帰呼び出しについても学べます。
出来上がるのが簡単な迷路ゲームであっても、GUI アプリ開発やプログラミングの基礎を多く学べますので、是非みなさんも迷路ゲーム開発に挑戦してみてください!
オススメ参考書(PR)
簡単なアプリやゲームを作りながら Python について学びたいという方には、下記の Pythonでつくる ゲーム開発 入門講座 がオススメです!ちなみに私が Python を始めるときに最初に買った書籍です!
下記ようなゲームを作成しながら Python の基本が楽しく学べます!素材もダウンロードして利用できるため、作成したゲームの見た目にも満足できると思います。
- すごろく
- おみくじ
- 迷路ゲーム
- 落ち物パズル
- RPG
また本書籍は下記のような構成になっているため、Python 初心者でも内容を理解しやすいです。
- プログラミング・Python の基礎から解説
- 絵を用いた解説が豊富
- ライブラリの使い方から解説(tkitner と Pygame)
- ソースコードの1行1行に注釈
ゲーム開発は楽しくプログラミングを学べるだけでなく、ゲームで学んだことは他の分野のプログラミングにも活かせるものが多いですし(キーボードの入力受付のイベントや定期的な処理・画像や座標を扱い方等)、逆に他の分野のプログラミングで学んだ知識を活かしやすいことも特徴だと思います(例えばコンピュータの動作に機械学習を取り入れるなど)。
プログラミングを学ぶのにゲーム開発は相性抜群だと思います。
Python の基礎や tkinter・Pygame の使い方をご存知なのであれば、下記の 実践編 をいきなり読むのもアリです。
実践編 では「シューティングゲーム」や「アクションゲーム」「3D カーレース」等のより難易度の高いゲームを作りながらプログラミングの力をつけていくことができます!
また、単にゲームを作るのではなく、対戦相手となるコンピュータの動作のアルゴリズムにも興味のある方は下記の「Pythonで作って学べるゲームのアルゴリズム入門」がオススメです。
この本はゲームのコンピュータ(AI)の動作アルゴリズム(思考ルーチン)に対する入門解説本になります。例えばオセロゲームにおけるコンピュータが、どのような思考によって石を置く場所を決めているか等の基本的な知識を得ることが出来ます。
プログラミングを挫折せずに続けていくためには楽しさを味わいながら学習することが大事ですので、特にゲームに興味のある方は、この辺りの参考書と一緒に Python を学んでいくのがオススメです!