BlackSheep-LSL@Wiki エラー対応

※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

エラーが出る原因と対処方法についてまとめてみました。
LSLに関わって発生するエラーには主に3種類あります。

  1. コンパイルエラー(Compiler Errors)
  2. ランタイムエラー(Run-Time Errors)
  3. ビルドエラー(Build Errors)

以下にエラーの種類と原因、対処方法について書いていますので参考にしてみて下さい。

コンパイルエラー


スクリプトコードを保存したときに発生するエラーです。
コードの記述間違いや抜けによって起きます。
エラー個所を修正して保存し直さないとスクリプトを動作させることはできません。

修正する際にはエラーメッセージとともに表示される行・列番号が参考になります。
(3,8):ERROR:Syntax Error
このような表示は、3行目の8文字目辺りに間違いがあることを意味します。
ただしあくまでも参考情報ですので、その位置ではない個所に間違いが潜んでいる場合もあります(特にSyntaxErrorの場合)。
なお、ここに表示される行数・文字数は0行目0文字目から数えることに注意して下さい(先頭行は1行目ではなく0行目です)。

コンパイルエラーには以下のような種類があります。

Syntax Error


シンタックスエラー、文法エラーとも言います。
普段遭遇するエラーの8割はこれです。
エラーの代名詞みたいな存在です。

コンパイルという作業は、コンピュータがコードの意味を解釈してマシンコードに変換していく作業ですが、このエラーはコンピュータがコードの意味を理解できなかったことを意味します。
例えば日本語で言うなら、以下のようなものはシンタックスエラーです。
「昨日怪獣を悲しくてまたの名をニュージーランド旅行が久しからず」
意味不明で解釈できないので、これは日本語としてエラーになります。

LSLにも文法がありますので、文法を間違うとシンタックスエラーが出ます。
例えばありがちなのは、「文末には;を付ける」という文法をうっかり忘れた場合です。

default {
  state_entry(){
    llSay(0,"Yes.")   ←文末に;が無い
  }
}

他には、{}や()の対応付けを間違うというパターンもあります。
if文などによる条件分岐が深くなると、うっかり閉じカッコを忘れたりします。

default {
  touch_start(integer detected){
    if (llDetectedKey(0) == llOwner()){
      if (llVecDist(llDetectedPos(0), llGetPos()) < 4.0){
        if (llDetectedName(0) == "Miz Cremorne"){
          llSay(0,"Hi, Miz!");
        }
      }
      ← ここでもう一つ}が必要
  }
}

このパターンのシンタックスエラーはエラー個所を示す行番号がズレるので気付きにくいエラーです。

Type mismatch


タイプミスマッチ、型不正とも言います。
変数にはがありますが、異なる型同士で演算を行おうとした場合などに起こります。

日本語を例にとると、例えば、
「彼は小さいときには皮を被っていたが、大きくなったら皮がむけてとっても堅くなった」
と言うと、なんだか誤解を与えます。
これは実は「タケノコ」についての記述であり、「彼」という型を使うのは間違いなのです。
「彼」のかわりに「それ」を使ってもあんまり変わらないという突っ込みはご容赦下さい。

LSLでタイプミスマッチが起こるのは以下のような場合です。

default {
  state_entry(){
    integer i;
    i = "Takenoko";
  }
}

変数iはinteger型(整数型)なのに、文字列の"Takenoko"を代入しようとしているのでエラーとなります。

Function call mismatches type or number of arguments


引数不正によるエラーです。
直訳すると「不正な型、または不正な数の引数を使って関数が呼ばれています」という感じでしょうか。

下手な喩えをまた使いますが、電子レンジというものは中に入れるのは通常「食べ物」と決まっています(例外もありますがw)。
にも関わらず、雨に濡れた猫を乾かそうとして電子レンジに入れたりすると、妙な都市伝説が生まれる結果になります。

同じように、関数はそれぞれ、指定すべき引数の型と数、順序が決まっています。
決まっている引数以外のものを指定したり、間違った順序で書いたりすると、このエラーが発生します。
どのような引数を使うべきかは関数によって様々ですので、リファレンスを参照して調べて下さい。

例えばllSayという関数は、
llSay(integer channel, string msg)
整数と文字列の二つの引数を指定すると決まっています。
これを間違えて以下のように書くとエラーです。

