人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

メッセージエンジン

画面の好きな位置にウィンドウを表示できるようになったので今度は画面の好きな場所に文字を描画できるようにします。今回は画面の好きな位置に文字列を描画できるメッセージエンジンの作り方を説明します。

pyrpg14.zip
f:id:aidiary:20100731153257p:plain

文字画像

画面に文字を表示する方法としてフォントを使う方法と文字画像を使う方法があります。フォントを使って画面に文字を描画する方法はテキストを描画する(2008/5/4)で解説しました。しかし、ゲームで自由に使えるフォントは数が限られていますし、使っている環境によってインストールされているフォントが違います。

そんなわけで今回は2番目の文字画像を使う方法をとります。メッセージ表示に使う文字画像は下のを使います。この画像は山亀本舗さん(リンク切れ)で公開されているGAMESYSフォントを元に改造した画像です。

f:id:aidiary:20100731153258p:plain

見るとわかりますが、ひらがな、カタカナ、アルファベット、数字、記号しか使えません。漢字が使えないのはイタイですが、ファミコン時代のゲームはこれでも何とかなってました。ひらがなだけのメッセージというのもなかなか味がありますよ。色も4色ですが使えます。赤文字は白文字から160ピクセル、緑文字は320ピクセル、青文字は480ピクセル右にずらして配置しています。

フォントを用いた漢字も表示できるメッセージエンジンの作り方はあとで取り上げる予定です。いつになるかわからないですが・・・

1文字は下図のように16x22ピクセルで構成されています。すべての文字がこの大きさなので表示するときの処理が簡単です。

f:id:aidiary:20100731153259p:plain

メッセージエンジン

ここでMessageEngineというクラスを作ります。メッセージエンジンを使うと文字列を画面の好きな位置に表示できるようになります。たとえば、

  msg_engine.draw_string(screen, (0, 0), u"おうさまじゃ")

とすると画面の (0,0) の位置に

f:id:aidiary:20100731153300p:plain

と表示されます。じゃ、コードを見ていきます。

class MessageEngine:
    FONT_WIDTH = 16
    FONT_HEIGHT = 22
    WHITE, RED, GREEN, BLUE = 0, 160, 320, 480
    def __init__(self):
        self.image = load_image("font.png", -1)
        self.color = self.WHITE
        self.kana2rect = {}
        self.create_hash()
    def set_color(self, color):
        """文字色をセット"""
        self.color = color
        # 変な値だったらWHITEにする
        if not self.color in [self.WHITE,self.RED,self.GREEN,self.BLUE]:
            self.color = self.WHITE
    def draw_character(self, screen, pos, ch):
        """1文字だけ描画する"""
        x, y = pos
        try:
            rect = self.kana2rect[ch]
            screen.blit(self.image, (x,y),
                (rect.x+self.color,rect.y,rect.width,rect.height))
        except KeyError:
            print "描画できない文字があります:%s" % ch
            return
    def draw_string(self, screen, pos, str):
        """文字列を描画"""
        x, y = pos
        for i, ch in enumerate(str):
            dx = x + self.FONT_WIDTH * i
            self.draw_character(screen, (dx,y), ch)
    def create_hash(self):
        """文字から座標への辞書を作成"""
        filepath = os.path.join("data", "kana2rect.dat")
        fp = codecs.open(filepath, "r", "utf-8")
        for line in fp.readlines():
            line = line.rstrip()
            d = line.split(" ")
            kana, x, y, w, h = d[0], int(d[1]), int(d[2]), int(d[3]), int(d[4])
            self.kana2rect[kana] = Rect(x, y, w, h)
        fp.close()

まずコンストラクタですが、self.imageに文字画像をロードしてます。あと、self.colorにWHITEを設定してデフォルトでは白文字で描画されるようにしてます。set_color()を使うと文字の色をWHITE(白)、RED(赤)、GREEN(緑)、青(BLUE)にできます。ドラクエだと仲間が死んだら赤文字、瀕死だったら緑文字、夜は青文字になりますが同じようなことができます。

