Beautifl - Flash Gallery

Thumbnail : Twitter Friend Location
Twitter Friend Location
narutohyper 2010-02-21 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package {
/*

Twiiterでフォローしているユーザーの現在地に登録されている情報で
都道府県に、配置します。
都道府県が記載されていないユーザーは、表示されません。
とりあえず、1000人まではサーチできるようにしていますが。
多いとそれだけ処理時間が長くなります。

最初かっこよくカメラ動かそうかとも思いましたが、めんどくさいので止めたw

@narutohyper
*/

	import alternativ5.engine3d.materials.FillMaterial;
	import alternativ5.engine3d.materials.WireMaterial;

	import alternativ5.engine3d.primitives.Box;
	import alternativ5.engine3d.primitives.Sphere;
	import alternativ5.engine3d.core.Object3D;
	import alternativ5.engine3d.events.MouseEvent3D
	import alternativ5.engine3d.controllers.WalkController
	import alternativ5.engine3d.controllers.ObjectController
	import alternativ5.types.Point3D;
	import alternativ5.engine3d.core.Camera3D;

	import alternativ5.utils.KeyboardUtils

	import flash.display.Sprite;
	import flash.display.BlendMode;

	import flash.events.KeyboardEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent
	import flash.events.MouseEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.text.*;
	import flash.system.LoaderContext
	import flash.system.Security

	//import org.libspark.betweenas3.*;
	//import org.libspark.betweenas3.easing.*;
	//import org.libspark.betweenas3.tweens.*;

	import caurina.transitions.Tweener;
		
	[SWF(width = 465, height = 465, frameRate = 60,backgroundColor=0x000000)]
	public class SimpleDemo extends Sprite {
		private var prefectureArray:Array;
		private var eye:Sphere
		private var camera:Camera3D

		
		public function SimpleDemo():void {

			var template:BasicTemplate = new BasicTemplate();
			addChild(template);

			camera=template.camera
			camera.z=500


			//eye=new Sphere(50,4,2,false,false);
			//eye.cloneMaterialToAllSurfaces(new WireMaterial(0,0xFF0000,1));
			//template.scene.root.addChild(eye);
			//eye.z=100


			//Controllerのインスタンスを作る
			var walkController:WalkController=new WalkController(this.stage,template.camera)
			walkController.lookAt(new Point3D(0,0,0));

			//キーボードのキー(Keyboard.KEY)を関連付ける(Bind)する	
			walkController.mouseEnabled=false
			walkController.bindKey(KeyboardUtils.PAGE_UP,	ObjectController.ACTION_UP);
			walkController.bindKey(KeyboardUtils.PAGE_DOWN,	ObjectController.ACTION_DOWN);
			walkController.bindKey(KeyboardUtils.E,	ObjectController.ACTION_UP);
			walkController.bindKey(KeyboardUtils.D,	ObjectController.ACTION_DOWN);			
			walkController.bindKey(KeyboardUtils.W,			ObjectController.ACTION_FORWARD);
			walkController.bindKey(KeyboardUtils.UP,		ObjectController.ACTION_FORWARD);
			walkController.bindKey(KeyboardUtils.Z,			ObjectController.ACTION_BACK);
			walkController.bindKey(KeyboardUtils.DOWN,	ObjectController.ACTION_BACK);
			walkController.bindKey(KeyboardUtils.A,			ObjectController.ACTION_YAW_LEFT);
			walkController.bindKey(KeyboardUtils.LEFT,	ObjectController.ACTION_YAW_LEFT);
			walkController.bindKey(KeyboardUtils.S,			ObjectController.ACTION_YAW_RIGHT);
			walkController.bindKey(KeyboardUtils.RIGHT,	ObjectController.ACTION_YAW_RIGHT);
			walkController.bindKey(KeyboardUtils.SHIFT,	ObjectController.ACTION_ACCELERATE);

			//移動スピードを設定する
			walkController.speed = 700;

			//初期位置を設定する
			walkController.coords=new Point3D(0,-4000,500);


			var base:Object3D = new Object3D();
			template.scene.root.addChild(base);
			//base.rotationX = -70 * Math.PI / 180;

			var areArray:Array=[
			['北海道','hokkaido'],
			['青森'	,'aomori'],
			['秋田'	,'akita'],
			['岩手'	,'iwate'],
			['山形'	,'yamagata'],
			['宮城'	,'miyagi'],
			['福島'	,'fukushima'],
			['茨城'	,'ibaragi'],
			['千葉'	,'chiba'],
			['栃木'	,'tochigi'],
			['群馬'	,'gunma'],
			['埼玉'	,'saitama'],
			['東京'	,'tokyo'],
			['神奈川','kanagawa'],
			['新潟'	,'nigata'],
			['山梨'	,'yamanashi'],
			['長野'	,'nagano'],
			['富山'	,'toyama'],
			['石川'	,'ishikawa'],
			['福井'	,'fukui'],
			['静岡'	,'shizuoka'],
			['愛知'	,'aichi'],
			['岐阜'	,'gifu'],
			['三重'	,'mie'],
			['滋賀'	,'shiga'],
			['奈良'	,'nara'],
			['和歌山','wakayama'],
			['京都'	,'kyoto'],
			['大阪'	,'osaka'],
			['兵庫'	,'hyougo'],
			['鳥取'	,'tottori'],
			['岡山'	,'okayama'],
			['島根'	,'simane'],
			['広島'	,'hiroshima'],
			['山口'	,'yamaguchi'],
			['香川'	,'kagawa'],
			['愛媛'	,'ehime'],
			['徳島'	,'tokushima'],
			['高知'	,'kouchi'],
			['福岡'	,'fukuoka'],
			['大分'	,'oita'],
			['宮崎'	,'miyazaki'],
			['熊本'	,'kumamoto'],
			['鹿児島','kagoshima'],
			['佐賀'	,'saga'],
			['長崎'	,'nagasaki'],
			['沖縄'	,'okinawa']
			]


			prefectureArray=[];

			prefectureArray.push(new prefecture(this,base, 0,0,'北海道',770,100,200,200));
			prefectureArray.push(new prefecture(this,base, 1,0,'青森',720,250,100,60));
			prefectureArray.push(new prefecture(this,base, 2,0,'秋田',695,320,50,80));
			prefectureArray.push(new prefecture(this,base, 3,0,'岩手',745,320,50,80));
			prefectureArray.push(new prefecture(this,base, 4,0,'山形',695,400,50,80));
			prefectureArray.push(new prefecture(this,base, 5,0,'宮城',745,400,50,80));
			prefectureArray.push(new prefecture(this,base, 6,0,'福島',720,470,100,60));
			prefectureArray.push(new prefecture(this,base, 7,1,'茨城',745,535,50,70));
			prefectureArray.push(new prefecture(this,base, 8,1,'千葉',745,625,50,110));
			prefectureArray.push(new prefecture(this,base, 9,1,'栃木',695,520,50,40));
			prefectureArray.push(new prefecture(this,base,10,1,'群馬',645,520,50,40));
			prefectureArray.push(new prefecture(this,base,11,1,'埼玉',670,560,100,40));
			prefectureArray.push(new prefecture(this,base,12,1,'東京',695,600,50,40));
			prefectureArray.push(new prefecture(this,base,13,1,'神奈川',695,640,50,40));
			prefectureArray.push(new prefecture(this,base,14,2,'新潟',620,470,100,60));
			prefectureArray.push(new prefecture(this,base,15,2,'山梨',645,605,50,50));
			prefectureArray.push(new prefecture(this,base,16,2,'長野',595,565,50,130));
			prefectureArray.push(new prefecture(this,base,17,2,'富山',545,515,50,50));
			prefectureArray.push(new prefecture(this,base,18,2,'石川',500,485,40,110));
			prefectureArray.push(new prefecture(this,base,19,2,'福井',475,565,50,50));
			prefectureArray.push(new prefecture(this,base,20,3,'静岡',635,665,70,70));
			prefectureArray.push(new prefecture(this,base,21,3,'愛知',570,655,60,50));
			prefectureArray.push(new prefecture(this,base,22,3,'岐阜',535,585,70,90));
			prefectureArray.push(new prefecture(this,base,23,3,'三重',520,680,40,100));
			prefectureArray.push(new prefecture(this,base,24,4,'滋賀',475,620,50,60));
			prefectureArray.push(new prefecture(this,base,25,4,'奈良',475,680,50,60));
			prefectureArray.push(new prefecture(this,base,26,4,'和歌山',455,740,90,60));
			prefectureArray.push(new prefecture(this,base,27,4,'京都',430,595,40,70));
			prefectureArray.push(new prefecture(this,base,28,4,'大阪',430,670,40,80));
			prefectureArray.push(new prefecture(this,base,29,4,'兵庫',380,605,60,90));
			prefectureArray.push(new prefecture(this,base,30,5,'鳥取',315,580,70,40));
			prefectureArray.push(new prefecture(this,base,31,5,'岡山',315,625,70,50));
			prefectureArray.push(new prefecture(this,base,32,5,'島根',245,580,70,40));
			prefectureArray.push(new prefecture(this,base,33,5,'広島',245,625,70,50));
			prefectureArray.push(new prefecture(this,base,34,5,'山口',180,615,60,70));
			prefectureArray.push(new prefecture(this,base,35,6,'香川',340,700,80,40));
			prefectureArray.push(new prefecture(this,base,33,6,'愛媛',260,700,80,40));
			prefectureArray.push(new prefecture(this,base,37,6,'徳島',340,740,80,40));
			prefectureArray.push(new prefecture(this,base,38,6,'高知',260,740,80,40));
			prefectureArray.push(new prefecture(this,base,39,7,'福岡',145,685,90,50));
			prefectureArray.push(new prefecture(this,base,40,7,'大分',170,740,40,60));
			prefectureArray.push(new prefecture(this,base,41,7,'宮崎',170,805,40,70));
			prefectureArray.push(new prefecture(this,base,42,7,'熊本',125,750,50,80));
			prefectureArray.push(new prefecture(this,base,43,7,'鹿児島',125,820,50,60));
			prefectureArray.push(new prefecture(this,base,44,7,'佐賀',80,685,40,50));
			prefectureArray.push(new prefecture(this,base,45,7,'長崎',40,695,40,70));
			prefectureArray.push(new prefecture(this,base,46,7,'沖縄',50,830,60,40));

			

			var friendArray:Array=[]


			var inputBox:TextField=new TextField()
			inputBox.type=TextFieldType.INPUT 
			inputBox.width=200
			inputBox.height=20
			inputBox.x=10
			inputBox.y=10
			inputBox.selectable=true;
			inputBox.mouseEnabled=true;
			inputBox.background=true;
			inputBox.backgroundColor=0xFFFFFF
			inputBox.border
			var format:TextFormat=new TextFormat();
			format.color=0x000000
			format.size=12;
			format.font='_ゴシック';
			inputBox.defaultTextFormat=format
			inputBox.text='narutohyper'
			addChild(inputBox)

			//searchButton
			var bt:Sprite=new Sprite()
			bt.graphics.beginFill(0xCCCCCC,1)
			bt.graphics.drawRect(0,0,100,20)
			bt.x=220
			bt.y=10

			var label:TextField=new TextField()
			label.autoSize=TextFieldAutoSize.CENTER
			label.selectable=false;
			label.mouseEnabled=false;
			format=new TextFormat();
			format.color=0x000000
			format.size=12;
			format.font='_ゴシック';
			label.defaultTextFormat=format
			label.text='search'
			bt.addChild(label)
			addChild(bt)
			bt.addEventListener(MouseEvent.CLICK,init)


			var guideBox:TextField=new TextField()
			guideBox.autoSize=TextFieldAutoSize.LEFT
			guideBox.width=400
			guideBox.y=40
			guideBox.selectable=true;
			guideBox.mouseEnabled=true;
			format=new TextFormat();
			format.color=0xFFFFFF
			format.size=12;
			format.font='_等幅';
			guideBox.defaultTextFormat=format
			guideBox.text="TwitterIDを入力して、searchを押してください。\n";
			guideBox.appendText(" W↑    E上 \n");
			guideBox.appendText("←A S→ D下 \n");
			guideBox.appendText(" Z↓   \n");
			
			
			guideBox.appendText("十字キーでも操作できます。\n");
			addChild(guideBox);

			var dbg:TextField=new TextField()
			//dbg.autoSize=TextFieldAutoSize.LEFT
			dbg.width=400
			dbg.y=120
			dbg.selectable=true;
			dbg.mouseEnabled=true;
			dbg.multiline=true;
			format=new TextFormat();
			format.color=0xFFFFFF
			format.size=12;
			format.font='_ゴシック';
			dbg.defaultTextFormat=format
			addChild(dbg);


			function init():void {
				var userId:String=inputBox.text
				var loader:URLLoader = new URLLoader();
				var page:uint=1
				var maxPage:uint=11
				var next_cursor:Number=-1

				for (var i:uint=0;i<prefectureArray.length;i++) {
					prefectureArray[i].init()
				}
				dbg.text="";
				loadXml()
				function loadXml():void {
					//Twitterのstatusesはcrossdomain許可されていない為、セキュリティエラーで取得できない
					//とりあえず串で対応
					Security.loadPolicyFile("http://marubayashi.net/crossdomain.xml");
					var url:String="http://marubayashi.net/tips/crossdomain-proxy.php?url="+"http://twitter.com/statuses/friends/"+userId+".xml?cursor="+next_cursor
					dbg.appendText("読み込み ");
					new LoaderContext(true)
					loader.load(new URLRequest(url));
					

					loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
					loader.addEventListener(Event.OPEN,onOpen);
					loader.addEventListener(Event.COMPLETE,onComplete);
					loader.addEventListener(IOErrorEvent.IO_ERROR, errorEvent);
				}


				function onSecurityError(e:SecurityErrorEvent):void {
					dbg.appendText("\nsecurityErrorHandler: " + e);
					dbg.appendText("\n");
				}

				function onOpen(e:Event):void {
					dbg.appendText("開始\n");
				}

				function onComplete(e:Event):void {
					
					dbg.appendText("終了\n");
										
					loader.removeEventListener(Event.COMPLETE,onComplete );
					loader.removeEventListener(IOErrorEvent.IO_ERROR, errorEvent);
					next_cursor=XML(loader.data).next_cursor
					var users:XMLList = XML(loader.data).users.user;
					var pattern:RegExp
					var matchId:int
					var temp:uint

					for each(var x:XML in users) {
						matchId=-1;
						for (var i:uint=0;i<areArray.length;i++) {
							pattern = new RegExp(areArray[i][0]+'|'+areArray[i][1], "i");
							if (pattern.exec(x.location)) {
								matchId=i;
								break;
							}
						}

						if (matchId>=0) {
							prefectureArray[matchId].addFriend(x.name,x.profile_image_url)
						}
						temp++
					}


					page++
					if (maxPage>page && next_cursor) {
						loadXml(userId)
					} else {
						dbg.text="";
						//配置が終わったら、各県のキャラクターを分布させる
						for (i=0;i<prefectureArray.length;i++) {
							prefectureArray[i].setPosition()
						}
					}
				}

				function errorEvent(e:IOErrorEvent):void {
					guideBox.appendText("ERROR"+e+"\n");
					loader.removeEventListener(Event.COMPLETE,onComplete );
					loader.removeEventListener(IOErrorEvent.IO_ERROR, errorEvent);
					trace(e);
				}


			}



			template.onPreRender = function():void {
				//walkController.lookAt(eye.coords);
				walkController.processInput()
				//camera.coords=new Point3D(eye.x,eye.y-1000,1000)
			}

		}
		

	}
}



