サイログ。

~雑多な記事置き場~

Yukiの仕様を変更してみた

Miyako2.0の仕様を固めるときに、どうしても悩んでいた箇所があります。
Yukiスクリプト(以降、「プロット」)のレシーバです。
DSLの場合(特に、ゲームでの「スクリプト」の場合)、大抵レシーバを意識せずにスクリプトを書けます。
Ruby的に言うと、以下の様に言えると思います。

レシーバがself(Yukiクラスインスタンス)の関数呼び出し(否privateメソッド)

Miyako内部では、プロットの実行と、Miyako本体の実行とで並行処理されています。
単純に考えれば、プロット本体をprocインスタンスにしてYukiインスタンス内部でinstance_execすれば良いわけですが・・・。

Proc形式の弱点

しかし、ここで問題が一つ。

「プロットの管理場所」をどこにするか。

という点です。
メソッド形式で置ければいちばん見た目が良いのですが、Rubyでの実装上困った問題があります(理由は後述)。
今のところの対応策は、「プロットを返すメソッドを用意する」ということです。
スッキリした解決策があればなぁ・・・。

Methodの呪縛

これをすると、更に問題が一つ。

メソッドをインスタンス化すると、コンテキストが切り替わらない。

つまり、シーンクラスで定義したメソッドをYukiインスタンスのレシーバ無しで実行できないということです。
これは流石に痛かったです。
僕が「コンテキスト」のことを勘違いしますね(どこをどう間違えたのか判断が難しいのですが・・・)。
ということで、今回、メソッドインスタンスの引き渡しはボツにしました。

↑の検証をしたときのサンプルプログラムを置いておきます。

#encoding: UTF-8
class Hoge
  def initialize(value)
    @value = value
  end

  def exec(proc)
    # 本来は、fugafugaが呼ばれた時、コンテキストはHogeインスタンスになるはず・・・
    self.instance_exec(&proc)
  end
  
  # 本来は、fugafuga内部でhogehogeが呼ばれるはずなので、@valueの値が出力されるはず
  def hogehoge
    puts @value
  end
end

class Fuga
  # Hogeクラスインスタンスに渡すメソッド
  def fugafuga
    hogehoge
  end

  # fugafugaをProcインスタンスに変換して返す
  def to_proc
    return self.method(:fugafuga).to_proc
  end
end

hg = Hoge.new(10)
fg = Fuga.new

hg.exec(fg.to_proc) # => 10?

こちらが実行結果。

>ruby method_test.rb
method_test.rb:21:in `fugafuga': undefined local variable or method `hogehoge' for # (NameError)
from method_test.rb:9:in `instance_exec'
from method_test.rb:9:in `exec'
from method_test.rb:33:in `

'

思いっきり、Fugaクラスにべったりでした・・・。

ということで

いっぺんProc形式にしてみて、解決策を探ろうということにしました。変更点は以下の通り。

  • Yuki内部処理を変更(プロットの評価開始をcallメソッド呼び出しからinstance_execメソッドの引数に変更)
  • Yuki#to_plotメソッドの仕様変更(引数として渡したブロック(プロット)をインスタンスとして返すように変更)
  • yuki_plot関数の追加(lambdaのエイリアス)
  • Yuki#call_plotメソッドの追加(プロット内で、別のプロットを呼び出す際に使用する)

これで、Yukiの問題に決着を付けることが出来れば・・・。

あと、追加で、以下の変更を行いました。

  • Yuki::Managerクラスの廃止(Yukiクラスインスタンスでの管理に一本化)
  • Yuki#select_plotメソッドの追加(評価するプロットの登録。このメソッドで登録されたプロットは、Yuki#start_plotメソッド呼び出し時に、優先的に評価される(Yuki#setupメソッドが再び呼ばれるまで有効))
  • Yuki#start_plotメソッドが引数無しでも呼び出し可能に(Yuki#select_plotメソッドが追加されたため)
  • Yuki#crメソッドに引数を追加(改行回数を引数として渡すことができる。デフォルトは1)

変更前・変更後のプロット実装の例は以下の通りです。どちらのほうが分かりやすいですか?

# 変更前のプロット登録
@yuki.start_plot(@yuki.to_plot(:plot)) # @yuki.to_plot(:plot) = self.method(:plot)

 :

def plot(yuki)
  yuki.text "「ねえ、あんたの担当のセリフ、ちゃんと覚えてるわよねぇ?"
  yuki.cr
  yuki.pause
  yuki.text " まさか、忘れてたなんて言わないわよねぇ?」"
  yuki.cr
  yuki.pause
  yuki.clear
  yuki.color(:red){
    yuki.size(32){
      yuki.size(24){ "「そんなこと" }
      yuki.text "ない"
      yuki.size(24){ "よぉ〜" }
    }
    yuki.cr
    yuki.pause
    yuki.text " ちゃんと覚えてるよぉ〜」"
    yuki.cr
  }
  yuki.pause
end

# 変更後のプロット登録

# select_plotメソッドについては後述
@yuki.setup(@box, plot){|box, pl|
  select_textbox(box)
  select_plot(pl)
}

 :

@yuki.start_plot

 :

def plot
  yuki_plot{
    text "「ねえ、あんたの担当のセリフ、ちゃんと覚えてるわよねぇ?"
    cr
    pause
    text " まさか、忘れてたなんて言わないわよねぇ?」"
    cr
    pause
    clear
    color(:red){
      size(32){
        size(24){ "「そんなこと" }
        text "ない"
        size(24){ "よぉ〜" }
      }
      cr
      pause
      text " ちゃんと覚えてるよぉ〜」"
      cr
    }
    pause
  }
end