業務アプリの開発者が趣味でPythonを使ってゲーム開発してみた ~tkinter編~

| 4 min read
Author: ryo-nakagaito ryo-nakagaitoの画像

はじめに

#

私は普段は業務アプリの開発に従事しております。開発言語はほぼJavaであり、Spring Framework/Spring Bootを使用することが多いです。

業務以外でプログラムを書く機会や趣味はほとんどなかったのですが、最近インディー系の2Dアクションゲームにハマっており(ホロウナイト、カップヘッド、オリシリーズなどが好きです。)自分でも簡単なもので良いからミニゲーム開発をしてみたい!と思い立ってやってみることにしました。

今回は私のミニゲーム開発に使用したPythonのライブラリのことや開発の様子についてお伝えしたいと思い記事を書くことにしました。読んでいただけますと幸いです。

開発の進め方

#

まずは本を読んで体系的に技術の知識を身に着けたいと思い、以下の書籍を参考にすることにしました。

Pythonでつくる ゲーム開発 入門講座 実践編 / 廣瀬 豪

この本を読めば書いてある通りのミニゲームが作成できますが、オリジナルの要素や機能の追加をしたくなることもあるかと思い、その際はChatGPTを頼ることにしました。本と生成AIのハイブリッド開発ですね。

使用するライブラリ「tkinter」について

#

本では、PythonのライブラリtkinterPygameが紹介されています。Pygameは高機能なゲーム用ライブラリですが、今回はシンプルなミニゲームを作るのでtkinterのみを使用することとしました。

ChatGPTに聞いてみたところ、以下のようなライブラリとのことです。

tkinter(ティーケーインター)は、PythonでGUI(グラフィカルユーザーインターフェース)アプリを作るための標準ライブラリです。
Pythonだけで「ウィンドウを出したり、ボタンやラベルを配置したり、画像を表示したり」といった
デスクトップアプリを作れる便利なツールです。

ゲームに限らずシンプルなGUIツールを作成するのにも使えそうですね。Pythonに標準で入っているのが嬉しい点だと思いました。

ミニゲームの要件

#

以下のような要件を満たす2Dのミニゲームを作ります。

  • ヘビからウサギが逃げるというコンセプトのゲーム
  • ウサギがヘビに接触するとゲームオーバー
  • ヘビは画面内をランダムな方向に移動する
  • ウサギはプレイヤーが操作し、マウスのカーソルに追従して移動する

ゲーム開発に関しては初心者なのでまずはこれくらいで良いでしょう。

tkinterの基本

#

本を読みながらコードを書いてゆきます。tkinterのミニゲームアプリは以下のコードが必須だそうです。

# メインウィンドウとキャンバスの設定
root = tkinter.Tk()
root.title("ウサギのミニゲーム")

# ----------------------
# 画像を描画するキャンバスの設定を行う。
# (縦横比の設定や背景画像素材の設定、マウスムーブのイベントとキャンバスの紐づけ等)
# ----------------------

root.mainloop()

本にはroot = tkinter.Tk()が「ウィンドウの部品を作る」、root.mainloop()が「ウィンドウを表示」の処理だと書かれていますが、もう少し詳しく知りたいのでChatGPTに聞いてみます。

  • root = tkinter.Tk()
    • 何をしているか
      • tkinter.Tk()アプリ全体のメインウィンドウ(トップレベルウィンドウ)を生成しています。つまり「ウィンドウを1つ作る」という命令です。
    • 具体的な動作
      • OS上で、新しい空のウィンドウを作ります。このウィンドウは、後でボタンやキャンバス、ラベルなどのウィジェットを配置する「土台」になります。rootはこのウィンドウを操作するための変数名(慣習的にrootwindowと書かれます)。
  • root.mainloop()
    • 何をしているか
      • イベントループ(main roop)を開始します。これは、ウィンドウを開いたままにして、ユーザー操作(クリック・キー入力など)を待ち続ける仕組みです。
    • 具体的な動作
      • この行を呼ぶと、プログラムは「ループ状態」に入ります。
      • tkinterが内部的に「イベント(クリック・キー入力など)」を監視して、対応する処理を呼び出します。
      • mainloopを呼ばないと、ウィンドウは一瞬で開いてすぐ閉じてしまいます。