default {
  state_entry(){
    llSay("cat", 0);
  }
}

Name not defined within scope


未定義エラーです。
定義されていない変数やユーザー関数を使ったときに発生します。
要するに「そんな言葉は知らないよ!」というエラーです。

日本語で言うなら、
「昨日さぁ、パップラドンカルメ食べたんだ~」
という感じですかね。
相手が人間なら「パップラドンカルメって何?」と聞けますが、コンピュータはシャイなので「それ何?」の一言が言えません。
変数やユーザー関数を使うときには、必ず前もって「定義」して、コンピュータがわかるようにしてあげなければいけません。

自分では定義したつもりでも、うっかりミスで未定義の変数を書いてしまうこともあります。
エラーの原因としてはこちらのほうが多いのではないでしょうか。

default {
  state_entry(){
    string pappuradon = "karume";
    llSay(0, papuradon);   ← うっかりpが抜けた
  }
}

落ち着いてよく見ればすぐわかるミスだと思いますので、このエラーが出たときはよくコードを見直してみましょう。

また、変数には「スコープ」という概念があります。
ごく簡単に言ってしまうと、ある変数が有効なのはその変数が定義された{}の中だけです。
定義された{}の外でその変数を使おうとすると、やはりこのエラーが出ます。

default {
  state_entry(){
    string pappuradon = "karume";
  }  ← 変数pappuradonが有効なのはここまで

  touch_start(integer detected){
    llSay(0, pappuradon);  ←ここは変数のスコープ外
  }
}

Name previously declared within scope


二重定義エラーです。
同じ名前の変数を同じスコープ内で定義しようとした場合などに起こります。

クラスに「山田太郎君」が2人いるような状況です。
コンピュータは人の顔を覚えるのが苦手ですので、同姓同名だと区別が出来ません。

default {
  state_entry(){
    string yamada = "taro"; ← 一人目の山田君
    llSay(0, yamada);
    string yamada = "taro"; ← 二人目の山田君。この時点でエラー
    llSay(0, yamada);
  }
}

仕方がないので二人目の山田君には今日から山下君に改名してもらえば解決です。

default {
  state_entry(){
    string yamada = "taro"; ← 一人目の山田君
    llSay(0, yamada);
    string yamashita = "taro"; ← 違う名前になったのでOK
    llSay(0, yamashita);
  }
}

Use of vector or quaternion method on incorrect type


あまり遭遇することのないエラーですが一応紹介しておきます。
ベクター型とローテーション型の変数は、内部の要素にアクセスするためのメソッド「.」を使うことができます。

vector p = <1.0, 2.0, 3.0>;
llSay(0, (string)p.x);  ← p.xはpの最初の要素。1.0が入っている

この特殊なメソッド「.」をベクター/ローテーション型以外で使うとこのエラーが出ます。

default {
  state_entry(){
    integer p;
    llSay(0, (string)p.x); ← pはintegerなのでエラー
  }
}

Lists can't be included in lists


リストにリストを入れようとしたときに起こるエラーです。
リスト型変数には、整数、小数、文字列などなど、あらゆる型のデータを入れることができますが、リスト型データを入れることはできません。

default {
  state_entry(){
    list a = [1,2,3];
    list b = [4,5,6,a]; ← aはリスト型なので入れられない
  }
}

二つのリストを連結したいときには、以下のように書き直しましょう。

default {
  state_entry(){
    list a = [1,2,3];
    list b = [4,5,6] + a; ← aを連結するのでbは[4,5,6,1,2,3]になる
  }
}

Return statement type doesn't match function return type


ユーザー関数にまつわるエラーです。
戻り値の型が、定義された型と違う場合に発生します。

ユーザー関数と戻り値については関数の説明の後半を参考にして下さい。

以下のようなコードがエラーの例です。

integer func(){
  return "hoge";
}

ユーザー関数funcにはinteger型の戻り値を定義しているのに、実際にreturnで返しているのは文字列です。
戻り値の型定義と実際の型が異なるので、このエラーが発生します。

Function returns a value but return statement doesn't


これもユーザー関数にまつわるエラーです。
ユーザー関数に戻り値が定義されているのに、どこにもreturn文が無い場合に発生します。

