【初心者向け】KivyによるWindowsアプリ作成27 ゲームオーバー時の基本的な処理実装

今回は前回の予告通り、ゲームオーバー時の処理を実装していきます。

なお、予め説明しておきますと、今回は終了処理の土台となる内容のみを実装します。その結果、一度は正しくゲームが行えるものの、再びゲームを行おうとすると、プログラムが異常な動作をしてしまいます。

今回の記事の分量が膨大になるのを防ぐため、その問題への対応は次回の記事の中で実装していきたいと思います。

なお、今回まで実装したコードの全量はいつものようにGitHubにアップしていますので、必要に応じてご参照下さい。

<スポンサーリンク>

実装方針の再確認

それでは早速、どのようなゲームオーバー処理とするか方針を固めておきたいと思いますが、処理内容については第20回目の記事にて、以下のような要件を洗い出していました。

【ゲームオーバー時の処理】

  • 画面上表示されている未タイプの文字が20個を超えた時点でゲームを終了する。
  • ゲーム終了時には、文字の追加表示を止め、その時点のスコアを記載したポップアップを表示する
それでは今回からゲーム画面の実装に入り、ゲーム内容を作り込んでいきたいと思います。 今回はまずゲーム画面の設計を簡単に行った上で、後半...

そこで、まずはこれらを最低限満たすよう、実装を行っていきたいと思います。

実装

まず、「未タイプの文字が20個を超えた時点で終了」ですが、これは定期的に呼び出しているupdateメソッドの中で条件判定を入れ、該当する場合はゲームオーバー処理を呼び出すことで対応できそうですね。

そこで、GameScreenクラス内のupdateメソッドの一番下に、以下のように追記します。

class GameScreen(Screen):
#省略
    def update(self, dt):
#省略
        if len(self.targets) >= 20: #このifブロックを追加
            gameOverView = GameOverView()
            gameOverView.ids["final_score"].text = str(self.score)
            gameOverView.open()
            self.end()

5行目が条件判定ですが、ゲーム画面に表示されている文字(Targetインスタンス)はtargetsリストに格納されていますので、そのリストの長さをlen()関数により取得し、20以上であれば以降の処理に移るようにしています。

そして、ifの処理の中身ですが、大きく6~8行目と9行目の処理に分かれます。

6~8行目は、画面にゲームオーバーのポップアップを表示する処理、つまり、見た目に関するクローズ処理にあたります。

記述しているGameOverViewクラスは、すぐこの後定義しますが、ゲームオーバー時に表示するポップアップのクラスです。6行目でこのクラスのインスタンスを生成し、7行目でそのインスタンスの1つの属性の値としてゲーム終了時のスコアを設定し、最後の8行目でこのポップアップを表示します。

続く9行目は、これまた後ほどメソッド定義しますが、以前、Startメソッドの中で生成したKeyboardインスタンスのリリースとupdateメソッドの定期実行の中止という、内部的なクローズ処理にあたります。

それではまず、6~8行目で使うGameOverViewクラスの定義に移ります。既にある、ReadyViewクラスの下あたりに追加すると、並びが良いでしょう。

class GameOverView(ModalView): #このクラスを追加
    def returnButtonClicked(self):
        sm.transition.direction = "right"
        sm.current = "title"
        self.dismiss()

ポップアップの見た目はKvファイル側で定義しますが、ポップアップ上にはタイトル画面へ戻るためのボタンを設置するつもりです。そこで、Pythonファイル側では、そのボタン押下時の処理をメソッドとして定義しています。

このGameOverViewクラスの中身については、これまで紹介した事項で一通り理解できるものと思いますので詳細を割愛しますが、ざっくり言うと、「ボタンを押された時に右から左へ「戻る」ようにタイトル画面へ遷移する」という処理を実装しています。

各行でやっていることがピンとこない場合、第18回、第19回の記事を改めてお読み頂くと、理解が促進されると思いますので、必要に応じてご参照下さい。

今回から、前回紹介したタイピングゲームを題材として、Kivyの使い方のうち、これまで紹介できなかった点を中心に紹介していきたいと思います。 ...
今回からタイピングゲームのメイン画面である、ゲーム画面を作り込んでいくことにより、Kivyの更なる使い方を紹介していきたいと思います。 ...

続いて、ポップアップの見た目をKvファイル側に定義します。これも、ReadyView Widgetのすぐ下に定義すると収まりが良いと思います。

