Beautifl - Flash Gallery

Thumbnail : Alternativa3Dでルービックキューブ 5x5
Alternativa3Dでルービックキューブ 5x5
narutohyper 2010-01-15 MIT License

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

// 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;
		}
	}
}