サイトトップ

Director Flash 書籍 業務内容 プロフィール

ActionScript 3.0 for 3D

□02 遠近法の投影 − PerspectiveProjectionクラス

コンピュータグラフィックス(CG)の3次元座標空間の描画は、最終的に2次元平面のスクリーンに対して行われます。したがって、3次元空間で計算した座標に遠近法の効果を加えたうえで、スクリーンに投影するという処理になります。この処理を英語で"perspective projection"と呼び,「遠近法投影」とか「透視投影」と訳されます。PerspectiveProjectionクラスは、タイムライン(DisplayObjectContainerインスタンス)上の(DisplayObject)インスタンスに対して、この遠近法の投影を行います。


02-01 [ブロパティ]インスペクタを使った3次元の操作
遠近法を操作するには、2次元平面とは異なった概念や設定について知る必要があります。Flash CS4 Professionalは、スクリプトを使わなくても、[プロパティ]インスペクタで遠近法の基本的な操作ができます。まずは、設定とその効果を、[プロパティ]インスペクタでイメージとしてつかんでおきましょう。

第1に、[プロパティ]インスペクタでは、奥行きを定めるz座標が設定できます。Flashのz座標は、奥に向かって値が増えます。[3D位置とビュー]セクションで[Z]に正の値を設定すると(図02-001左図)、3次元空間の奥行きを表現するためインスタンスが小さくなります。また、同時に位置もステージ中央に向かって移動します(図02-001右図)。

図02-001■インスタンスのz座標と遠近法表現の変化
 
[プロパティ]インスペクタの[3D位置とビュー]セクションで[Z]に正の値を設定すると(左図)、インスタンスが小さくなり、位置はステージ中央に向かって移動する(右図)。

遠近法では、奥行き(z座標)が遠いものほど小さく、また距離も縮めて表現します。そして、かぎりなく遠ざかるにつれ、ある1点に向かって小さくなり、やがて見えなくなります。この点を「消失点」と呼びます(図02-002左図)。

[消失点]は、[プロパティ]インスペクタの[3D位置とビュー]セクションで設定できます(図02-002右図)。その位置は、デフォルトではステージ中央です。なお、[プロパティ]インスペクタで[消失点]を変えるには、予めタイムライン上のMovieClipインスタンスを選んでおかなければなりません。

図02-002■[プロパティ]インスペクタで消失点を設定する
 
奥行きが遠くなるにつれ、インスタンスの表示は消失点に向けて小さくなる。[消失点]は、[プロパティ]インスペクタの[3D位置とビュー]セクションで設定できる。

Tips 02-001■[消失点]はドキュメントプロパティ
[プロパティ]インスペクタの[3D位置とビュー]セクションで設定する[消失点]は、メインタイムラインに対するドキュメントプロパティです。その設定は、ステージを頂点としたタイムラインの階層に配置されるすべての3次元の操作が加えられたインスタンスに適用されます([ヘルプ]の[Flash CS4 Professionalユーザガイド] > [アートワークの作成および編集] > [3Dグラフィック]参照)。

[プロパティ]インスペクタの[3D位置とビュー]セクションには、もうひとつ[遠近の角度]というプロパティがあります(図02-003中段右図)。これは、一般には「画角」とか「視野角」とよばれる角度です。1から179までの数値で指定します。値が増えるほど、遠近の差が大きく表現されます(図02-003左列の図)。

図02-003■[遠近の角度]による表現の違い

遠近の角度: 20
   

遠近の角度: 55
 

遠近の角度: 100
   
[遠近の角度]の値を増やすと、遠近の差が大きく表現される。なお、ステージサイズは、240×180ピクセル。

Word 02-001■画角(視野角)
画角とか視野角ということばは、カメラで使われます。たとえば、広角レンズというのは、この角度が大きく、同じ距離でも広い範囲が撮影できます。他方、画角の小さい望遠レンズは、写せる範囲は狭くなるものの、遠くのものを近寄せます。それだけでなく、画角は遠近感にも影響を及ぼします。

ふたつのものの奥行きの距離は、レンズが広角になるほど、離れて見えるようになります。これを、遠近感の「誇張効果」といいます。逆に、望遠レンズでは、肉眼よりも距離が近づいて見えます。これが「圧縮効果」です。一眼レフカメラでは、撮影する範囲や距離だけでなく、このような遠近感の効果も考えてレンズを選びます。

[*筆者用参考] 「焦点距離の違いによる遠近感の変化」、デジタル一眼レフカメラ “α”:アルファ「背景と遠近感」。


[イラスト] [遠近の角度]が広いほど、遠近の差が大きく見える。

Maniac! 02-001■[遠近の角度]の値の範囲
[ヘルプ]([Flash CS4 Professionalユーザガイド] > [アートワークの作成および編集] > [3Dグラフィック])の「遠近の角度の調整」の項には、「値の範囲は1〜180度です」とされています。しかし、[プロパティ]インスペクタに180を入力すると、179が設定されます。


02-02 [ヘルプ]の遠近投影法のサンプル
消失点をスクリプトで制御するには、PerspectiveProjectionクラスPerspectiveProjection.projectionCenterプロパティを用います。[ヘルプ]([ActionScript 3.0のプログラミング] > [3次元(3D)での操作])の「例:遠近法投影」には、サンプルとなるクラスが紹介されています(図02-004)。

図02-004■消失点をコントロールするサンプルのクラスProjectionDragger
[ヘルプ]の「例:遠近法投影」の項には、消失点をコントロールするクラスのサンプルが掲載されている。

このクラスProjectionDraggerを試すには、[ヘルプ]からスクリプトをコピーし、新規の[ActionScript(AS)ファイル]にペーストします。そして、ファイルはクラス名と同じProjectionDragger.asとして保存します。つぎに、新規[Flashファイル(AS 3.0)]をActionScriptファイルと同じ階層に保存します。そのうえで、Flash(ムービー)ファイルの[プロパティ]インスペクタで、[ドキュメント]の[クラス]に、ドキュメントクラスとしてProjectionDraggerを入力してください(図02-005)。

図02-005■[プロパティ]インスペクタでドキュメントクラスを設定
[プロパティ]インスペクタの[ドキュメント]の設定で、[クラス]にドキュメントクラスとしてProjectionDraggerを入力

これで準備は済みました。[ムービープレビュー]を確かめると、手前から奥に向けて複数の矩形が重ねて描かれます。そして、消失点を示すインスタンスのドラッグによって、3次元空間における奥行きの向きが変化します(図02-006)。


図02-006■消失点がドラッグで移動できる
消失点のインスタンスをドラッグすると、3次元の奥行きを示す向きが変わる。

