Alternativa3D Picture Book
Alternativa3D を簡単に扱うためのベーシックテンプレート
@author Yasu (clockmaker)
Alternativa3D
読み込んだ画像で、絵本を作る
画像の読み込みに少し時間がかかります.
キーボードの十字キーとPageUp PageDownで、カメラを動かせます。
最新のAlternativa3D Ver 7.6版は以下
http://wonderfl.net/c/A6r2
/**
* Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/8VwD
*/
// forked from clockmaker's [Alternativa3D] Basic Template
package {
import alternativ5.engine3d.materials.FillMaterial;
import alternativ5.engine3d.primitives.Box;
import alternativ5.engine3d.primitives.Plane;
import alternativ5.engine3d.events.MouseEvent3D
import alternativ5.types.Point3D;
import flash.display.*;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.ui.Mouse;
import flash.system.Security;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
[SWF(width = 465, height = 465, frameRate = 60)]
/**
* Alternativa3D を簡単に扱うためのベーシックテンプレート
* @author Yasu (clockmaker)
*/
/**
* Alternativa3D
* 読み込んだ画像で、絵本を作る
* 画像の読み込みに少し時間がかかります.
* キーボードの十字キーとPageUp PageDownで、カメラを動かせます。
* @narutohyper
*/
public class SimpleDemo extends Sprite {
//後々外部xmlからの読み込みに対応させるられるよう、XMLで、画像のURLを設定
private var dataXml:XML =
<menu>
<imgurl>http://marubayashi.net/archive/sample/images/top.jpg</imgurl>
<imgurl>http://marubayashi.net/archive/sample/images/sample01.jpg</imgurl>
<imgurl>http://marubayashi.net/archive/sample/images/sample02.jpg</imgurl>
<imgurl>http://marubayashi.net/archive/sample/images/sample03.jpg</imgurl>
<imgurl>http://marubayashi.net/archive/sample/images/sample04.jpg</imgurl>
<imgurl>http://marubayashi.net/archive/sample/images/sample05.jpg</imgurl>
</menu>;
private var plate:Plane;
private var dataCounter:uint=0;
private var dataArray:Array;
private var imgArray:Array;
private var nowPage:uint=0;
private var nextPage:uint=0;
private var interactiveFlag:Boolean=true;
private var buttonFlag:Boolean=true;
private var pagePitch:Number=0.5;
private var cameraZoom:Number;
private var cameraAngle:Number;
private var dbg:TextField;
public function SimpleDemo():void {
dbg=new TextField()
dbg.autoSize=TextFieldAutoSize.LEFT
dbg.selectable=false;
dbg.mouseEnabled=false;
var format:TextFormat=new TextFormat();
format.color=0x666666
format.size=12;
format.font='_ゴシック';
dbg.defaultTextFormat=format
this.addChild(dbg)
dataArray = new Array();
imgArray = new Array();
//取り込んだXMLデータの中の画像URL情報を配列に収納
for each (var item:String in dataXml.imgurl) {
dataArray.push(item)
}
Security.loadPolicyFile('http://marubayashi.net/crossdomain.xml');
//画像の読み込み開始
imgLoader(0)
}
//画像読み込み
private function imgLoader(no:uint):void {
var bytesLoaded:Number;
var bytesTotal:Number;
dbg.appendText(dataArray[no]+' now loading '+"\n");
var loader:Loader = new Loader();
loader.load(new URLRequest(dataArray[no]));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,loaded);
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,progressHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, error);
//読み込み監視
function progressHandler(event:ProgressEvent):void {
bytesLoaded = event.bytesLoaded
bytesTotal = event.bytesTotal
dbg.text=dataArray[no]+' now loading '+uint(bytesLoaded / bytesTotal)+"\n";
}
function loaded(e:Event):void {
dbg.appendText(dataArray[no]+' END'+"\n");
imgArray.push(loader);
dbg.appendText(dataArray[no]+' OK'+"\n");
nextProc()
}
function error(e:IOErrorEvent):void {
dbg.appendText(dataArray[no]+' BAT'+"\n");
nextProc()
}
function nextProc():void {
dataCounter++
if (dataArray.length>dataCounter) {
imgLoader(dataCounter)
} else {
init();
}
}
}
private function init():void {
this.removeChild(dbg)
// テンプレートを作成します
var template:BasicTemplate = new BasicTemplate();
addChild(template);
template.camera.x = 0
template.camera.y = -940
template.camera.z = 500
var myController:rollingCameraController = new rollingCameraController(template,stage,template.camera,200,-90)
// 背景用プリミティブを作成します
plate=new Plane(2000,1000,8,8,true,true)
plate.cloneMaterialToAllSurfaces(new FillMaterial(0x666666,1,BlendMode.NORMAL,-1,0x666666));
plate.coords=new Point3D(0,0,-100);
template.scene.root.addChild(plate);
//Plateでは、rolloverしても、マウスカーソルはオフのまま
plate.addEventListener(MouseEvent3D.MOUSE_OVER, cursorOff);
plate.addEventListener(MouseEvent3D.MOUSE_OUT, cursorOff);
// プリミティブを作成します
var planeArray:Array=[]
for (var i:uint=0;i<=imgArray.length;i++) {
if (i==0) {
planeArray[i] = new page3D(i,imgArray[0],'front');
} else if (i==imgArray.length) {
planeArray[i] = new page3D(i,imgArray[0],'back');
} else {
planeArray[i] = new page3D(i,imgArray[i]);
}
template.scene.root.addChild(planeArray[i]);
planeArray[i].z=(imgArray.length-i)*pagePitch;
planeArray[i].addEventListener(page3D.MOUSE_OVER, cursorOn);
planeArray[i].addEventListener(page3D.MOUSE_OUT, cursorOff);
planeArray[i].addEventListener(page3D.LEFT_CLICK, onNextPage);
planeArray[i].addEventListener(page3D.RIGHT_CLICK, onBackPage);
}
function cursorOn(e:*):void {
if (interactiveFlag) {
template.view.buttonMode = true;
}
buttonFlag=true;
}
function cursorOff(e:*):void {
template.view.buttonMode = false;
buttonFlag=false;
}
function onNextPage(e:*):void {
var maxPage:uint=imgArray.length
if (interactiveFlag) {
nextPage++
if (nextPage>maxPage) {
nextPage=maxPage
}
if(nowPage!=nextPage) {
interactiveFlag=false;
template.view.buttonMode = false;
if (planeArray[nextPage-1]) {
planeArray[nextPage-1].closeLeft()
}
if (planeArray[nextPage]) {
planeArray[nextPage].openRight()
planeArray[nextPage].addEventListener(page3D.OPEN_END, clickEnd);
}
for (var i:uint=0;i<=maxPage;i++) {
planeArray[i].setIndex(maxPage,pagePitch,Math.abs(i-nextPage),i-nextPage)
}
nowPage=nextPage
}
}
}
function onBackPage(e:*):void {
var maxPage:uint=imgArray.length
if (interactiveFlag) {
nextPage--
if (nextPage<0) {
nextPage=0
}
if(nowPage!=nextPage) {
interactiveFlag=false;
template.view.buttonMode = false;
if (planeArray[nextPage+1]) {
planeArray[nextPage+1].closeRight()
}
if (planeArray[nextPage]) {
planeArray[nextPage].openLeft()
planeArray[nextPage].addEventListener(page3D.OPEN_END, clickEnd);
}
for (var i:uint=0;i<=maxPage;i++) {
planeArray[i].setIndex(maxPage,pagePitch,Math.abs(i-nextPage),i-nextPage)
}
nowPage=nextPage
}
}
}
function clickEnd(e:Event):void {
planeArray[nextPage].removeEventListener(page3D.OPEN_END, clickEnd);
interactiveFlag=true;
if (buttonFlag==true) {
template.view.buttonMode = true;
}
Mouse.show();
}
//プリミティブのロールオーバー、クリックを有効にする為
//viewのbuttonModeとinteractiveをtrueにする
template.view.buttonMode = true;
template.view.interactive = true;
// Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
// レンダリング前に実行したい処理を記述します。
template.onPreRender = function():void {
// カメラの座標を中央に向かせる
template.cameraContoller.lookAt(new Point3D());
}
}
}
}
/*----------------------------------------------------------------------------------------*/
import alternativ5.engine3d.core.Object3D;
import alternativ5.engine3d.primitives.Box;
import alternativ5.engine3d.primitives.Plane;
import alternativ5.engine3d.events.MouseEvent3D
import alternativ5.engine3d.materials.FillMaterial;
import alternativ5.engine3d.materials.TextureMaterial;
import alternativ5.engine3d.materials.TextureMaterialPrecision
import alternativ5.types.Texture
import alternativ5.utils.MathUtils
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.*
import caurina.transitions.Tweener;
/**
* ページクラス
*/
class page3D extends Object3D{
public static const MOUSE_OVER:String = 'mouse_over';
public static const MOUSE_OUT:String = 'mouse_out';
public static const LEFT_CLICK:String = 'left_click';
public static const RIGHT_CLICK:String = 'right_click';
public static const OPEN_END:String = 'open_end';
public static const CLOSE_END:String = 'close_end';
private var colorArray:Array=[0xFF0000,0x0000FF,0x00FF00,0xFF00FF,0xFFFF00,0x00FFFF]
private var colorCounter:Object={front:0,back:0}
private var material:FillMaterial
private var leftPage:Object3D
private var rightPage:Object3D
private var openTime:uint=1;
private var side:String;
private var id:uint;
public function page3D(_id:uint,img:DisplayObject,_side:String=null):void {
id=_id;
var mt:Matrix = new Matrix()
side=_side
if (side!='back') {
//左ページ、表紙作成
leftPage = new Object3D()
this.addChild(leftPage)
var leftPlane:Plane=new Plane(img.width/2, img.height,1,1)
leftPage.addChild(leftPlane)
leftPlane.x=img.width/-4
//左用materialの作成
var leftImage:BitmapData
var leftMaterial:TextureMaterial
leftImage = new BitmapData(img.width/2, img.height, true, 0x00000000);
leftMaterial = new TextureMaterial(new Texture(leftImage), 1, false, true, BlendMode.NORMAL, -1);
//leftMaterial.precision = TextureMaterialPrecision.VERY_LOW;
leftPlane.setMaterialToSurface(leftMaterial,'front')
leftImage.draw(img);
leftPlane.addEventListener(MouseEvent3D.MOUSE_OVER, onMouseOver);
leftPlane.addEventListener(MouseEvent3D.MOUSE_OUT, onMouseOut);
}
if (side!='front') {
//右ページの作成、裏表紙作成
rightPage = new Object3D()
this.addChild(rightPage)
var rightPlane:Plane=new Plane(img.width/2, img.height,1,1)
rightPage.addChild(rightPlane)
rightPlane.x=img.width/4
mt.translate(-img.width/2,0)
//右用materialの作成
var rightImage:BitmapData
var rightMaterial:TextureMaterial
rightImage = new BitmapData(img.width/2, img.height, true, 0x00000000);
rightImage.draw(img,mt);
rightMaterial = new TextureMaterial(new Texture(rightImage), 1, false, true, BlendMode.NORMAL, -1);
//rightMaterial.precision = TextureMaterialPrecision.VERY_LOW;
rightPage.rotationY=MathUtils.toRadian(-180)
rightPlane.setMaterialToSurface(rightMaterial,'front')
rightPlane.addEventListener(MouseEvent3D.MOUSE_OVER, onMouseOver);
rightPlane.addEventListener(MouseEvent3D.MOUSE_OUT, onMouseOut);
}
//Planeにロールオーバー、クリックのリスナーを追加
if (side=='front') {
leftPlane.addEventListener(MouseEvent3D.CLICK, onLeftClick);
} else if (side=='back') {
rightPlane.addEventListener(MouseEvent3D.CLICK, onRightClick);
} else {
leftPlane.addEventListener(MouseEvent3D.CLICK, onLeftClick);
rightPlane.addEventListener(MouseEvent3D.CLICK, onRightClick);
}
}
/*-------------------------------------------------------
ロールオーバー、クリック動作を設定
-------------------------------------------------------*/
private function onLeftClick(e:MouseEvent3D):void {
var sid:Object=e.surface.id
dispatchEvent(new Event(LEFT_CLICK))
}
private function onRightClick(e:MouseEvent3D):void {
var sid:Object=e.surface.id
dispatchEvent(new Event(RIGHT_CLICK))
}
private function onMouseOver(e:MouseEvent3D):void {
var sid:Object=e.surface.id
dispatchEvent(new Event(MOUSE_OVER))
}
private function onMouseOut(e:MouseEvent3D):void {
var sid:Object=e.surface.id
dispatchEvent(new Event(MOUSE_OUT))
}
/*-------------------------------------------------------
ページがめくれる動作
-------------------------------------------------------*/
public function openLeft():void {
if (leftPage) {
if (leftPage.rotationY==MathUtils.toRadian(180)) {
Tweener.addTween(leftPage, {rotationY:MathUtils.toRadian(0),time:openTime,transition:"linear", onComplete:endOpen});
}
}
}
public function openRight():void {
if (rightPage) {
if (rightPage.rotationY==MathUtils.toRadian(-180)) {
Tweener.addTween(rightPage, {rotationY:MathUtils.toRadian(0),time:openTime,transition:"linear", onComplete:endOpen});
}
}
}
public function closeLeft():void {
if (leftPage) {
if (leftPage.rotationY==MathUtils.toRadian(0)) {
Tweener.addTween(leftPage, {rotationY:MathUtils.toRadian(180),time:openTime,transition:"linear", onComplete:endClose});
}
}
}
public function closeRight():void {
if (rightPage) {
if (rightPage.rotationY==MathUtils.toRadian(0)) {
Tweener.addTween(rightPage, {rotationY:MathUtils.toRadian(-180),time:openTime,transition:"linear", onComplete:endClose});
}
}
}
public function setIndex(maxPage:uint,pagePitch:Number,numZ:Number,numX:Number):void {
var pz:Number=(maxPage-numZ)*pagePitch
Tweener.addTween(this, {z:pz,time:openTime,transition:"linear"});
}
private function endOpen():void {
dispatchEvent(new Event(OPEN_END))
}
private function endClose():void {
dispatchEvent(new Event(CLOSE_END))
}
private function aRadian(radian:Number):Number {
return radian/Math.PI*180
}
}
/*----------------------------------------------------------------------------------------*/
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 {
atPreRender();
_onPreRender();
scene.calculate();
atPostRender();
_onPostRender();
}
/**
* @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;
}
}
}
//---------------------------------------------------------------------------------
//自作カメラコントロール
//---------------------------------------------------------------------------------
import alternativ5.utils.KeyboardUtils
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.utils.Timer
import flash.ui.Mouse;
class rollingCameraController {
private var camera:Camera3D;
private var angle:Number
private var action:String=''
private var zoom:Number=1
private var parentMc:*;
function rollingCameraController(_parent:*,stage:*,_camera:Camera3D,_zoom:Number=300,_angle:Number=-90):void {
parentMc=_parent; //親クラス
camera = _camera; //操作するカメラ
angle=_angle
zoom=_zoom
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
parentMc.addEventListener(Event.ENTER_FRAME, onEnterFrame);
/*
var loopTimer:Timer = new Timer(50,0);
var i=0
loopTimer.addEventListener(TimerEvent.TIMER, onEnterFrame);
loopTimer.start();
*/
rollingCamera()
}
private function onEnterFrame(e:Event):void {
if (action=='right') {
angle+=1
angle%=360
rollingCamera()
} else if (action=='left') {
angle-=1
angle%=360
rollingCamera()
} else if (action=='up') {
camera.z+=10
rollingCamera()
} else if (action=='down') {
camera.z-=10
rollingCamera()
} else if (action=='zoomIn') {
zoom+=10
rollingCamera()
} else if (action=='zoomOut') {
zoom-=10
if (zoom<1) {
zoom=1
}
rollingCamera()
trace(zoom)
} else if (action=='cameraOffsetXRight') {
//parentMc.cameraOffsetX+=10
} else if (action=='cameraOffsetXLeft') {
//parentMc.cameraOffsetX-=10
} else if (action=='cameraOffsetYUp') {
//parentMc.cameraOffsetY+=10
} else if (action=='cameraOffsetYDown') {
//parentMc.cameraOffsetY-=10
}
}
private function KeyDown(e:KeyboardEvent):void {
switch (e.keyCode) {
case KeyboardUtils.W:
case KeyboardUtils.UP:
action='up'
break;
case KeyboardUtils.S:
case KeyboardUtils.DOWN:
action='down'
break;
case KeyboardUtils.A:
case KeyboardUtils.LEFT:
action='left'
break;
case KeyboardUtils.D:
case KeyboardUtils.RIGHT:
action='right'
break;
case KeyboardUtils.Q:
case KeyboardUtils.PAGE_UP:
action='zoomIn'
break;
case KeyboardUtils.E:
case KeyboardUtils.PAGE_DOWN:
action='zoomOut'
break;
case KeyboardUtils.NUMPAD_8:
action='cameraOffsetYUp'
break;
case KeyboardUtils.NUMPAD_2:
action='cameraOffsetYDown'
break;
case KeyboardUtils.NUMPAD_4:
action='cameraOffsetXLeft'
break;
case KeyboardUtils.NUMPAD_6:
action='cameraOffsetXRight'
break;
}
}
private function Wheel(e:MouseEvent):void {
}
private function KeyUp(e:KeyboardEvent):void {
action=''
}
public function rollingCamera():void {
var radian:Number=angle/180*Math.PI;
camera.x=Math.cos(radian)*zoom
camera.y=Math.sin(radian)*zoom
Mouse.show();
}
}