より詳しく知ることができました。tkinterでウィンドウを作成し、表示・維持する最も基本的なコードだということが分かりました。GUIアプリはユーザの操作を待つ必要があるので内部的にループ状態を管理する仕組みがあるんですね。

作ってみる

#

一旦できました。拙いですが一応ちゃんとミニゲームとして動いていて感動です。

細かい部分は全て書ききれないので、このミニゲームのキモとなる当たり判定のロジックについてピックアップして書いておきます。

当たり判定のロジック

#

本では「円同士の当たり判定」と「長方形同士の当たり判定」の2種類が紹介されていました。等身の高いキャラクターなんかは長方形の方が適していそうですが、今回は円同士の当たり判定を実装してみました。

ウサギとヘビそれぞれのx、y座標の値と半径の長さrを使用して計算します。座標同士の距離を求め、それが半径の合計以下の長さになっているかを判定しています。

def hit_check(self):
    dis = math.sqrt((self.rabbit.x - self.snake.x) ** 2 + (self.rabbit.y - self.snake.y) ** 2)
    return dis <= self.rabbit.r + self.snake.r

マウスムーブ時に実行する処理の中でhit_checkメソッドを呼び、Trueが返されたときはゲームオーバー画面を表示するようにしています。

改善点

#

一応形にはなりましたが改善したい部分も出てきました。ヘビは0.05秒間隔でランダムな方向に一定距離移動するように実装していたのですが、目で見ると動きがかなりぎこちないです。ChatGPTに相談しつつ、ゆっくりとウサギの位置に追従するような動きになるよう処理内容を修正してみます。

以下が、ヘビを管理するSnakeクラスに実装した修正後のヘビ移動メソッドです。target_x、target_yはウサギの座標を受け取り、on_move_doneはこの移動処理を繰り返し呼ぶためのコールバックです。

def move_toward(self, target_x, target_y, on_move_done):
    # ウサギに向かって移動
    dx = target_x - self.x
    dy = target_y - self.y
    dist = math.sqrt(dx**2 + dy**2)
    # 距離が0でないときだけ移動方向を正規化
    if dist != 0:
        dx /= dist
        dy /= dist
    new_x = self.x + dx * self.speed
    new_y = self.y + dy * self.speed

    # 画面範囲チェック 範囲内なら更新
    if 0 < new_x < 1200 and 0 < new_y < 676:
        self.x, self.y = new_x, new_y
        self.draw()

    # 50msごとに再実行
    self.job = self.canvas.after(50, on_move_done)

修正後の動きがこんな感じになりました。

自然な動きでウサギを追いかけるようになりました。他にも工夫すれば、追従とランダムな方向を組み合わせたり、一定時間おきにスピードアップしたりできそうですね。

おわりに

#

ゲームを作るのは初めてでしたが、やってみると結構簡単にちょっとしたミニゲームが作れて面白かったです。今は本だけではなく生成AIに相談しながら開発を進めることができるのがかなり便利ですね。本で基礎を学んで、ChatGPTと相談しながらオリジナル要素を実装してゆくという進め方が楽しいです。

まだまだ改善したい点はたくさんあります。

  • ウサギをマウスムーブに追従ではなくボタン操作できるようにする
  • ジャンプを実装する
  • 障害物を配置する
  • ヘビとバトルして倒せるようにする(攻撃アクションとHPの導入)

などなど。

また、今回は素材画像をいらすとやさんからダウンロードして使用しましたが、素材も全て生成AIで作るのも良さそうだと思いました。

引き続き趣味でゲーム開発をやっていき、次はPygameを使って何かしら作り記事にできたらと考えています。
読んでいただいてありがとうございました。

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。