import alternativ5.engine3d.primitives.Plane;
import alternativ5.engine3d.primitives.Cone;
import alternativ5.engine3d.core.Sprite3D;
import alternativ5.engine3d.materials.FillMaterial;
import alternativ5.engine3d.materials.TextureMaterial;
import alternativ5.engine3d.materials.SpriteTextureMaterial;
import alternativ5.types.Texture;
import alternativ5.engine3d.events.MouseEvent3D

import flash.display.Sprite;
import flash.display.BitmapData;
import flash.display.LineScaleMode;
import flash.text.*;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.geom.Matrix;
import flash.utils.escapeMultiByte;

import jp.progression.casts.CastDocument;
import jp.progression.commands.lists.LoaderList;
import jp.progression.commands.lists.SerialList;
import jp.progression.commands.net.LoadBitmapData;
import jp.progression.commands.net.LoadURL;
import jp.progression.data.Resource;
import jp.progression.data.getResourceById;

class prefecture extends Object3D{

	private var mainMc:SimpleDemo
	private var id:uint
	private var bmd:BitmapData
	private var friendList:Array
	private var friendSet:Object3D
	private var width:Number
	private var height:Number
	private var colorArray:Array=[0xB0CFEA,0xFFB0B0,0xB0F1C0,0xDBB6FF,0xFFD3B0,0xD9FFB0,0xFFD8FF,0xFFFDB0]
	private var prefectureName:String
	private var cone:*
	private var cone2:*
	private var bmdCounter:uint=0

