
// forked from clockmaker's [Alternativa3D] Basic Template package { import alternativ5.engine3d.core.Object3D; import alternativ5.types.Point3D; import alternativ5.types.Matrix3D; import alternativ5.utils.*; import flash.display.Sprite; import flash.display.BitmapData; import flash.events.MouseEvent; import flash.events.Event; import flash.utils.* import flash.ui.Mouse; [SWF(width = 465, height = 465, frameRate = 24,backgroundColor=0x000000)] /** * 勝手に何でもAlternativa3Dで作っちゃおうのコーナー 3D始めるときっと作りたくなるルービックキューブです PV3では、以前に投稿されてましたが、Alternativa3Dでも作ってみました。 3x3では、当たり前すぎなので、5x5にです。 基本設定のキューブの数で、3x3~?x?に調整できますが、数を増やすと当然重くなります。 ルービックキューブ以外をドラッグすると、ルービックキューブ全体を回転できます。 ルービックキューブの同一面で2つのキューブ上をドラッグすると、ドラッグした方向に回転します。 昔、AS2の頃に書いたコードひっぱりだして改造したんで、かなり力技な部分もありますm(_ _)m * Alternativa3D を簡単に扱うためのベーシックテンプレート * @author Yasu (clockmaker) */ public class SimpleDemo extends Sprite { public var dragFlag:Boolean=false; public var dragWey:Point3D; public var clickCubePosition:Point3D; public var clickFaceNormal:Point3D; public var nextCubePosition:Point3D; public var isCubeDrag:Boolean=false public var isOribiting:Boolean=false public var rotationFlag:Boolean=false; public var defColor:Object public var overColor:Object private var prevPosY:Number=0 private var prevPosX:Number=0 private var prevMouseY:Number=0 private var prevMouseX:Number=0 private var differenceX:Number=0 private var differenceY:Number=0 private var rubikCube:Object3D; private var cubes:Array; public function SimpleDemo():void { // テンプレートを作成します FPS.init(this) var template:BasicTemplate = new BasicTemplate(); addChild(template); //基本設定 var col:uint=5; //キューブの数 col x col var size:uint=150; //一つのキューブの大きさ var frameSize:uint=10; //枠の太さ var pitch:uint=10; //回転スピード(90を割り切れる数字で) template.camera.z=-1500 //配色 defColor={} overColor={} defColor['def'] =0x111111 defColor['red'] =0x990000 defColor['blue'] =0x000099 defColor['white'] =0x999999 defColor['yellow']=0x999900 defColor['green'] =0x009900 defColor['orange']=0x994400 //rolloverの配色 overColor['def'] =0x000000 overColor['red'] =0xFF0000 overColor['blue'] =0x0000FF overColor['white'] =0xFFFFFF overColor['yellow'] =0xFFFF00 overColor['green'] =0x00FF00 overColor['orange'] =0xFF8800 // ルービックキューブ(全体の入れ物)を作成します rubikCube=new Object3D(); // ルービックキューブを3Dシーンのルートに追加します template.scene.root.addChild(rubikCube); rubikCube.rotationX=MathUtils.toRadian(30); rubikCube.rotationY=MathUtils.toRadian(30); // 各キューブを作成します // Cubeは、Boxクラスを継承したUserクラス cubes=new Array(); for(var i:int=0;i<col;i++) { for(var n:int=0;n<col;n++) { for(var m:int=0;m<col;m++) { cubes.push(new Cube(this,rubikCube,col-1,size,frameSize,i,n,m)); } } } //----------------------------------------------- //全体の操作 //----------------------------------------------- stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown); stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove); stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp); /*------------------------------------------------------- プリミティブのロールオーバー、クリックを有効にする為 viewのbuttonModeとinteractiveをtrueにする -------------------------------------------------------*/ template.view.buttonMode = true; template.view.interactive = true; // カメラの座標を中央に向かせる template.cameraContoller.lookAt(new Point3D()); //回転に使う変数 var moveCubes:Array=new Array(); var angle:uint=0; var xRota:int=0; var yRota:int=0; var zRota:int=0; // Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。 // レンダリング前に実行したい処理を記述します。 template.onPreRender = function():void { var box:* var str:String=''; var action:String=''; //キューブがDragされていなければ、全体を回す if(isOribiting && !isCubeDrag) { rubikCube.rotationY=prevPosY-MathUtils.toRadian(differenceX); rubikCube.rotationX=prevPosX+MathUtils.toRadian(differenceY); } else if(isCubeDrag) { if (nextCubePosition && !dragFlag) { //ドラッグで、2つのキューブを選択したら・・回転方向と位置を決める moveCubes=new Array(); dragWey = Point3D.difference(clickCubePosition,nextCubePosition); dragWey.x=Math.round(dragWey.x) dragWey.y=Math.round(dragWey.y) dragWey.z=Math.round(dragWey.z) xRota=0; yRota=0; zRota=0; dragFlag=true //動きの方向を決める if (clickFaceNormal.y) { if (dragWey.z && !dragWey.x) { //上・下面 縦; xRota=-dragWey.z*clickFaceNormal.y } else if (dragWey.x && !dragWey.z) { //上・下面 横 zRota=dragWey.x*clickFaceNormal.y } } else if (clickFaceNormal.z) { if (dragWey.y && !dragWey.x) { //前・後面 縦 xRota=dragWey.y*clickFaceNormal.z } else if (dragWey.x && !dragWey.y) { //前・後面 横 yRota=-dragWey.x*clickFaceNormal.z } } else if (clickFaceNormal.x) { if (dragWey.y && !dragWey.z) { //左・右面 縦 zRota=-dragWey.y*clickFaceNormal.x } else if (dragWey.z && !dragWey.y) { //左・右面 横 yRota=dragWey.z*clickFaceNormal.x } } //動かすキューブを抽出する for each(box in cubes) { var tempFlag:Boolean=false; if (clickFaceNormal.y) { if (dragWey.x && !dragWey.z && box.position().z==clickCubePosition.z) { tempFlag=true; } else if (dragWey.z && !dragWey.x && box.position().x==clickCubePosition.x) { tempFlag=true; } } else if (clickFaceNormal.z) { if (dragWey.x && !dragWey.y && box.position().y==clickCubePosition.y) { tempFlag=true; } else if (dragWey.y && !dragWey.x && box.position().x==clickCubePosition.x) { tempFlag=true; } } else if (clickFaceNormal.x) { if (dragWey.z && !dragWey.y && box.position().y==clickCubePosition.y) { tempFlag=true; } else if (dragWey.y && !dragWey.z && box.position().z==clickCubePosition.z) { tempFlag=true; } } if (tempFlag) { //色かえ box.over() moveCubes.push(box) } } angle=0 rotationFlag=true for each(box in cubes) { box.doStopAction() } //回ってる間、Mouseを反応させない template.view.buttonMode = false; template.view.interactive = false; } //抽出終わり } else if (rotationFlag) { //実際に動かす angle+=pitch; for each (var key:* in moveCubes) { //回転情報の作成 var mtr:Matrix3D=new Matrix3D() mtr.rotate(MathUtils.toRadian(xRota/2*pitch),MathUtils.toRadian(yRota/2*pitch),MathUtils.toRadian(zRota/2*pitch)); //キューブの位置を変更 var pt1:Point3D=key.coords; pt1.transform(mtr); key.x=pt1.x key.y=pt1.y key.z=pt1.z //ボックスの各頂点を回す。 //(rotationX,Y,Zを使うと、軸が回転するので、ややこしくなる。この方が手っ取り早い) var pt2:Point3D for each(var vt:* in key.vertices) { pt2=vt.coords pt2.transform(mtr); vt.x=pt2.x vt.y=pt2.y vt.z=pt2.z } } } } //Prerender終わり template.onPostRender = function():void { if (rotationFlag) { if (angle>=90) { //90度回ったら終了 rotationFlag=false template.view.buttonMode = true; template.view.interactive = true; for each(var box:* in cubes) { box.doStartAction() } } } } } //コンストラクタ終わり //----------------------------------------------- //全体の操作 //----------------------------------------------- private function onStageMouseDown(e:MouseEvent=null):void { isOribiting = true; prevMouseX = e.stageX; prevMouseY = e.stageY; differenceX = 0; differenceY = 0; //ルービックキューブの回転情報を記憶 prevPosY=rubikCube.rotationY prevPosX=rubikCube.rotationX } private function onStageMouseUp(e:MouseEvent=null):void { isOribiting = false; if (dragFlag) { dragFlag=false for each(var box:* in cubes) { box.def() } } isCubeDrag=false nextCubePosition=null } private function onStageMouseMove(e:MouseEvent=null):void { differenceX = e.stageX - prevMouseX; differenceY = e.stageY - prevMouseY; } } } //------------------------------------------------------------------------ //キューブクラス //------------------------------------------------------------------------ import alternativ5.engine3d.materials.FillMaterial; import alternativ5.engine3d.materials.TextureMaterial; import alternativ5.engine3d.primitives.Box; import alternativ5.engine3d.events.MouseEvent3D import alternativ5.types.Point3D; import alternativ5.types.Texture; import flash.display.BitmapData; import flash.display.BlendMode; import flash.geom.Rectangle; class Cube extends Box{ private var main:SimpleDemo private var size:Number private var textureDef:Array private var textureOver:Array private var textureMaterials:Array private var textureFaces:Array private var overFlag:Boolean=false public function Cube(mc:*,rubikCube:Object3D,col:uint,_size:Number,_frameSize:uint,posX:Number,posY:Number,posZ:Number) { main=mc; size=_size; super(size, size, size); posX=posX-col/2 posY=posY-col/2 posZ=posZ-col/2 this.x=(posX*size) this.y=(posY*size) this.z=(posZ*size) //textureを作成して、面に割り付ける var bmd:BitmapData=new BitmapData(size, size,false,0x000000); var frame:Rectangle=new Rectangle(_frameSize,_frameSize,size-_frameSize*2,size-_frameSize*2); var defColor:Object=main.defColor var overColor:Object=main.overColor var defMaterial:FillMaterial=new FillMaterial(defColor['def'],1) this.cloneMaterialToAllSurfaces(defMaterial); textureDef=[] textureOver=[] textureMaterials=[] textureFaces=[] var bmd1:BitmapData var bmd2:BitmapData bmd1=new BitmapData(size, size,false,defColor['def']); bmd2=new BitmapData(size, size,false,overColor['def']); if (posX>0) { bmd1.fillRect(frame,defColor['red']); bmd2.fillRect(frame,overColor['red']); textureDef[0]=new Texture(bmd1) textureOver[0]=new Texture(bmd2) textureFaces[0]='right'; textureMaterials[0]=new TextureMaterial(textureDef[0],1,true,true) this.setMaterialToSurface(textureMaterials[0],'right'); } else if (posX<0) { bmd1.fillRect(frame,defColor['orange']); bmd2.fillRect(frame,overColor['orange']); textureDef[0]=new Texture(bmd1) textureOver[0]=new Texture(bmd2) textureFaces[0]='left'; textureMaterials[0]=new TextureMaterial(textureDef[0],1,true,true) this.setMaterialToSurface(textureMaterials[0],'left'); } bmd1=new BitmapData(size, size,false,defColor['def']); bmd2=new BitmapData(size, size,false,overColor['def']); if (posZ>0) { bmd1.fillRect(frame,defColor['white']); bmd2.fillRect(frame,overColor['white']); textureDef[1]=new Texture(bmd1) textureOver[1]=new Texture(bmd2) textureFaces[1]='top'; textureMaterials[1]=new TextureMaterial(textureDef[1],1,true,true) this.setMaterialToSurface(textureMaterials[1],'top'); } else if (posZ<0) { bmd1.fillRect(frame,defColor['yellow']); bmd2.fillRect(frame,overColor['yellow']); textureDef[1]=new Texture(bmd1) textureOver[1]=new Texture(bmd2) textureFaces[1]='bottom'; textureMaterials[1]=new TextureMaterial(textureDef[1],1,true,true) this.setMaterialToSurface(textureMaterials[1],'bottom'); } bmd1=new BitmapData(size, size,false,defColor['def']); bmd2=new BitmapData(size, size,false,overColor['def']); if (posY>0) { bmd1.fillRect(frame,defColor['blue']); bmd2.fillRect(frame,overColor['blue']); textureDef[2]=new Texture(bmd1) textureOver[2]=new Texture(bmd2) textureFaces[2]='back'; textureMaterials[2]=new TextureMaterial(textureDef[2],1,true,true) this.setMaterialToSurface(textureMaterials[2],'back'); } else if (posY<0) { bmd1.fillRect(frame,defColor['green']); bmd2.fillRect(frame,overColor['green']); textureDef[2]=new Texture(bmd1) textureOver[2]=new Texture(bmd2) textureFaces[2]='front'; textureMaterials[2]=new TextureMaterial(textureDef[2],1,true,true) this.setMaterialToSurface(textureMaterials[2],'front'); } //textureを作成して、面に割り付ける ここまで rubikCube.addChild(this) doStartAction() } public function doStartAction():void{ this.addEventListener(MouseEvent3D.MOUSE_DOWN, onCubeMouseDown); //this.addEventListener(MouseEvent3D.MOUSE_UP, onCubeMouseUp); //画面全体のeventで処理するので、いらない this.addEventListener(MouseEvent3D.MOUSE_OUT, onCubeMouseOut); this.addEventListener(MouseEvent3D.MOUSE_OVER, onCubeMouseOver); this.addEventListener(MouseEvent3D.MOUSE_MOVE, onCubeMouseMove); } public function doStopAction():void { this.removeEventListener(MouseEvent3D.MOUSE_DOWN, onCubeMouseDown); //this.addEventListener(MouseEvent3D.MOUSE_UP, onCubeMouseUp); //画面全体のeventで処理するので、いらない this.removeEventListener(MouseEvent3D.MOUSE_OUT, onCubeMouseOut); this.removeEventListener(MouseEvent3D.MOUSE_OVER, onCubeMouseOver); this.addEventListener(MouseEvent3D.MOUSE_MOVE, onCubeMouseMove); } public function over():void { //textureをrolloverに変える if (textureFaces[0]) { textureMaterials[0]=new TextureMaterial(textureOver[0],1,true,true) this.setMaterialToSurface(textureMaterials[0],textureFaces[0]); } if (textureFaces[1]) { textureMaterials[1]=new TextureMaterial(textureOver[1],1,true,true) this.setMaterialToSurface(textureMaterials[1],textureFaces[1]); } if (textureFaces[2]) { textureMaterials[2]=new TextureMaterial(textureOver[2],1,true,true) this.setMaterialToSurface(textureMaterials[2],textureFaces[2]); } overFlag=true } public function def():void { //textureをdefaultに戻す if (textureFaces[0]) { textureMaterials[0]=new TextureMaterial(textureDef[0],1,true,true) this.setMaterialToSurface(textureMaterials[0],textureFaces[0]); } if (textureFaces[1]) { textureMaterials[1]=new TextureMaterial(textureDef[1],1,true,true) this.setMaterialToSurface(textureMaterials[1],textureFaces[1]); } if (textureFaces[2]) { textureMaterials[2]=new TextureMaterial(textureDef[2],1,true,true) this.setMaterialToSurface(textureMaterials[2],textureFaces[2]); } overFlag=false } public function position():Point3D { var result:Point3D = new Point3D(); //回転した値には、微妙にゴミが入るので丸める result.x=Math.round(x/size*2); result.y=Math.round(y/size*2); result.z=Math.round(z/size*2); return result; } public function onCubeMouseOver(e:MouseEvent3D=null):void { if (!main.isCubeDrag && !main.rotationFlag && !main.isOribiting) { this.over() } else if (!main.rotationFlag) { //次にrollOverした、Cubeで回転方向を決める //回転中はドラッグを感知させない if (!main.nextCubePosition) { main.nextCubePosition=this.position() } } } public function onCubeMouseOut(e:MouseEvent3D=null):void { this.def() } public function onCubeMouseDown(e:MouseEvent3D=null):void { //clickした情報を、SimpleDemo(親)の変数に入れる main.clickCubePosition=this.position() main.clickFaceNormal=e.face.normal; main.clickFaceNormal.x=Math.round(main.clickFaceNormal.x); main.clickFaceNormal.y=Math.round(main.clickFaceNormal.y); main.clickFaceNormal.z=Math.round(main.clickFaceNormal.z); main.isCubeDrag=true main.dragFlag=false; } public function onCubeMouseMove(e:MouseEvent3D=null):void { if (!main.isCubeDrag && !main.rotationFlag && !main.isOribiting) { this.over() } } } 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; } } }