流れるメッセージ
MessageWindowをもう少し改良してドラクエのようにメッセージが流れるようにアニメーションで表示されるようにします。これは思ったより簡単です。今までアニメーションするときどうしてたか思い出してください。フレームごとに状態を更新するupdate()があればいいですね!
メッセージウィンドウの更新
まずはMessageWindowのコンストラクタから始めます。
def __init__(self, rect): Window.__init__(self, rect) # テキストを表示する矩形 self.text_rect = self.inner_rect.inflate(-32, -32) self.text = [] # メッセージ self.cur_page = 0 # 現在表示しているページ self.cur_pos = 0 # 現在ページで表示した最大文字数 self.next_flag = False # 次ページがあるか? self.hide_flag = False # 次のキー入力でウィンドウを消すか? self.msg_engine = MessageEngine() # メッセージエンジン self.cursor = load_image("cursor.png", -1) # カーソル画像 self.frame = 0
前回までなかったself.frameとself.cur_posがポイントです。self.cur_pageは現在表示中のページを表していましたが、self.cur_posはself.textの何文字目まで表示中かを表す変数です。このself.cur_posをMessageWindowのupdate()が呼ばれるたびに+1で更新することで表示される範囲が長くなり、文字が流れてるように見えます。次にupdate()を見てみます。
def update(self): """メッセージウィンドウを更新する メッセージが流れるように表示する""" if self.is_visible: if self.next_flag == False: self.cur_pos += 1 # 1文字流す # テキスト全体から見た現在位置 p = self.cur_page * self.MAX_CHARS_PER_PAGE + self.cur_pos if self.text[p] == "/": # 改行文字 self.cur_pos += self.MAX_CHARS_PER_LINE self.cur_pos = (self.cur_pos/self.MAX_CHARS_PER_LINE) * self.MAX_CHARS_PER_LINE elif self.text[p] == "%": # 改ページ文字 self.cur_pos += self.MAX_CHARS_PER_PAGE self.cur_pos = (self.cur_pos/self.MAX_CHARS_PER_PAGE) * self.MAX_CHARS_PER_PAGE elif self.text[p] == "$": # 終端文字 self.hide_flag = True # 1ページの文字数に達したら▼を表示 if self.cur_pos % self.MAX_CHARS_PER_PAGE == 0: self.next_flag = True self.frame += 1
制御文字があるので少し複雑ですが、ポイントはself.cur_posを+1していることです。set()で制御文字があるたびにpを移動させていましたが、今回はpではなく、self.cur_posを移動させるだけで移動のさせ方は同じです。また今回はset()で終端文字$をメッセージの最後に追加しています。update()で$が見つかるとself.hide_flagをTrueにしてメッセージが最後まで表示されたことを記録しておきます。このupdate()は毎フレーム呼び出されます。呼び出されるたびにself.cur_posが+1される仕掛けです。次にnext()も見ておきます。
def next(self): """メッセージを先に進める""" # 現在のページが最後のページだったらウィンドウを閉じる if self.hide_flag: self.hide() # ▼が表示されてれば次のページへ if self.next_flag: self.cur_page += 1 self.cur_pos = 0 self.next_flag = False
先ほど説明したようにメッセージの最後までたどり着くとself.hide_flagがTrueになります。その状態でnext()を呼び出すとメッセージウィンドウをhide()で消します。self.hide_flagがFalseでself.next_flagがTrueなら次ページがあるのでnext()で次ページへ進めます。
1文字ずつ描画
次にメッセージを描画するdraw()を見てみます。前回は、1ページ分まるごと描画しましたが、今回はself.cur_posまでの文字列を描画します。self.cur_posは1フレームごとに+1されるのでdraw()で表示されるメッセージは1フレームごとに長くなっていきます。これによってメッセージが流れているように見えるわけです。
def draw(self, screen): """メッセージを描画する メッセージウィンドウが表示されていないときは何もしない""" Window.draw(self, screen) if self.is_visible == False: return # 現在表示しているページのcur_posまでの文字を描画 for i in range(self.cur_pos): ch = self.text[self.cur_page*self.MAX_CHARS_PER_PAGE+i] # 制御文字は表示しない if ch == "/" or ch == "%" or ch == "$": continue dx = self.text_rect[0] + MessageEngine.FONT_WIDTH * (i % self.MAX_CHARS_PER_LINE) dy = self.text_rect[1] + (self.LINE_HEIGHT+MessageEngine.FONT_HEIGHT) * (i / self.MAX_CHARS_PER_LINE) self.msg_engine.draw_character(screen, (dx,dy), ch) # 最後のページでない場合は▼を表示 if (not self.hide_flag) and self.next_flag: if self.frame / self.animcycle % 2 == 0: dx = self.text_rect[0] + (self.MAX_CHARS_PER_LINE/2) * MessageEngine.FONT_WIDTH - MessageEngine.FONT_WIDTH/2 dy = self.text_rect[1] + (self.LINE_HEIGHT + MessageEngine.FONT_HEIGHT) * 3 screen.blit(self.cursor, (dx,dy))
▼を点滅させる点にも注意してください。これは、キャラクターアニメーション(2008/5/18)で説明した方法と同じです。animcycleを定義してフレームごとに表示、非表示を切り替えています。表示、非表示を切り替えれば点滅しているように見えますね。最後にmain()のゲームループでMessageWindowのupdate()を呼び出すのを忘れないでください。
これでドラクエのように流れるメッセージウィンドウができました。ただこれでも少し違いますね。ドラクエは行ごとに上に流れているからです。まあとりあえずページごとに流れる方法で止めておきます。ここら辺はちょっと複雑というか面倒くさい処理でした。次回からはもっと簡単です。