前回は、第2回に書上げたコード(第2回コード2)を、2014年11月5日付のAway3D最新ビルドで動くように手直しした(第3回コード1)。今回は、それにふたつ手を加えて仕上げよう。第1に、テクスチャが貼られた床を3次元空間に加える。そして第2に、マウスドラッグでカメラの視界が変えられるようにしたい。
テクスチャが貼られた床を加える
床は矩形の平面として3次元空間に加える。貼りつけるテクスチャは、ビーチボールと同じく「Away 3D TypeScript」サイトの作例「GitHub: StageGL Examples」にある素材から、floor_diffuse.jpgを使う。升目に敷石を並べたようなテクスチャだ(図1)。
矩形の平面もボールのように、別に定める関数(createPlane())でつくることにする。引数には矩形の幅と高さ、およびライトに加えて、ボールの下に置きたいので垂直位置を与えた。
平面をつくる関数(createPlane())は、つぎのように初期設定の関数(initialize())から呼び出す。床のテクスチャは、AssetLibrary.load()メソッドで読み込む。なお、View.sceneプロパティは、球体(sphere)と平面(plane)のふたつで参照するため、あらかじめローカル変数(scene)にとった。また、アニメーションで秒間60回の再描画をするため、ここであえてView.render()メソッドを呼び出すのは止めた。
平面をつくる関数(createPlane())は、以下のように定めた。用いるプレハブのクラスがPrimitivePlanePrefabであることを除けば、組立ては球体をつくる関数(createSphere())と同じだ。PrimitivePlanePrefab()コンストラクタには幅と高さを引数に渡す。そして、球と同じくPrefabBase.getNewObject()メソッドでオブジェクトを得る。そして、マテリアルとライトを定め、垂直位置を動かしたら、平面のオブジェクトを返している。
読み込んだテクスチャをLoaderEvent.RESOURCE_COMPLETEイベントのリスナー関数(onResourceComplete())で扱うことは変わらない。ただし、素材がビーチボールと石畳のふたつになったので、そのどちらが読込まれたのかを確かめなければならない。そのために、引数のイベントオブジェクトのLoaderEvent.urlプロパティを用いる。
LoaderEvent.urlプロパティは、読み込んだ素材のURLを文字列で示す。それを、あらかじめ変数(imageDiffuseとplaneDiffuse)に与えてあったURLと比べればよい。リスナー関数(onResourceComplete())は大幅に書き替えることになるため、つぎに関数本体すべてを抜書きした。LoaderEvent.assetsプロパティは素材の配列なので、for文ですべてを取り出す。そして、switch文でLoaderEvent.urlプロパティがどちらのURLかを確かめたうえで、そのマテリアルのテクスチャ(MaterialBase.textureプロパティ)に素材を定めた。
これらの手が加えられたJavaScriptコードを以下にまとめた(コード1)。これで、3次元空間の球体の下に平面が置かれ、石畳のテクスチャが与えられる(図2)。床にはとくにアニメーションを定めていないので動かない。
コントローラでカメラをパンやチルトさせる
3次元の見せ方はオブジェクトそのものを動かすだけでなく、カメラの位置や向きで視界を変えることによっても表せる。そうしたカメラワークに用いるのがコントローラだ。HoverControllerオブジェクトは、カメラをパンやチルトできる。HoverController()コンストラクタの引数にはカメラのオブジェクトを与える。
HoverControllerクラスのプロパティは、つぎの表1に掲げた。そして、プロパティHoverController.panAngleやHoverController.tiltAngleでパンあるいはチルトを定めると、カメラは直ちにその角度に切り替わるのではなく、ステップ(デフォルトは8)に分けてトゥイーンされる。
表1 HoverControllerクラスに備わる基本的なプロパティ
HoverControllerクラスのプロパティ | プロパティの値 |
distance | カメラと撮影対象との距離で、デフォルト値は1000 |
panAngle | カメラがy軸を中心に回る度数の角度で、デフォルト値は0 |
tiltAngle | カメラの仰角を示す度数で、デフォルト値は90 |
maxTiltAngle minTiltAngle | チルトできる角度の範囲の最大度数(デフォルト値90)と最小度数(デフォルト値-90) |
HoverControllerクラスでコントローラのオブジェクトをつくる関数(setupCameraController())は、つぎのように定める。引数にはカメラオブジェクトのほか、表1のHoverControllerクラスのプロパティに定める値も加えた。
カメラのコントローラ(cameraController)をつくる関数(setupCameraController())は、つぎのように初期設定の関数(initialize())から呼び出した。新たにつくられるHoverControllerオブジェクトに、引数の5つのプロパティ値を与えて返す。
前述のとおり、HoverControllerオブジェクトでパンやチルトをするとトゥイーンアニメーションで表される。そこで、球体も回すのは止め、カメラワークだけを確かめられるようにしよう。したがって、RequestAnimationFrame()コンストラクタに与える引数のコールバックは、View.render()メソッドのみ呼出す。また、関数名(render())もそれに合わせて変えた。
これで、HoverControllerオブジェクトによりカメラにパンとチルトが与えられた。しかも、定められた角度にトゥイーンアニメーションする(図3)。ここまでをまとめたのが、以下のコード2だ。
マウスドラッグでカメラをパンやチルトさせる
お題の仕上げとして、カメラのパンやチルトをマウスドラッグで変えられるようにしよう。もっとも、マウス操作の扱いは、JavaScriptに組込み済みのイベントハンドラ(コールバック関数)を使って行う。Away3Dのプロパティは、パンとチルトの角度を変えるだけだ。マウスイベントについては、(1)マウスボタンを押す、(2)マウスポインタを動かす、(3)マウスボタンを放すという3つの操作のコールバック関数を以下のように定める。
第1に、マウスボタンを押すdocument.onmousedownイベントのコールバック(startDrag())は、ふたつのことを行う。まず、ボタンを押したときのマウスポインタ座標とカメラのパンおよびチルト角をそれぞれ変数(lastMouseXとlastMouseY、lastPanAngle、lastTiltAngle)に覚えさせる。つぎに、マウスを動かしたときとボタンを放したときのハンドラ(drag()とstopDrag())を定めている。
第2に、マウスを動かすdocument.onmousemoveイベントのコールバック(drag())は、今のマウスポインタの位置とボタンを押したときの座標の差にもとづいて、カメラのパン(HoverController.panAngleプロパティ)とチルト(HoverController.tiltAngleプロパティ)の角度を変える。なお、座標の差に乗じている小数値(0.5と0.3)は調整係数だ。
第3に、マウスボタンを放すdocument.onmouseupイベントのコールバック(stopDrag())は、マウスを動かすときとボタンを放したときのコールバックをイベントハンドラから除く(nullを与える)。これで、初めの状態に戻ることになる。
3つのマウスイベントのハンドラを組み入れると、ドラッグでカメラがパンあるいはチルトできるようになる(サンプル1)。試してみるとわかるのは、水平方向のパンは何周でも回せるのに対して、垂直方向のチルトは0度から90度までしか動かせないことだ。この範囲が、HoverController.minTiltAngleとHoverController.maxTiltAngleプロパティで決められている。でき上がりをコード3にまとめた。