第24回 野符「武烈クライシス」 (2)

前回はとりあえず使い魔を登場させるところまで作りました。しかし、色々と無駄があることに気付きましたか? 今回は新たな挙動の実装は置いておいて、その無駄を取り除いてみましょう。

要旨

それではいってみましょう。

共通部分の共有化

さて、今回は新しい挙動は追加しません。前回のプログラムを、動作を変えずにちょっと書き換えたいと思います。というのも、前回のプログラムには大きな無駄があるからです。

前回のプログラムを眺めてみましょう。プログラムは、大まかに見ると script_enemy_main の部分と script_enemy の部分に分けられます。これらは別々に書かれます。この「別々に書かれる」というところが曲者で、script_enemy_main の中の変数や関数などを script_enemy の中から使う、ということはできませんscript_enemy_main の中から script_enemy のものを使えないのは、敵スクリプトの敵は何体も出せることから何となく分かるかと思いますが、実はその逆もできないのです。

要するに、第9回で話したとおり、変数や関数は { } で囲まれた中しか使えないのです。これはこれで理にかなっているのですが、前回のプログラムを見ると、実はそのせいで無駄が発生しているのです。それは、imgFamwait などが、script_enemy_mainscript_enemy Familiar の両方で定義されているのです。例えば、使い魔のグラフィックのパスを変更したいと思ったら、両方の imgFam を変更しないといけないのです。このようなことは極力避けなければならないということは、今まで何度も言ってきたことです。同じ定義は共通化し、一箇所にまとめるべきです。では、今回のような状況では、どのようにしたら一箇所にまとめることができるのでしょうか?

これには、#include_function というものを使います。これは、別ファイルに書かれた変数定義や関数定義を、その位置に埋め込むための命令です。このような作業のことをインクルードと言います。つまり、imgFamwait などの定義を別ファイルに書いておいて、script_enemy_mainscript_enemy Familiar の両方でインクルードしてやればいいのです。

#include_function を使うには、次のようにします。

#include_function ".\野符「武烈クライシス」.h.txt"

#include_function の後に、埋め込みたいファイル名を " " で囲んで指定します。

インクルードを使ったプログラムをここに置いておきます。これで見事に共通部分を一箇所にまとめることができるようになりました。ちなみに、このようにプログラムの挙動を変えずにプログラムを整理することをリファクタリングと言います。プログラムがごちゃごちゃしてきたら、一度リファクタリングを行うとプログラムが組みやすくなると思います。このとき、欲を出して挙動を変えてしまうのは、動作の検証が面倒になるので止めておきましょう。

インクルードについて

今回のプログラム変更はこれで終わりですが、残りを使ってインクルードについて少し話したいと思います。

インクルードという作業は、さっき話したとおり、別ファイルの中身を埋め込む作業のことです。この「埋め込む」という言葉は抽象的な意味ではなく、非常に具体的な意味を持っています。プログラムの解析作業は、埋め込み作業を行ったに行われます。解析作業が別ファイルへ移動することによって実質的に埋め込んだのと同じような処理になるのではないのです。

「どっちでも実質的には何も変わらないのでは?」と思われるかもしれません。確かにどっちにしろ挙動が変わるわけではありません。しかし、この違いが問題になる状況が1つだけあります。それはエラー行の表示です。

このことを実感してもらうには、実際にエラーを起こしてみるのが手っ取り早いです。例えば、適当にこんな感じにエラーを起こしてみましょう。

野符「武烈クライシス」.h.txt
let csd     = GetCurrentScriptDirectory;
let imgFam  = csd ~ "img\familiar.png";

// 使い魔の配置に必要な時間
let wSetFam = 30;
a
// w フレームだけ待つ
function wait(w) {
    loop(w) { yield; }
}

6 行目に a と書いただけです。当然、この部分でエラーが出るはずですね。では、実行してみて下さい。次のようなエラーが出るはずです。

図 1. エラーダイアログボックス (1)
図 1. エラーダイアログボックス (1)

エラー行の部分に注目してください。119 行目と書いてありますね。この行は 野符「武烈クライシス」.h.txt の 6 行目なのに、119 行目と出てしまうのです。インクルード元のスクリプトの 114 行目でインクルードしているため、野符「武烈クライシス」.h.txt の 1 行目が丁度 114 行目に対応します。その 5 行先なので、119 行目と判断されてしまうのです。

次に、さっきの所は元に戻して、別の箇所でもエラーを起こしてみましょう。例えばここにしてみます。

    #include_function ".\野符「武烈クライシス」.h.txt"
a}

// 使い魔
script_enemy Familiar {

ここは、本来は 115 行目になります。しかし、実際に実行してみると次のようになります。

図 2. エラーダイアログボックス (2)
図 2. エラーダイアログボックス (2)

野符「武烈クライシス」.h.txt の分だけ行が増えるので、125 行目と表示されてしまうのです。このような問題があるため、例のようにインクルードは一番最後に行うのがいいでしょう。

では、次にもう1箇所エラーを出してみましょう。

// 使い魔
script_enemy Familiar {a
    // 使い魔の中心位置
    let xCenter = GetX;
    let yCenter = GetY;

ここは 118 行目です。これを実行してみると、次のようなエラーが出ます。

図 3. エラーダイアログボックス (3)
図 3. エラーダイアログボックス (3)

今度は正しい行番号が表示されました。実は、script_enemy_main の解析と script_enemy Familiar の解析は別のタイミングで行われるため、script_enemy Familiar を解析する時には script_enemy_main でのインクルードは行われないのです。

というわけで、インクルードされたファイルの中でのエラー行に関しては正しく表示されませんが、インクルード元のファイルのエラー行に関しては、各ブロックの最後にインクルードしさえすれば、特に影響がないというわけです。

最後に少し補足しておきます。今回は変数や関数のインクルードを行いましたが、敵スクリプト丸ごとをインクルードしたい場合も、もしかしたらあるかもしれません。つまり、複数のスペルにおいて、同じ挙動の使い魔を使うという場合などです。このような場合は、敵スクリプト全体を別ファイルにしておき、#include_function ではなく #include_script を使ってインクルードします。これもエラー行に問題が起こるので、とりあえず同一ファイルで完成させておいて、その後、別ファイルに分離した方がいいでしょう。

要旨

次回は、高速移動と低速移動とで使い魔に変化を与えてみましょう。

第23回 | 第25回 | 目次へ戻る

この講座はロベールが作成しました。
inserted by FC2 system