	public function prefecture(_mainMc:SimpleDemo,_parentMc:Object3D,_id:uint,_areaid:uint,_name:String,_x:Number,_y:Number,_width:Number,_height:Number) {
		mainMc=_mainMc
		var s:uint=6
		friendList=[]
		prefectureName=_name
		width=_width*s-4*s
		height=_height*s-4*s
		var base:Plane = new Plane(width,height);
		base.cloneMaterialToAllSurfaces(new FillMaterial(colorArray[_areaid]));
		this.addChild(base)

		x=_x*s-850/2*s+50*s
		y=-_y*s+850/2*s

		_parentMc.addChild(this)

		mainMc.addEventListener(Event.ENTER_FRAME, onEnterFrame);

		function onEnterFrame(e:Event):void {
			if (cone) {
				cone.rotationZ+=1*Math.PI/180
			}
			if (cone2) {			
				cone2.rotationZ+=1*Math.PI/180
			}
		}

	}

	public function init():void {
		if (cone) {
			this.removeChild(cone);
			cone=null;
		}
		if (cone2) {
			this.removeChild(cone2);
			cone2=null;
		}

		for (var i:uint = 0; i < friendList.length; i++) {
			var res:Resource = getResourceById(friendList[i].filename);
			if (res) {
				res.dispose()
				res=null
			}
		}
		//2010/2/24メモリリーク修正
		if(bmd) {
			bmd.dispose()
		}


		friendList=[]

	}


