VRML with JavaScript Tutorial

戻る はじめに

書式について

Script ノードは入出力と機能を制作者が自由にデザインできます。  その他のノードがフィールドの種類、名前、型が固定で値しか変更できないのに対して、Script ノードは必要な数だけフィールドを記述でき、任意の型を宛って、好きに名付けることができます。

Scriptノードは JavaScript もしくは Java によって機能を記述できます。  Java ならば JavaScript よりも複雑な機能を実現できますが、ここでは易しく手軽に開発できる JavaScript を解説します。 

VRML の Script ノード

Script {
eventIn 型 イベントイン名
eventOut 型 イベントアウト名
field 型 フィールド名 初期値
mustEvaluate FALSE
directOutput FALSE
url "javascript:
function イベントイン名 (イベント値, イベント時刻) {

イベントアウト名 = 計算結果;
}
"
}

ユーザー定義フィールド

eventIn と eventOut と field は異なる名前でそれぞれ必要な数だけ定義できます。
それぞれ、名前の前に型を必ず記述する必要があり、 SFTime や SFVec3f など仕様で定義されている全種類の型を指定できます。
field は 型と名前の次に 初期値 を記述してください。  eventIn もしくは eventOut ならば初期値は必要ありません。
eventIn
ROUTE などからのイベントを受け取ると、スクリプト内にあるイベントイン名と同じ名前の関数(function)を実行します。
イベント入力された値とそのイベント時刻はその関数の引数に引き継がれます。  詳しくはイベント入力の値とその時刻を参照してください。
eventOut
関数内の イベントアウト名と同名の変数の値が変わったならば、その関数の終了時にその変数値をイベント出力します。(*1)
また、スクリプト内にあるイベントアウト名と同名の変数はグローバル変数となり、複数の関数の間で値を引き継ぐことができます。
field
スクリプト内にあるフィールド名と同名の変数はグローバル変数となり、複数の関数の間で値を引き継ぐことができます。
exposedField
VRML2.0 の仕様により、Script ノードで exposedField を定義することは禁じられています。(*2)

(*1) Contact の場合は イベントアウト名と同名の変数の値が変化する度に、その値をイベント出力します。  for 文などにより何度も繰り返しこの変数の値が変化した場合、処理速度の低下や誤動作を招く場合がありますので注意してください。

(*2) Cortona と Contact は以下のように記述して exposedField を定義できます。  これは eventIn と eventOut と field の全ての特徴を併せ持ちます。
exposedField 型 フィールド名 初期値

ユーザー定義以外のフィールド

