7.6 選択肢プラグイン(その2)

結局、前回エラーが起きちゃったのって何が原因だったの?
いい機会だから、今回は選択肢プラグインのスクリプトを直すついでに、 エラーが起きた時の原因の調べ方も見てこうと思うんだけど、どうかな?
え? う〜ん、そういえば今までエラーの原因の調べ方ってちゃんと説明してもらってなかったし、 やっぱりそーいうのって知ってた方がいいんだよね?
まぁ、プラグインとかを作ってる途中でエラーが起きることはよくあるから、 知ってた方が早く原因を調べて修正できると思うよ。
そうなんだ。
じゃあどうやってエラーの原因を調べたらいいのか教えてもらおっかな。
OK。
まず、エラーが起きたら krkr.eXe.console.log っていうファイルが出来るのは知ってる?
※ krkr の部分は実行ファイルのファイル名になります。 例えば、krkr.eXe を game.eXe という名前に変更していた場合は、エラーが起きた時に作成されるログファイル名は game.eXe.console.log となります。

<エラーが発生した時に生成されるログファイル(krkr.eXe.console.log)>

※version 2.29-dev.20061218 以降は、このファイルは Debug.logLocation プロパティで設定されたフォルダに作成されます。

あー、なんかそーいうファイルが時々出来てるのは気づいてたけど、これってエラーの内容が書いてあるファイルだったの?
そうだよ。
ちなみにエラーの内容以外にも色んな情報が書いてあるんだけどね。
へぇ、そーいうファイルだってのは知らなかった。
とりあえず krkr.eXe.console.log を開いてみて。
わかった。

<krkr.eXe.console.log の中身>

==============================================================================
==============================================================================

Logging to C:\tjs\krkr.eXe.console.log started on Saturday, March 08, 2008 13:06:24
13:06:12 ! (info) Loading options from embedded area...
13:06:12 ! 吉里吉里[きりきり] 2 実行コア/2.28.1.9904 (Compiled on Dec 17 2006 23:06:18) TJS2/2.4.21 Copyright (C) 1997-2006 W.Dee and contributors All rights reserved.
13:06:12 ! バージョン情報の詳細は Ctrl + F12 で閲覧できます
13:06:12 ! Program started on Windows NT 6.0.1904  (Win32)

…以下略

うわ、なんか難しいことがいっぱい書いてあるんだけど…
あー、最初の方に書いてある情報はエラーとは直接関係ないから気にしなくていいよ。
ちょっとファイルの最後の方を表示してみて。
う、うん…

<krkr.eXe.console.log の中身(最後の方)>

…これ以前の部分は省略

 1:13:06:13 Scenario loaded : first.ks
 2:13:06:13 first.ks : returned to : (start) line offset 1
 3:13:06:13 first.ks : call stack depth after returning : 0
 4:13:06:13 first.ks : 
 5:13:06:13 first.ks : どこへ行く?
 6:13:06:13 first.ks : 
 7:13:06:13 first.ks : [blink text="商店街" graphic="buttonlink_bg" left=50 top=51 target=*mall]
 8:13:06:13 ButtonLayer.tjs を読み込みました(4ms)
 9:13:06:13 first.ks : [blink text="公園" graphic="buttonlink_bg" left=50 top=139 target=*park]
10:13:06:13 first.ks : [blink text="家に帰る" graphic="buttonlink_bg" left=50 top=226 target=*home]
11:13:06:13 first.ks : [s]
12:13:06:24 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
13:13:06:24 first.ks : jumped to : *mall
14:13:06:24 処理を開始します
15:13:06:24 ==== An exception occured at buttonlayer.tjs(155)[(function) onMouseUp], VM ip = 3 ====
16:13:06:24 -- Disassembled VM code --
17:13:06:24 #(155)    Butt_mouseDown = false;
18:13:06:24 00000000 const %1, *0  // *0 = (int)0
19:13:06:24 00000003 spd %-2.*1, %1  // *1 = (string)"Butt_mouseDown"
20:13:06:24 -- Register dump --
21:13:06:24 %-2=(object)(object 0x0012F50C:0x00000000)  %-1=(object)(object 0x0276AEF8:0x0276AEF8)
22:13:06:24 %0=(void)  %1=(int)0  %2=(void)
23:13:06:24 ---------------------------------------------------------------------------------------
24:13:06:24 オブジェクトはすでに無効化されています at buttonlayer.tjs(155)[(function) onMouseUp]
25:13:06:24 スクリプトで例外が発生しました
26:オブジェクトはすでに無効化されています
27:13:06:24 trace : ButtonLinkPlugin.tjs(120)[(function) onMouseUp] <-- immediate event
赤字は行番号を表しており、実際にはログファイルに書き込まれません。