integer func(){
  llSay(0, "It's OK."); ← 全然OKじゃない
}

integer型を返す、と定義しているのですから、正しくreturn文でinteger型のデータを返すようにしなければいけません。

integer func(){
  llSay(0, "It's OK.");
  return TRUE;
}

これならOKです。

Not all code paths return a value


これもユーザー関数にまつわるエラーです。
ユーザー関数内で条件分岐などを多用すると陥りやすいエラーです。

直訳すると「全てのコードが戻り値を返すようになってない」ということになります。

例えば、

integer func(string mode){
  if (mode == "A"){
    llSay(0, "mode A");
    return 0;              ← modeが"A"のときしか戻り値を返さない
  }else if (mode == "B"){
    llSay(0, "mode B");
  }
}

modeが"B"のときにはretuen文が実行されないので、戻り値が返されません。
ユーザー関数funcはinteger型の戻り値を返すように定義されていますので、必ずreturn文でinteger型のデータを返すようにしなければいけません。

Dead code found beyond return statement


永久に日の目を見ない哀れなコードがあるときに発生します。
以下のような場合です。

integer func(){
  return 0;           ← ここでリターンされる
  llSay(0, "Help!");  ← ここは絶対に実行されない
}

return以下のコードが不要ならば消去して成仏させましょう。
必要ならばreturnの前に移動し、活躍の舞台を作ってあげて下さい。

Global functions can't change state


ユーザー関数で発生するエラーの一つです。
LSLの仕様で、ユーザー関数内でのステートチェンジは使用不可になっています。

ChangeState(){
  state active;  ← ユーザー関数内でステートチェンジはNG
}

逃げ道として、以下のように修正する方法があります。

ChangeState(){
  if (TRUE){
    state active;  ← if文に入れると何故かOK
  }
}

しかしながらこの逃げ道自体がLSLコンパイラーのバグである可能性もあり、オススメは出来ません。

Byte code assembly failed -- out of memory


サイズオーバーのときに出るエラーです。
LSLは1スクリプトで16KByteまでという制限があり、それを越えるような超大作を書いてしまったときにこのエラーになります。

コードを分割し、llMessageLinkedなどを使って通信するように改造して下さい。

ランタイムエラー


コンパイル(コードの保存)は問題なくできたけれども、動かしている最中に発生するエラーのことをランタイムエラーと言います。
ランタイムエラーが起きると、スクリプトの入っているオブジェクトにエラーアイコン(書類のようなマーク)が表示され、デバッグチャンネルにエラーメッセージが表示されます。
クライアント画面の上方、メニューの右あたりにもエラーアイコンが出るので、そこをクリックするとエラーメッセージの内容を確認できます。

エラーメッセージを手がかりにしてコードの修正を行うわけですが、時としてコードのどの位置でエラーになっているのかわからない場合があります。
LSLがどこまで正常に動き、どこでエラーとなるのかを見極めるには、デバッグメッセージを活用するのが常套手段です。

例えば、タッチしたときにランタイムエラーになるとしましょう。
タッチイベントの中には複数の処理が書いてあり、一体どこが問題なのかがさっぱりわからないとします。

default {
  touch_start(integer detected) {
    float a;
    float b;
    float c;
    for (a = 3.0;a >= 0.0; a -= 1.0){
      b = 10.0 / a;
      c = b * b;
    }
  }
}

このまま動かしても、タッチイベント内のどこでエラーになるのかわからないので、llOwnerSayを使ってデバッグメッセージを出すように改造します。

default {
  touch_start(integer detected) {
llOwnerSay("init start.");  ← これがデバッグ用のメッセージ
    float a;
    float b;
    float c;
llOwnerSay("init end, for-loop start.");  ← これがデバッグ用のメッセージ
    for (a = 3.0;a >= 0.0; a -= 1.0){
      b = 10.0 / a;
      c = b * b;
    }
llOwnerSay("for-loop end.");  ← これがデバッグ用のメッセージ
  }
}

もしも"init start."と表示された後、"init end, for-loop start."の表示が出る前にエラーが起きれば、エラーの原因は変数定義の部分にあるとわかります。
"init end, for-loop start."も表示され、最後の"for-loop end."が出る前にエラーとなれば、for文の中身に問題があるということになります。
このようにして、まずは問題になっている部分を絞り込んでいきます。

