Alternativa3Dでルービックキューブ 3x3
勝手に何でもAlternativa3Dで作っちゃおうのコーナー
3D始めるときっと作りたくなるルービックキューブです
PV3では、以前に投稿されてましたが、Alternativa3Dでも作ってみました。
3x3では、当たり前すぎなので、5x5にです。
基本設定のキューブの数で、3x3~?x?に調整できますが、数を増やすと当然重くなります。
ルービックキューブ以外をドラッグすると、ルービックキューブ全体を回転できます。
ルービックキューブの同一面で2つのキューブ上をドラッグすると、ドラッグした方向に回転します。
Alternativa3D を簡単に扱うためのベーシックテンプレート
@author Yasu (clockmaker)
/**
* Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/55sJ
*/
// forked from narutohyper's Alternativa3Dでルービックキューブ 5x5
// 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つのキューブ上をドラッグすると、ドラッグした方向に回転します。
* 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=3; //キューブの数 col x col
var size:uint=200; //一つのキューブの大きさ
var frameSize:uint=15; //枠の太さ
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;
}
}
}