mustEvaluate
FALSE (初期値)ならば、イベント入力があっても出力が必要とされるまでスクリプトの実行が待たされる場合があります。  TRUE ならば、出力の必要にかかわらずイベント入力ごとにスクリプトが実行されます。  ネットワーク越しに情報を送るようなブラウザーが掌握していない機能をスクリプトに実装している場合のみ TRUE にしてください。  でないとパフォーマンスが落ちるかもしれません。  (恐らく FALSE ならば複数の関数が同時実行されることはないということだと思うのですが、よく分かっていません。)
directOutput
TRUE ならば、スクリプトがアクセスしたノードへ直接的にイベント値を送ることができます。  FALSE (初期値)ならば、eventOutを介してのみイベント出力できます。  FALSE の状態でノードへ直接イベントを出力すると、結果どうなるかは確定しません。  TRUE, FALSE に関わらず、アクセスしたノードのフィールド値の読み込みは可能です。
具体的な使い方はノードへの直接出力を参照してください。
url
スクリプトのソース、もしくはスクリプトが記述された外部ファイルへのパスをダブルクォーテーション ( " ) で囲んで記述します。
ソースを記述する場合、先頭に javascript: を全て半角小文字で記述してください。
ファイルへのパスを記述する場合、例えばファイル名が script1.js であるならば、url "script1.js" のように記述します。  url [ "script1.js" "script2.js" ] のように複数のパスを記述した場合、最初のパスが見つからなければ2番目のパスに書かれたファイルが読み込まれます。
ちなみに JavaScript ではなく Java である場合、コンパイル済の class ファイルのパスを指定できますが、ソースを記述することはすることは出来ません。

スクリプトのソース

スクリプトのソースは JavaScript のルールに従って記述してください。
ソース内の文字列はシングルクォーテーション ( ' ) で囲ってください。
Contact の場合は各命令の最後に必ずセミコロン ( ; ) を付けるようにしてください。 さもないとエラーになります。
コメント(注釈)
VRML2.0 のコメント(注釈)は # 記号で始まりますが、JavaScript の場合 // で始まります。 複数行にまたがったコメントは /* と */ の間に記述します。
Contact の場合は、JavaScript の注釈で日本語などの全角文字を使うとエラーになります。

X3D の Script ノード

<Script mustEvaluate='FALSE' directOutput='FALSE' url=''>
<field accessType='inputOnly' type='型' name='イベントイン名'/>
<field accessType='outputOnly' type='型' name='イベントアウト名'/>
<field accessType='initializeOnly' type='型' name='フィールド名' value='初期値'/>
<field accessType='inputOutput' type='型' name='フィールド名' value='初期値'/>
<![CDATA[ecmascript:
function イベントイン名 (イベント値, イベント時刻) {

イベントアウト名 = 計算結果;
}
]]>
</Script>
X3D は XML でコード化されており、機能的にはVRML2.0の上位互換です。

ユーザー定義フィールドは Script の子ノードとして field タグに記述します。  accessType 属性 に VRML2.0 での eventIn, eventOut, field, exposedField に相当する内容を記述します。  VRML2.0 では exposedField の定義は禁じられていましたが、X3D では accessType='inputOutput' とすれば定義できます。

フィールドの種類
機能 VRML2.0 X3D 初期値
イベント入力 eventIn inputOnly 不要
イベント出力 eventOut outputOnly 不要
入出力不可 field initializeOnly 必要
入出力可 exposedField inputOutput 必要

スクリプトのソースは <![CDATA[ と ]]> の間に記述してください。

処理の流れ

簡単な例で全体の処理の流れを追ってみましょう。
VRML - ソース / X3D - ソース
中央の赤い球をクリックすると、ランダムに色が変わります。
#VRML V2.0 utf8

Viewpoint {}
NavigationInfo {
type "EXAMINE"
}

Group {
children [
Shape {
appearance Appearance {
# (8)
material DEF Ball-Mt Material {
diffuseColor 1 0 0
}
}
geometry Sphere {}
}
# (1)
DEF ThS TouchSensor {}
]
}

DEF Sc Script {
# (3)
eventIn SFTime colorChange

# (6)
eventOut SFColor diffuseColorOfBall

url "javascript:

// (4)
function colorChange () {
var r = Math.random();
var g = Math.random();
var b = Math.random();

// (5)
diffuseColorOfBall = new SFColor (r,g,b);
}
"
}

# (2)
ROUTE ThS.touchTime TO Sc.colorChange

# (7)
ROUTE Sc.diffuseColorOfBall TO Ball-Mt.diffuseColor
ソースのコメントに (1) から (8) までの番号が振られていますが、これはタッチセンサーが仕掛けられた球をクリックしてから球の色が変化するまでの処理の流れの順番を示しています。

例えば (2) で ROUTE の TO の前後で型が合っているかどうかや、 (2) と (3) との間で型と名前がどちらも合致しているかどうかは、VRMLブラウザーはきちんとチェックし、合っていないならばエラーを表示します。
しかし、(4) で (3) と異なる関数名を記述してしまった場合、関数は実行されないばかりか、エラーも表示されないので注意してください。  (5) で (6) と異なる変数名を記述してしまった場合も、イベント出力されませんし、エラーも出ません。  エラーが出ないのにうまく動かない場合、この当たりに記述ミスがないかどうかチェックしてみてください。

ノードへの直接出力

ノードへの直接出力を行う場合、directOutput を TRUE に設定します。
VRML - ソース / X3D - ソース
先の作例を少し改造して、Material ノードの diffuseColor フィールドに値を直接出力してみました。 
DEF Sc Script {
eventIn SFTime colorChange
field SFNode BallMt USE Ball-Mt
directOutput TRUE
url "javascript:
function colorChange () {
var r = Math.random();
var g = Math.random();
var b = Math.random();

BallMt.diffuseColor = new SFColor (r,g,b);
}
"
}
ユーザー定義フィールドでノードを SFNode 型もしくは MFNode 型で定義し、 スクリプト内でそのノードとフィールドとの間をピリオド ( . ) で区切って記述すると、そのノードのフィールド値に直接書き込めます。

例えば、変数 c に BallMt の diffuseColor の値を代入する場合は以下のように記述してください。
c = BallMt.diffuseColor;
このようにフィールド値の読み込みしか行わない場合は directOutput を TRUE にする必要はありません。

VRML2.0 ( X3D ) の全てのフィールドは、入出力機能の違いにより eventIn (イベント入力), eventOut (イベント出力), field (入出力不可), exposedField (入出力可)の4種類うち何れかに分類されます。  eventIn 型フィールドに対しては書き込みのみ、eventOut 型フィールドに対しては読み込みのみ可能です。  Material ノードの diffuseColor フィールドは exposedField ですので読み書き共に可能ですが、 例えば Box ノードの size フィールドは field ですので、外部からの読み書きは仕様上できません。  field型のフィールドに対してこの例のような読み書きを試みると、Cosmo ではエラーとなりますが、 Cortona と Contact は読み書きできます。

イベント入力の値とその時刻

Script ノードの eventIn にイベントされた入力値とそのイベント時刻は、 eventIn と同名の関数の引数に引き継がれます。
function eventInName ( value, eventTime ) { }

1つ目の引数はイベントで入力された値が入ります。  このイベント値がどの様なタイプかは eventIn の後に記述された型によります。  例えば SFFloat ならばイベント値は単精度実数であり、SFBool ならば TRUE または FALSE となります。  SFVec3f ならばイベント値は SFVec3f オブジェクトです。

2つ目の引数はイベントが発生した時刻が入ります。  この時刻を参照することにより、このイベント入力のタイミングでアニメーション開始もしくは停止などが可能になります。

関数内で引数を使用しないならば、引数の記述を省略できます。


VRML - ソース / X3D - ソース
中央の黄色い円柱上でマウスボタンを押し続けると円柱がX軸を中心に回転し、離すと回転が止まります。
DEF Sc Script {
eventIn SFBool isActive_ThS
eventOut SFTime startTime
eventOut SFTime stopTime
url "javascript:
function isActive_ThS (val,et) {

if (val == TRUE) startTime = et;
else stopTime = et;

}
"
}
このスクリプトは TouchSensor のオンとオフのタイミングで動作し、オンならば TimeSensor の開始時刻を、オフならば終了時刻を出力します。  isActive_ThS 関数の最初の引数 val は イベント入力値が入ります。  この場合、TouchSensor の isActive の値であり、押されたときに val は TRUE、離されたときには FALSE となります。  2番目の引数 et にはイベントが行われた時刻が入ります。  この場合、et は TouchSensor がオン・オフした時刻(円柱上でマウスボタンが押された・離された時刻)が入っています。

たとえば、スクリプトのソースの
stopTime = et;
と記述されている箇所を
stopTime = et + 2;
に書き換えた場合、マウスボタンを離してから2秒後に円柱の回転が止まります。
VRML - ソース / X3D - ソース

このページのトップ | 次へ 初期化について