福澤テクノロジー

おっさんプログラマーが、主に技術的な話をするブログ。

Hayat言語の特殊機能 sayCommand

スクリプト言語Hayatは、ゲーム機組み込み用に設計された言語なので、ゲーム用の特殊機能があったりします。
その1つ、sayCommandを紹介します。

こんなスクリプトを書くことができます。

  アインシュタイン「人生は{青}退屈すれば長く{白}、
           {赤}充実すれば短い{白}。」

これはコンパイル時に以下のように展開されます。

  アインシュタイン.sayCommandStart(1,2)
  アインシュタイン.sayCommand("人生は")
  アインシュタイン.sayCommand(青)
  アインシュタイン.sayCommand("退屈すれば長く")
  アインシュタイン.sayCommand(白)
  アインシュタイン.sayCommand("、\n")
  アインシュタイン.sayCommand(赤)
  アインシュタイン.sayCommand("充実すれば短い")
  アインシュタイン.sayCommand(白)
  アインシュタイン.sayCommand("。")
  アインシュタイン.sayCommandEnd()

"" から ""までの間の文字列と式展開が、sayCommandメソッドに送られます。
sayCommandStart の引数は、スクリプト内でのsayCommandの通し番号と、行数です。
他の言語では、文字列中の式展開やHeredocument中の式展開は、その場で計算され、文字列に変換されて全て結合され、
1つの文字列となってしまいます。
HayatのsayCommand機能では、式展開はsayCommandメソッドの引数として渡されるようになっています。
ゲームでは、文字を表示するときに1文字ずつ徐々に表示させて、しゃべっているような演出をする事が多々あります。
また、文字毎に色を変更したり、フォントサイズを変更したり、表示速度を変えたりする事がよくあります。
他の言語の式展開のように1つの文字列にされてしまうと、こういった制御が面倒なのです。
sayCommandであれば、3つのメソッドを定義すれば好きな様にコントロールする事が可能です。

  enum { 白, 青, 赤 }   // 0,1,2
  class Person
    def initialize(name) { @name = name }
    def sayCommandStart(serialNo, numLines) {
      myGamePrintName(@name)
    }
    def sayCommand(x) {
      if (x.getClass() == Int)
        Font.setColor(x)
      else
        myGamePrintString(x)
    }
    def sayCommandEnd() {}
  end
  アインシュタイン = new Person("アインシュタイン")


Rubyでは式展開は #{ } ですが、Hayatでは { } です。
複数行に渡る場合、行頭と行末の空白文字は削除されます。なので好きな所で好きなだけインデント可能です。全角スペースも空白文字です。
敢えて行頭などに空白を入れたい場合は、空白文字をエスケープすればOKです。

この機能は、ノベル系のゲームを作る時に大変役立ちました。プログラマでないスクリプターにもわかりやすく書けます。
また、ゲーム中のイベントでゲーム内のキャラに喋らせる演出をする時も役に立ちました。
ゲーム用のスクリプト言語には、この機能があるととても便利です。Hayat以外の言語でも実装して欲しい所です。
他の言語を採用した場合は、スクリプトとは別の文章記述ファイルに、各ゲーム独自のルールで文章を書き、
別のツールでコンバートするといった面倒なことになりがちです。

便利ではありますが、メソッドを3つ定義する必要があるとか、言語仕様としてスマートでない所もあります。
それを解決しつつ、ゲーム専用ではなく一般化する方法を考えたのですが、それは次のエントリにて。