チュートリアル 7
ネットワーク①:簡単なスプライン(直線・曲線道路)
前章ではネットワークの基礎を学びました。この知識を生かし、まずは最も簡単なネットワークとして、スプラインと呼ばれる直線・曲線道路を敷設してみましょう。
最も簡単なスプライン
初めての道路ネットワークの敷設ですから、できる限り簡単なものから始めていきます。
前章で説明したように、道路などのネットワークは多数のノード(頂点; 交差点)とエッジ(辺; 交差点以外)が繋がったものとして表現され、
- ノードを簡単に定義するための機能としてジャンクションが
- エッジを簡単に定義するための機能としてスプラインが
存在するのでした。
当然、分岐が存在しない方が構造上シンプルですので、ジャンクションよりもスプラインの方が簡単に敷設できます。 また、スプラインの中にも何種類かの作り方があるのですが、その中でも最も簡単なのは「テンプレートを使わず、進路情報も持たず、位置を直接指定して敷設する、まっすぐなスプライン」です (これがどういう意味かはまだ分からなくて構いません)。まずはこちらを敷設してみることとしましょう。
入力端子を設置する
第5章まで作ってきたワールドファイルを開いてください。スプラインをこの最も簡単な方法で定義するには、ワールドファイルへ次の構文を記述します。
var fSpline = Chunks[チャンクX, チャンクZ].BeginSpline(X座標, Y座標, Z座標);
チャンクX、チャンクZにはスプラインの入力端子(開始位置)を設置するチャンク番号を、X座標、Y座標、Z座標には入力端子を設置する座標を指定します。
fSpline はいわゆる変数で、この後 fSpline を介して線形やプロップなどの詳細を指定していくこととなります。Factory of Splineを省略して fSpline と命名するのが一般的です。
チャンク(0,0)、チャンク内座標(110,0.2,150)にスプラインの入力端子を設置してください。スクリプトは次のようになります。
var fSpline = Chunks[0, 0].BeginSpline(110, 0.2, 150);
注意点として、この構文を記述しただけでは、実際にスプラインは敷設されません(あるいは不完全な状態で出現します)。
この後 fSpline という変数を通じてこのスプラインの線形などを指定していき、最終的に形状を確定する構文を記述することで、スプラインの敷設が完了します。
円曲線半径と長さを指定する
まだスプラインの入力端子の位置を指定しただけで、肝心のスプライン自身の線形を定義できていません。次は線形の最も基本的な要素として、スプラインの曲線(円曲線)半径と長さを指定する必要があります。
ここでは直線のスプラインを敷設することとしましょう。定義中のスプライン fSpline を直線とするには、ワールドファイルへ次の構文を記述します。
fSpline.Curves.Straight(長さ);
長さにはスプラインを直線とする長さを[m]単位で指定します。ここでは50 mの直線としてみましょう。スクリプトは次のようになります。
fSpline.Curves.Straight(50);
スプラインに沿ってプロップを連続設置する
これでスプラインの線形が定義できましたが、まだこのスプラインに沿って何のプロップ(3Dモデル)も設置していないため、このまま実行しても正常に動作しているのかどうか見た目で分かりません。 サンプルワールドから道路の3Dモデルを借りてきて、初めてのスプラインの仕上げとしましょう。
やることは第3章と同様です。BasicSampleサンプルワールドの3Dモデルが格納されているフォルダ(samples\BasicSample\Models)へ移動し、次のフォルダを丸ごとコピーしてください。 コピー先は第3章で作成したフォルダとしてください。
Road2WhiteLine
使用するモデルは以下の5つですので、これらの定義をモデルリストに追記してください。サンプルワールドのモデルリストからコピーしても構いません。
Models\Road2\Straight.glbModels\Road2\3Forked.glbModels\Road2\DeadEnd.glbModels\WhiteLine\150.glbModels\WhiteLine\500.glb
モデルリスト全体は次のようになるはずです。
Background Models\Background\Background.glb $NonCollision
Grass Models\Grass\Grass.glb $OpenModel(0.6, 1, 15, 1)
BusStop Models\BusStop\Model.glb $ClosedModel
Road2_Straight Models\Road2\Straight.glb $OpenModel(0.9, 1, 30, 1)
Road2_3Forked Models\Road2\3Forked.glb $OpenModel(0.9, 1, 30, 1)
Road2_DeadEnd Models\Road2\DeadEnd.glb $OpenModel(0.9, 1, 30, 1)
WhiteLine150 Models\WhiteLine\150.glb $NonCollision
WhiteLine500 Models\WhiteLine\500.glb $NonCollision
モデルをワールドに正しく組み込めたら、スプラインに沿って道路の3Dモデルをプロップとして設置しましょう。ワールドファイルへ次の構文を記述します。
fSpline.PutProp(["モデルキー"], X座標, Y座標, Z座標, 開始S座標, 弧, 間隔);
モデルキーにはモデルリストで定義したキーを指定します。Chunks.PutProp構文と異なり、括弧[]で囲む必要があるので注意してください。
S・X・Y・Z座標は次の画像のように取ります。

