【Python/tkinter】横スクロールアクションゲームを作る(ウィジェットの作成および背景の表示)

横スクロールアクションゲームを作る(ウィジェットの作成と背景表示)の解説ページアイキャッチ

このページでは、Python で tkinter を利用した簡単な「横スクロールアクションゲーム」の作り方を解説していきます。

このページは「横スクロールアクションゲームの作り方の解説」の2ページ目となります。

1ページ目は下記ページとなり、1ページ目では作成していくゲームの紹介やクラスの構成等の解説を行なっていますので、まだお読みで無い方は事前に下記ページを読んでいただくことをオススメします。

横スクロールアクションゲームを作る(イントロダクション)の解説ページアイキャッチ【Python/tkinter】横スクロールアクションゲームを作る(イントロダクション)

また、このページでは上記ページの スクリプトの大枠 で紹介したスクリプトを変更していくことで、主に ウィジェットの作成ゲームの背景の表示 の2つを行っていきます。ですので、事前準備としてそのスクリプトを自身の開発環境にコピペしておく必要がありますのでご注意ください。

ウィジェットの作成

では、まずはウィジェットの作成を行なっていきたいと思います。

このウィジェットの作成機能は、Screen クラスの createWidgets メソッドに実装していきます。

作成するウィジェット

今回作成するウィジェットは下記の2つになります。

  • キャンバス
  • スクロールバー

このページではゲームを作成する上で必要な事のみを解説していきますが、キャンバスに関しては下記ページで、

tkinterのキャンバスの作り方解説ページアイキャッチTkinterの使い方:キャンバスウィジェットの作り方 tkinterキャンバスに図形を描画する方法解説ページのアイキャッチTkinterの使い方:Canvasクラスで図形を描画する tkinterキャンバスの図形の操作方法解説ページのアイキャッチTkinterの使い方:Canvasクラスで描画した図形を操作する

スクロールバーに関しては下記ページで詳しく解説していますので、これらのウィジェットについて詳しく知りたい方は別途これらのページを参照していただければと思います。

スクロールバーの作成方法解説ページアイキャッチTkinterの使い方:スクロールバー(Scrollbar)の使い方

スポンサーリンク

Screen クラスのオブジェクトの生成

キャンバスおよびスクロールバーは「ゲームの画面を表示するためのウィジェット」になりますので、Screen クラスにこれらのウィジェットを作成させるようにしていきたいと思います。

まずは、その Screen クラスのオブジェクトを生成するようにしていきます。

Screen クラスの枠組みだけは既に用意していますので、あとは Game クラスから Screen クラスのコンストラクタを実行させるようにすれば、Screen クラスのオブジェクトの生成は完了します。

具体的には、Game クラスの __init__ を下記のように変更します。

Screenオブジェクトの生成
class Game:

	def __init__(self, master):
		self.master = master

		# ↓これを追加
		self.screen = Screen(self.master)
		# ↑これを追加

引数の master はアプリのメインウィンドウとなるウィジェットになります。以降では、この master 上にキャンバスを作成していきます。

キャンバスの作成と配置

続いてキャンバスの作成と配置を行うように Screen クラスを実装していきます。

キャンバス作成時に必要なパラメータ

キャンバスは tkinter.Canvas クラスのコンストラクタを実行することで作成できます。今回のような横スクロールゲームを作成する上では、コンストラクタの引数として最低限下記が必要になります。

  • 第1引数:親ウィジェット
  • width:キャンバスの幅
  • height:キャンバスの高さ
  • scrollregion:スクロール可能な領域を示す矩形の座標のタプル

特に scrollregion の詳細については下記ページで解説していますので、詳しく知りたい方は別途下記ページを参照していただければと思います。このページでは必要部分に絞って解説していきます。

スクロールバーの作成方法解説ページアイキャッチTkinterの使い方:スクロールバー(Scrollbar)の使い方

上記で指定する widthheightscrollregion の位置関係を図示すると下図のようになります。

scrollregionとwidthとheightの関係図

要はゲームの本来の画面のサイズは scrollregion で指定する矩形分となるのですが、表示される領域のサイズがキャンバスのサイズ、すなわち widthheight の分のみとなります。