このサンプルのクラスProjectionDraggerは、100行近いスクリプトです。けれど、消失点そのものは、数行のメソッドふたつで操作しています。さらにいえば、消失点をコントロールするのは、PerspectiveProjection.projectionCenterプロパティを設定する1行のステートメントです(図02-007)。

図02-007■消失点をコントロールするのはわずか1行のステートメント
消失点をコントロールするのはふたつのメソッドで、しかも具体的にはprojectionCenterプロパティを設定する1行のステートメント。

Tips 02-002■Flash CS4 Professionalの[ヘルプ]のサンプルファイル
ProjectionDraggerクラスなど[ヘルプ]の[ActionScript 3.0のプログラミング]のサンプルファイルは、Adobeサイトからダウンロードできます。ただし、本稿執筆時においては日本のサイトには掲載されていません。米国のFlash Developer Center「Flash CS4 Professional samples」(<http://www.adobe.com/devnet/flash/?view=samples>)からダウンロードしてください。


02-03 消失点の操作 − PerspectiveProjection.projectionCenterプロパティ
スクリプトでは、消失点をPerspectiveProjection.projectionCenterプロパティにより操作します。PerspectiveProjectionオブジェクトは、Transformクラスのプロパティです。Transformオブジェクトは、DisplayObjectインスタンスに適用されるカラーや座標など、さまざまな変換のためのオブジェクトを備えています。そして、DisplayObjectインスタンスに設定されているTransformオブジェクトは、DisplayObject.transformプロパティで参照できます(シンタックス02-001)。

ひとつ注意しなければならないのは、デフォルトではメインタイムライン以外はTransform.perspectiveProjectionオブジェクトをもたないことです。MovieClipインスタンスを参照すると、transform.perspectiveProjectionの値はnullになります。したがって、消失点の設定は、基本的にはメインタイムラインに対して行います。

シンタックス02-001■消失点を操作するためのプロパティ
DisplayObject.transformプロパティ
文法 transform:Transform
プロパティ値 インスタンスに適用されるTransformオブジェクトの参照。Transformオブジェクトは、DisplayObjectインスタンスに対して加えられるさまざまな変換のオブジェクトをもつ。それらのオブジェクトには、カラー変換のColorTransform、座標変換は2次元がMatrixで3次元はMatrix3D、遠近法による投影のPerspectiveProjectionなどがある。
Transform.perspectiveProjectionプロパティ
文法 perspectiveProjection:PerspectiveProjection
プロパティ値 3次元座標の操作が加えられたインスタンスに適用される遠近法の設定をもったPerspectiveProjectionオブジェクトの参照。その効果は、すべての子のインスタンスにも加えられる。メインタイムライン以外の初期値はnull
PerspectiveProjection.projectionCenterプロパティ
文法 projectionCenter:Point
プロパティ値 遠近法の消失点を示すPointインスタンス。ステージ左上隅の基準点(0, 0)を起点とした位置座標を示す。デフォルトのPointインスタンスは、ステージ中央の位置座標をもつ。

たとえば、消失点をステージ右下隅に設定するには、以下のようなフレームアクションでPerspectiveProjection.projectionCenterプロパティにステージ右下隅の座標のPointインスタンスを指定します(図02-008)。PerspectiveProjectionオブジェクトは、DisplayObject.transformプロパティから参照します。なお、スクリプトをメインタイムラインに記述するときは、rootの参照は省略できます。

root.transform.perspectiveProjection.projectionCenter =
new Point(stage.stageWidth, stage.stageHeight);

図02-008■消失点をステージ右下隅に設定する

DisplayObject.rootプロパティでメインタイムラインを参照して、PerspectiveProjection.projectionCenterプロパティにステージ右下隅の座標のPointインスタンスを設定する。

それでは、[ヘルプ]の「例:遠近法投影」に掲げられたサンプルと同じように、消失点をドラッグして動かせるようにしてみましょう。ただし、手前から奥に向けて並べる3次元空間のインスタンスや消失点のインスタンスは、MovieClipシンボルとしてつくり、予めメインタイムラインに置いておくことにします。

つぎのスクリプト02-001を消失点のMovieClipシンボルのフレームアクションとして設定すると、そのインスタンスがドラッグでき、消失点はその基準点の位置に移動します(図02-009)。

スクリプト02-001■インスタンスをドラッグするとその位置に消失点が移動する
    // フレームアクション
    // MovieClip: ドラッグする消失点のシンボル
  1. var myPerspectiveProjection:PerspectiveProjection = root.transform.perspectiveProjection;
  2. addEventListener(MouseEvent.MOUSE_DOWN, moveProjectionCenter);
  3. function moveProjectionCenter(eventObject:Event):void {
  4.   startDrag(true);
  5.   addEventListener(Event.ENTER_FRAME, setProjectionCenter);
  6.   stage.addEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  7. }
  8. function setProjectionCenter(eventObject:Event):void {
  9.   myPerspectiveProjection.projectionCenter = new Point(x, y);
  10. }
  11. function stopProjectionCenter(eventObject:Event):void {
  12.   stopDrag();
  13.   removeEventListener(Event.ENTER_FRAME, setProjectionCenter);
  14.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  15. }

図02-009■消失点となるMovieClipシンボルにフレームアクションを設定

インスタンスがドラッグでき、消失点はその基準点に移動する。

スクリプトの組立てとして、まず消失点のインスタンスに対するInteractiveObject.mouseDownイベント(定数MouseEvent.MOUSE_DOWN)に登録したリスナー関数moveProjectionCenter()(スクリプト3〜7行目)で、インスタンスのドラッグと消失点の移動の基本的な設定を行います。

インスタンスのドラッグを始めたり止めたりするのは、Sprite.startDrag()Sprite.stopDrag()メソッドです(シンタックス02-002)。消失点はドラッグしている間繰返し設定し直す必要がありますので,DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)に登録したリスナー関数setProjectionCenter()で行います(スクリプト8〜10行目)。

シンタックス02-002■Sprite.startDrag()とSprite.stopDrag()メソッド
Sprite.startDrag()メソッド
文法 startDrag(lockCenter:Boolean = false, bounds:Rectangle = null):void
概要 Spriteインスタンスをマウスポインタの位置に追随させる。このメソッドで動かせるのは、ひとつのインスタンスのみ。他のインスタンスに対してこのメソッドを呼出せば、すでに追随していたインスタンスの動きは止まる。
引数

lockCenter:Boolean = false ― インスタンスの基準点をマウスポインタの座標に合わせる(true)かどうかを示すブール(論理)値。デフォルトはfalseで、このメソッドを呼出したときのインスタンスとマウスポインタの座標との相対位置を保つ。

bounds:Rectangle = null ― インスタンスの動く矩形の範囲を制限するためのRectangleインスタンス。親インスタンスの座標で指定する。デフォルト値はnull(指定なし)。

戻り値 なし。
Sprite.stopDrag()メソッド
文法 stopDrag():void
概要 Sprite.startDrag()メソッドでマウスポインタを追随していたインスタンスの動きを止める。
引数 なし。
戻り値 なし。

消失点の設定の仕方は、すでにご説明したとおりです。インスタンスのxy座標値をPoint()コンストラクタに渡して、PerspectiveProjection.projectionCenterプロパティに設定しています。なお、メインタイムラインのDisplayObject.transformプロパティがもつPerspectiveProjectionオブジェクトの参照(Transform.perspectiveProjectionプロパティの値)は何度もアクセスしますので、予め変数(myPerspectiveProjection)に取っています(スクリプト1行目)。

[イラスト] 何度もアクセスするオブジェクトは、予め変数に取っておくとお得。

関数moveProjectionCenter()では、マウスボタンが放されたときドラッグを止めるステートメント(スクリプト6行目)も加えてあります。InteractiveObject.mouseDownイベント(定数MouseEvent.MOUSE_UP)に登録した関数stopProjectionCenter()がその処理を受持ちます。ただし、マウスボタンを放したのがたまたまインスタンス上でないときもドラッグは止めたいので、EventDispatcher.addEventListener()メソッドはStageオブジェクト(DisplayObject.stageプロパティ)をターゲットとして(参照して)呼出しています。

関数stopProjectionCenter()(スクリプト11〜15行目)は、Sprite.stopDrag()メソッドでドラッグを止めるとともに、EventDispatcher.removeEventListener()メソッドを呼出して関数setProjectionCenter()とstopProjectionCenterをイベントリスナーから削除します(シンタックス02-003)。

シンタックス02-003■EventDispatcher.removeEventListener()メソッド
EventDispatcher.removeEventListener()メソッド
文法 removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
概要 インスタンスが扱うイベントに登録されたリスナー関数を削除する。
引数

type:String − リスナー関数を削除するイベントとなる文字列。通常、イベント定数で指定する。

listener:Function − イベントリスナーから削除する関数。

useCapture:Boolean = false − 登録されているリスナーがイベントを、そのフローの3つのフェーズのうち、キャプチャフェーズで受取る(true)かどうかを示すブール(論理)値。デフォルトはfalseで、ターゲットおよびバブリングフェーズでイベントを受取る。イベントリスナーを登録したときと同じ指定にする必要がある。

戻り値 なし。

02-04 視野角の操作 − PerspectiveProjection.fieldOfViewプロパティ
[プロパティ]インスペクタの[3D位置とビュー]セクションの[遠近の角度](前述02-01「[ブロパティ]インスペクタを使った3次元の操作」)は、PerspectiveProjection.fieldOfViewプロパティで操作できます(シンタックス02-004)。PerspectiveProjectionオブジェクトの参照は、前節02-03のPerspectiveProjection.projectionCenterプロパティと同じく、インスタンスのDisplayObject.transformプロパティからTransform.perspectiveProjectionプロパティを経て取得します。

シンタックス02-004■視野角を操作するためのプロパティ
PerspectiveProjection.fieldOfViewプロパティ
文法 fieldOfView:Number
プロパティ値 遠近法の視野角を定める度数値。0度より大きく、180度よりも小さい値を取る。z座標値の異なるオブジェクトの奥行きが、視野角を狭く(小さく)すると互いに近づいて表示され、拡げる(大きくする)ほど隔たって見える(前述Word 02-001「画角(視野角)」参照)。

Maniac! 02-002■PerspectiveProjection.fieldOfViewプロパティに0を設定したときのエラー
PerspectiveProjection.fieldOfViewプロパティに、0を指定することはできません。設定すれば、つぎのようなエラーが示されます。

ArgumentError: Error #2182: fieldOfView値が無効です。1以上180未満の値を指定する必要があります。

しかし、シンタックス02-004に述べたとおり、0より大きければ1未満の値も設定できます。ただし、[プロパティ]インスペクタの[遠近の角度]は1より小さい値を入れると、1.0に直されます。

なお、英語の"field of view"は、通常「視野」と訳されます。したがって、本書ではPerspectiveProjection.fieldOfViewプロパティの表す角度を、[遠近の角度]ではなく視野角と呼ぶことにします。

たとえば、フレームアクションで視野角を100度に設定するには、メインタイムラインとなるDisplayObject.rootプロパティを参照したうえで、PerspectiveProjection.fieldOfViewプロパティをつぎのように設定します。下図02-010は赤い+の印が消失点を示し、異なった視野角による表示結果を比べています。

root.transform.perspectiveProjection.fieldOfView = 100;   // 視野角を設定

図02-010■PerspectiveProjection.fieldOfViewプロパティの設定値による表現の違い

視野角: 100度

視野角: 55度

視野角: 20度
遠近の隔たりは、視野角が広いほど大きく、狭いと小さく表現される。赤い+印は消失点を示す。なお、ステージサイズは、240×180ピクセル。

では、視野角の操作を、前掲スクリプト02-001に加えましょう。消失点のインスタンス以外のステージ上で、[shift]キーを押しながら上下にドラッグしたときに、視野角の値を増減します。そのために書き加えるスクリプトは、つぎのように構成してみます。

// [1]ステージ上でマウスボタンを押したときのリスナー関数登録
stage.addEventListener(MouseEvent.MOUSE_DOWN, changeFieldOfView);
// [2]ステージ上でマウスボタンを押したら
function changeFieldOfView(eventObject:MouseEvent):void {
  // [shift]キーが押されていたら
    // [2-1]ドラッグしたときの視野角増減の処理をリスナー関数として登録
    stage.addEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
    // [2-2]マウスボタンを放したときの終了の処理をリスナー関数として登録
    stage.addEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
}
// [3]マウスポインタの動きに合わせて視野角を増減
function setFieldOfView(eventObject:MouseEvent):void {
  // マウスポインタの垂直位置に応じた視野角の変更
}
// [4]イベントリスナーを削除して処理を終える
function stopFieldOfView(eventObject:MouseEvent):void {
  // 視野角を操作するイベントリスナーの削除
}

[1]まず、ステージ上でマウスボタンをクリックしたときに、視野角の変更に関わる処理を始めます。そのためのリスナー関数changeFieldOfView()を、Stageオブジェクトに対するInteractiveObject.mouseDownイベント(定数MouseEvent.MOUSE_DOWN)に登録します。

[2]つぎに、リスナー関数changeFieldOfView()は、マウスクリックされたとき同時に[shift]キーが押されていた場合に、つぎのふたつの処理を行います。

[2-1]第1に、垂直方向にマウスをドラッグしたら、視野角をその動きに合わせて増減します。その処理は関数setFieldOfView()として定義し、StageオブジェクトのInteractiveObject.mouseMoveイベント(定数MouseEvent.MOUSE_MOVE)にリスナーとして登録します。

[2-2]第2に、マウスボタンを放したとき、視野角の変更に関わる処理を終えます。その処理は関数stopFieldOfView()として定義し、StageオブジェクトのInteractiveObject.mouseUpイベント(定数MouseEvent.MOUSE_UP)にリスナーとして登録します。

[3]関数setFieldOfView()を定義し、ドラッグしたマウスポインタの垂直位置に応じて視野角を増減します。

[4]関数stopFieldOfView()を定義し、Stageオブジェクトに加えたリスナー関数setFieldOfView()と自身の登録を削除して一連の処理を終えます。

以上の視野角の操作についての処理を、実際に前掲スクリプト02-001に組込んだのが、つぎのスクリプト02-002です。

スクリプト02-002■消失点の操作に加えて[shift]+ドラッグで視野角を変更する
    // フレームアクション
    // MovieClip: ドラッグする消失点のシンボル
  1. var myPerspectiveProjection:PerspectiveProjection = root.transform.perspectiveProjection;
  2. var myFieldOfView:Number = myPerspectiveProjection.fieldOfView;
  3. var startY:Number;
  4. var sensitivity:Number = 0.5;
  5. addEventListener(MouseEvent.MOUSE_DOWN, moveProjectionCenter);
  6. stage.addEventListener(MouseEvent.MOUSE_DOWN, changeFieldOfView);
  7. function moveProjectionCenter(eventObject:Event):void {
  8.   startDrag(true);
  9.   addEventListener(Event.ENTER_FRAME, setProjectionCenter);
  10.   stage.addEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  11.   eventObject.stopPropagation();
  12. }
  13. function setProjectionCenter(eventObject:Event):void {
  14.   myPerspectiveProjection.projectionCenter = new Point(x, y);
  15. }
  16. function stopProjectionCenter(eventObject:Event):void {
  17.   stopDrag();
  18.   removeEventListener(Event.ENTER_FRAME, setProjectionCenter);
  19.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  20. }
  21. function changeFieldOfView(eventObject:MouseEvent):void {
  22.   if (eventObject.shiftKey) {
  23.     startY = eventObject.stageY;
  24.     stage.addEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
  25.     stage.addEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  26.   }
  27. }
  28. function setFieldOfView(eventObject:MouseEvent):void {
  29.   var newFieldOfView:Number = myFieldOfView + (eventObject.stageY - startY) * sensitivity;
  30.   myPerspectiveProjection.fieldOfView = Math.max(Math.min(179, newFieldOfView), 1);
  31. }
  32. function stopFieldOfView(eventObject:MouseEvent):void {
  33.   myFieldOfView = myPerspectiveProjection.fieldOfView;
  34.   stage.removeEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
  35.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  36. }

それでは、追加したステートメントを、簡単に見ていきましょう。

まず、スクリプト第2〜4行目は、処理に用いる変数の宣言とその初期値の設定です。[shift]+ドラッグで視野角の変更を始めたときの視野角と垂直座標の値は、ふたつの変数(myFieldOfViewとstartY)に記録しておきます。また、ドラッグの大きさに対する視野角変更の度合いを、調整係数として変数(sensitivity)に設定します。

    // 変数宣言とその初期設定
  1. var myFieldOfView:Number = myPerspectiveProjection.fieldOfView;
  2. var startY:Number;
  3. var sensitivity:Number = 0.5;

スクリプト第6行目は、前述[1]の視野角変更に関わる処理の設定です。ステージ上のどこでマウスボタンをクリックしてもよいので、Stageオブジェクト(DisplayObject.stageプロパティ)に対して、InteractiveObject.mouseDownイベント(定数MouseEvent.MOUSE_DOWN)にリスナー関数changeFieldOfView()を登録します。

    // [1]ステージ上でマウスボタンを押したときのリスナー関数登録
  1. stage.addEventListener(MouseEvent.MOUSE_DOWN, changeFieldOfView);

スクリプト第21〜27行目が、前述[2]のマウスクリックされたときに処理を行う関数changeFieldOfView()の定義です。リスナー関数とてしてMouseEventオブジェクトを引数として受取って、そのMouseEvent.shiftKeyプロパティによりクリック時に[shift]キーが押されていたかどうかを調べます(シンタックス02-005)。

[shift]キーが押されていたら(スクリプト第22行目)、ふたつのイベントリスナーを登録します。なお、スクリプト第23行目は、ドラッグを始めた垂直座標の記録で、関数setFieldOfView()が視野角増減の幅を計算するのに用いられます。

スクリプト第24行目は前述[2-1]に対応するステートメントです。ドラッグ時の視野角増減の処理を関数setFieldOfView()に定義するので([3])、StageオブジェクトのInteractiveObject.mouseMoveイベント(定数MouseEvent.MOUSE_MOVE)にリスナーとして登録します。

スクリプト第25行目は前述[2-2]に対応するステートメントです。マウスボタンを放したとき処理を終える関数がstopFieldOfView)0としてされるので([4])、StageオブジェクトのInteractiveObject.mouseUpイベント(定数MouseEvent.MOUSE_UP)にリスナー登録します。

    // [2]ステージ上でマウスボタンを押したら
  1. function changeFieldOfView(eventObject:MouseEvent):void {
  2.   if (eventObject.shiftKey) {   // [shift]キーが押されていたら
  3.     startY = eventObject.stageY;   // ドラッグ開始時の垂直マウス座標を記録
        // [2-1]ドラッグしたときの視野角増減の処理をリスナー関数として登録
  1.     stage.addEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
        // [2-2]マウスボタンを放したときの終了の処理をリスナー関数として登録
  1.     stage.addEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  2.   }
  3. }
シンタックス02-005■MouseEventオブジェクトの基本的なプロパティ
プロパティ プロパティ値
データ型
MouseEvent.altKey [Alt](Windows)または[option](Macintosh)キーが押されているとtrue、押されていなかったらfalse
Boolean
MouseEvent.ctrlKey [Ctrl](Windows)または[command](Macintosh)キーが押されているとtrue、押されていなかったらfalse
Boolean
MouseEvent.shiftKey [shift]キーが押されているとtrue、押されていなかったらfalse
Boolean
MouseEvent.localX マウスイベントの発生した位置を示す、イベントのターゲットとなったインスタンス(Event.targetプロパティ値)から見た水平座標値。
Number
MouseEvent.localY マウスイベントの発生した位置を示す、イベントのターゲットとなったインスタンス(Event.targetプロパティ値)から見た垂直座標値。
Number
MouseEvent.stageX [読取り専用] マウスイベントの発生した位置を示す、Stageオブジェクト(DisplayObject.stageプロパティ値)から見たグローバルな水平座標値。
Number
MouseEvent.stageY [読取り専用] マウスイベントの発生した位置を示す、Stageオブジェクト(DisplayObject.stageプロパティ値)から見たグローバルな垂直座標値。
Number

スクリプト第28〜31行目は、前述[3]のマウスポインタの動きに合わせて視野角を増減する関数setFieldOfView()の定義です。

[shift]+ドラッグを始めたときのマウスポインタの垂直座標と視野角の値は、それぞれ変数startY(スクリプト第23行目)とmyFieldOfView(スクリプト第2行目)に保持してあります。現在のマウスポインタの垂直座標はMouseEvent.stageYプロパティ(シンタックス02-005)で得られますので、マウスドラッグの垂直方向の大きさを求め、調整係数を乗じたうえで、現行の視野角に加算し、新たな視野角の値とします(スクリプト第29行目)。

ただし、視野角は0度より大きく、180度未満でなければなりません。そのため、最小値を1度、最大値を179度に制限して(Tips 02-003)、PerspectiveProjection.fieldOfViewプロパティに設定しています(スクリプト第30行目)。

    // [3]マウスポインタの動きに合わせて視野角を増減
  1. function setFieldOfView(eventObject:MouseEvent):void {
  2.   var newFieldOfView:Number = myFieldOfView + (eventObject.stageY - startY) * sensitivity;
  3.   myPerspectiveProjection.fieldOfView = Math.max(Math.min(179, newFieldOfView), 1);
  4. }

Tips 02-003■Math.max()とMath.min()メソッドで値の最大値と最小値を限定する
ある値に最大値と最小値を定めて、取りうる値の範囲を限定したいことがあります。本文のスクリプト02-002の第30行目は、視野角の範囲を1〜179度とするため、変数(newFieldOfView)値が1より小さければ1、179より大きいときは179に、視野角PerspectiveProjectionプロパティの値を設定しています。

このとき用いたメソッドMath.max()は引数のうちの最大値を、Math.min()は引数の中から最小値を返します(シンタックス02-006)。すると、つぎの式は、変数(newFieldOfView)値が179より小さければその値のまま、大きかったら値を179に直すことになります。

newFieldOfView = Math.min(179, newFieldOfView)

同じように、変数値が1より大きければその値のまま、小さいときは値を1に直すのがつぎの式です。

newFieldOfView = Math.max(newFieldOfView, 1)

これらふたつの式を入れ子にしてひとつにすると、本文のスクリプト02-002の第30行目の式の右辺になります。つまり、値の最大値と最小値を限定するには、一般につぎのような式を書けばよいでしょう。

Math.max(Math.min(最大値, 値), 最小値)

シンタックス02-006■最大値・最小値を調べるMath.max()とMath.min()
Math.max()メソッド
文法 Math.max(value1:Number, value2:Number, ... rest):Number
概要 [静的] 指定された数値のうちの最大値を返す。
引数

value1:Number ― 大きさを比較する数値。

value2:Number ― 大きさを比較する数値。

... rest ― 任意に加えられる3つ目以降の大きさを比較する数値。

戻り値 指定された引数のうちの最大の数値。
Math.min()メソッド
文法 Math.min(value1:Number, value2:Number, ... rest):Number
概要 [静的] 指定された数値のうちの最小値を返す。
引数

value1:Number ― 大きさを比較する数値。

value2:Number ― 大きさを比較する数値。

... rest ― 任意に加えられる3つ目以降の大きさを比較する数値。

戻り値 指定された引数のうちの最小の数値。

Maniac! 02-003■条件判定で最大値と最小値を限定する
値の最大値と最小値を限定するには、ifステートメントにより条件で判定してもよいでしょう。本文の前掲スクリプト02-002の第30行目の式は、ifステートメントを用いてつぎのように書替えられます。

if (newFieldOfView > 179) {
  newFieldOfView = 179;
} else if (newFieldOfView < 1) {
  newFieldOfView = 1;
}
myPerspectiveProjection.fieldOfView = newFieldOfView;

見た目の行数が増えるほかには、この処理でとくに問題はありません。それどころか、実はifステートメントを使った方が、処理は速くなると期待されます。Mathクラスの数学演算より、true(真)とfalse(偽)の2値で扱えるifステートメントの方が、デジタルの0/1で演算するコンピュータにとって得意な処理だからです。

また、01-01「イベントリスナーと条件判定」で述べたとおり、if条件がtrueと評価されれば「勝抜け」で、後のelse ifステートメントは処理されません。その場合は処理が減りますので、その分も速められる可能性があるのです。

なお、条件を判定する処理としては、条件(三項)演算子(?:)を使うことも考えられます。こちらの方が、記述は短くなります。上記と同じ前掲スクリプト02-002第30行目のステートメントは、つぎのように書けます。

myPerspectiveProjection.fieldOfView =
  (newFieldOfView > 179) ? 179 :
  (newFieldOfView < 1) ? 1 : newFieldOfView;

ただ、処理速度にあまり重きを置かないスクリプト02-002のような場合は、慣れればスクリプト第30行目の書き方が見やすく感じられます(前掲Tips 02-003参照)。

[*筆者用参考] [ActionScript 3.0 のプログラミング]「条件演算子

最後のスクリプト第32〜36行目は、前述[4]のイベントリスナーを削除して処理を終える関数stopFieldOfView()の定義です。スクリプト第33行目は、最終的に設定された視野角を更新して変数(myFieldOfView)に記録します。スクリプト第34〜35行目は、Stageオブジェクトに対してイベントリスナーを登録した[2]の処理(スクリプト第24〜25行目)に対して、その削除を行うステートメントです。これで、マウスクリックを待つ(スクリプト第5〜6行目)初めの状態に戻ります。

    // [3]マウスポインタの動きに合わせて視野角を増減
  1. function stopFieldOfView(eventObject:MouseEvent):void {
  2.   myFieldOfView = myPerspectiveProjection.fieldOfView;
  3.   stage.removeEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
  4.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  5. }

[ムービープレビュー]で動作を確かめます。消失点のインスタンス上以外で、[shift]キーを押しながら上方向にドラッグすると、視野角は狭まって遠近感が縮まります。下方向にドラッグすれば、逆に視野角が広がって遠近の差が大きくなります(図02-011)。

図02-011■消失点以外で上下に[shift]+ドラッグすると視野角が変わる
 


02-05 焦点距離の操作 − PerspectiveProjection.focalLengthプロパティ
遠近感の差は、プロパティPerspectiveProjection.fieldOfViewだけでなく、PerspectiveProjection.focalLengthで変えることもできます。PerspectiveProjectionオブジェクトの参照は、前節02-04のPerspectiveProjection.fieldOfViewプロパティと同じく、インスタンスのDisplayObject.transformプロパティからTransform.perspectiveProjectionプロパティを経て取得します。

たとえば、フレームアクションで焦点距離を150に設定するには、メインタイムラインとなるDisplayObject.rootプロパティを参照したうえで、PerspectiveProjection.focalLengthプロパティをつぎのように設定します(シンタックス02-007)。下図02-012は赤い+の印が消失点を示し、焦点距離1000の場合と表示結果を比べています。

root.transform.perspectiveProjection.focalLength = 150;   // 焦点距離を設定

図02-012■PerspectiveProjection.focalLengthプロパティの設定値による表現の違い

焦点距離: 150

焦点距離: 1000
遠近の隔たりは、焦点距離が短いほど大きく、長いと小さく表現される。赤い+印は消失点を示す。

シンタックス02-007■焦点距離を操作するためのプロパティ
PerspectiveProjection.focalLengthプロパティ
文法 focalLength:Number
プロパティ値

遠近法の焦点距離を定める数値。視点からスクリーンの投影面までの距離を示す。z座標値の異なるオブジェクトの奥行きが、焦点距離を長く(大きく)すると互いに近づいて表示され、短く(小さく)するほど隔たって見える。視野角PerspectiveProjection.fieldOfViewプロパティの値と連動して、以下の式で計算される(なお、計算式について後述Maniac! 02-004「焦点距離の計算式」参照)。

焦点距離 = ステージ幅/2 tan(視野角/2)

焦点距離は、3次元座標空間における奥行きとなるz軸座標の視点から、投影面(スクリーン)までの距離を表します。焦点距離が長いほど、z位置の離れた(奥の)オブジェクトは、投影像がスクリーンに相対的に大きく表示されます。また、視野角は視点から投影面全体を見た角度です(図02-013)。したがって、焦点距離は、視野角と連動します(シンタックス02-007)。焦点距離を操作すれば、視野角もそれに応じて変わります。

図02-013■z軸における焦点距離と視野角
焦点距離が長いと、視野角は狭く、オブジェクトの投影像は大きく表示される。逆に、焦点距離が短ければ、視野角は広がり、投影像は小さく見える。

もっとも、視野角が0から180度までなのに対して、焦点距離は無限大から0までの極めて幅広い値を取ります(図02-013)。ですから、遠近感の調整は、視野角の方が操作しやすいでしょう。おそらくそのため、[プロパティ]インスペクタで[遠近の角度]つまり視野角は変えられても、焦点距離のコントロールはありません(図02-015)。

図02-014■視野角と焦点距離
視野角と焦点距離は連動する。焦点距離は、無限大から0まで、変化の幅が広い。なお、ステージサイズは、240×180ピクセル。

図02-015■[プロパティ]インスペクタの[3D位置とビュー]のコントロール
[遠近の角度](視野角)と[消失点]はあっても、焦点距離は項目にない。

けれども、3次元空間の座標を2次元平面に遠近法投影(透視投影)しようとするときは、焦点距離からその比率を求める必要が生じます(Tips 02-004)。簡単な比例計算で求められるこの比率は、後述06「3次元空間の座標を扱う − Vector3Dクラス」で遠近法投影のためのメソッドVector3D.project()を扱う際に用いられます。

Tips 02-004■焦点距離と遠近法投影(透視投影)の比率
前掲図02-013で、ふたつの三角形から比率を求めます。ともに視点が頂点で、底辺をそれぞれ投影像ともとのオブジェクトとするふたつの三角形は相似です。したがって、つぎの比例式が導かれます。

投影像の大きさ/オブジェクトの大きさ = 焦点距離 / (焦点距離 + z位置)
投影像の大きさ = オブジェクトの大きさ×焦点距離 / (焦点距離 + z位置)

この式は、オブジェクトの大きさだけでなく、3次元空間の座標すべてに対して成立ちます。よって、3次元空間の座標を2次元平面に変換する遠近法投影(透視投影)の比率はつぎのとおりです。

焦点距離 / (焦点距離 + z位置)

02-06 消失点再び − インスタンスごとの消失点設定
すでにシンタックス02-001「消失点を操作するためのプロパティ」で述べたとおり、メインタイムライン以外のDisplayObjectインスタンスはTransform.perspectiveProjectionプロパティの値がnullですので、そのままではPerspectiveProjectionクラスのプロパティを操作することができません。けれども、Transform.perspectiveProjectionプロパティにPerspectiveProjectionインスタンスを設定すれば、操作は可能になります。つまり、インスタンスごとにPerspectiveProjectionオブジェクトのプロパティ値を変えることができるのです。

そこで、3次元空間に並べたインスタンス(ペンギン)と消失点のインスタンスを、まとめてシンボル(名前はParent)に変換してみました(図02-016)。そして、このシンボルのインスタンスのTransform.perspectiveProjectionプロパティにPerspectiveProjectionインスタンスを設定し、消失点や視野角を操作してみましょう。

図02-016■3次元空間に並べたインスタンスと消失点のインスタンスをシンボルにまとめる
3次元空間に並べたインスタンス(ペンギン)と消失点のインスタンスを、まとめてシンボル(Parent)に変換。

まず、テスト用の簡単なフレームアクションを書きます。記述場所は、消失点のシンボルの第1フレームです。2行目のステートメントのtrace()関数は、インスタンスをまとめてシンボル化した(シンボル名はParent)親インスタンスのTransform.perspectiveProjectionプロパティの値を[出力]し、前述のとおりそれがnullであることを確かめています。そして、つぎの3行目のステートメントで、PerspectiveProjectionクラスのコンストラクタメソッドを呼出し(シンタックス02-008)、返されたインスタンスを親インスタンスのTransform.perspectiveProjectionプロパティに設定しました。

シンタックス02-008■PerspectiveProjectionクラスのコンストラクタメソッド
PerspectiveProjection()コンストラクタ
文法 PerspectiveProjection()
概要 PerspectiveProjectionインスタンスを生成する。
引数

なし。

var myPerspectiveProjection:PerspectiveProjection;
trace(parent.transform.perspectiveProjection);   // 出力: null
parent.transform.perspectiveProjection = new PerspectiveProjection();
myPerspectiveProjection = parent.transform.perspectiveProjection;
myPerspectiveProjection.projectionCenter = new Point(x, y);
myPerspectiveProjection.fieldOfView = 100;

あとは、すでに解説したとおり、新たに設定されたTransform.perspectiveProjectionプロパティのオブジェクトを変数に取って,PerspectiveProjection.projectionCenterPerspectiveProjection.fieldOfViewするだけです。[ムービープレビュー]を見ると、消失点のインスタンスの位置に消失点が設定され、視野角も100度に変わっています。

図02-017■インスタンスをまとめたシンボルのインスタンスに消失点と視野角が設定された
インスタンスをまとめたシンボルのインスタンスに新たに設定したTransform.perspectiveProjectionプロパティの操作により、消失点のインスタンスの位置に消失点が設定され、視野角も100度に変わった。

それでは、前掲スクリプト02-002に修正を加えて、消失点のインスタンスが入れ子にされた親インスタンスのPerspectiveProjectionオブジェクトを制御するように書替えてみます。追加・変更するステートメント数は、さほど多くありません。消失点のシンボルに記述する修正後のフレームアクションが、つぎのスクリプト02-003です。

スクリプト02-003■入れ子になった消失点のインスタンスの親インスタンスを制御する
    // フレームアクション
    // MovieClip: ドラッグする消失点のシンボル
  1. var myPerspectiveProjection:PerspectiveProjection = new PerspectiveProjection();
  2. var myFieldOfView:Number = myPerspectiveProjection.fieldOfView;
  3. var startY:Number;
  4. var sensitivity:Number = 0.5;
  5. x = stage.stageWidth / 2 - parent.x;
  6. y = stage.stageHeight / 2 - parent.y;
  7. parent.transform.perspectiveProjection = myPerspectiveProjection;
  8. myPerspectiveProjection = parent.transform.perspectiveProjection;
  9. addEventListener(MouseEvent.MOUSE_DOWN, moveProjectionCenter);
  10. stage.addEventListener(MouseEvent.MOUSE_DOWN, changeFieldOfView);
  11. setProjectionCenter(null);
  12. function moveProjectionCenter(eventObject:Event):void {
  13.   startDrag(true);
  14.   addEventListener(Event.ENTER_FRAME, setProjectionCenter);
  15.   stage.addEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  16.   eventObject.stopPropagation();
  17. }
  18. function setProjectionCenter(eventObject:Event):void {
  19.   myPerspectiveProjection.projectionCenter = new Point(x, y);
  20. }
  21. function stopProjectionCenter(eventObject:Event):void {
  22.   stopDrag();
  23.   removeEventListener(Event.ENTER_FRAME, setProjectionCenter);
  24.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopProjectionCenter);
  25. }
  26. function changeFieldOfView(eventObject:MouseEvent):void {
  27.   if (eventObject.shiftKey) {
  28.     startY = eventObject.stageY;
  29.     stage.addEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
  30.     stage.addEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  31.   }
  32. }
  33. function setFieldOfView(eventObject:MouseEvent):void {
  34.   var newFieldOfView:Number = myFieldOfView + (eventObject.stageY - startY) * sensitivity;
  35.   myPerspectiveProjection.fieldOfView = Math.max(Math.min(179, newFieldOfView), 1);
  36. }
  37. function stopFieldOfView(eventObject:MouseEvent):void {
  38.   myFieldOfView = myPerspectiveProjection.fieldOfView;
  39.   stage.removeEventListener(MouseEvent.MOUSE_MOVE, setFieldOfView);
  40.   stage.removeEventListener(MouseEvent.MOUSE_UP, stopFieldOfView);
  41. }

スクリプト第1行目は、PerspectiveProjectionクラスのコンストラクタを呼出し、そのインスタンスを変数(myPerspectiveProjection)に代入しています。そして、スクリプト第7行目で、そのPerspectiveProjectionインスタンスを親インスタンス(DisplayObject.parentプロパティ)のTransform.perspectiveProjectionプロパティに設定します。これで、変数(myPerspectiveProjection)に取ったPerspectiveProjectionインスタンスのプロパティを操作すれば、消失点や視野角は変えられそうに思えます。しかし、実際には親インスタンスのPerspectiveProjectionオブジェクトには何の変化も生じません。

スクリプト第7行目でPerspectiveProjectionオブジェクトを設定したとき、親インスタンスのTransform.perspectiveProjectionプロパティにはオブジェクトの参照が代入される訳ではないようです。PerspectiveProjectionオブジェクトは複製されて、Transform.perspectiveProjectionプロパティに設定されていると考えられます。そこで、スクリプト第8行目のステートメントが必要になります。変数(myPerspectiveProjection)に改めて親インスタンスのTransform.perspectiveProjectionプロパティの値を代入し直すと、変数のPerspectiveProjectionオブジェクトへの操作が親インスタンスに及ぶようになります。

  1. var myPerspectiveProjection:PerspectiveProjection = new PerspectiveProjection();
  1. parent.transform.perspectiveProjection = myPerspectiveProjection;
  2. myPerspectiveProjection = parent.transform.perspectiveProjection;

以下の3行のステートメントは、親シンボル(シンボル名Parent)のインスタンスをメインタイムラインのどこに置いてもよいように、消失点のインスタンスの位置を設定するための処理です。スクリプト第5〜6行目は、消失点のインスタンスをステージ中央に配置します。ただこのままでは、親インスタンスの消失点は、消失点のインスタンスの座標に設定されません。そこで、スクリプト第11行目で、親インスタンスの消失点を設定する関数setProjectionCenter()が呼出されています。ただし、この関数はイベントリスナーとして登録されているので、引数を渡す必要があります。しかし、関数内でとくにイベントオブジェクトを用いていませんので、nullを引数にしました(Tips 02-005)。

  1. x = stage.stageWidth / 2 - parent.x;
  2. y = stage.stageHeight / 2 - parent.y;
  1. setProjectionCenter(null);

Tips 02-005■リスナー関数をスクリプトで直接呼出したい
イベントリスナーとして登録した関数を、イベント発生時ではなく、スクリプトから直接呼出したいことがあります。その場合、リスナー関数は引数にイベントオブジェクトを受取るため、関数の呼出しには引数が必要とされます。しかし、関数内でとくにイベントオブジェクトを使っていないとき、わざわざイベントオブジェクトを生成して渡すのも無駄な手間です。

そういうときは、引数としてnullを渡すことができます(前掲スクリプト02-003第11行目参照)。nullはすべてのオブジェクトのデータ型のデフォルト値ですので、データ型の不一致をきたしません。また、そのような直接の呼出しがたびたび必要になる関数には、引数のデフォルト値にnullを設定しておけば、その関数を引数なしに呼出すことができます。

listenerFunction();   // 引数なしに呼出し
function listenerFunction(eventObject:Event = null):void {   // デフォルト値をnullに設定
  // 処理内容
}

たとえば、親シンボル(シンボル名Parent)のインスタンスをふたつメインタイムラインに配置して[ムービープレビュー]で確かめると、それぞれの消失点のインスタンスをドラッグすれば、親インスタンスの消失点を個別に設定できます(図02-018)。なお、視野角はともにステージの[shift]+ドラッグで変更されますので、同じ動作になります。

図02-018■ふたつのインスタンスの消失点を別個に設定できる
それぞれの消失点のインスタンスをドラッグすれば、ふたつの親インスタンスの消失点を個別に設定できる。

PerspectiveProjectionクラスは、プロパティやメソッドの数が多くありません。遠近法投影(透視投影)の基本が理解できれば、PerspectiveProjectionクラスの扱いはそれほど難しくないでしょう。また、この基本的な考え方は、以降の3次元座標空間に関わるクラスを学ぶ際にも役立つはずです。

Tips 02-006■Transform.perspectiveProjectionとTransform.Matrix3Dプロパティ
[ヘルプ]で[ActionScript 3.0言語およびコンポーネントリファレンス]の[Transform]クラス冒頭の説明を読むと、以下のような注意が書かれています。後に解説するMatrix3Dクラスを用いるときには、気をつけましょう。

PerspectiveProjectionオブジェクトとMatrix3Dオブジェクトはどちらも遠近法に基づく変形を実行するため、両方を同時に表示オブジェクトに割り当てないでください。焦点の長さと投影の中心(野中注: 焦点距離と消失点)を変更するには、PerspectiveProjectionオブジェクトを使用します。遠近法に基づく変換をより正確に制御するには、遠近法に基づく投影のMatrix3Dオブジェクトを作成します。

Column 02 焦点距離と視野角
焦点距離と視野角との関係について、少し細かな説明を加えておきます。本文02-05「焦点距離の操作 − PerspectiveProjection.focalLengthプロパティ」で述べたとおり、焦点距離と視野角は幾何学的に図02-019のような関係にあり、つぎの式で表されます(シンタックス02-007「焦点距離を操作するためのプロパティ」参照)。

焦点距離 = ステージ幅/2 tan(視野角/2)
図02-019■焦点距離と視野角の関係
図より「tan(視野角/2) = (ステージ幅/2)/焦点距離」となり、上記の式が導かれる(後述数学編「三角関数」参照)。

Maniac! 02-004■焦点距離の計算式
[ヘルプ]の[ActionScript 3.0言語およびコンポーネントリファレンス]で[PerspectiveProjection]クラスの「focalLengthプロパティ」の項を読むと、つぎのように説明されています。

遠近法に基づく変形が行われるとき、視野の角度とステージの縦横比(ステージの幅をステージの高さで除算)を使用して、focalLengthが動的に計算されます。

しかし、筆者の検証では、ステージの高さはプロパティ値に影響を及ぼしません。実際の値は、上記の式の計算結果となります。

再掲図02-013を見ても、焦点距離を長くすれば視野角は狭まり、逆に焦点距離を縮めると視野角は広がることが理解できるでしょう。そして上記の式から、ステージ幅が変更されると、焦点距離か視野角の少なくとも一方の値は変わらざるを得ません。

図02-013■z軸における焦点距離と視野角(再掲)
焦点距離が長いと、視野角は狭く、オブジェクトの投影像は大きく表示される。逆に、焦点距離が短ければ、視野角は広がり、投影像は小さく見える。

視野角を固定するならば、変更されたステージサイズがスクリーンの視野に収まるよう視点を前後して、焦点距離で調整することになります。すると、変更前に視野にあったオブジェクトとの距離は変わり、遠近感の表現が異なってきます(表02-001右上図)。逆に、焦点距離を固定すると、新たなサイズのステージがスクリーンに投影されるように視野角を変えなければなりません。スクリーンに収まる領域は変わるものの、オブジェクトとの距離はそのままで、遠近感の表現は保たれます(表02-001右下図)。

表02-001■ステージ幅の変更による視野角と焦点距離の変化
ステージ幅 240ピクセル(変更前) 550ピクセル(変更後)
視野角固定
焦点距離: 約230.5
視野角: 55度

焦点距離: 約528.3
視野角: 55度
焦点距離固定
焦点距離: 約230.5
視野角: 約100.1度

ステージサイズを変えようとして[ドキュメントプロパティ]のダイアログボックスを開くと、「3D遠近の角度を調整して、現在のステージ投影法を保持します」というチェックボックスがあります(図02-020)。このチェックをつけると、視野角が変わる替わりに焦点距離が固定されて、遠近感の表現が保たれます。

図02-020■ステージサイズを変えるための[ドキュメントプロパティ]ダイアログボックス
「3D遠近の角度を調整して、現在のステージ投影法を保持します」にチェックすると、視野角が変わる代わりに焦点距離が固定されて、遠近感の表現が保たれる。

なお、視野角を変更するには、3Dの操作を加えたインスタンスが何かしら配置されていなければならないようです(図02-021)。もちろん、3次元で操作されたインスタンスがなければ、視野角などの設定に意味はありません。しかし、インスタンスに対する3次元の設定をスクリプトで行う場合もあります。その効果を正しく与えるためには、オーサリング(ムービー作成)時に、3D操作の加わったインスタンスが必要なのです。

図02-021■視野角を有効に変更するには3D操作の加わったインスタンスが必要
たとえば、[プロパティ]インスペクタでz座標が設定されていると、視野角の変更が正しく反映される。

Tips 02-007■視野角の変更とインスタンスへの3D操作
初期状態に3次元の表現を加えたくない場合には、[プロパティ]インスペクタの([位置とサイズ]でなく)[3D位置とビュー]セクションでxy座標を設定するだけでも足ります。

なお、一切のインスタンスに3Dの操作を加えていない場合、本稿執筆時の筆者の環境では、視野角の変更が有効になりません。テスト用ムービーには、メインタイムラインに3D操作の加わっていないMovieClipインスタンスmy_mcを配置し(図02-022)、つぎのようなフレームアクションを記述しました。

var nStageWidth:int = stage.stageWidth;
var myPerspectiveProjection:PerspectiveProjection = transform.perspectiveProjection;
var myFocalLength:Number = myPerspectiveProjection.focalLength;
var myFieldOfVidew:Number = myPerspectiveProjection.fieldOfView;
my_mc.z = 100;   // スクリプトでz座標を操作
trace(nStageWidth, myFocalLength, myFieldOfVidew);   // 視野角の値に注目
// 出力: 240 230.51785278320313 55

図02-022■3D操作の加わったインスタンスなしに視野角を変更する
3D操作の加わったインスタンスがタイムラインにないと、視野角の変更が正しく反映されない。

[ムービープレビュー]を確かめると、スクリプトで3次元の操作を加えたインスタンスの表示が、視野角の変更を反映していません(図02-022右図を前掲02-021右図と比較)。trace()関数の出力も、変更前の視野角の値を表示しています。


[*筆者用参考] 「Calculation of focalLength」。

[Prev/Next]


作成者: 野中文雄
更新日: 2010年1月19日 Maniac! 02-002を追加
作成日: 2009年10月15日


Copyright © 2001-2009 Fumio Nonaka.  All rights reserved.