<GameOverView>:
    size_hint: None, None
    size: 400, 400
    BoxLayout:
        orientation: "vertical"
        Label:
            text: "Your score is :"
            font_size: 48
        Label:
            id: final_score
            text: ""
            font_size: 72
        BoxLayout:
            padding: 100, 40
            Button:
                text: "to Title"
                on_press: root.returnButtonClicked()

こちらも、これまでの記事から内容は大体想像がつくものと考えて、基本的に説明を省きます。1点だけ、先ほどupdateメソッドで、ゲームオーバー時のスコアをポップアップに表示する行「gameOverView.ids[“final_score”].text = str(self.score)」を入れましたが、それを実現するために、中段当たりのLabel Widgetにid属性を設定して、このLabelに表示する文字列にPythonファイル側からアクセスできるようにしている点にご留意下さい。

さて、あとはゲームオーバー時の内部処理を行うendメソッドをGameScreenクラス内に実装するだけです。

今回、この内部処理としては、先ほど少し記載した、

  • Keyboardインスタンスのリリース
  • updateメソッドの定期実行を中止

の2つの処理を入れておきます。

なお、endメソッドを記述する場所ですが、個人的には、startメソッドの後ろあたりが良いのではないかと思います。(GitHub上にアップしたソースでも、そのようにしています。)

def end(self):#このメソッドを追加
    self.keyboard.release()
    Clock.unschedule(self.update)

今回の記事では、過去の記事で説明済みの内容が多かったため、基本的にコードの説明を省略しましたが、このメソッドの中身で使われているメソッドについては未説明ですので、以下に説明します。

まず、2行目はキーボードをリリースする処理ですが、説明の前に、以前startメソッド内に実装した、Keyboardオブジェクトを生成するための記述を思い出して下さい。

self.keyboard = Window.request_keyboard(self.keyboardClosed, self)

この行の中の「self.KeyboardClosed」については、別途以下のように、メソッドとして定義したのでした。

def keyboardClosed(self):
    self.keyboard.unbind(on_key_down=self.keyboardDown)
    self.keyboard = None

さて、ここまでのところ、endメソッド内に記述したreleaseメソッドは定義されていないですし、逆に、定義したkeyboardClosedメソッドは全く使われていないですね。一体どういうことなのでしょうか?

実は、Window.request_keyboardメソッドで生成されるKeyboardインスタンスのクラス内には、キーボードをリリースするためのreleaseメソッドが予め定義されています。さらにこのメソッドが実行される中で、Window.request_keyboardメソッドの第一引数として与えたメソッドが実行されるのです。

結果として、releaseメソッドを実行することで、

  • キーボードリリース時に通常行われるべき処理(Kivy側で定義済みの処理) プラス
  • ユーザがWindow.request_keyboardメソッドの第一引数として与えたメソッド(ユーザ独自処理)

の2つが実行されることになります。

ちょっとややこしいですね。でも、使って徐々に慣れていくしかないと思います。

さて、気を取り直して、endメソッド内の2つ目の行ですが、「Clock.unschedule(self.update)」で、スケジューリングしていたupdateメソッドの定期実行を中止することができます。

これはイメージしやすいですね。

これにより、定期的に行われていた文字の表示処理がストップすることになります。

テスト実行

今回の実装範囲は以上ですので、ここで一旦テスト実行してみましょう。

普通にゲームをプレイして、文字の表示にタイプが追い付かなくなり、表示文字数が20になった時点で、ゲームオーバーを示すポップアップが表示されます。最終スコアもポップアップ中に表示されます。

「to Title」ボタンを押すと、タイトル画面に戻ります。

ここで、「Start」ボタンを押して、もう一度ゲームを開始してみましょう。

いつも通りのポップアップが表示されますが、背面にうっすらと、ゲームオーバー時のスコアと文字が見えています。なにか嫌な予感がしますね。一応、「Yes」ボタンを押して、ゲームをスタートしてみます。

案の定、うまく動きません。ゲームを開始した途端、前回ゲーム時のスコアが表示されてゲームが終了してしまいました。

終わりに

今回の実装内容では、ゲームを一度だけ行う場合はうまく動きましたが、繰り返しゲームを行う場合に、適切に動作しないことが分かりました。

そこで次回の記事では、初期化/終了処理を修正して、何度も繰り返しゲームを行えるようにしたいと思います。

<スポンサーリンク>

シェアする

フォローする