今回は横スクロールのゲームになりますので、縦方向にはスクロールは行いません。また、ゲーム開始時点では画面の左端を表示するようにしたいと思います。

この場合、下の図のように scrollregion には (0, 0, ゲームの画面の幅, height) を指定すれば良いことになります(下の図の eゲームの画面の幅 となります)。

横スクロールゲームにおけるscrollregionの指定例

従って、結局キャンバスを作成する際には、widthheight および ゲームの画面の幅 のみを決めれば、後の座標は自然と決まることになります。

widthheight に関しては、あなたご自身の PC の画面のサイズ等を考慮して設定していただければ良いですし、ゲームの画面の幅 に関しては作りたいゲームに合わせて設定すれば良いです。

とりあえずこのページでは、これらのパラメータを下記のように設定してゲームの作成を進めていきたいと思います。

  • width600
  • height400
  • ゲームの画面の幅1500

また、これらの値は後からも参照できるように、Screen クラスのデータ属性として保持するようにしておきたいと思います。

このために、Screen クラスの __init__ を下記のように変更します。

Screen関連の設定値
class Screen:
	def __init__(self, master):
		self.master = master

		# ↓これを追加
		self.view_width = 600
		self.view_height = 400
		self.game_width = 1500
		self.game_height = self.view_height
		# ↑これを追加

後から参照しやすいよう、値は view_height と同じではありますが、データ属性 game_height も追加しています。

この view_widthview_height が表示領域の幅と高さとなり、さらに game_widthgame_height がゲーム画面の幅と高さとなります。

view_widthとview_heightとgame_widthとgame_heightの関係性

キャンバスの作成と配置の処理の実装

次は実際にキャンバスの作成と配置の処理を実装していきます。

下記のように Screen クラスに createWidgets メソッドを追加し、このメソッドの中で tkinter.Canvas のコンストラクタの実行処理を記述します。この時、上記で挙げた引数 widthheightscrollregion を指定するようにします。

キャンバスの作成
class Screen:

	# ↓これを追加
	def createWidgets(self):
		self.canvas = tkinter.Canvas(
			self.master,
			width=self.view_width,
			height=self.view_height,
			scrollregion= (
				0,0,self.game_width,self.game_height
			),
			highlightthickness=0
		)
		self.canvas.grid(column=0, row=0)
	# ↑これを追加

tkinter.Canvas のオブジェクトも今後何度も利用するので、tkinter.Canvas の返却値をデータ属性 self.canvas に参照させるようにしています。

また、highlightthickness=0 を指定するようにしているのは、キャンバスの図形の位置のズレを解消するためです。詳細は下記ページで解説していますので詳しく知りたい方はこちらを参照していただければと思います。

キャンバスに描画した図形の左上が欠ける、図形が左上に寄る場合の解決方法の解説ページアイキャッチ【Python/tkinter】キャンバスに描画した図形の左上が欠ける・図形の位置が左上に寄る時の対処法

また、上記の最後に実行している grid メソッドは、作成したキャンバスを self.master 上(メインウィンドウ上)に配置するための処理になります。これを実行しないと画面上にキャンバスが表示されないので注意してください。

こういった配置を行うメソッドについては下記ページで解説していますので、詳しく知りたい方は別途参照していただければと思います。

ウィジェット配置方法解説ページのアイキャッチTkinterの使い方:ウィジェットの配置(pack・grid・place)

キャンバスの作成と配置の実行

続いては、先程作成した createWidgets を実行するようにしていきたいと思います。Game クラスから実行されるようにしても良いのですが、今回は Screen クラスの __init__ の最後で実行するようにしたいと思います。

具体的には、Screen クラスの __init__ を下記のように変更します。

createWidgetsの実行
class Screen:
	def __init__(self, master):
		self.master = master

		self.view_width = 600
		self.view_height = 400
		self.game_width = 1500
		self.game_height = self.view_height

		# ↓これを追加
		self.createWidgets()
		# ↑これを追加

これにより、Screen クラスのオブジェクト生成時に createWidgets メソッドが実行され、キャンバスが作成されるようになります。

変更後のスクリプトを実行すれば、下の図のようなアプリが起動するようになると思います。

キャンバス作成後のアプリ起動画面

といってもキャンバスが真っ白なのでアプリの画面も真っ白です…。ですが、ここまで説明してきた変更を行なっていれば、ゲームの画面を表示させるためのキャンバスの作成自体は行えているはずです。