えっと、やっぱり難しくてわかんないんだけど…
まぁまぁ、ちゃんと説明するから。
まず 1行目 を見てみて。
13:06:13 Scenario loaded : first.ks』っていうとこ?
そうそう。
これは『13時06分13秒に first.ks っていうシナリオファイルを読み込みました』ってことを表してるの。
あ、なるほど。確かにそんな感じだね。
それから、ちょっと飛ばして次は 5行目 を見てみて。
『どこへ行く?』…ってこれメッセージウィンドウに表示された文章だよね?
そ。これは見ての通り『13時06分13秒に、first.ks の中に書いてある「どこへ行く?」っていうメッセージを表示しました』ってことを表してるの。
へぇ…
じゃ次は 7行目
これって… blink マクロだよね?
そう。これはつまり、『13時06分13秒に、first.ks の中に書いてある blink マクロ [blink text="商店街" graphic="buttonlink_bg" left=50 top=51 target=*mall] を実行しました』ってことね。
シナリオファイルに書いてある通りにシナリオスクリプトの実行履歴が表示されてるでしょ?
上記のログファイルの記述では省略していますが、プラグインを読み込む部分([call storage="ButtonLinkPlugin.tjs"])もログファイルに出力されています。
うん、そーだね。
8行目 は、ここで ButtonLayer クラスの定義が必要になったから読み込んだってことを表してるの。
あと 9行目11行目 はわかるよね?
これもシナリオファイルに書いてある通りだよね。
ん。で、「商店街」をクリックしたから、*mall ってラベルにジャンプしました、ってのを表してるのが 13行目
これもなんとなくわかるよ。
んで、エラーが起こったのがその後の 15行目 のとこ。
う〜ん… buttonlayer.tjs とか onMouseUp とか書いてあるから、 ButtonLayer.tjsonMouseUp メソッドがエラーに関係してそうだけど、 ここから先に書いてあることの意味が全然わかんない…
あ、これはわからなくても大丈夫だよ。
そうなの?
まぁもし解れば結構複雑なエラーの原因も判るかもだけどね。
次は 24行目 を見てみて。
『オブジェクトはすでに無効化されています』って、エラーが起きた時にダイアログボックスに表示されてたよね。
ん、ダイアログボックスに表示されたエラーはこのファイルにも書き込まれるようになってるんだ。
あ、そうなんだ。
その次に書いてある『at buttonlayer.tjs(155)[(function) onMouseUp]』っていう部分に注目してみて。
これ 15行目 にも書いてあったよね。
あ、もしかして、これってエラーが起きた場所を表してるの?
そのとーり。
つまり、ButtonLayer.tjs の中に書かれてる onMouseUp メソッドでエラーが起きたってことだよ。
ちなみに (155) ってのは 155 行目でエラーが起きたって意味ね。
なるほどねぇ。
じゃそこのスクリプトを見れば、エラーの原因がわかるんだね。
でもちょっとヘンだと思わない?
えっ、何が?
ButtonLayer.tjs に書いてあるのは ButtonLayer クラスの定義だったよね。
§5.3 参照。
うん、そーだけど?
ButtonLayer クラスは KAG に元々含まれてるクラスなわけでしょ。
そのスクリプトが間違ってると思う?
あ、そっか。確かに KAG に元々含まれてるクラスのスクリプトが間違ってるとは思えないよね…
まぁもしかしたら KAG のバグって可能性もないわけじゃないけど、 さすがに今回のエラーは KAG のバグってことはないよね。
まーそうだよねぇ。
でも、じゃあどうやってエラーが起きた場所を調べればいいの?
そういう時は、ButtonLayer クラスの onMouseUp メソッドから遡って調べていくの。
遡って…って、どーゆーこと?
27行目 を見てみて。
trace って書いてある行のこと?
そう。この行は、エラーが起きる前にどんなふうにスクリプトが実行されたのかを表してるんだ。
まず、一番右に immediate event って書いてあるでしょ。
うん、書いてあるね。
これは「何かのイベントが起こった」って事を表してるの。
選択肢をクリックした時にエラーが起きたんだから、 「何かのイベント」は「選択肢をクリックしたこと」だってわかるよね。
う〜ん、まぁそー言われればそーだね。
で、『ButtonLinkPlugin.tjs(120)[(function) onMouseUp] <-- immediate event』っていう部分が、 選択肢がクリックされたから、ButtonLinkPlugin.tjs の中で定義されてる ButtonLinkLayer クラスの onMouseUp メソッドが呼び出された、ってことを表してるの。
えっと、じゃあ『onMouseUp メソッドが呼び出された ← 選択肢がクリックされた』って感じ?
そうそう。だから、エラーの流れをまとめるとこうなるね。