ここで重要なのは、self.kana2rectという辞書です。この辞書は、文字を文字画像中の矩形範囲へ変換します。この文字→矩形範囲を作成するのがself.create_hash()です。この関数では、kana2rect.datというファイルを読み込んでセットしています。このファイルは、

 あ 0 0 16 22
 い 16 0 16 22
 う 32 0 16 22
 え 48 0 16 22
 お 64 0 16 22
 か 0 22 16 22
 き 16 22 16 22
 く 32 22 16 22
 け 48 22 16 22
 こ 64 22 16 22

こんなデータです。たとえば、「あ」という文字が文字画像中の (0,0) の位置にあり、幅は16、高さは22という意味です。すべての文字は幅16、高さ22なので後ろの2つはいらないんですが一応つけてあります。 self.kana2rectはこのファイルを読み込んで、文字からRectオブジェクトへの辞書を作ります。たとえば、

 self.kana2rect[u'あ'] → Rect(0, 0, 16, 22)
 self.kana2rect[u'か'] → Rect(0, 22, 16, 22)

といった感じです。「あ」という文字を表示したいときは文字画像(font.png)のRect(0, 0, 16, 22)の位置にある画像を描画すればいいわけです。

kana2rect.datには白文字の座標しか格納していません。赤文字、緑文字、青文字を使いたいときは、X座標にそれぞれ160ピクセル、320ピクセル、480ピクセル足せばよいだけです。色の定数が

   WHITE, RED, GREEN, BLUE = 0, 160, 320, 480

のようになっているのはそのためです。次にdraw_character()を見てください。

(注)characterは主人公などのキャラクターのことではありません!文字のことを英語でcharacterといいます。C言語などでchar型というのはcharacter型の略ですね。少しややこしいですがメッセージエンジンでcharacterと書いたら文字のことだと思ってください。

    def draw_character(self, screen, pos, ch):
        """1文字だけ描画する"""
        x, y = pos
        try:
            rect = self.kana2rect[ch]
            screen.blit(self.image, (x,y),
                (rect.x+self.color,rect.y,rect.width,rect.height))
        except KeyError:
            print "描画できない文字があります:%s" % ch
            return

このメソッドは1文字だけ描画します。たとえば、

 draw_character(screen, (10,10), 'お')

とすると画面の (10,10) の位置に

f:id:aidiary:20100731153301p:plain

と表示されます。ここで先ほど作成したkana2rectという辞書を使っています。文字chを渡すと文字chの文字画像中での矩形rectを返します。ここで取得したrectを使って画面の指定した位置に画像を描画しています。kana2rectに登録されていない文字は例外を発生するので描画しないようにしています。

先ほど、「おうさまじゃ」で紹介したような文字列を描画するのがdraw_string()です。文字列は文字の位置を少しずつ右にずらしながら並べただけであることに注目してください。

    def draw_string(self, screen, pos, str):
        """文字列を描画"""
        x, y = pos
        for i, ch in enumerate(str):
            dx = x + self.FONT_WIDTH * i
            self.draw_character(screen, (dx,y), ch)

文字列strを受け取ったらstrを構成する文字(ch)を1つずつ位置をずらしながらdraw_character()で描画しています。

文字列の描画

具体的な文字列の描画はmain()でやってます。

        # メッセージエンジン
        msg_engine = MessageEngine()

        # メッセージエンジンで文字列を描画
        msg_engine.set_color(MessageEngine.WHITE)
        msg_engine.draw_string(screen, (0, 0),
                               u"メッセージエンジンをつかうと")
        msg_engine.set_color(MessageEngine.RED)
        msg_engine.draw_string(screen, (30, 30),
                               u"すきなばしょに もじをびょうができます。")
        msg_engine.set_color(MessageEngine.GREEN)
        msg_engine.draw_string(screen, (60, 60),
                               u"ただし かんじはつかえません。")
        msg_engine.set_color(MessageEngine.BLUE)
        msg_engine.draw_string(screen, (90, 90),
                               u"でも なんとかよめるでしょ?")

set_color()で色を変えると以後draw_string()で書いた文字はすべてその色になります。元の色に戻したかったらもう一度set_color()する必要があります。あと文字列の改行はできません。メッセージエンジンのdraw_string()は横一直線に文字列を描画するだけです。

えっ?せっかくメッセージウィンドウを作ったのにその中に描画してないって?次回は、メッセージウィンドウの中にきちんと文字列がおさまるようにメッセージを描画する方法を考えます。