	public function addFriend(_name:String,_filename:String):void {
		friendList.push({name:_name,filename:_filename})
	}

	public function addFriendBmd(i:uint,f:BitmapData,_scale:Number):void {
		bmd.draw(f,new Matrix(_scale,0,0,_scale,i%12*80,(bmd.height-80)-Math.floor(i/12)*80))
	}

	private static const LOADER_CONTEXT:LoaderContext = new LoaderContext(true);

	public function setPosition():void {
		var r:Number=0
		var pol:uint=12
		if (friendList.length==1 || friendList.length==2) {
			cone=new Sprite3D();
			bmd=new BitmapData(80*friendList.length,80,true,0x00000000)
			cone.z=80/2;
			addChild(cone)
			cone.material=new SpriteTextureMaterial(new Texture(bmd),1,true);

		} else if (friendList.length) {
			pol=(friendList.length<12)? friendList.length:12
			bmd=new BitmapData(80*pol,80*Math.ceil(friendList.length/pol),true,0x00000000)
			r=80/(2*Math.sin(Math.PI/pol))
			cone=new Cone(80*Math.ceil(friendList.length/pol),r,r,1,pol,false,true);
			cone.z=80*Math.ceil(friendList.length/pol)/2*0.7;
			cone.scaleX=cone.scaleY=cone.scaleZ=0.7
			addChild(cone)
			cone.setMaterialToSurface(new TextureMaterial(new Texture(bmd),1,false,true),'side');

			cone2=new Cone(80*Math.ceil(friendList.length/pol),r,r,1,pol,true,false);
			cone2.z=80*Math.ceil(friendList.length/pol)/2*0.7;
			cone2.scaleX=cone2.scaleY=cone2.scaleZ=0.7
			addChild(cone2)
			cone2.setMaterialToSurface(new FillMaterial(0x666666),'side');

		}
		//画像の読み込み
		
		new SerialList(null,
			function():void {
				var cmd:LoaderList = new LoaderList();
				for (var i:uint = 0; i < friendList.length; i++) {
					cmd.addCommand(
						new LoadBitmapData(new URLRequest(friendList[i].filename),
							{
								context: new LoaderContext(true),
								catchError: function(target:Object, error:Error):void {
									target.executeComplete();
								}
							}
						)
					);
				}
				this.parent.insertCommand(cmd);
			}
			,
			function():void {
				for (var i:uint = 0; i < friendList.length; i++) {
					var s:Number
					var res:Resource = getResourceById(friendList[i].filename);
					var bmd:BitmapData = res.toBitmapData();
					if (bmd) {
						if (bmd.width>bmd.height) {
							s=80/bmd.width
						} else {
							s=80/bmd.height
						}
						addFriendBmd(i,bmd,s)
					}
					
				}
			}
		).execute();

	}

}