スクロールバーの作成と配置

次はスクロールバーの作成と配置を行なっていきます。

スクロールバー付きのキャンバスの作り方については下記ページで解説していますので、このページでは詳細な解説は省略させていただこうと思います。

スクロールバーの作成方法解説ページアイキャッチTkinterの使い方:スクロールバー(Scrollbar)の使い方

ただし、上記ページではキャンバスを縦スクロールも横スクロールも行えるようにしていますが、今回は横スクロールのみを行うため、水平方向のスクロールバーのみを作成していきます。

具体的には、先程作成した Screen クラスの createWidgets メソッドの後半を下記のように変更します。これにより、スクロールバーの作成と配置およびキャンバスとの連動を行うことができます。

スクロールバーの作成
class Screen:

	def createWidgets(self):

		# 略

		self.canvas.grid(column=0, row=0)

		# ↓これを追加
		xbar = tkinter.Scrollbar(
			self.master,
			orient=tkinter.HORIZONTAL,
		)

		xbar.grid(
			row=1, column=0,
			sticky=tkinter.W + tkinter.E
		)

		xbar.config(
			command=self.canvas.xview
		)

		self.canvas.config(
			xscrollcommand=xbar.set
		)
		# ↑これを追加

各処理の内容については先程紹介したページで解説していますので、必要に応じて参照していただければと思います。

上記のように createWidgets メソッドを変更することで、スクリプトを実行すると今度は下の図のようにスクロールバー付きのキャンバスが表示されるようになります。

スクロールバー作成後のアプリ起動画面

スクロールバーのスライダーもマウス操作で動かすことも確認できると思いますが、まだキャンバスが真っ白なのでスクロールしていることは実感できないと思います。ですが、次に行うキャンバスへの背景の描画により、画面がスクロールすることを実感できるようになります。

スポンサーリンク

ゲームの背景の表示

次は、Screen クラスに drawBackground メソッドを作成することで、背景の表示機能を実現していきます。

背景画像の準備

今回はゲームの背景としては画像を使用したいと思います。つまりキャンバスに画像の描画を行うことでゲームの背景の表示を行ないます。

このためには、まずは描画する背景画像を準備する必要があります。

背景画像なんでも良いのですが、このページでは いらすとや の下記 URL の画像を背景画像として利用していきたいと思います。

http://www.irasutoya.com/2016/01/blog-post_135.html

下の図は上記 URL の画像を転載させていただいたものになります。爽やかな素敵な画像ですね!

アプリの背景画像

転載元:いらすとや

特にゲームの背景として使用したい画像がないのであれば、上の画像を適当なフォルダに保存しておいてください。画像を読み込む際には、この画像を保存した先のファイルパスを指定する必要がありますので、メモしておくと良いと思います。

背景画像の拡大縮小

画像ファイルが準備できれば、あとは背景画像をキャンバスに描画してやれば良いのですが、背景画像とゲーム画面のサイズが合わない場合、余白ができることになってしまいます。

そのため、ゲーム画面のサイズ(scrollregion で指定した矩形のサイズ)いっぱいに画像を描画ができるよう、事前に拡大縮小を行なってからキャンバスに画像を描画していきたいと思います。

背景画像の拡大縮小の仕方

このページでは単純に、ゲーム画面のサイズに合わせて画像を拡大縮小するようにしたいと思います。

ただし、単純にゲーム画面のサイズに合わせて画像を拡大縮小してしまった場合、背景画像の縦横比が変わってしまって見栄えが悪くなる可能性があるので注意してください。

極端な例を示すと、下の図のように背景画像が横になが〜く拡大縮小される場合があります。

画像の縦横比が変化して画像の見栄えが悪くなる例

もし画像の縦横比が変わってしまうことが気になる場合、下記ページで画像の縦横比を保ったまま画像の拡大縮小を行い、さらにそれを横方向に何枚も並べることで綺麗に背景を表示する方法の解説を行なっていますので、こちらを参考にしていただければと思います。

横スクロールアクションゲームを作る(カスタマイズ例)の解説ページアイキャッチ【Python/tkinter】横スクロールアクションゲームを作る(カスタマイズ例)

