AS3で自作3Dをやる際の、カメラの実装方法です。
以前自作3Dをやった時の、カメラだけ抜き出した版。
前は行列計算とかも自作してたけど、今回はビルトインのMatrix3DとVector3Dを使ってます。
とりあえず、こんな感じになります。カメラがY軸で円運動してます。
カメラを実装するときは、「ビュー変換」という考え方を使います。
これは、3D座標を「カメラの位置を中心として、カメラの視点方向をz軸とした座標」に変換することです。
よく3D関連の本にも載っていて、簡単な行列計算で実装できます。
今回の例では、MyCamera3Dという自作クラスの中のgetViewingTransformMatrix()メソッドがビュー変換行列を作る役割をしています。
以下、ソースです。
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
[SWF(backgroundColor="#000000")]
public class Main extends Sprite
{
private var vertices:Array;
private var camera:MyCamera3D;
private var theta:Number;
public function Main()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.quality = StageQuality.HIGH;
stage.frameRate = 40;
// 座標を計算・格納
vertices = [];
vertices.push(new Vector3D(100, 100, 0));
vertices.push(new Vector3D(100, -100, 0));
vertices.push(new Vector3D(-100, -100, 0));
vertices.push(new Vector3D(-100, 100, 0));
vertices.push(new Vector3D(200, 200, 0));
// カメラの初期化
camera = new MyCamera3D(0,0,400, 400);
theta = 0;
addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame(e:Event):void
{
// カメラの円運動
camera.x = 400 * Math.sin(theta*Math.PI/180);
camera.z = 400 * Math.cos(theta*Math.PI/180);
theta ++;
// 座標の変換
var view:Matrix3D = camera.getViewingTransformMatrix();
var newPoints:Array=[];
var i:int;
graphics.clear();
for(i=0 ; i<5 ; i++)
{
var eyePoint:Vector3D = view.transformVector(vertices[i]);
var newX:Number = eyePoint.x * (400 / eyePoint.length) + stage.stageWidth*0.5;
var newY:Number = -eyePoint.y * (400 / eyePoint.length) + stage.stageHeight*0.5;
// 変換後の座標点に円を描く
newPoints.push(new Point(newX, newY));
graphics.beginFill(0xffffff,1);
graphics.drawCircle(newX, newY, 10);
graphics.endFill();
}
// 図形を描写 (線とか)
var sp:Point = newPoints[0] as Point;
graphics.lineStyle(2, 0xffffff);
graphics.moveTo(sp.x, sp.y);
var p:Point;
for(i=1 ; i<4 ; i++)
{
p = newPoints[i] as Point;
graphics.lineTo(p.x, p.y);
}
graphics.lineTo(sp.x, sp.y);
p = newPoints[2] as Point;
graphics.moveTo(sp.x, sp.y);
graphics.lineTo(p.x, p.y);
}
}
}
/*
* カメラを表すclass。
* getViewingTransformMatrix()メソッドで、ビュー変換行列を取得出来る。
*/
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
class MyCamera3D
{
// 上と原点を示すベクトル
private static const UP:Vector3D = new Vector3D(0, 1, 0);
private static const CENTER:Vector3D = new Vector3D(0, 0, 0);
private var _coord:Vector3D;
public function MyCamera3D(x:Number, y:Number, z:Number, focus:Number)
{
_coord = new Vector3D(x, y, z, 0);
}
public function getViewingTransformMatrix():Matrix3D
{
// Eye - Center ベクトルを作成。Z軸となる。
var n:Vector3D = _coord.subtract(CENTER);
n.normalize();
// UP×Nを作成。X軸となる。
var u:Vector3D = UP.crossProduct(n);
u.normalize();
// N×Uを作成。Y軸となる。
var v:Vector3D = n.crossProduct(u);
v.normalize();
// 内積の計算
var dx:Number = -_coord.dotProduct(u);
var dy:Number = -_coord.dotProduct(v);
var dz:Number = -_coord.dotProduct(n);
// ビュー変換行列の作成(Vectorには列ごとに値を収納)
var vec:Vector.<Number> = Vector.<Number>([u.x, v.x, n.x, 0, u.y, v.y, n.y, 0, u.z, v.z, n.z, 0, dx, dy, dz, 1]);
var mat:Matrix3D = new Matrix3D(vec);
return mat;
}
// setter & getter
public function set x(value:Number):void{_coord.x = value;}
public function set y(value:Number):void{_coord.y = value;}
public function set z(value:Number):void{_coord.z = value;}
public function get x():Number{return _coord.x;}
public function get y():Number{return _coord.y;}
public function get z():Number{return _coord.z;}
}