import alternativ5.engine3d.controllers.CameraController;
import alternativ5.engine3d.core.Camera3D;
import alternativ5.engine3d.core.Object3D;
import alternativ5.engine3d.core.Scene3D;
import alternativ5.engine3d.display.View;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;


/**
 * BasicTemplate for Alternativa3D
 * Alternativa3Dを扱いやすくするためのテンプレートです
 * @author Yasu
 */
class BasicTemplate extends Sprite{
	/**
	 * シーンインスタンスです。
	 */
	public var scene:Scene3D;
	/**
	 * ビューインスタンスです。
	 */
	public var view:View;
	/**
	 * カメラインスタンスです。
	 */
	public var camera:Camera3D;
	/**
	 * カメラコントローラーです。
	 */
	public var cameraContoller:CameraController;
	
	private var _viewWidth:int;
	private var _viewHeight:int;
	private var _scaleToStage:Boolean;

	/**
	 * 新しい BasicTemplate インスタンスを作成します。
	 * @param	viewWidth
	 * @param	viewHeight
	 * @param	scaleToStage
	 */
	public function BasicTemplate(viewWidth:int=640, viewHeight:int=480, scaleToStage:Boolean = true) {
		_viewWidth = viewWidth;
		_viewHeight = viewHeight;
		_scaleToStage = scaleToStage;
		
		// Creating scene
		scene = new Scene3D();
		scene.splitAnalysis = false; // not analysis for performance
		scene.root = new Object3D();
		
		// Adding camera
		camera = new Camera3D();
		camera.z = -1000;
		scene.root.addChild(camera);
		
		// camera contoller
		cameraContoller = new CameraController(this);
		cameraContoller.camera = camera;
		
		// set view
		view = new View();
		view.camera = camera;
		addChild(view);
		
		// stage
		if (stage) init();
		else addEventListener(Event.ADDED_TO_STAGE, init);
	}
	