背景画像の拡大縮小処理の実装

さて、背景画像の拡大縮小ですが、これは PIL の resize メソッドにより行うことができます。

この resize メソッドは、PIL 用の画像オブジェクトからしか実行できないため、まずは PIL の open 関数を利用して背景画像の読み込み& PIL 用の画像オブジェクトの生成を行い、それから resize メソッドを実行します。

resize メソッドの引数にはゲーム画面の幅と高さをタプルで指定します。

これによりゲーム画面のサイズに合わせた拡大縮小後の画像を取得できるのですが、PIL 用の画像オブジェクトはキャンバスに描画できません。

そのため、この画像オブジェクトを tkinter 用の画像オブジェクトに変換する必要があります。この変換については下記ページで解説しているので詳しく知りたい方はぜひ読んでいただきたいのですが、結論としては PIL の ImageTk.PhotoImage クラスのコンストラクタを実行することにより、この変換を実現することができます。

画像オブジェクト変換方法の解説ページアイキャッチ【Python】PIL ⇔ OpenCV2 ⇔ Tkinter 画像オブジェクトの相互変換

まずはここまで解説した内容の処理を行うメソッドを作成していきましょう。具体的には、Screen クラスに下記の drawBackground メソッドを追加します。

画像の拡大縮小
class Screen:

	# ↓これを追加
	def drawBackground(self):
		image = Image.open("bg_natural_sougen.jpeg")

		size = (self.game_width, self.game_height)
		resized_image = image.resize(size)

		self.bg_image = ImageTk.PhotoImage(resized_image)
	# ↑これを追加

Image.open 関数の引数には、読み込みたい背景画像のファイルパスを指定する必要があります。上記では "bg_natural_sougen.jpeg" を指定していますが、ご自身が用意した背景画像のファイルパスに応じて変更してください。

また、game_widthgame_heightキャンバス作成時に必要なパラメータ で追加したデータ属性であり、それぞれゲーム画面の幅と高さを示す値です。これを resize メソッドの引数に指定していますので、ゲーム画面のサイズに合わせて背景画像が拡大縮小されることになります。

上記の drawBackground メソッドが実行されれば、データ属性 bg_image が拡大縮小後の背景画像を参照することになりますので、あとはこの bg_image をキャンバスに描画すれば背景画像が表示されるようになります。

スポンサーリンク

背景画像の描画

キャンバスへの画像の描画は、tkinter.Canvas クラスの create_image メソッドの実行により行うことができます。

描画したい画像は create_image メソッドのオプション image で指定します。今回はデータ属性 bg_image の画像を描画したいので、オプション image=self.bg_image を指定して create_image メソッドを実行することになります(image オプションには tkinter 用の画像オブジェクトを指定する必要があります)。

ということで、先程作成した drawBackground メソッドの後半でキャンバスの画像の描画を行うようにしていきましょう。具体的には drawBackground メソッドを下記のように変更します。

キャンバスへの画像の描画
class Screen:

	def drawBackground(self):

		# 略 

		self.bg_image = ImageTk.PhotoImage(resized_image)
		
		# ↓これを追加
		self.canvas.create_image(
			0, 0,
			anchor=tkinter.NW,
			image=self.bg_image
		)
		# ↑これを追加

上記では、self.canvas のキャンバスに対して bg_image の画像の描画を行なっています。anchortkinter.NW を指定していますので、画像の左上がキャンバスの (0, 0) 座標、つまりキャンバスの左上に合わさるように画像が描画されます。

anchor 等の各引数・オプション等の意味は下記ページで解説していますので、詳しく知りたい方は下記ページを参照していただければと思います。

tkinterキャンバスに図形を描画する方法解説ページのアイキャッチTkinterの使い方:Canvasクラスで図形を描画する

以上により、ゲームの背景の表示を行う drawBackground メソッドは完成です。

あとは、drawBackground メソッドを実行するように Screen クラスの __init__ の最後に下記を追記してやれば、Screen クラスのオブジェクト生成時に自動的にゲームの背景表示まで行われるようになります。

ゲームの背景の表示の実行
class Screen:
	def __init__(self, master):
		
		# 略
		
		self.createWidgets()

		# ↓これを追加
		self.drawBackground()
		# ↑これを追加