仮に"init end, for-loop start."の後にエラーとなったとしましょう。
怪しいのはfor文の中身ということになりますので、さらに詳細にデバッグメッセージを出力するようにします。

default {
  touch_start(integer detected) {
    float a;
    float b;
    float c;
    for (a = 3.0;a >= 0.0; a -= 1.0){
llOwnerSay("check: a=" + (string)a
              + ", b=" + (string)b
              + ", c=" + (string)c);  ← 変数の中身をデバッグ出力
      b = 10.0 / a;
      c = b * b;
    }
  }
}

このようにすると、変数a,b,cの中身がどうなっているときにエラーが起きたのかを調べることができます。
出力はたくさん出てきますが、デバッグ時には情報が大事ですので、詳細にスクリプトの動きを調べるべきです。
上記を実行したところ、以下のような結果になったとします。

Object: check: a=3.0, b=0.0, c=0.0
Object: check: a=2.0, b=3.3333, c=11.1111
Object: check: a=1.0, b=5.0, c=25.0
Object: check: a=0.0, b=10.0, c=100.0
Object: Script run-time error  ← ここでエラーとなった

a=0.0,b=10.0,c=100.0の時にエラーが起きているのがわかります。
では変数の値が上記のような場合の処理をよくよく確かめてみると・・・。

b = 10.0 / a;
 ↓この計算式は
b = 10.0 / 0.0
 0で割り算をしているので答えは無限大になってしまう

というのがエラーの原因であると突き止められます。
原因がわかれば、あとは対処するだけです。
aが0.0にならないよう、for文の条件を変えるなどすればOKですね。

以上のように、ランタイムエラーは原因を見つけにくいやっかいなエラーですが、落ち着いてじっくり見極めていけば問題個所は必ず見つかります。
では次に、ランタイムエラーにどのようなものがあるのか見ていきましょう。

Math Error


計算エラーです。
なんらかの計算をした結果、コンピュータでは処理不可能な答えになってしまった場合に発生します。

例えば0による割り算です。
答えは無限大になってしまうのでコンピュータでは扱うことができません。

default {
  state_entry() {
    float one = 1.0;
    float zero = 0.0;
    float quotient = one / zero; ← ここでエラーとなる
  }
}

Stack Heap Collision


いわゆるメモリ不足です。
スクリプトが使用可能なメモリサイズは16KByteまでという制限がありますが、そのサイズを越える巨大なデータを扱おうとした場合などにこのエラーになります。

よくありがちなのはリスト型変数にどんどん値を追加していき、膨れ上がったリストがメモリを食いつぶしてエラーとなるケースです。

default {
  state_entry() {
    list entries = [0];
    while (TRUE) {        ← 無限にループ
      entries += entries; ← 要素を延々と追加。やがて制限サイズを越える
      llOwnerSay((string) llGetListLength(entries));
    }
  }
}

このエラーを予防するにはllGetFreeMemoryを使います。
llGetFreeMemoryはスクリプトが使える残りメモリサイズをByte単位で返してくれます。

例えば、残メモリサイズが1KByte以下になったら処理を中断するのであれば以下のように判定します。

default {
  state_entry() {
    list entries = [0];
    while (TRUE) {        ← 無限にループ
      if (llGetFreeMemory() <= 1024){ ← 残メモリが1KByte以下
        return;                       ← 処理中断
      }
      entries += entries; ← 要素を延々と追加。
      llOwnerSay((string) llGetListLength(entries));
    }
  }
}

スクリプトが現在使用しているメモリサイズを得るには以下の式を使います。

(16 * 1024) - llGetFreeMemory()

不特定数の要素をリストに追加するようなコードを組む際には、必ず予防策を講じておいたほうが無難です。

Too many listens


リッスンが多すぎ!なエラーです。
一つのスクリプトが同時に起動できるllListenは64個が限界です。
それ以上のllListenを使おうとするとこのエラーが出ます。

通常そんなに大量のリッスンを必要とするケースは考えにくいですが、llListenRemoveによるリッスンの閉じ忘れなどによって発生する場合があります。

