ビルボードで3D
-------------------------------------------------
ビルボードで3D
300 体 + 20本 配置
上下左右キーでカメラの移動ができます。
1、2キーで視野角を変更できます。
contentLoaderInfo.bytes からのインスタンス化を試してみました。
-------------------------------------------------
/**
* Copyright Hakuhin ( http://wonderfl.net/user/Hakuhin )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/k7Ru
*/
// -------------------------------------------------
//
// ビルボードで3D
//
// 300 体 + 20本 配置
// 上下左右キーでカメラの移動ができます。
// 1、2キーで視野角を変更できます。
//
// contentLoaderInfo.bytes からのインスタンス化を試してみました。
//
// -------------------------------------------------
package {
import flash.events.*;
import flash.display.*;
import flash.net.*;
import flash.text.*;
import flash.utils.*;
import flash.system.*;
import flash.geom.*;
import flash.filters.*;
import flash.ui.*;
public class Main extends Sprite {
public function Main() {
// -------------------------------------------------
// コンストラクタ
// -------------------------------------------------
// フレームレート
stage.frameRate = 60;
// 100%表示
stage.scaleMode = StageScaleMode.NO_SCALE;
// 左上
stage.align = StageAlign.TOP_LEFT;
stage.align = "TL";
// スプライトを作成
var board:Sprite = new Sprite();
addChild(board);
// 画像のURL
var graphic_url:Array = [
"http://actionscript.web.officelive.com/wonderfl/meme.png",
"http://actionscript.web.officelive.com/wonderfl/tree.png",
];
// リソース読み込み開始
load(load_complete_func,graphic_url);
// 読み込み終了
function load_complete_func():void{
// 初期化へ
init();
}
// リソースを格納する入れ物
var bitmap_container : Array = new Array();
// -------------------------------------------------
// 読み込み
// -------------------------------------------------
function load(complete_func:Function,url_array:Array):void{
// URLを取り出す
var url : String = url_array.shift();
// URLが無くなればコールバック関数を呼び出して終了
if(!url){
// 読み込み終了
complete_func();
return;
}
// ローダー
var loader_obj : Loader = new Loader();
// 読み込み開始
loader_obj.load(new URLRequest(url));
// 読み込み終了
var info : LoaderInfo = loader_obj.contentLoaderInfo;
info.addEventListener (Event.INIT,LoaderInfoInitFunc);
function LoaderInfoInitFunc (event : Event):void {
// メモリからインスタンス化
var loader_memory : Loader = new Loader();
loader_memory.contentLoaderInfo.addEventListener (Event.COMPLETE,LoaderInfoCompleteFunc);
function LoaderInfoCompleteFunc (event : Event):void {
// キャプチャ
var bmp : BitmapData = new BitmapData(loader_memory.width,loader_memory.height,true,0);
stage.addChild(loader_memory);
bmp.draw(stage);
stage.removeChild(loader_memory);
// コンテナに格納
bitmap_container.push(bmp);
// 再帰読み込み
load(complete_func,url_array);
}
// 読み込み開始
loader_memory.loadBytes(loader_obj.contentLoaderInfo.bytes);
}
}
// -------------------------------------------------
// 初期化
// -------------------------------------------------
function init():void{
// ステージサイズ
var w:uint;
var h:uint;
var w_half:uint;
var h_half:uint;
// リサイズ時にフィット
stage.addEventListener(Event.RESIZE,ResizeFunc);
function ResizeFunc(e:Event):void{
w = stage.stageWidth;
h = stage.stageHeight;
w_half = w / 2;
h_half = h / 2;
}
ResizeFunc(null);
// テキスト設定
var tf : TextField = new TextField();
tf.x = 5;
tf.y = 5;
tf.width = 200;
tf.height = 40;
tf.border = true;
tf.background = true;
tf.alpha = 0.9;
// テキスト書式
var format : TextFormat = new TextFormat();
format.font = "MS ゴシック";
tf.defaultTextFormat = format;
// テキスト表示
addChild(tf);
addEventListener(Event.ENTER_FRAME,function(e:Event):void{
var str:String = "";
str += "上下左右キーで移動\n";
str += "視野角:" + (Math.floor(camera_angle * 100) / 100) + " (1,2キーで変更)\n";
tf.text = str;
});
// オブジェクトを生成する関数
var WORLD_SIZE : Number = 1200;
function ObjectCreate():void{
var src:BitmapData = bitmap_container[0];
var pos : Vector3D = new Vector3D(0,0,0); // 座標
var spd : Vector3D = new Vector3D(0,0,0); // 速度
var exec:Function = wait_init; // 実行
var frame:int = 0; // フレーム
// 描画オフセット
var ofs : Point = new Point(-src.width / 2,-src.height);
// 初期配置
pos.x = (Math.random() * 2 -1) * 1000;
pos.z = (Math.random() * 2 -1) * 1000;
spd.x = (Math.random() * 2 -1) * 0.5;
spd.z = (Math.random() * 2 -1) * 0.5;
// 実行
addEventListener(Event.ENTER_FRAME,function(e:Event):void{
// 重力
spd.y -= 0.35;
// 座標に速度を加算
pos = pos.add(spd);
// 地面
if(pos.y < 0){
pos.y = 0;
spd.y *= -0.5;
}
// 外に出たら戻す
if(pos.x < -WORLD_SIZE) spd.x *= -1;
if(pos.x > WORLD_SIZE) spd.x *= -1;
if(pos.z < -WORLD_SIZE) spd.z *= -1;
if(pos.z > WORLD_SIZE) spd.z *= -1;
exec();
// 描画要求
RenderSetRequest(src,pos,ofs);
});
function wait_init():void{
frame = Math.floor(Math.random() * 300) + 60;
exec = wait_exec;
exec();
}
function wait_exec():void{
frame -= 1
if(frame < 0){
exec = jump_init;
}
}
function jump_init():void{
spd.y = Math.random() * 5 + 3;
exec = jump_exec;
exec();
}
function jump_exec():void{
exec = wait_init;
}
}
// オブジェクトを生成
var i:Number;
for(i=0;i<300;i++){
ObjectCreate();
}
// 木を生成する関数
function TreeCreate():void{
var src:BitmapData = bitmap_container[1];
var pos : Vector3D = new Vector3D(0,0,0); // 座標
// 描画オフセット
var ofs : Point = new Point(-src.width / 2,-src.height);
// 初期配置
pos.x = (Math.random() * 2 -1) * WORLD_SIZE;
pos.z = (Math.random() * 2 -1) * WORLD_SIZE;
// 実行
addEventListener(Event.ENTER_FRAME,function(e:Event):void{
// 描画要求
RenderSetRequest(src,pos,ofs);
});
}
// 木を生成
for(i=0;i<20;i++){
TreeCreate();
}
// カメラ
var camera_pos : Vector3D = new Vector3D(0,100,-1000); // 座標
var camera_spd : Vector3D = new Vector3D(0,0,0); // 速度
var camera_rot : Number = 0; // 角度
var camera_rot_spd : Number = 0; // 角速度
// キー操作
var key:Array = new Array;
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void{
key[e.keyCode] = true;
});
stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent):void{
key[e.keyCode] = false;
});
addEventListener(Event.ENTER_FRAME, function(e:Event):void{
// 視野角を変更
if(key[49]) camera_angle -= 1;
if(key[50]) camera_angle += 1;
if(camera_angle < 1) camera_angle = 1;
if(camera_angle > 180) camera_angle = 180;
var dx:Number = 0;
if(key[Keyboard.LEFT]) dx -= 1;
if(key[Keyboard.RIGHT]) dx += 1;
var dy:Number = 0;
if(key[Keyboard.UP]) dy -= 1;
if(key[Keyboard.DOWN]) dy += 1;
// キー入力を角速度に加算
camera_rot_spd += dx * 0.1;
// カメラ角度に角速度を加算
camera_rot += camera_rot_spd;
// 角速度の摩擦
camera_rot_spd *= 0.9;
// 進行方向に移動
var rad:Number = (-camera_rot - 90) * Math.PI / 180;
camera_spd.x += Math.cos(rad) * dy;
camera_spd.z += Math.sin(rad) * dy;
// カメラ座標に速度を加算
camera_pos = camera_pos.add(camera_spd);
// 速度の摩擦
camera_spd.scaleBy(0.9);
});
// ビュー行列作成
function ViewMatrixCreate():Matrix3D{
var mtx : Matrix3D = new Matrix3D();
mtx.identity(); // 単位行列
mtx.appendRotation(10,Vector3D.X_AXIS); // x軸回転
mtx.appendRotation(camera_rot,Vector3D.Y_AXIS); // y軸回転
mtx.appendTranslation(camera_pos.x,camera_pos.y,camera_pos.z); // 座標
mtx.invert(); // 逆行列
return mtx;
}
// 描画要求
var draw_container : Array = new Array();
var pos_vector : Vector.<Number> = new Vector.<Number>();
// 描画要求クリア
function RenderRequestClear():void{
draw_container = new Array();
pos_vector = new Vector.<Number>();
}
// 描画要求
function RenderSetRequest(bmp:BitmapData,pos:Vector3D,ofs:Point):void{
var id:uint = pos_vector.length;
pos_vector[id+0] = pos.x;
pos_vector[id+1] = pos.y;
pos_vector[id+2] = pos.z;
draw_container.push({bmp:bmp,id:id,ofs:ofs});
}
// 描画処理
var camera_angle : Number = 60; // 視野角
addEventListener(Event.ENTER_FRAME,function(e:Event):void{
// 表示リストから外す
ContainerRemoveChildAll(board);
// 視点からの距離
var camera_fov : Number = 1 / Math.tan( camera_angle * 0.5 * Math.PI / 180);
var i:int;
var num:int = draw_container.length;
var view_mtx:Matrix3D = ViewMatrixCreate();
// ビュー行列変換を一括で行う
view_mtx.transformVectors(pos_vector,pos_vector);
// z コンテナ(zソート用の双方向リストを複数作成する)
var z_container : Array = new Array();
var z_list:Object;
var Z_DIVISION:uint = 20; // 分割数
var Z_LENGTH:Number = 2000; // 遠方の z 値
// 双方向リストを作成
for(i=0;i<Z_DIVISION;i++){
z_list = new Object();
z_list.pref = z_list;
z_list.post = z_list;
z_list.z = i * Z_LENGTH / Z_DIVISION;
z_container[i]= z_list;
}
for(i=0;i<num;i++){
var obj : Object = draw_container[i];
// 座標取り出し
var pos : Vector3D = new Vector3D(pos_vector[obj.id+0],pos_vector[obj.id+1],pos_vector[obj.id+2]);
// カメラの裏側なので処理をしない
if(pos.z < 1) continue;
// パスペクティブプロジェクション変換
pos.x = pos.x / pos.z * camera_fov;
pos.y = pos.y / pos.z * camera_fov;
// ビューポート変換
pos.x = pos.x * w + w_half;
pos.y =-pos.y * w + h_half;
obj.pos = pos;
obj.z = pos.z;
// z 値を分割してリスト格納位置を決める
var div:uint = Math.floor(obj.z / Z_LENGTH * Z_DIVISION);
if(div > Z_DIVISION - 1) div = Z_DIVISION - 1;
// z値でソート
z_list = z_container[div];
var pref : Object;
var post : Object = z_list.post;
while(true){
if(z_list == post) break;
if(post.z > obj.z) break;
post = post.post;
}
// zコンテナに登録
pref = post.pref;
obj.pref = pref;
obj.post = post;
pref.post = obj;
post.pref = obj;
}
// 描画をクリア
var g : Graphics = board.graphics;
g.clear();
// ビットマップ行列
var bmp_mtx:Matrix = new Matrix();
bmp_mtx.identity();
// 奥から順番に描画
for(i=Z_DIVISION-1;i>=0;i--){
z_list = z_container[i];
var list : Object = z_list.pref;
while(true){
if(z_list == list) break;
var s:Number = 1.0 / list.z * camera_fov * w;
var x:Number = list.pos.x + list.ofs.x * s;
var y:Number = list.pos.y + list.ofs.y * s;
// 矩形描画
bmp_mtx.a = bmp_mtx.d = s;
bmp_mtx.tx = x;
bmp_mtx.ty = y;
g.beginBitmapFill ( list.bmp , bmp_mtx , false , true );
g.drawRect ( x , y , list.bmp.width * s , list.bmp.height * s );
list = list.pref;
}
}
// 描画要求をクリア
RenderRequestClear();
});
// -------------------------------------------------
// 表示リストをすべて外す
// -------------------------------------------------
function ContainerRemoveChildAll(container:DisplayObjectContainer):void{
while(true){
if(!container.numChildren) return;
container.removeChild(container.getChildAt(0));
}
}
}
}
}
}