以上の変更を行なった後にスクリプトを起動すれば、下の図のようなアプリが起動するはずです。

背景画像描画後のアプリ起動画面

背景画像が表示されていること、および、スクロールバーのスライダーの左右への移動に伴って、その背景の表示領域が変化していくことも確認できると思います。

このページで作成したスクリプト

以上で、このページの解説は終了です。

最後に、ここまでの解説を踏まえて作成したスクリプトの全体を下記に掲載しておきます。次のページではこのスクリプトをベースに解説を進めていきたいと思います。

このページで作成したスクリプト
import tkinter
from PIL import Image, ImageTk, ImageOps
import random

# アプリの設定
VIEW_WIDTH = 600
VIEW_HEIGHT = 400
GAME_WIDTH = 1500

BG_IMAGE_PATH = "bg_natural_sougen.jpeg"

class Character:

	def __init__(self):
		pass

class Player(Character):

	def __init__(self):
		pass

class Enemy(Character):

	def __init__(self):
		pass

class CatEnemy(Enemy):

	def __init__(self):
		pass

class DogEnemy(Enemy):

	def __init__(self):
		pass

class Goal(Character):

	def __init__(self):
		pass

class Screen:

	def __init__(self, master):
		self.master = master

		self.view_width = VIEW_WIDTH
		self.view_height = VIEW_HEIGHT
		self.game_width = GAME_WIDTH
		self.game_height = self.view_height

		self.createWidgets()
		self.drawBackground()

	def createWidgets(self):
		self.canvas = tkinter.Canvas(
			self.master,
			width=self.view_width,
			height=self.view_height,
			scrollregion= (
				0,0,self.game_width,self.game_height
			),
			highlightthickness=0
		)
		self.canvas.grid(column=0, row=0)

		xbar = tkinter.Scrollbar(
			self.master,
			orient=tkinter.HORIZONTAL,
		)

		xbar.grid(
			row=1, column=0,
			sticky=tkinter.W + tkinter.E
		)

		xbar.config(
			command=self.canvas.xview
		)

		self.canvas.config(
			xscrollcommand=xbar.set
		)

	def drawBackground(self):
		image = Image.open(BG_IMAGE_PATH)

		size = (self.game_width, self.game_height)
		resized_image = image.resize(size)

		self.bg_image = ImageTk.PhotoImage(resized_image)

		self.canvas.create_image(
			0, 0,
			anchor=tkinter.NW,
			image=self.bg_image
		)

class Game:

	def __init__(self, master):
		self.master = master
		self.screen = Screen(self.master)

def main():
	app = tkinter.Tk()
	game = Game(app)
	app.mainloop()

if __name__ == "__main__":
	main()

基本的には、ここまで紹介してきたスクリプトと同じなのですが、キャンバスの画面のサイズや背景画像のファイルパスに関してはグローバル変数として定義するようにしています。これは、後から変更しやすい&他のクラスからも参照できるようにするためです。

各グローバル変数の意味合いは下記のようになります。

  • VIEW_WIDTH:表示領域の幅(ピクセル数)
  • VIEW_HEIGHT:表示領域の高さ(ピクセル数)
  • GAME_WIDTH:ゲーム画面の幅(ピクセル数)
  • BG_IMAGE_PATH:ゲームの背景となる画像のファイルパス

もし、ここまでの解説の中でこれらを自身の環境に合わせて変更された方は、こちらの定義値も変更しておく必要があるので注意してください。

まとめ

このページでは、横スクロールアクションゲームを作成するにあたって、まずウィジェットの作成とゲームの背景の表示を行いました。

正直まだ全くゲームを作っている感じがしないと思いますが、今回作成したキャンバス上にキャラクターの画像を描画することでゲームを作っていくことになりますので、その土台が出来上がったと考えて良いと思います。

また、tkinter でキャンバスへの画像の描画は様々なアプリ開発で役に立つと思いますので、画像を描画する流れは覚えておくと良いと思います!

次のページでは、キャラクターのクラスを作成し、さらに今回作成したキャンバスを利用してキャラクターの画面上への表示を行なっていきます!

横スクロールアクションゲームを作る(キャラクターの表示)の解説ページアイキャッチ【Python/tkinter】横スクロールアクションゲームを作る(キャラクターの表示)