Papervision3DとWiiFlashの接続のまとめ
解説
つなぐ際の問題になりそうなのは「加速度の軸の方向」と、「どうやって直方体を回転させるか」あたり。「加速度の軸の方向」はコードを見てもらえばわかるので、「どうやって直方体を回転させるか」だけ説明。
加速度はベクトルであり3次元の値である。これでは「方向」はわかっても「姿勢」はわからない。例えば「指差してる方向」はわかっても「親指がどっちの方向にあるか(手の回転角度)」まではわからない。
Wiiリモコンから取れるのも加速度のみなので、ここから姿勢は一意には決まらない。例えばWiiリモコンを床に置いた場合の加速度は一定であり、そのリモコンが北を向いてるか東を向いてるかまではわからないということ。
なので、こちらで回転の仕方を制限してやる必要がある。
ここでやっているのは「"Y軸"と"リモコンの上方向の軸"によって形成される平面状の回転」。簡単に言うと「もともとの上方向をリモコンの上方向に持っていく」というだけのこと。その回転軸を「Y軸」と「リモコンの加速度の逆方向」から求めている。角度の求め方まで含めて、結構ここらへんは適当にやってしまっている。
(説明が上手くまとまらなかったので、あとで書き直すかも。書き直さないかも。)
(あと、YouTubeとかに動画を上げた方がわかりやすそう)
コード
//wiimotedemo.as package{ //label import flash.display.*; import flash.text.*; import flash.events.*; import flash.ui.*; import flash.utils.*; //PV3D import org.papervision3d.core.*; import org.papervision3d.core.proto.*; import org.papervision3d.scenes.*; import org.papervision3d.objects.*; import org.papervision3d.cameras.*; import org.papervision3d.materials.*; import mx.core.BitmapAsset;//Material //WiiFlash import org.wiiflash.Wiimote; import org.wiiflash.events.ButtonEvent; //Class public class wiimotedemo extends Sprite { //WiiFlash private var wiimote:Wiimote = new Wiimote(); //PV3D private var container : Sprite; private var scene : Scene3D; private var camera : Camera3D; private var rootNode : DisplayObject3D; private var obj:Array = new Array(); //Label private var label:TextField; //Constructor public function wiimotedemo():void { //Init Common { //リサイズ対応 stage.addEventListener(Event.RESIZE, onStageResize); //毎フレーム呼ばせる関数の登録 addEventListener(Event.ENTER_FRAME, myLoopEvent); } //Init WiiFlash { //サーバに接続したときのリスナ wiimote.addEventListener(Event.CONNECT, handleConnect); //Aボタンが押されたときのリスナ wiimote.addEventListener(ButtonEvent.A_PRESS, handleAPress); //接続開始 wiimote.connect(); } //Init PV3D { //表示用のSpriteオブジェクトを生成 container = new Sprite(); container.x = 400 / 2; // at center : swf width = 400 container.y = 400 / 2; // at center : swf height = 400 addChild(container); //シーンオブジェクトを作成 scene = new Scene3D(container); //カメラオブジェクトを作成 camera = new Camera3D(); camera.x = 0; camera.y = 0; camera.z = -100;//リモコンに見立てた直方体を後ろから見る camera.focus = 500; camera.zoom = 1; //ルートノードを作成 rootNode = new DisplayObject3D(); scene.addChild(rootNode); //オブジェクトの作成 obj.push(createCube()); //オブジェクトをルートノードに追加 for(var i:int; i<obj.length; i++){ rootNode.addChild(obj[i]); } } //Init Debug Label { //デバッグ表示用のラベルの作成 label = makeLabel(""); addChild(label); } } //=WiiFlash= //WiiFlash Serverに接続したときのリスナ private function handleConnect(e:Event):void { trace("Connect"); } //WiiリモコンのAボタンが押されたときのリスナ private function handleAPress(e:Event):void { trace("A Press"); wiimote.rumbleTimeout = 500; wiimote.rumble = true; //Wiiリモコンを振動させる } //=PV3D= //Create Material private function CreateMaterial():MaterialsList { //前後左右の区別のため、各面の色を分ける var materialList:MaterialsList = new MaterialsList(); materialList.addMaterial(new ColorMaterial(0x000000), "front"); materialList.addMaterial(new ColorMaterial(0xFFFFFF), "back"); materialList.addMaterial(new ColorMaterial(0xFF0000), "right"); materialList.addMaterial(new ColorMaterial(0x00FF00), "left"); materialList.addMaterial(new ColorMaterial(0x0000FF), "top"); materialList.addMaterial(new ColorMaterial(0xFFFF00), "bottom"); return materialList; } //Create Cube private function createCube():DisplayObject3D { var material:MaterialsList = CreateMaterial(); var width:Number = 80; var depth:Number = 40; var height:Number = 30; var cube:Cube = new Cube(material, width, depth, height); // cube.x = 0;//50;//位置はループ内で指定 // cube.y = 0;//-100; // cube.z = 0;//-100 return cube; } //=Common= //Main Loop private function myLoopEvent( event:Event ):void { //Update for(var i:int; i<obj.length; i++){ // wiimote.sensorY;//横持ち時のXの値 // wiimote.sensorZ;//横持ち時のYの値 // wiimote.sensorX;//横持ち時のZの値 var axisY:Number3D = new Number3D(0, 1, 0);//直方体の上方向 var nrm:Number3D = new Number3D(wiimote.sensorY, wiimote.sensorZ, -wiimote.sensorX);//リモコンの上方向 //nrmを長さ1に収める(加速度なので、1より大きかったり0だったりしうる) nrm.normalize(); if(nrm.modulo < 0.01){ nrm = new Number3D(0, 1, 0); } //axisYとnrmによって作られる平面上で回転させることにする var axis:Number3D = Number3D.cross(axisY, nrm);//なので、その平面での回転軸を求める if(axis.modulo < 0.01){//平面が形成できない=axisYとnrmがほぼ同じ軸=じゃあ適当にZ軸でよい axis = new Number3D(0, 0, 1); }else{ axis.normalize(); } //回転する角度をaxisYとnrmから求める var angle:Number = Math.acos(Number3D.dot(axisY, nrm)); if(Number3D.dot(Number3D.cross(axis, axisY), nrm) < 0){ angle = -angle; } //実際に回転を適用 obj[i].transform = Matrix3D.rotationMatrix( axis.x, axis.y, axis.z, angle ); //位置はそのまま原点 } //Render scene.renderCamera(camera); //Debug label.text = ""+wiimote.sensorX+"\n"+wiimote.sensorY+"\n"+wiimote.sensorZ+"\n"; } //Resize private function onStageResize(event:Event):void { container.x = stage.stageWidth / 2; container.y = stage.stageHeight / 2; } //=Debug= //Create Label private function makeLabel(text:String):TextField { var label:TextField=new TextField(); label.text =text; label.autoSize =TextFieldAutoSize.LEFT; label.selectable=false; return label; } } }