スプラインの中心線がS座標の座標軸となります(すなわち、曲線などでは座標系が“歪む”場合もあります)。S=0となるのはスプラインの開始地点、すなわち入力端子の位置です。 構文のX座標、Y座標、Z座標にはスプライン中心線からの相対座標を、開始S座標にはプロップの設置を開始するS座標を指定します。
弧、間隔については次の画像を参照してください。ピンクの四角をプロップとします。

間隔はその名の通りプロップを設置する間隔を指定します。弧はプロップをカーブ上に設置する際、スプラインの中心線に合わせる2点の距離を指定します。
さて、今回は道路の3Dモデル Road2_Straight をスプラインに沿って設置していきます。このモデルは、単体では次のような形状となっています。

画像を見てもよく分からないかもしれませんが、白線などが一切引かれていない道路の1.2 m分の短冊です。これを1.2 mおきに連続して設置していくことで、どのような線形の道路も表現できるというわけです。
設置位置はスプラインの中心線に合わせて、弧1.2 m、間隔1.2 mでモデル Road2_Straight を連続設置してください。スクリプトは次のようになります。
fSpline.PutProp(["Road2_Straight"], 0, 0, 0, 0, 1.2, 1.2);
スプラインを確定(敷設)する
スプラインの線形やプロップを指定したら、最後にそれを確定させる構文を記述する必要があります。スクリプトは次のようになります。
var spline = fSpline.Build();
この構文によって、これまで fSpline 変数を通じて定義してきたスプラインが実際に敷設されます。設計図を描いて、それを基に実際に建築する、の2段階と考えてください。
動作確認
現時点でのワールドファイル全体は次のようになっているはずです。
Avatar.Load(@"..\LV290\TransportX.Sample.LV290.dll");
Avatar.Locate(0, 0, 130, 0.2, 160, 0, 0, 0);
DirectionalLight.SetColor("#FFFFFF");
DirectionalLight.SetDirection(-1, -4, 2);
DirectionalLight.SetIntensity(100000);
Models.LoadList("Models.txt");
Background.Add("Background");
Chunks[0, 0].PutProp("Grass", 0, 0, 0);
Chunks[0, 1].PutProp("Grass", 0, 0, 0);
Chunks[0, 0].PutProp("BusStop", 125, 0, 150);
var fSpline = Chunks[0, 0].BeginSpline(110, 0.2, 150);
fSpline.Curves.Straight(50);
fSpline.PutProp(["Road2_Straight"], 0, 0, 0, 0, 1.2, 1.2);
var spline = fSpline.Build();
ここまでできたら、これまでの章と同様に bin\TransportX.Player.exe を実行し、ワールドを動作確認してみましょう。第4章で設置したバス停標柱の左側に、直線道路が出現したら成功です。