default {
  touch_start(integer detected) {
    llListen(channel, "", llDetectedKey(0), "");  ← タッチした人ごとにリッスンを起動
  }

  listen(integer channel, string name, key id, string message){
    llSay(0, name + " said, '" + message + "'";
  }
}

※リッスンを閉じるコードがどこにもない

Heap Error


「不正なメモリアクセス」が原因で発生するエラーだと思われます。
遭遇したことがないのであまりわかりませんが、ユーザー関数の中からステートチェンジをしたりすると出ることがあるようです。

X error running rule #Y: non-Z rule


引数にリスト型変数を指定するような関数(llSetPrimitiveParams, llParticleSystem等)で起こります。

Xにはエラーとなった関数の名前(llSetPrimitiveParams,llParticleSystem等)が表示されます。
Yは引数のリストのうち、Y番目の要素の型が不正であることを意味します。
Zは本来指定すべき型名です。

default {
  state_entry(){
    llSetPrimitiveParams([
      PRIM_SIZE, <1, 1, 1, 1>, ← Vector型でなければならないのにRotation型になっている
      PRIM_COLOR, ALL_SIDES, <1, 0, 0>,1.0
     ]);
  }
}

エラーメッセージ:
llSetPrimitiveParams error running rule #1: non-vector rule.

上記の例では、PRIM_SIZEを指定するパラメータとして、Vector型でなければいけないところを間違ってRotation型にしてしまっています。

X error running rule #Y: unknown rule


上のエラーと似ていますが、こちらは型の間違いではなく要素の順番を間違ったり、余計な要素が混ざっていると出ることがあります。
Xにはエラーとなった関数の名前(llSetPrimitiveParams,llParticleSystem等)が表示されます。
Yは引数のリストのうち、Y番目の要素が不正であることを意味します。

default {
  state_entry(){
    llSetPrimitiveParams([
      PRIM_SIZE, <1, 1, 1>, <0, 0, 1>,  ← 余分なVector型がある
      PRIM_COLOR, ALL_SIDES, <1, 0, 0>,1.0
     ]);
  }
}

エラーメッセージ:
llSetPrimitiveParams error running rule #2: unknown rule

PRIM_SIZEを指定するパラメータとして、Vector型を一つ指定するべきところを、消し忘れたのか、2つ書いてあるためにエラーになります。

ビルドエラー


厳密に言えばLSLに関わるエラーではありませんが、製作中に遭遇するエラーとして挙げておきます。

The object may be out of range or may have been deleted.


スクリプト編集を行っているにも関わらず、そのスクリプトが入っているオブジェクトをワールド上から削除してしまったりした場合に起こります。
編集したスクリプトを保存しようとすると、
「オブジェクトがないので保存できないよ!」
というエラーになります。

タイマーでllDieするオブジェクトや、temprezオブジェクトを扱っているときにしばしば遭遇します。

この場合、インベントリの中に新たにスクリプトを作成し、編集したスクリプトコードを丸々コピー&ペーストして保存するか、オブジェクトをrezし、改めてスクリプト編集ウインドウを開いて、そこにペーストして保存する必要があります。
保存しないでスクリプト編集ウインドウを閉じた場合、編集結果は失われますので注意しましょう。

また、オブジェクトが存在するにも関わらずこのエラーが出る場合、アバターがオブジェクトから離れすぎていることが考えられます。
オブジェクトの近くまで移動するか、あるいはオブジェクトのほうをアバターの近くに移動させてから保存して下さい。

移動するオブジェクトの作成中に、思わぬ遠方にオブジェクトがすっ飛んでいってしまったときなどによく起こるエラーです。

Can't enable physics for objects that interpenetrate others


オブジェクトの編集中、物理属性を付与しようとしたときに、すでに他のオブジェクトや地面とぶつかっているとこのエラーが起きます。
何もない空間にオブジェクトを移動させた上で物理属性を付与すればOKです。

Pieces too far apart


オブジェクトをリンクさせようとしたとき、オブジェクト同士の距離が離れすぎていると起こります。
オブジェクト同士を近づけてリンクしましょう。
リンク後、個別にprimの位置を動かすことで、prim間の距離をあけることが出来ます。

またはリンクしようとしているprimを巨大化させるのも解決方法の一つです。
大きな状態でリンクし、その後個別にprimのサイズを元の大きさに戻します。


名前:
コメント: