Flash CS4から、ActionScript3.0に幾つかのクラスが追加されましたが、その中で何といっても魅力的なのは3D表現でしょう。私は、これまでFlashにおける3D表現にはPaperVision3Dを使用してきましたが、簡単な3D表現であればActionScript3.0のクラスだけで実現できることになります。今回は、ActionScript3.0のPerspectiveProjectionクラスに関する学習メモです。
PerspectiveProjectionクラスとは、DisplayObjectインスタンスに対して、遠近法に基づく投影を行うクラスです。PerspectiveProjectionクラスは、下記3つのプロパティを持っています。
- fieldOfView
- 遠近法の視野角を0より大きく180より小さいNumber型の数値で指定します。値が小さいとオブジェクトの遠近感が弱くなり、大きいと遠近感が強くなります。値が180に近づくほど歪みが強くなり、魚眼レンズのような効果になります。この値は、下記focalLengthプロパティの値と連動しています。
- focalLength
-
遠近法の焦点距離をNumber型の数値で指定します。この値は、上記fieldOfViewプロパティの値と連動して動的に計算され設定されます。逆にfocalLengthの値を指定するとfieldOfViewプロパティの値も動的に計算され設定されます。計算式は、
focalLength = stageWidth / 2 * (cos(fieldOfView / 2) / sin(fieldOfView / 2) - projectionCenter
- 遠近法の消失点(vanishing point)をステージ左上隅を基準とした座標(Pointインスタンス)で指定します。DisplayObjectインスタンスのz座標を大きくするほど、この座標に近づいていきます。
PerspectiveProjectionを使ったサンプルを作ってみました。NumericStepperでroot.transform.perspectiveProjectionのプロパティを変更できます。十字のマークの中心が消失点の座標で、ドラッグして変更することもできます。左の赤いSpriteはz座標を0に中央の緑と右の青のSpriteはz座標を100に指定しています。また、中央の緑のSpriteは、独自のPerspectiveProjectionインスタンスを生成しているので、rootのperspectiveProjectionの影響を受けないことが確認できます。
以下がソースになります。target_spは消失点を示すSprite、fov_nsはfieldOfView用のNumericStepper、fl_nsはfocalLength用のNumericStepper、pcx_nsはprojectionCenter.x用のNumericStepper、pcy_nsはprojectionCenter.y用のNumericStepperになります。
ActionScript3.0
package {
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.geom.Point;
import flash.geom.PerspectiveProjection;
public class PerspectiveProjectionTest extends Sprite {
private var a_sp:Sprite;
private var b_sp:Sprite;
private var c_sp:Sprite;
public function PerspectiveProjectionTest() {
a_sp = new Sprite();
var g:Graphics = a_sp.graphics;
g.beginFill(0xFF0000, 1);
g.drawRect(-50, -50, 100, 100);
g.endFill();
a_sp.x = stage.stageWidth * 0.2;
a_sp.y = stage.stageHeight * 0.5;
addChildAt(a_sp, 0);
b_sp = new Sprite();
g = b_sp.graphics;
g.beginFill(0x00FF00, 1);
g.drawRect(-50, -50, 100, 100);
g.endFill();
b_sp.x = stage.stageWidth * 0.5;
b_sp.y = stage.stageHeight * 0.5;
b_sp.z = 100;
addChildAt(b_sp, 0);
c_sp = new Sprite();
g = c_sp.graphics;
g.beginFill(0x0000FF, 1);
g.drawRect(-50, -50, 100, 100);
g.endFill();
c_sp.x = stage.stageWidth * 0.8;
c_sp.y = stage.stageHeight * 0.5;
c_sp.z = 100;
addChildAt(c_sp, 0);
var _pp:PerspectiveProjection = root.transform.perspectiveProjection;
fov_ns.value = _pp.fieldOfView;
fl_ns.value = _pp.focalLength;
pcx_ns.value = _pp.projectionCenter.x;
pcy_ns.value = _pp.projectionCenter.y;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
fov_ns.addEventListener(Event.CHANGE, changeHandler);
fl_ns.addEventListener(Event.CHANGE, changeHandler);
pcx_ns.addEventListener(Event.CHANGE, changeHandler);
pcy_ns.addEventListener(Event.CHANGE, changeHandler);
target_sp.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
target_sp.mouseChildren = false;
target_sp.buttonMode = true;
b_sp.transform.perspectiveProjection = new PerspectiveProjection();
b_sp.transform.perspectiveProjection.projectionCenter = new Point(b_sp.x, b_sp.y);
}
private function changeHandler(e:Event):void {
var _ns:NumericStepper = NumericStepper(e.target);
var _pp:PerspectiveProjection = root.transform.perspectiveProjection;
var _point:Point;
switch(_ns) {
case fov_ns :
_pp.fieldOfView = _ns.value;
fl_ns.value = _pp.focalLength;
break;
case fl_ns :
_pp.focalLength = _ns.value;
fov_ns.value = _pp.fieldOfView;
break;
case pcx_ns :
_point = new Point(_ns.value, _pp.projectionCenter.y);
_pp.projectionCenter = _point;
target_sp.x = _point.x;
target_sp.y = _point.y;
break;
case pcy_ns :
_point = new Point(_pp.projectionCenter.x, _ns.value);
_pp.projectionCenter = _point;
target_sp.x = _point.x;
target_sp.y = _point.y;
break;
}
}
private function mouseDownHandler(e:MouseEvent):void {
target_sp.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
target_sp.startDrag(false, new Rectangle(0, 0, 600, 376));
addEventListener(Event.ENTER_FRAME, mouseMoveHandler);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
}
private function mouseUpHandler(e:MouseEvent):void {
removeEventListener(Event.ENTER_FRAME, mouseMoveHandler);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
target_sp.stopDrag();
target_sp.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
}
private function mouseMoveHandler(e:Event):void {
var _point:Point = new Point(target_sp.x, target_sp.y);
pcx_ns.value = _point.x;
pcy_ns.value = _point.y;
var _pp:PerspectiveProjection = root.transform.perspectiveProjection;
_pp.projectionCenter = _point;
}
private function enterFrameHandler(e:Event):void {
a_sp.rotationY += 2;
b_sp.rotationX += 2;
c_sp.rotationX += 2;
}
}
}
感想としては、簡単な3D表現であれば十分にPerspectiveProjectionクラスで表現できると感じました。より高度な表現には、Matrix3Dクラスなどの理解が必須だと思うので、少しづつ勉強していきたいと思います。