RGSS講座

フィリス「は〜い、今日は皆でスクリプトのお勉強をしましょ〜!」

ハム「はいはい」

フィル「うっわ、やる気無っ!」

ハム「お前が主導権握ってる時って大体ろくな事ねーからなー」

フィル「ま、失礼ね。私がいつ変な事した?」

ハム「いっつもだろうが!」

フィル「はいはい。話が進まないから放っておきましょうか」

ハム「こらっ!」

閑話休題

フィル「今回は、RGSSの仕組みについて、このフィリスお姉さんが優しく説明しますね」

ハム「こいつの説明聞くより、ヘルプとかRubyの本読んだ方が――」

ぐしゃああああああ!! ビチャッ

フィル「うわ、なんか出ちゃった……こ、今回は、ヘルプとかに書いてない、基本的な内容について扱いますね」


講師フィリス先生
と言うわけで、今回はRGSSがどのような流れで動作しているのか、いったいどんな仕組みで作られているのか、といった 最も基本的な内容について見てみますね。
スクリプト、というのはプログラムの一種ですので、色々な難しい言葉が出てきます。さまざまな横文字、専門用語……そう いった言葉を逐一説明していくのも良いのですが、この難しさのせいで「あ、無理」と思ってしまう方も多いと思います。なの で、可能な限りそういう言葉は使わないように気をつけていきますね。

……けっして、私のスクリプト制作術が知識に裏づけされていない経験則によるもので、名前がよく分からないというわけじ ゃありませんよ?(悪いのはハムちゃんなんだから!)


まずは、ツクールを起動しましょう。起動したら、スクリプトを表示。左側の名前の列を見てみましょう。

その中でも、特に名前の左側に注目。
Game
Sprite
Window
Arrow
Interpreter
Scene
Main
このようなグループに分けられることに気づきましたか? さぁ、問題。ゲームが始まると、どこからスタートするでしょうか?

……考えましたか? 答えは簡単。一番上からです。
じゃあ、ゲームはGameから始まるのか。そう、Gameから始まるんです。ただし、「実際の処理」が始まるのは別の場所です。

