この記事は後編となっております。前編をご覧になっていない方はぜひご覧ください。
前編では、pygameの基本や画面に線を引く方法、ノーツのクラスの実装を説明しました。後編では、判定の表示とプログラム全体の構成について書きます。
前編と同様、メソッドの細かい内容まで説明していないところがあります。分からない部分は適宜調べていただきたいです。pythonは情報が豊富なのでメソッドで検索すると大体とても分かりやすい記事が出てきます。pygame特有のメソッドについてはこちらが参考になります。
判定とコンボ数のテキストの作成と表示
判定とコンボ数のテキストのクラスを作成する
音ゲーでは、ノーツを取った後しばらく、そのノーツの判定(perfect,great,good,miss)と現在のコンボ数を表示することが多いです。ここではそのテキストの作成と表示の方法を紹介します。結論から言うと、これも一つのクラスとして作成しました。クラスとそのコンストラクタの定義は以下のようになりました。
class Judgetext:
def __init__(self, number, combo, lane):
self.num = number
self.lane = lane
self.font = pygame.font.SysFont(None, 50)
if(number == 0):
self.text = "perfect"
elif(number == 1):
self.text = "great"
elif(number == 2):
self.text = "good"
elif(number == 3):
self.text = "miss"
else:
self.text = ""
self.starttime = time.time()
if(combo > 0):
self.text += " {0}".format(combo)
少し長くなってしまいました。コンストラクタの引数はnumber(判定の種類を表す数値)、combo(表示するコンボ数)、lane(テキストを表示するレーン)の3つです。
numberとlaneをクラスのメンバー変数にコピーした後、5行目ではテキストに使うフォントを指定しています。font.Sysfont()を使い、引数はフォントの種類と文字サイズです。
判定に応じたテキストを生成しています(6~15行目)。pythonにはswitch-case文がないのでif文を並べて書いています。どの判定にも当てはまらない場合、テキストの文字列は空にします。テキストの初期化の際に使用します。
15行目は、time.time()メソッドで現在の時間を取得します。テキストは時間経過で消える必要があるので、生成したタイミングの時間を取得しておきます。
16~17行目では、コンボ数の情報をテキストに追加します。文字列の中に{0}を置き、.format(combo)とすることでコンボ数を文字列に変換して、テキストの末尾に追加します。コンボが切れた(コンボ数が0)場合はコンボ数の表示はしません。
時間経過でテキストが消えるようにする
コンボ数の表示がずっとゲーム画面に残っているのは不自然なため、時間経過でテキストが消えるような実装をします。これを実現するメソッドの実装は以下のようになりました。
def Count_time(self):
if(time.time() - self.starttime > 1):
self.text = ""
たった3行でできました。このメソッドが呼ばれるたびに現在の時刻を測定し、テキストが作成された時間から1秒以上経過していたならテキストを空にします。これで文字が表示されなくなります(内部処理では空の文字列を表示しています)。このメソッドを定期的に呼び出すことで、時間経過でテキストを消せます。
テキストを画面に表示する
せっかく作成したテキストも画面に表示しなければ意味がありません。自分で作成したクラスなので、直接画面に表示するメソッドはありません。したがって、テキストを画面に表示するメソッドも作成する必要があります。実装は以下のようになりました。
def Blit(self, screen):
self.judgetext = font.render(self.text, True, (0,0,0))
screen.blit(self.judgetext, (256 * (self.lane - 1), 670))
表示するスクリーンを引数で渡しています。
2行目のfont.render()で表示するためのテキストを作成します。
3行目のscreen.blit()で実際に画面に表示します。blitとは、グラフィックをコピーするという意味で、テキストや画像などを画面に表示するために用います。
プログラム全体の処理
プログラムに必要なクラスの定義はすべて完了しました。ここからはプログラム全体の処理、つまり、毎回のループで行うべき処理について説明します。前編で示したウィンドウが閉じられたときの処理、背景の着色、線を引く以外に追加する処理は以下のようになります。
- 押されたキーの情報を取得する
- ノーツを動かす
- スペースキーが押されているか判定し、押されているならノーツを取るべきか判定する
- ノーツを取る。つまり、ノーツの判定を行い、コンボ数を計算してテキストを生成する。ノーツの位置をリセットする。
- ノーツが押されないまま判定ラインを通過して0.25s以上経過したらmissとして処理する
- テキストが生成されてからの経過時間を測定、一定時間経過したらテキストを消す
- テキストを表示
これらを、whileループの中の背景の着色とflip()の間に記述します。実装は以下のようになりました。
keys = pygame.key.get_pressed()
for n in notes:
n.Move()
if(keys[pygame.K_SPACE]): #スペースキー押されたとき
if(note.error < 60 * 0.35 * note.speed):
if(note.Judge() < 3): #miss以外
combo += 1
else:
combo = 0
text = Judgetext(note.Judge(), combo, note.lane)
notes.clear()
note = Note(5,3)
notes.append(note)
if(note.rect.top >= 650 + 60 * 0.25 * note.speed):
combo = 0
text = Judgetext(note.Judge(), combo, note.lane)
notes.clear()
note = Note(5,3)
notes.append(note)
text.Count_time()
text.Blit(screen)
3行目のfor文は今回ノーツが1つなので不要ですが、ノーツが1画面に複数存在してもいいようにノーツの一覧のリストを作ってあります。15行目のclear()はリストの中身を空にします。ここではノーツを削除するという意味です。
key.get_pressed()でキーの状態の一覧を取得できます。7行目のkeys[pygame.K_SPACE]で、スペースキーの状態が分かります。trueならばスペースキーは押されています。
7行目からのif文はスペースキーが押された場合の処理で、8行目のif文はノーツが判定ラインの0.35s以内にあるという条件です。
18行目のif文は、ノーツが判定ラインを過ぎて0.25s経過したときという条件です。
プログラム全体のダウンロード
作成したプログラム全体は、https://github.com/MARBAS0610/pygame_musicgametestにあるmusicgametest.pyで参照、ダウンロードできます。よければダウンロードして動かしてみてください。
まとめ
今回は、pygameを使った音ゲーの基本中の基本の部分のプログラムを紹介しました。これ以降、このプログラムをUnityに移植してさらに機能を追加して本格的な音ゲーを作ろうと思っています。また進捗がありましたら記事にしようと思います。
プログラムの説明が結構長くなってしまいましたが、ここまでご覧いただきありがとうございます!