曲線道路を敷設する
直線道路の次は、その横に簡単な曲線道路を敷設してみましょう。
入力端子を設置する
先ほどと同様にスプラインの入力端子を設置するのですが、ひとつ注意点があります。 次のスクリプトを先ほど敷設した直線道路のスクリプトの下に記述してください。座標が(140,0.2,150)に変わっていること以外で、直線道路の時との違いが分かるでしょうか?
fSpline = Chunks[0, 0].BeginSpline(140, 0.2, 150);
正解は、var fSpline = ~ ではなく fSpline = ~ と、変数名(fSpline)の手前に var という句が付いていないことです。
理由を簡単に言えば、var 句はその名前の変数を初めて使うときのみ必要だからです。
fSpline という名前の変数は、先ほどの直線道路を敷設する時に初めて使ったため、その時は var 句をつける必要がありました。
しかし今回は既に直線道路の定義の際に fSpline 変数を使っており、2回目の登場ですので、var 句は不要なのです(付けるとエラーが発生します)。
少し厄介な仕様と感じるかもしれませんが、データを制作しながら少しずつ慣れていきましょう。
円曲線半径と長さを指定する
先ほどは直線のスプラインだったため fSpline.Curves.Straight 構文を使いましたが、今回は曲線道路のため異なる構文を使います。
定義中のスプライン fSpline を指定した半径の円曲線とするには、ワールドファイルへ次の構文を記述します。
fSpline.Curves.ByRadius(半径, 長さ);
半径には円曲線の半径を[m]単位で指定します。正の値を指定すれば右カーブに、負の値を指定すれば左カーブになります。長さにはスプラインをこの半径の円曲線とする長さを[m]単位で指定します。
ここでは半径100 m、長さ60 mの右カーブとしてみましょう。スクリプトは次のようになります。
fSpline.Curves.ByRadius(100, 60);
スプラインに沿ってプロップを連続設置する
直線道路の時と同様に、道路のプロップを設置しましょう。スクリプトは直線道路向けのものから変更ありません。
fSpline.PutProp(["Road2_Straight"], 0, 0, 0, 0, 1.2, 1.2);
スプラインを確定(敷設)する
最後は忘れずに確定するスクリプトを記述しましょう。
var spline = fSpline.Build();
動作確認
現時点でのワールドファイル全体は次のようになっているはずです。
Avatar.Load(@"..\LV290\TransportX.Sample.LV290.dll");
Avatar.Locate(0, 0, 130, 0.2, 160, 0, 0, 0);
DirectionalLight.SetColor("#FFFFFF");
DirectionalLight.SetDirection(-1, -4, 2);
DirectionalLight.SetIntensity(100000);
Models.LoadList("Models.txt");
Background.Add("Background");
Chunks[0, 0].PutProp("Grass", 0, 0, 0);
Chunks[0, 1].PutProp("Grass", 0, 0, 0);
Chunks[0, 0].PutProp("BusStop", 125, 0, 150);
var fSpline = Chunks[0, 0].BeginSpline(110, 0.2, 150);
fSpline.Curves.Straight(50);
fSpline.PutProp(["Road2_Straight"], 0, 0, 0, 0, 1.2, 1.2);
var spline = fSpline.Build();
fSpline = Chunks[0, 0].BeginSpline(140, 0.2, 150);
fSpline.Curves.ByRadius(100, 60);
fSpline.PutProp(["Road2_Straight"], 0, 0, 0, 0, 1.2, 1.2);
spline = fSpline.Build();
ここまでできたら、F5キーを押下して再読込してみましょう。今度はバス停標柱の右側に右カーブの道路が出現したら成功です。

終わりに
本章では最も基本的なスプラインとして直線道路、曲線道路を敷設しました。しかしスプラインの神髄はここからです。次章ではスプライン敷設の更なる応用を説明していきます。
付録
付録として、この章の完成データを配布しています。どうしても動かないときなどの参考にお使いください。