<選択肢がクリックされてからエラー発生までの流れ>

選択肢がクリックされた
ButtonLinkLayer クラスの onMouseUp メソッドが呼び出された
ButtonLayer クラスの onMouseUp メソッドが呼び出された
ButtonLinkLayer クラスの onMouseUp メソッドの中で super.onMouseUp が実行された)
エラー発生!

これでどこのスクリプトが間違ってたか見当がつくでしょ?
ん〜と、ButtonLayer クラスの onMouseUp メソッドじゃないんだから、 ButtonLinkLayer クラスの onMouseUp メソッドの方ってこと?
ん、そういうこと。
じゃあ ButtonLinkLayer クラスの onMouseUp メソッドのどこが間違ってたかわかる?
えっと、ButtonLinkPlugin.tjs(120) だから、120行目がアヤシイよね。
120行目は super.onMouseUp(...); だね。
う〜ん、普通にスーパークラスの onMouseUp メソッドを呼び出してるだけだよね…
もしかして、実はスーパークラスの onMouseUp メソッドを呼び出しちゃいけなかったとか?
んー、まぁこれはちょっとわかりにくいかな。
エラーが起きた原因は super.onMouseUp(...); を実行する位置が間違ってたことなんだ。
実行する位置?
onClickFunction を呼び出す前に super.onMouseUp(...); を呼び出さなくちゃいけなかったの。
え、どうして?
onClickFunction が呼び出されるとどうなるんだった?
え〜っと、onClickFunction って ButtonLinkPlugin クラスの onLinkClicked メソッドへの参照になってたよね?
ん、そうだね。
で、onLinkClicked メソッドの中で kag.process メソッドを呼び出して、 それから clear メソッドを呼び出してたね。
そう、そこがポイント。
え、どこ?
clear メソッドを呼び出すってトコ。
clear メソッドって、 選択肢がクリックされたら自動的に選択肢を消すために選択肢オブジェクトを無効化するメソッドでしょ?
これのどこにエラーの原因があるの?
clear メソッドが呼び出されると、全部の選択肢オブジェクトが無効化されるよね。
うん、for ループで配列に入ってる全部のオブジェクトを無効化してたよね。
つまり、クリックされた選択肢オブジェクトも無効化されるわけだよね。
まぁ全部無効化するんだから当然だよね。
無効化されるとオブジェクトはどうなるんだった?
えーと、無効化するっていうのは、そのオブジェクトを使えなくするってことじゃなかったっけ?
もうちょっと正確に言うと、無効化するってことは、もうこのオブジェクトは要らないよって宣言することなんだけど、 まぁいずれにしても無効化するとそのオブジェクトは使えなくなっちゃうよね。
※無効化の詳細については §2.2 参照。
うん。
ButtonLinkLayer クラスの onMouseUp メソッドonClickFunction を呼び出した後に スーパークラスの onMouseUp メソッドを呼び出そうとしたから、ここでエラーが起きちゃったわけ。
えっ、どういうこと?
onClickFunction を呼び出すと、 onClickFunction を呼び出したオブジェクト自体が無効化されて使えなくなっちゃうから、 その後はメソッドを呼び出したり、メンバ変数やプロパティを使ったりできなくなっちゃうの。
あ、そーいうことね。
無効化されてメソッドが呼び出せなくなってるのに、スーパークラスの onMouseUp メソッドを呼び出そうとしたからエラーが起きちゃったんだね。
krkr.eXe.console.log にも 26行目 に「オブジェクトはすでに無効化されています」って書いてあるでしょ。
あ、ホントだ。
これでエラーの原因がわかったでしょ?
うん。
じゃあ、エラーが起こらないようにスクリプトを修正しよっか。
これってどうやって修正すればいいの?
こうやって、スーパークラスの onMouseUp メソッドを onClickFunction が呼び出される前に呼び出せば OK だよ。