……いきなり意味不明ですね(笑
実は、Game 〜Sceneまでの全て、すなわちMain以外の全ては「関数の定義」という処理を行っているんです。

関数とは、ツクールで言うところのイベント一個だと思ってください。「変数の処理」や「条件分岐」といった処理をひとまとめ にして、何がしかの結果を出すようにした塊が関数です。

たとえば、町の人のイベントを作って話しかけたとしましょう。するとメッセージが表示されたり、キャラが歩き回ったり、画面 が変わったり……イベントごとに色々作れますね? 同様に

Game_Switchesでは、スイッチの設定を行い
Sprite_Pictureではピクチャーの描画処理一式が設定され
Window_Helpではヘルプウィンドウの作成・描画を実行し
Interpreterはイベントの処理を決め
Scene_Saveはセーブ画面の処理を統括し

と言うように、名前のついたそれぞれの物は決められた処理を「定義」します。定義された処理は、その定義が呼び出され た時に実行され、結果を返します(イベントでたとえるなら話しかけられた時、みたいな感じ)。
では、どこから呼び出しが始まるのか。ゲームでは主人公が歩き回って町の人に話しかけたり出来ますが、スクリプトでは そうは行きません。

その始まるポイントが一番下、Mainです。


ゲームで言うなら、主人公はMainというキャラに話しかけます。すると、MainはScene_Titleを呼び出します。Scene_Titleは まずゲームを動かすためにデータベースに設定されたデータを呼び出し、次にタイトル画面を作ります。
実際に絵を表示する能力を、Scene_Titleは持っていません。そこで、Scene_TitleはSpriteを呼び出します。SpriteはScene_ Titleから指定されたファイル名の画像を探し出し、表示します。
タイトル画面が表示されたと言っても、まだBGMも流れなければ、ニューゲームなどのコマンドも表示されません。しかし、 これらの機能も、やはりScene_Titleは持っていません。
そこで、他力本願ですがScene_Titleはこれらの能力を持った「関数」を呼び出します。呼び出された関数が、BGMを流し たりコマンドウィンドウを作ったりします。

こうして、無事、タイトル画面が表示されました。が、このままだと処理はどんどん進んで終わりまで行ってしまいます。終わ りまで行ってしまうと、終わってしまいます。
スクリプトは、基本的に上から下へ処理を行っていくようにできているのです。

しかし、それでは不都合ですね?
そこで、下から上へ戻る処理を作ってやる必要があります。それがループ処理です。

ループを作ればそれでよし……というわけにも行かないんですね、大変ですけど。
ためしに、
loop do
end
上の2行をそのままScene_Titleの中に書き加えてみましょう。

この2行を入れた位置で処理がとまって、フリーズしてしまいます。
しばらく待っていると「スクリプトがハングアップしました」という表示と共に強制終了してしまいます。

これは、立派なバグなんですね。でも、普通に遊んでいてもこうはなりません。どうしてでしょう?

実は
 # ゲーム画面を更新
 Graphics.update
 # 入力情報を更新
 Input.update
 # フレーム更新
 update

という「更新」という処理をループの中で行っているからです。更新されていると言うことは、なにか処理が行われている= バグではない、とプログラム側が認識してくれるわけです。
何も処理が行われていないのにループしているとバグなんですね。

さて、この更新。何をしているのでしょう?
一番下のupdateという文字に注目してください。
これが、関数を呼び出している部分、それも最も基本的な形の部分です。
これは、updateという名前の関数を呼び出しています。さて、updateってなんでしょうか?

ここで、classとdefという名前が出てきます。全ての関数で使われています。
実は、これが「ここから関数だよ」という目印なのです。classは大きい箱、defは小さい箱、のイメージです。
Mainをのぞいた全ての関数は、

class

 def
 end

 def
 end

 def
 end

end

という構成を持っています。
今の場合、Scene_Titleがclass、updateがdefです。Scene_Titleの中を良く見てみましょう。フレーム更新と訳注で書かれた 部分に、
 def update
という記述があります。updateが呼び出された場合、このdef〜endまでの処理、すなわち

 # コマンドウィンドウの更新
 @command_window.update
 # C ボタンが押された場合
  if Input.trigger?(Input::C)
  # コマンドウィンドウのカーソル位置で分岐
  case @command_window.index
  when 0 # ニューゲーム
  command_new_game
  when 1 # コンティニュー
  command_continue
  when 2 # シャットダウン
  command_shutdown
  end
 end

が呼び出されます。ツクールでいう、イベントの呼び出しと同じですね。
updateといっても、Scene_Titleの中にあるupdateです。ほかのclassの中でupdateと記述してもこの処理は呼び出されませ ん。
classという大きい箱の中で、ただ単にupdateという小さな箱を指定したら、同じ箱の中に有る小さい箱が呼び出されるので す。

なぜ、いちいち呼び出すなんて面倒なことをするのでしょう?
……いいえ、面倒なことではありません。
関数を効率よく利用するために、このようにしているのです。

上のupdateの処理の中で、関数の呼び出し以外の処理はいくつ有るでしょうか? 考えてみてください。

答えは2つ。
if Input.trigger?(Input::C)とcase @command_window.indexという条件分岐だけです。
あとの処理は全て関数の呼び出しです。

もちろん、呼び出しを使わなくてもスクリプトを作ることは出来ます。しかし、スクリプトが複雑になればなるほど、関数を利用 して作らなければ、スクリプトが膨大になりすぎて手がつけられなくなります。

徹底的にスクリプトの合理化を図っていくと、自然、こういう形になるんですね。
ちなみに、原始的ですけどUPRISING完全版の戦闘システム(ツクール2000@コモンイベント)も、イベントの呼び出しを使 って、同じような形のシステムを使っています。


さて、ここまでは大丈夫ですか?
ここから、さらにもう一歩難しいことを学びましょう。でも、これが分かるとRGSSの流れが理解できますよ。

さきほど、updateと記述すると、同じclass箱の中にある関数が呼び出される、と言いました。しかし、他の大きな箱(class)か らその中に有る小さい箱(関数、def)を呼び出せなければ不便ですし、いちいちgameやwindow等のようにグループ分けし た意味がありません。

そこで、他のclassから関数を呼び出すときにピリオドを使うのです。

@command_window.update

というのに注目してみましょう。
@command_windowとupdateの二つから成り立っています。
これは、@command_windowという箱の中のupdateという処理を呼び出せ、と命令しているんですね。
じゃあ、@command_windowって?

@command_window = Window_Command.new(192, [s1, s2, s3])

という記述が、40行目にあります。見つけましたか?
多少面倒ですけど、このようにclassを変数に代入してやって初めて、そのclassが使えるようになります。

たとえて言うと、classというのは大きい箱でしたね?
大きい箱の中に、そのまま大きい箱を入れることは出来ないんです。そこで、変数という器の中にclassを放り込んで、その 変数を使って処理を行っていきます。

え? 変数って数字しか入れられないんじゃないの……?
そう、そうなんです! そこが普通にツクーラー人生歩んできた人にはわからないんです!

ツクールにおける変数は、一つの変数に一つの数字しか入れられません。

しかしRGSSでは違います。
もちろん、一つの変数に一つの数字を入れられます。それに加え、

変数に文字を入れる(文字列変数)。 a = "文字"
変数に複数の数字・文字を入れる(配列変数)。 a = [0, 1, 2, 3, 4] または a = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
変数に関数を入れる。 a = 関数.new

といったことが出来るんです。ちなみに、RGSSにはツクールのスイッチなんてありませんよ? 変数の「true」「false」がスイッ チのオンオフの替わりです(これは余談)。

この、変数に関数を入れる、という処理をしないと、他のclassの処理は原則使えません。例外的に、組み込み関数などの、 RGSSに元から組み込まれている関数(これはヘルプでないと見れませんし、書き換えも出来ません)は、そのまま呼び出 すことが出来ます(FileTest.exist? など)。


話を戻しましょう。a = 関数.newという記述によって、a という変数に関数が代入されました。これからは、この a を使って、 その関数を利用できるようになります。
だけではありません。実はnewによって関数を代入したときに、すでに処理が行われています(行わない物もあります)。
その証拠に、
@command_window = Window_Command.new(192, [s1, s2, s3])
という処理を行った瞬間、コマンドウィンドウが表示されます(ただし表示されるだけです)。

その後、@command_window + ピリオド + Window_Command内の関数(def)
という記述を行うことで、Window_Commandの関数を利用できます。
@command_window.updateはWindow_Command内のupdateという関数を利用しているんですね。

最後に、自分で定義できる関数の中でもちょっと特殊な物をご紹介しましょう。
def initialize
というのがそれです。

普通の関数は、直接指名して呼び出さないと機能しません。しかし、def initializeのみは、a = 関数.newという形で変数に 代入されたとき、自動的に起動して実行されます。

@command_window = Window_Command.new(192, [s1, s2, s3])
と言う記述で、ウィンドウが生成されるのは、このdef initializeにその処理が書き込まれているからですね。初期化処理、な んて呼ぶこともあります。

さらに!
@command_window = Window_Command.new(192, [s1, s2, s3])と言う記述の「(192, [s1, s2, s3])」の部分。
これは、引数(ひきすう)と言います。

これはWindow_Commandのdef initializeに192と[s1, s2, s3]という2つの変数を持っていってくれ、という合図なんです。
ちなみに、このScene_Titleの場合、s1, s2, s3はそれぞれニューゲーム、コンティニュー、シャットダウンという文字列が代 入されています。

どうしてこんな事をするか、みなさん分かりますか?

Window_Commandというclassは、とてもたくさんの場面で使われています。あなたが新しいスクリプトを作ろうと思った時も、 ほぼ間違いなくお世話になるでしょう。それぐらい、使用頻度の高いものです。
今まで説明したとおり、関数は呼び出して利用するものです。
しかし、いろいろな場面で利用するのに呼び出す関数は一つ……困りますよね?

そこで、固有に必要な情報(ウィンドウのサイズや書きたい文字列)などは呼び出す側で用意して、それを呼び出される関 数に渡して処理を行ってもらうのです。すると、一つの関数で何通りもの処理を作れます(もっとも、何種類ものコマンドウィ ンドウを作れる、と言う意味です。だから、色々なclassが必要になるんですね)。


さぁ、長かったですが、関数については分かりましたか? 実は、ここまで理解できてしまうと、RGSSの半分は理解できた も同然です。

Scene_Titleの続きを見てみましょう。
ニューゲームが選択された場合、command_new_gameという関数が呼び出されます。command_new_gameはニューゲーム に必要な色々な情報を用意して、$scene = Scene_Map.newという記述を行います。
これが、次のシーンに進むと言う合図です。

これが代入されたら何が起きるのか。さっきのループ処理に目を戻してください。

ループは、文字通り下から上に戻る処理でしたね。しかし、これだけだと永遠に同じ処理を繰り返す、いわゆる無限ループ になってしまいます。そこで、

 # 画面が切り替わったらループを中断
 if $scene != self
  break
 end

という処理をしています。$sceneという変数が別のものになったら、break、すなわちループを中断する、と言う意味です。
こうして、晴れてループを脱したら、画面に表示されているタイトル画面やコマンドウィンドウを消します(dispose、「解放す る」と呼びます)。

これで処理が終わり? いえいえそんな事はありません。$scene = Scene_Map.newという記述のとおり、今度はScene_Map の処理が開始されます。

以下、この繰り返しなんですね。

簡単な図にまとめると、こんな感じです。



きちんと調べたわけじゃないから、多少正確じゃないかもしれないけど……
こんな仕組みで動いているんだな、っていうのをイメージできるようになってくれれば大丈夫です。


とりあえず、全体としてはこんな感じですね。
後はヘルプを参照してくれれば、大体のところは分かると思います。

一つだけ注意を。スクリプトは、全て半角英数で記述されます。全角を使うとバグの原因になるので気をつけてくださいね。 とくに、” ”←全角スペースはやりやすいので注意!
ちなみに頭に # をつけると訳注となりますので(文字色が変わる)、全角を使っても大丈夫です(動作には関係しませ ん)。



フィリス「ふぅ、大変だったけど終わった〜……さ、帰ろう」


がちゃっ


ハム「あ、ママおかえりなさ〜い」

フィル「…………」

ハム「ママ、お土産は〜?」

フィル「…………」

フィリスは、無言で水銀を差し出した。

ハム「わぁ、このお人形さんすごくかわいい! わたし、これから毎日だっこして寝るね!」

フィル「は、ハムちゃんが壊れた……! やっぱりなんか出ちゃったせい!?」


おしまい

’06年3月 神鏡学斗
back