このページでは、Python で tkinter を利用した簡単な「横スクロールアクションゲーム」の作り方を解説していきます。
このページは「横スクロールアクションゲームの作り方の解説」の3ページ目となります。
2ページ目は下記ページとなり、2ページ目では必要なウィジェットの作成や背景の表示の解説を行なっています。
【Python/tkinter】横スクロールアクションゲームを作る(ウィジェットの作成および背景の表示)また、このページでは、上記ページの このページで作成したスクリプト で紹介しているスクリプトを変更していきながらゲームを作成していきます。
ですので、事前に上記のページを読んでおくことをオススメします。
この「横スクロールアクションゲームの作り方の解説」の3ページ目では、主に操作キャラクターの表示および、次のページで解説する操作キャラクターの移動を行うための前準備を行なっていきたいと思います。
Contents
Character
クラスのオブジェクトの生成
今回、操作キャラクターの表示を行うための機能の一部は Character
クラスに実装していきたいと思います。
操作キャラクター用のクラスとして Character
クラスのサブクラスである Player
を用意しているのですが、ゲーム内に登場するキャラクターはまだ操作キャラクターだけですので、他のキャラクターが登場するまでは Character
クラスのみを実装していきます。
そして、他のキャラクターが登場したタイミングで、Player
クラスを利用するようにしていきたいと思います。
ということで、まずは、その Character
クラスのオブジェクトを生成するようにしていきます。
Character
クラスの枠組みだけは既に用意していますので、あとは Game
クラスから Character
クラスのコンストラクタを実行させるようにすれば、Character
クラスのオブジェクトの生成は完了します。
具体的には、Game
クラスの __init__
を下記のように変更します。
class Game:
def __init__(self, master):
self.master = master
self.screen = Screen(self.master)
# ↓これを追加
self.characters = []
self.player = Character()
self.characters.append(self.player)
# ↑これを追加
リスト characters
をデータ属性に追加し、Character
クラスのオブジェクトをそのリストに追加するようにしています。
わざわざリストを用意しているのは、今後登場するキャラクターが増えることを見越してのことです。
今後扱うキャラクターが増えた際にも、ループ処理により一括で処理の依頼等を行えるよう、全てのオブジェクトをリストで管理するようにしています。
ただし、操作キャラクターは他のキャラクターに比べて特別で、キーボードのキー入力により移動を行ったり、このキャラクターの位置に応じて画面のスクロールを行ったりする必要があります。
そのため、リストからだけではなく直接オブジェクトが参照できるようにもした方が便利なので、別途データ属性 player
を持たせるようにしています。
操作キャラクターに対してのみ処理を行いたいような場合は、この player
を利用するようにしていきます(といってもこのページで利用するのは characters
の方のみとなります)。
操作キャラクターの画像の準備
今回作成するゲームでは、キャラクターの画像を準備し、その画像をキャンバスに描画することでキャラクターを画面に表示するようにしていきたいと思います。
そのため、事前に操作キャラクターの画像を準備しておく必要があります。
ご自身が操作したいキャラクターの画像であればなんでも良いのですが、背景が透明になっている画像が望ましいです。背景が透明でないと、下の図のようにキャラクターの周りが白色になってしまいます。
また、この背景が透明であることを利用すれば、敵キャラクター等との詳細な当たり判定も行うことができるようになります。この背景が透明であることを利用した当たり判定については下記ページで解説しています。
【Python/tkinter】横スクロールアクションゲームを作る(より詳細な当たり判定)ひとまず、このページでは いらすとや の下記 URL の画像を操作キャラクター画像として利用していきたいと思います。
http://www.irasutoya.com/2014/04/blog-post_15.html
下の図は上記 URL の画像を転載させていただいたものになります。
転載元:いらすとや
特に操作キャラクターとして使用したい画像にこだわりがないのであれば、上の画像を適当なフォルダに保存しておいてください。画像を読み込む際には、この画像を保存した先のファイルパスを指定する必要がありますのでメモしておくと良いと思います。
スポンサーリンク
キャラクターの画像オブジェクト生成
続いて、準備したキャラクターの画像からキャンバスに描画可能な画像オブジェクトを生成していきます。
この画像オブジェクトの生成は、Character
クラスに prepareImage
メソッドを作成することで実現していきたいと思います。
画像の拡大縮小
単にキャンバスに描画可能な画像オブジェクトを作成するだけであれば、tkinter.PhotoImage
クラスのコンストラクタを実行するだけで実現することができます。
ですが、単にこれを行うだけだと、下の図のように操作キャラクターが大きすぎたり小さすぎたりする可能性があります。そのため、PIL を利用して画像の拡大縮小を行なってから、PIL 用の画像オブジェクトを tkinter 用の画像オブジェクトに変換するという手順を踏んでいきたいと思います。
基本的に行うことは、下記の2ページ目で行なった 背景画像の拡大縮小 とほぼ同じです。
【Python/tkinter】横スクロールアクションゲームを作る(ウィジェットの作成および背景の表示)ただし、背景は画像の縦横比を無視し、キャンバスのサイズに合わせて拡大縮小を行いましたが、今回の操作キャラクターの画像の拡大縮小は、画像の縦横比を保ったまま、矩形のサイズに合わせて拡大縮小を行うようにしたいと思います。
この矩形のサイズに合わせて拡大縮小を行う方法は下記ページで解説していますので、詳しく知りたい方はこちらをご参照いただければと思います。
【Python/PIL】縦横比を保ったまま矩形に合わせて画像をリサイズ(resize・thumnail)具体的には、Character
クラスに下記の prepareImage
メソッドを追加することで、上記のような操作キャラクターの画像の拡大縮小を実現することができます。
class Character:
# ↓これを追加
def prepareImage(self, path, size):
image = Image.open(path)
width, height = size
ratio = min(width / image.width, height/ image.height)
resize_size = (round(ratio * image.width), round(ratio * image.height))
resized_image = image.resize(resize_size)
# ↑これを追加
引数の path
には操作キャラクター表示用に使用したい画像のファイルパスを指定し、size
は (width, height)
形式のタプルを指定します。この size
の矩形に合わせて縦横比を保ったまま画像の拡大縮小が行われます。
描画用画像オブジェクトの生成
ただし、上記で作成される画像オブジェクトは PIL 用のものであり、このままだとキャンバスに描画できません。そのため、背景画像の拡大縮小 でも行なったように、PIL 用の画像オブジェクトを tkinter 用の画像オブジェクトに変換を行う必要があります。
具体的には、先程作成した prepareImage
の一番最後を下記のように変更します。
class Character:
def prepareImage(self, path, size):
image = Image.open(path)
width, height = size
ratio = min(width / image.width, height/ image.height)
resize_size = (round(ratio * image.width), round(ratio * image.height))
resized_image = image.resize(resize_size)
# ↓これを追加
self.right_image = ImageTk.PhotoImage(resized_image)
# ↑これを追加
これにより、Character
クラスのデータ属性 right_image
にキャンバスに描画可能な画像オブジェクトを参照させることができます。
ちなみに、right_image
の right
は右方向を向いている画像であることを示すために付けています。
もしかしたら、現状では用意した操作キャラクターの画像によっては左方向を向いてしまうかもしれませんが、下記ページで行う変更により、その画像の向きを考慮して right_image
に右方向を向いた画像を参照させることができるようになります(特別なことを行うわけではなく、単に引数を追加するだけです)。
スポンサーリンク
画像の生成の実行
続いて、prepareImage
の実行部分を実装していきます。今回は Character
クラスの __init__
から prepareImage
メソッドを実行するようにしたいと思います。
具体的には、Character
クラスの __init__
を下記のように変更します。
class Character:
def __init__(self):
#↓これを削除
#pass
#↑これを削除
# ↓これを追加
self.prepareImage("hashiru_boy.png", (100, 100))
# ↑これを追加
前述の通り、prepareImage
メソッドの第1引数には、用意した操作キャラクターの画像のファイルパスを指定する必要があります。上記では "hashiru_boy.png"
を指定していますが、ご自身が保存されたキャラクターの画像のフォルダやファイルパスに応じて、この引数は適切に変更していただければと思います。
また prepareImage
メソッドの第2引数には、画像の拡大縮小時に用いる矩形の幅と高さをタプル形式で指定する必要があります。この矩形に合わせて、画像の縦横比を保ったまま第1引数で指定した画像の拡大縮小が行われます。
この第2引数も、お好みに合わせて適切に変更していただければと思います。
上記の変更により、Character
クラスのオブジェクト生成時にキャンバスに描画可能な画像オブジェクトが生成され、その画像オブジェクトがデータ属性 right_image
から参照されるようになります。
といっても、まだここで生成した画像オブジェクトをキャンバスに描画していませんので、スクリプトを実行しても操作キャラクターは表示されません。
次は、この right_image
を他のクラスから取得を行えるようにし、その次にその画像をキャンバスに描画を行って操作キャラクターを画面に表示できるようにしていきたいと思います。
キャラクターの画像オブジェクトの取得
では、まずは他のクラスから画像オブジェクトを取得できるようにするためのメソッドを作成していきたいと思います。
具体的には、Character
クラスに下記の getImage
メソッドを追加します。
class Character:
# ↓これを追加
def getImage(self):
return self.right_image
# ↑これを追加
単に画像オブジェクトを取得するだけですので、getImage
メソッドは現状、上記のように self.right_image
メソッドを返却するだけのメソッドになります。
ただし、キャラクターの移動を実現する際に、操作キャラクターの移動方向を考慮して画像オブジェクトを返却するように変更します。
キャラクターの描画
続いて操作キャラクターの描画を行なうためのメソッドを Screen
クラスに用意していきたいと思います。
スポンサーリンク
キャンバスへの描画
この操作キャラクターの描画に関しても、下記ページで行なった ゲームの背景の表示 と同様に、tkinter.Canvas
クラスの create_image
メソッドにより実現することができます。
現状では、描画するキャラクターは操作キャラクター1つのみですが、今後登場するキャラクターが増えていくことを見据えて、画像のオブジェクト単体ではなく画像のオブジェクトのリストを受け取り、そのリストに格納されたオブジェクトを1つ1つ create_image
メソッドで描画していくようにしておきたいと思います。
具体的には、Screen
クラスに下記の update
メソッドを追加します。
class Screen:
#↓これを追加
def update(self, image_infos):
for image in image_infos:
self.canvas.create_image(
0, 0,
anchor=tkinter.NW,
image=image
)
#↑これを追加
リスト image_infos
に対するループの中で create_image
メソッドを実行することで、image_infos
内の画像オブジェクト全てを描画するようにしています。
また create_image
メソッドのオプションについては、image
オプションを除いて ゲームの背景の表示 の時と全く同じになります(ただし後から変更を行います)。
あとは、上記の update
メソッドが実行されるようにすれば、操作キャラクターの画像が描画されることになります。
キャンバスへの描画の実行
Screen
クラスに指示を出すのは Game
クラスの役割ですので、先程作成した update
メソッドは Game
クラスから実行させるようにしたいと思います。
update
メソッドの引数には画像オブジェクトのリストを指定する必要がありますので、まずは キャラクターの画像オブジェクトの取得 で用意した getImage
メソッドを利用して各キャラクター(といっても現状は操作キャラクター一人のみ)の画像オブジェクトを取得し、それをリストに格納していきます。
続いて、その画像オブジェクトのリストを引数に指定して update
メソッドを実行するようにしていきたいと思います。
具体的には、下記のように、Game
クラスの __init__
を変更します。
class Game:
def __init__(self, master):
#略
self.characters.append(player)
#↓これを追加
image_infos = []
for character in self.characters:
image = character.getImage()
image_infos.append(image)
self.screen.update(image_infos)
#↑これを追加
以上のように変更を行なった後にスクリプトを起動すれば、操作キャラクターが画面上に表示されるようになるはずです。
キャラクターの位置に応じた画像の描画
一応操作キャラクターが画面上に表示されるようになったものの、上の図を見れば分かるように、操作キャラクターはキャンバスの左上に描画されています。
これは、Screen
クラスの update
メソッドで create_image
を実行する際に、第1引数と第2引数に 0
を指定しているからです(この第1引数と第2引数の位置に画像が描画される)。
逆に言えば、第1引数と第2引数に指定する値を変更すれば、キャラクターが描画される位置を変化させることができます。今後キャラクターの移動などを実現していくことを考えると、この描画位置も制御できるようにしたほうが便利ですので、次はこの描画位置の制御を行うようにしていきたいと思います。
スポンサーリンク
キャラクターの位置の管理
この描画位置の制御を行うために、まずは Character
クラスで、キャラクターの位置を管理できるようにしたいと思います。
具体的には、Character
クラスの __init__
を下記のように変更し、データ属性 x
と y
でキャラクターの位置を管理できるようにします。
class Character:
def __init__(self):
self.prepareImage("hashiru_boy.png", (100, 100))
#↓これを追加
self.x = 0
self.y = 0
#↑これを追加
この x
と y
が、操作キャラクターの現在位置を示すデータ属性となります。x
が横方向の位置で、y
が縦方向の位置となります。
一つここで注意点を挙げておくと、tkinter におけるキャンバスの縦方向における正方向は「下方向」なので注意してください。つまり、y
が小さいほどキャラクターが上側に、大きいほどキャラクターが下側に存在することになります。
数学でよく使っていた座標のまま考えると上下が逆になってしまうので注意が必要です。
キャラクターの位置の受け渡し
次は、Screen
クラスの update
メソッドで実行する create_image
の第1引数と第2引数に、このデータ属性 x
と y
を指定できるようにしていきたいと思います。
Screen
クラスの update
メソッドは Game
クラスの __ init__
から実行していますが、現状では実行時に単に画像オブジェクトのリストを渡しているだけなので、Screen
クラスの update
メソッドは Character
クラスのデータ属性 x
と y
が参照できません。
なので、create_image
の第1引数と第2引数に指定すべき値がわからない状態です。
そのため、Screen
クラスの update
メソッドを実行する際に、画像オブジェクトだけではなく、Character
クラスのデータ属性 x
と y
の値も渡すように変更します。
具体的には、Game
クラスの __ init__
を下記のように変更します。
class Game:
def __init__(self, master):
#略
image_infos = []
for character in self.characters:
image = character.getImage()
#↓これを追加
image_info = (image, character.x, character.y)
image_infos.append(image_info)
#↑これを追加
#↓これを削除
#image_infos.append(image)
#↑これを削除
self.screen.update(image_infos)
これにより、Screen
クラスの update
メソッドは、単なる画像オブジェクトのリストではなく、画像オブジェクトと描画すべき位置のリストを引数で受け取ることになり、どこに描画すべきかが分かるようになります。
キャラクターの位置を考慮した描画
続いて Screen
クラスの update
メソッドを変更し、create_image
の第1引数と第2引数にキャラクターを描画する位置を指定するようにしていきます。
具体的には、Screen
クラスの update
メソッドを下記のように変更します。
class Screen:
def update(self, image_infos):
for image, x, y in image_infos: # ←ここを変更
self.canvas.create_image(
x, y, # ←ここを変更
anchor=tkinter.NW,
image=image
)
以上の変更により、キャラクターの位置、すなわち Characte
クラスのデータ属性 x
と y
に応じて操作キャラクターの画像の描画位置が自動的に変化するようになります。
つまり、キャラクターの移動は、Character
クラスのデータ属性 x
と y
を変更した後に Screen
クラスの update
メソッドを実行することで実現できるようになったことになります。
スポンサーリンク
キャラクターの初期位置の調整
といっても、まだ x
と y
の値は 0
と 0
のままなので、スクリプト実行時にキャラクターが描画される位置は先ほどと変わらず左上のままです。
キャラクターの位置に応じて画像が描画できるようになったので、次はキャラクターの初期位置の調整を行なっていきたいと思います。
現状操作キャラクターは画面の左上に描画されていることになります。左側にいるのは別にいいのですが、上側にいるので宙に浮いている・空を飛んでいる感じになってしまっています。
そのため、キャラクターを地面に立たせるイメージで、キャラクターの初期位置を画面の最下部に移動したいと思います。
キャラクターの縦方向の位置を移動させるので Character
クラスのデータ属性 y
を変更すれば良いのですが、この y
はいくつにすれば良いでしょうか?これについて考えていきたいと思います。
先ほど変更したように、Screen
クラスの update
メソッドでは下記のように操作キャラクターの描画を行います。
class Screen:
def update(self, image_infos):
for image, x, y in image_infos:
self.canvas.create_image(
x, y,
anchor=tkinter.NW,
image=image
)
上記のように引数を指定して create_image
メソッドを実行した場合、anchor=tkinter.NW
を指定しているので、画像の左上が座標 (x
, y
) の位置にくるように画像の描画が行われます。
したがって、画像の一番下の位置に操作キャラクターを描画したいのであれば、y
の値が ゲーム画面の高さ - 画像の高さ
であれば良いことになります。
前述の通り、キャンバスでは左上が原点であり、y
の正方向は下方向であることに注意してください
数学等で学ぶ座標系で考えると上下が逆になってしまいます
画像の高さ
に関しては、tkinter 用の画像オブジェクトに height
メソッドを実行させることで取得することができます。現状 Character
のデータ属性 right_image
が tkinter 用の画像オブジェクトを参照するようになっていますので、right_image.height()
を実行すれば良いことになります。
したがって、Character
クラスの __init__
にてデータ属性 y
を下記のように設定してやれば、操作キャラクターを画面の一番下に立たせるようにすることができます。
class Character:
def __init__(self):
self.prepareImage("hashiru_boy.png", (100, 100))
#↓これを追加
self.base_y = VIEW_HEIGHT - self.right_image.height()
#↑これを追加
self.x = 0
self.y = self.base_y #←ここを変更
VIEW_HEIGHT
は表示領域(すなわちゲーム画面)の高さを示すグローバル変数になります。
データ属性として base_y
の追加も行っていますが、このデータ属性はキャラクターの基準位置(ジャンプ中以外に存在する位置)の縦方向の位置を示すデータ属性としたいと思います。操作キャラクターは基本的にゲーム画面の一番下にいるようにしたいので、前述で説明した方法で座標の計算を行なっています。そして、この値を y
にも設定しています。
この base_y
があれば、例えばジャンプしたキャラクターが最終的に着地する座標などとして使用できますので、今後参照しやすいようにデータ属性として追加しています(このデータ属性は次のページのキャラクターの移動で活躍します)。
上記のように変更してからスクリプトを実行すれば、表示される操作キャラクターの位置が左下に変化したことが確認できると思います。また、これにより、キャラクターの位置に応じた画像の描画 も行えるようになっていることも実感できると思います。
定期的なキャラクターの描画
このページの最後として、キャラクターの描画を定期的に行う方法について解説していきたいと思います。
定期的な描画の実現
キャラクターの位置に応じた画像の描画 の最後でも説明しましたが、キャラクターの位置を考慮して画像を描画できるようにしたことで、キャラクターの移動は、Character
クラスのデータ属性 x
と y
を変更した後に Screen
クラスの update
メソッドを実行することで実現できるようになっています。
ただし、現状では Screen
クラスの update
メソッドは Game
クラスの __init__
から一回しか実行されていないので、それ以降一度も実行されないことになります。したがって、x
と y
が変更されたとしても、その後 Screen
クラスの update
メソッドが実行されることはありません…。
これだと Character
クラスのデータ属性 x
と y
を変化させても意味がないので、ここで Screen
クラスの update
メソッドを定期的に実行するようにしていきたいと思います。
tkinter を用いた場合、この定期的な処理の実行は after
メソッドを利用することで簡単に実現することができます。この after
メソッドに関しては下記ページで詳細を解説していますので、詳しく知りたい方は下記ページをご参照いただければと思います。
今回は Game
クラスに update
メソッドを用意し、このメソッドの中から Screen
クラスの update
メソッドの実行および、定期的に処理を実行するための after
メソッドの実行を行うようにしたいと思います。
具体的には、まず Game
クラスに下記の update
メソッドを追加します。
class Game:
#↓これを追加
def update(self):
self.master.after(100, self.update)
image_infos = []
for character in self.characters:
image = character.getImage()
image_info = (image, character.x, character.y)
image_infos.append(image_info)
self.screen.update(image_infos)
#↑これを追加
この update
メソッドでは上記のように after
メソッドを実行していますので、最初に1回実行すれば 100
ms 単位で定期的に繰り返し実行されることになります。
after
メソッドは、tkinter のウィジェットに実行させる必要があるので注意してください。上記では self.master
、すなわち今回作成するゲームのメインウィンドウウィジェットに実行させています。
で、今回定期的に行いたいのはキャラクターの描画、すなわち Screen
クラスの update
メソッドの実行ですので、元々 Game
クラスの __init__
で実行していた Screen
クラスの update
メソッドの実行処理を、上記の Game
クラスの update
メソッドの中で実行するようにしています。
これにより、Screen
クラスの update
メソッドが 100
ms 毎に実行され、定期的なキャラクターの描画を実現することができます。
スポンサーリンク
定期的処理の開始
ただし、この定期的な処理が行われるのは、Game
クラスの update
メソッドが1度実行されてからになります。
そのため、Game
クラスの update
メソッドの最初の一回を Game
クラスの __init__
から実行するよう、下記のように変更します。
class Game:
def __init__(self, master):
self.master = master
self.screen = Screen(self.master)
self.characters = []
self.player = Character()
self.characters.append(self.player)
#↓これを追加
self.update()
#↑これを追加
#↓これを削除
#image_infos = []
#for character in self.characters:
# image = character.getImage()
# image_info = (image, character.x, character.y)
# image_infos.append(image_info)
#
#self.screen.update(image_infos)
#↑これを削除
Screen
クラスの update
メソッドの実行は Game
クラスの update
から実行するようになったので、__init__
からはその処理は削除しています。
上記の変更により、Game
クラスのオブジェクト生成時に Game
クラスの update
メソッドが実行され、以降は after
メソッドにより 100
ms ごとに定期的に Game
クラスの update
メソッドが実行されるようになります。
描画前の画像の削除
上記の変更により定期的に画像がキャンバスに描画されていくことになります。
ただし、キャンバスに描画した画像は削除しない限りはキャンバス上に残り続けてしまうので、このままだと操作キャラクターが移動しても移動前の画像がキャンバスに残ってしまうことになります。
これを防ぐためには、キャンバスに描画したキャラクターの画像を一旦削除してから、描画を実行するようにする必要があります。
このキャンバスに描画した画像の削除は、tkinter.Canvas
の delete
メソッドにより実現することができます。この delete
メソッドの引数には、画像の描画時に実行する create_image
メソッドの返却値を指定する必要があります(タグ名を指定するのでも良い)。
現状、キャンバスに描画するキャラクターは操作キャラクターのみですが、今後敵キャラクターやゴールも登場することになります。
そのため、各キャラクター描画時の create_image
メソッドの返却値はリストに保持するようにしたいと思います。
そして、次回キャラクターを描画する前にそのリストの持つ値全てに対して delete
メソッドを実行するようにします。
ということで、まずは、create_image
メソッドの返却値を格納するためのリストを Screen
クラスのデータ属性 draw_images
として追加したいと思います。
具体的には、Screen
クラスの __init__
を下記のように変更します。
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.draw_images = []
#↑これを追加
#略
続いて Screen
クラスの update
メソッドで、最初にリスト draw_images
の値全てに対して delete
メソッドを実行するようにします。
さらに、create_image
メソッドの返却値をリスト draw_images
を格納するようにします。
具体的には、Screen
クラスの update
メソッドを下記のように変更します。
class Screen:
def update(self, image_infos):
#↓これを追加
for draw_image in self.draw_images:
self.canvas.delete(draw_image)
self.draw_images.clear()
#↑これを追加
for image, x, y in image_infos:
draw_image = self.canvas.create_image( # ←ここを変更
x, y,
anchor=tkinter.NW,
image=image
)
#↓これを追加
self.draw_images.append(draw_image)
#↑これを追加
これにより、前回の Screen
クラスの update
メソッドで描画されたキャラクターの画像全てをキャンバスから削除した後、移動後のキャラクターの画像が描画されるようになります。
といっても、まだ操作キャラクターが移動していないのでここまで変更してきた内容に効果があるのかが実感しにくいと思います。
早く効果を実感したいという方は、お試しで Game
クラスの update
メソッドを下記のように変更してみてください(効果が確認できたら変更は戻しておいてください)。
class Game:
def update(self):
image_infos = []
for character in self.characters:
#↓これを追加
character.x += 10
#↑これを追加
image = character.getImage()
#略
この変更で、定期的にキャラクターの位置が右側に移動するようになります。この状態でスクリプトを実行すれば、以前に描画した画像が残るようなこともなく、キャラクターが移動していくように見えると思います。
上記の変更だと自動的にキャラクターが移動してしまいますが、この移動をキーボード入力時に行うようにすれば、プレイヤーがキャラクターを操作できるようになることになります。
このための方法については、下記の次のページで解説していきたいと思います。
【Python/tkinter】横スクロールアクションゲームを作る(キャラクターの移動)また、ここで使用した delete
メソッドを始めとする、tkinter.Canvas
クラスの各メソッドについては下記ページでも詳しく解説していますので、いろんなメソッドを使いこなしたい方はぜひ読んでみてください。
このページで作成したスクリプト
以上で、このページの解説は終了です。
最後に、ここまでの解説を踏まえて作成したスクリプトの全体を下記に掲載しておきます。次のページではこのスクリプトをベースに解説を進めていきたいと思います。
import tkinter
from PIL import Image, ImageTk, ImageOps
import random
# アプリの設定
VIEW_WIDTH = 600
VIEW_HEIGHT = 400
GAME_WIDTH = 1500
UPDATE_TIME = 100
BG_IMAGE_PATH = "bg_natural_sougen.jpeg"
PLAYER_IMAGE_PATH = "hashiru_boy.png"
class Character:
def __init__(self):
self.prepareImage(PLAYER_IMAGE_PATH, (100, 100))
self.base_y = VIEW_HEIGHT - self.right_image.height()
self.x = 0
self.y = self.base_y
def getImage(self):
return self.right_image
def prepareImage(self, path, size):
image = Image.open(path)
width, height = size
ratio = min(width / image.width, height/ image.height)
resize_size = (round(ratio * image.width), round(ratio * image.height))
resized_image = image.resize(resize_size)
self.right_image = ImageTk.PhotoImage(resized_image)
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.draw_images = []
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
)
def update(self, image_infos):
for draw_image in self.draw_images:
self.canvas.delete(draw_image)
self.draw_images.clear()
for image, x, y in image_infos:
draw_image = self.canvas.create_image(
x, y,
anchor=tkinter.NW,
image=image
)
self.draw_images.append(draw_image)
class Game:
def __init__(self, master):
self.master = master
self.screen = Screen(self.master)
self.characters = []
self.player = Character()
self.characters.append(self.player)
self.update()
def update(self):
self.master.after(UPDATE_TIME, self.update)
image_infos = []
for character in self.characters:
image = character.getImage()
image_info = (image, character.x, character.y)
image_infos.append(image_info)
self.screen.update(image_infos)
def main():
app = tkinter.Tk()
game = Game(app)
app.mainloop()
if __name__ == "__main__":
main()
基本的には、ここまで紹介してきたスクリプトを寄せ集めてきたものですが、操作キャラクターの画像のファイルパスと画面を更新する間隔をグローバル変数として定義するようにしています。
今回定義したグローバル変数の意味合いは下記のようになります。
PLAYER_IMAGE_PATH
:操作キャラクターの画像のファイルパスUPDATE_TIME
:画面やキャラクターの状態を更新する間隔(単位は ms)
もし、ここまでの解説の中でこれらを自身の環境に合わせて変更された方は、こちらの定義値も変更しておく必要があるので注意してください。
スポンサーリンク
まとめ
このページでは、横スクロールアクションゲームにおける「キャラクターの表示」について解説しました。
まだキャラクターが動いていないのでゲームっぽさはないかも知れませんが、キャラクターの位置に応じて画像の描画ができるようになったので、キャラクターの移動の実現まではもうすぐです!
特に今回使用した after
メソッドに関しては、tkinter でアプリ開発を行う上で必須級のメソッドになりますので、ぜひこの機会に使い方を覚えておいていただければと思います。
次のページでは「キャラクターの移動」について解説していきます!おそらく次のページあたりからグッとゲーム開発っぽくなってくると思います!
【Python/tkinter】横スクロールアクションゲームを作る(キャラクターの移動)オススメ参考書(PR)
簡単なアプリやゲームを作りながら Python について学びたいという方には、下記の Pythonでつくる ゲーム開発 入門講座 がオススメです!ちなみに私が Python を始めるときに最初に買った書籍です!
下記ようなゲームを作成しながら Python の基本が楽しく学べます!素材もダウンロードして利用できるため、作成したゲームの見た目にも満足できると思います。
- すごろく
- おみくじ
- 迷路ゲーム
- 落ち物パズル
- RPG
また本書籍は下記のような構成になっているため、Python 初心者でも内容を理解しやすいです。
- プログラミング・Python の基礎から解説
- 絵を用いた解説が豊富
- ライブラリの使い方から解説(tkitner と Pygame)
- ソースコードの1行1行に注釈
ゲーム開発は楽しくプログラミングを学べるだけでなく、ゲームで学んだことは他の分野のプログラミングにも活かせるものが多いですし(キーボードの入力受付のイベントや定期的な処理・画像や座標を扱い方等)、逆に他の分野のプログラミングで学んだ知識を活かしやすいことも特徴だと思います(例えばコンピュータの動作に機械学習を取り入れるなど)。
プログラミングを学ぶのにゲーム開発は相性抜群だと思います。
Python の基礎や tkinter・Pygame の使い方をご存知なのであれば、下記の 実践編 をいきなり読むのもアリです。
実践編 では「シューティングゲーム」や「アクションゲーム」「3D カーレース」等のより難易度の高いゲームを作りながらプログラミングの力をつけていくことができます!
また、単にゲームを作るのではなく、対戦相手となるコンピュータの動作のアルゴリズムにも興味のある方は下記の「Pythonで作って学べるゲームのアルゴリズム入門」がオススメです。
この本はゲームのコンピュータ(AI)の動作アルゴリズム(思考ルーチン)に対する入門解説本になります。例えばオセロゲームにおけるコンピュータが、どのような思考によって石を置く場所を決めているか等の基本的な知識を得ることが出来ます。
プログラミングを挫折せずに続けていくためには楽しさを味わいながら学習することが大事ですので、特にゲームに興味のある方は、この辺りの参考書と一緒に Python を学んでいくのがオススメです!