ButtonLinkLayer クラスの onMouseUp メソッド(修正版)>

function onMouseUp(x, y, button, shift)
{
    // スーパークラスの onMouseUp メソッドを呼び出します
    super.onMouseUp(...);

    if(enabled && button == mbLeft)
    {
        // clickse が指定されていれば効果音を再生します
        if(clickse !== void)
            window.se[clicksebuf].play(%["storage" => clickse, "loop" => false]);

        // exp が指定されていれば TJS 式として実行します
        if(exp !== void)
            Scripts.eval(exp);

        // クリックされた時に実行するメソッドを this を引数にして呼び出します
        onClickFunction(this);
    }
}

えと、これって super.onMouseUp(...); の位置が変わっただけ?
ん、そうだよ。
こうすれば onClickFunction が呼び出されて、このオブジェクトが無効化されても、 その後メソッドを呼び出したりメンバ変数やプロパティを使ったりしないから、エラーは起こらないよ。
なるほどね。
でも、スーパークラスの onMouseUp メソッドを呼び出す位置を変えちゃって大丈夫なの?
スーパークラス(ButtonLayer クラス)の onMouseUp メソッド は メンバ変数 Butt_mouseDown の値を false にして、update メソッドを呼び出して、 さらにスーパークラス(Layer クラス)の onMouseUp メソッドを呼び出してるわけだよね。
うん、そだね。
まず、Butt_mouseDownButtonLinkLayer クラスの onMouseUp メソッドの中で使ってないから問題ないよね。
確かにそーだね。
あと、update メソッドは呼び出す位置を変えても大丈夫だったでしょ。
そーだったっけ?
そうだよ。§5.5 で説明したよね。
あー、そういえば…
あとは Layer クラスの onMouseUp メソッドだけど、 これは基本的に呼び出す位置を変えても問題ないんだ。
そうなの?
うん。ネイティブクラスのイベントハンドラ(名前が on で始まるメソッド)は大抵呼び出す位置は気にしなくて大丈夫だよ。
※ネイティブクラスについては §3.1 参照。
ふぅん、そーなんだ。
さて、それじゃこれでスクリプトも修正できたことだし、改めて実行してみよっか。
うん、そうだね。
必要なデータはここに置いとくから、ちょっと実行してみて。
りょーかい!

<実行結果>

表示されたウィンドウ

じゃ改めて選択肢をクリックしてみて。
うん。

<「商店街」をクリックした結果>

あ、今度はうまくいったね!
だね。
一時はどーなるかと思ったけど、ちゃんとできてよかった。
ゴメンね、今回は段取りが悪くって。
ん〜、でもエラーの原因の見つけ方もわかったし、結果的にはよかったんじゃない?
ま、まぁね。
えっと、それじゃあこれでとりあえず選択肢のプラグインができたから、 次回から制限時間付き選択肢を作っていくことにするね。
は〜い!
それじゃ、また次回ね!


前へ | TOP | 次へ