	/**
	 * 初期化されたときに実行されるイベントです。
	 * 初期化時に実行したい処理をオーバーライドして記述します。
	 */
	protected function atInit():void {}
	
	/**
	 * 初期化されたときに実行されるイベントです。
	 * 初期化時に実行したい処理を記述します。
	 */
	private var _onInit:Function = function():void { };
	public function get onInit():Function { return _onInit; }
	public function set onInit(value:Function):void {
		_onInit = value;
	}
	
	/**
	 * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
	 * レンダリング前に実行したい処理をオーバーライドして記述します。
	 */
	protected function atPreRender():void {}
	
	/**
	 * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
	 * レンダリング前に実行したい処理を記述します。
	 */
	private var _onPreRender:Function = function():void{};
	public function get onPreRender():Function { return _onPreRender; }
	public function set onPreRender(value:Function):void {
		_onPreRender = value;
	}
	
	/**
	 * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
	 * レンダリング後に実行したい処理をオーバーライドして記述します。
	 */
	protected function atPostRender():void {
	}
	
	/**
	 * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
	 * レンダリング後に実行したい処理を記述します。
	 */
	protected var _onPostRender:Function = function():void{};
	public function get onPostRender():Function { return _onPostRender; }
	public function set onPostRender(value:Function):void {
		_onPostRender = value;
	}
	
	/**
	 * レンダリングを開始します。
	 */
	public function startRendering():void {
		addEventListener(Event.ENTER_FRAME, onRenderTick);
	}
	/**
	 * レンダリングを停止します。
	 */
	public function stopRendering():void {
		removeEventListener(Event.ENTER_FRAME, onRenderTick);
	}
	
	/**
	 * シングルレンダリング(レンダリングを一回だけ)を実行します。
	 */
	public function singleRender():void {
		onRenderTick();
	}
	
	/**
	 * @private
	 */
	private function init(e:Event = null):void {
		stage.scaleMode = StageScaleMode.NO_SCALE;
		stage.align = StageAlign.TOP_LEFT;
		stage.quality = StageQuality.HIGH;

		// resize
		stage.addEventListener(Event.RESIZE, onResize);
		onResize(null);
		
		// render
		startRendering();
		
		atInit();
		_onInit();
		
	}
	
	/**
	 * @private
	 */
	private function onRenderTick(e:Event = null):void {
		atPostRender();
		_onPostRender();
		scene.calculate();
		atPreRender();
		_onPreRender();
	}
	
	/**
	 * @private
	 */
	private function onResize(event:Event = null):void {
		if (_scaleToStage) {
			view.width = stage.stageWidth;
			view.height = stage.stageHeight;
		}else {
			view.width = _viewWidth;
			view.height = _viewHeight;
		}
	}
}