ゲーム開発で使用したテクニック～エンドロール～

個人でゲーム開発をしている中で、実装に苦労した部分が多かったので、テクニックとして紹介していこうと思います。

今回は「エンドロール」について書きます。
## 概要
ゲームのエンディング後に流れるアレです。
画面下から文字が上昇し、画面上に消えていき、「Thank you for playing！」で止まるという仕様にしました。
## 問題点
普通に実装するとエンドロールが終わるまでボタン入力を受け付けません。
エンドロール中もボンタン入力を受け付けて、スキップできるようにしたかったです。
ということで、並列処理の出番だと思いました。
## threadingモジュールを利用する
```py
import threading

class ThreadingCredits:

    def __init__(self, screen):
        """
		初期化
		"""
        self.SCREEN = screen
        self.TEXT_LIST = [{"title":"", "name":[""]},{"title":"", "name":[""]}]
        self.STOPPED = False
		self.FINISHED = False

        self.FONT_LOW = pg3.font.Font("font_name.ttf", 24)
		self.FONT_MIDDLE = pg3.font.Font("font_name.ttf", 36)
        
    def start(self):
        """
		スレッドを開始する
		"""
		thread = threading.Thread(target=self.update, daemon=True)
		thread.start()
		return self
        
    def update(self):
        """
		スレッドで実行する処理
		"""
        (中略)
        
    def stop(self):
        """
		スレッドの処理を停止する
		"""
		self.STOPPED = True
        
    def reset(self):
        """
		スレッドの処理を再開できるようにフラグをリセットする
		"""
		self.STOPPED = False
		self.FINISHED = False
```
自作したスレッドクラスの骨組みです。
今回はthreadingモジュールを使用しました。
呼び出し元のサーフェイスを流用する為、初期化時にscreenを引き継ぐようにしました。
「TEXT_LIST」に表示する文字列を入れておきます。クレジットなので担当（title）と名前（name）が必要でしょう。複数名いるかもしれないので、名前は配列にしました。
フォントもサイズ別に2種類用意しました。
あとはスレッドの停止と終了を管理するフラグと関数を用意しておきます。
## 使い方
```py
import ThreadingCredits

test_thread = ThreadingCredits.ThreadingCredits(self.screen)
test_thread.start()

run = True

while run:

    for event in pg3.event.get():

        if event.type == pg3.KEYDOWN and event.key == pg3.K_RETURN:
            test_thread.stop()
            self.screen.fill((0, 0, 0))
            pg3.display.update()
```
「self.screen」は事前に用意したサーフェイスを指定します。
任意のタイミングでスレッドを開始しておき、Enterキー押下でスレッドを止めることができます。
スレッドを止める際は、resetも呼んでフラグをリセットしておくとよいでしょう。
## update関数の中身
```py
def update(self):
    """
    スレッドで実行する処理
    """
    if self.FINISHED == False:
        for i in range(750, -2301, -1):

            # 停止されたら処理を中断する
            if self.STOPPED:
                return
            
            self.SCREEN.fill((0, 0, 0))
            
            # クレジットを下から上にスクロール表示する
            if i <= 750 and i >= -200:
                self.draw_text(0, 350, i, 50, 0)
            if i <= 450 and i >= -500:
                self.draw_text(1, 350, i, 50, 300)
            if i <= 150 and i >= -800:
                self.draw_text(2, 350, i, 50, 600)
            if i <= -150 and i >= -1100:
                self.draw_text(3, 350, i, 50, 900)
            if i <= -450 and i >= -1400:
                self.draw_text(4, 350, i, 50, 1200)
            if i <= -750 and i >= -1700:
                self.draw_text(5, 350, i, 50, 1500)
            if i <= -1050 and i >= -2000:
                self.draw_text(6, 350, i, 50, 1800)
            if i <= -1350 and i >= -2300:
                x = 250
                x_offset = -50
                if i > -1800:
                    # 途中までは他と同じように表示する
                    self.draw_text(7, x, i, x_offset, 2100)
                else:
                    # 特定のタイミングでスクロール表示を止める
                    title = self.FONT_MIDDLE.render(self.TEXT_LIST[7]["title"], True, (255,255,255))
                    self.SCREEN.blit(title, (x, 300))
                    for j in range(len(self.TEXT_LIST[7]["name"])):
                        name = self.FONT_LOW.render(self.TEXT_LIST[7]["name"][j], True, (255,255,255))
                        self.SCREEN.blit(name, (x+x_offset, 350+(j*50)))

            pg3.display.update()
            pg3.time.delay(1)

        self.FINISHED = True
```
「for i in range(750, -2301, -1):」に関しては、画面の下限が700だったので、画面の外から開始する為、700以上の値を開始値にしています。終了値もクレジットがすべて表示しきるまで上昇させるように調整しました。
for文の先頭で停止判定をしています。スレッドの外からstop関数を呼ぶことで、update内の処理が強制的に終了します。
クレジットの表示タイミングはfor文の増分を見て、一定値ごとに表示されていくようにしました。
増分が一定値を超えたら、スクロールを止めて、「Thank you for playing！」を表示し続けるという処理にしました。
「draw_text」はTEXT_LISTにセットされたtitleとname[ ]を表示する関数です。
## 注意点
停止や終了などの管理フラグはクラス変数ではなくインスタンス変数で保持しましょう。
スレッド毎に停止や終了を判定できた方が都合がよいと思います。

「エンドロール」に関しては以上となります。

## リリース済みのゲームはこちら
・PC向け無料ゲーム
『LAbyrinth』（2Dの迷路探索ゲーム）
●Freem!
https://www.freem.ne.jp/win/game/33791
●Unityroom
https://unityroom.com/games/2025-labyrinth-isukaka