PV3D+WiFlash+WoW-Engineのサンプル

内容

Wiiリモコンの傾きに応じて箱が回転し、その中を球が転がるサンプルです。


実際には、箱が回転しているのではなく、重力方向を変えているだけなので、回転によって球に箱からの力が加わったりすることはありません。
基本的に、視点はWiiリモコンを上から見た形になるので、ボールは奥側に転がっていきます。


WiiFlash Serverを立ち上げなければ操作できないので、コードのみ置いておきます。

コード

//mxmlc WiiWoW.as
//author Show=O=Healer

/*
  Wiiリモコンで傾けた箱の中を球が転がるサンプル
  実際には箱は移動しておらず、重力方向を変化させている
  そのため、急激に傾けても箱からの力は加わらないため、球の移動はゆっくり
  箱(m_BoundArea)の中をボール(m_Sphere)が転がっていて、それを上から見てる状況
*/

package {
	//Common
	import flash.display.*;
	import flash.events.*;

	//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.*;

	//WoW
	import fr.seraf.wow.constraint.*;
	import fr.seraf.wow.core.*;
	import fr.seraf.wow.core.data.*;
	import fr.seraf.wow.math.*;
	import fr.seraf.wow.primitive.*;
	import fr.seraf.wow.util.*;

	//WiiFlash
	import org.wiiflash.Wiimote;
	import org.wiiflash.events.ButtonEvent;

	//Class
	public class WiiWoW extends Sprite {
		//Common
		private var m_Container	: Sprite;

		//PV3D
		private var m_Scene		: Scene3D;
//		private var m_Camera	: Camera3D;
		private var m_Camera	: FreeCamera3D;
		private var m_RootNode	: DisplayObject3D;
//		private var m_BoundArea	: DisplayObject3D;
		private var m_Sphere	: DisplayObject3D;
		private var m_Cube		: DisplayObject3D;

		//WoW
		private var m_WoWEngine:WOWEngine;
		private var m_BoundArea_WoW:WBoundArea;
		private var m_Sphere_WoW:WSphere;

		//WiiFlash
		private var m_Wiimote:Wiimote = new Wiimote();

		//Param
		private const BOUND_AREA_W:Number = 600;
		private const BOUND_AREA_H:Number = 200;
		private const BOUND_AREA_D:Number = 500;

		private const SPHERE_INIT_X:Number = 0;
		private const SPHERE_INIT_Y:Number = 100;
		private const SPHERE_INIT_Z:Number = -50;

		private const SPHERE_RAD:Number = 20;

		private const CAMERA_DISTANCE:Number = 400;

		private const GRAVITY:Number = 10.0;


		//Constructor
		public function WiiWoW():void {
			{//Init Common
				//リサイズ対応
				stage.addEventListener(Event.RESIZE, onStageResize);

				//定期的にupdateを呼ばせる
				addEventListener(Event.ENTER_FRAME, update);
			}

			{//Init PV3D
				{//m_Container
					// 表示用の Sprite オブジェクトを生成
					m_Container = new Sprite();
					m_Container.x = 400 / 2; // at center : swf width	= 400
					m_Container.y = 400 / 2; // at center : swf height = 400
					addChild(m_Container);
				}

				{//m_Scene
					// シーンオブジェクトを作る
					m_Scene = new Scene3D(m_Container);
				}

				{//m_Camera
					// カメラオブジェクトを作る
//					m_Camera = new Camera3D();
					m_Camera = new FreeCamera3D();
					m_Camera.z = -CAMERA_DISTANCE;
					m_Camera.focus = 500;
					m_Camera.zoom = 1;
				}

				{//m_RootNode
					// ルートノードを作る
					m_RootNode = new DisplayObject3D();
					m_Scene.addChild(m_RootNode);
				}

				{//m_BoundArea
					//Planeを内側に表示する

					//front
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFFFFF), BOUND_AREA_W, BOUND_AREA_H, 8);
							obj.z =  0.5*BOUND_AREA_D;
							return obj;
						}()
					);
					//back
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xAAAAAA), BOUND_AREA_W, BOUND_AREA_H, 8);
							obj.z = -0.5*BOUND_AREA_D;
							obj.rotationY = 180;
							return obj;
						}()
					);
					//right
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFAAAA), BOUND_AREA_D, BOUND_AREA_H, 8);
							obj.x = 0.5*BOUND_AREA_W;
							obj.rotationY = 90;
							return obj;
						}()
					);
					//left
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xAAAAFF), BOUND_AREA_D, BOUND_AREA_H, 8);
							obj.x = -0.5*BOUND_AREA_W;
							obj.rotationY = -90;
							return obj;
						}()
					);
					//top
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xAAFFAA), BOUND_AREA_W, BOUND_AREA_D, 8);
							obj.y = 0.5*BOUND_AREA_H;
							obj.rotationX = 90;
							return obj;
						}()
					);
					//bottom
					m_RootNode.addChild(
						function():DisplayObject3D
						{
							var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFFFAA), BOUND_AREA_W, BOUND_AREA_D, 8);
							obj.y = -0.5*BOUND_AREA_H;
							obj.rotationX = -90;
							return obj;
						}()
					);
				}

				{//m_Sphere
					m_Sphere = function():DisplayObject3D
					{
						var material:ColorMaterial = new ColorMaterial(0x000000);

						var sphere:DisplayObject3D = new Sphere(material, SPHERE_RAD);
						sphere.x = SPHERE_INIT_X;
						sphere.y = SPHERE_INIT_Y;
						sphere.z = SPHERE_INIT_Z;

						return sphere;
					}();

					m_RootNode.addChild(m_Sphere);
				}
			}

			{//Init WoW
				{//m_WoWEngine
					m_WoWEngine= new WOWEngine();
					m_WoWEngine.collisionResponseMode = m_WoWEngine.SELECTIVE;
					m_WoWEngine.addMasslessForce(new WVector(0.1, -1, 0));//重力っぽいもの
					m_WoWEngine.damping = 1;
				}

				{//m_BoundArea
					m_BoundArea_WoW = new WBoundArea(BOUND_AREA_W, BOUND_AREA_H, BOUND_AREA_D);
					m_BoundArea_WoW.setPosition(0, 0, 0);//中心位置
					m_BoundArea_WoW.elasticity = 1;//弾性
					m_BoundArea_WoW.friction = 0.050;//摩擦
					m_WoWEngine.setBoundArea(m_BoundArea_WoW);
				}

				{//m_Sphere_WoW
					var IsFixed:Boolean = false;
					m_Sphere_WoW = new WSphere(SPHERE_INIT_X, SPHERE_INIT_Y, SPHERE_INIT_Z, SPHERE_RAD, IsFixed, 43, 0.65);
					m_WoWEngine.addParticle(m_Sphere_WoW);
				}
			}

			//Init WiiFlash
			{
				//サーバに接続したときのリスナ
				m_Wiimote.addEventListener(Event.CONNECT, handleConnect);

				//接続開始
				m_Wiimote.connect();
			}

		}


		//=Common=

		private function update( event:Event ):void {
			//リモコンの方向から、重力の方向を決定して変更。カメラ位置もそれに合わせる
			{
				var gravityX:Number = -m_Wiimote.sensorY;
				var gravityY:Number = -m_Wiimote.sensorZ;
				var gravityZ:Number =  m_Wiimote.sensorX;

				m_WoWEngine.masslessForce = new WVector(GRAVITY*gravityX, GRAVITY*gravityY, GRAVITY*gravityZ);

				var cameraDir:Number3D = new Number3D(-gravityX, -gravityY, -gravityZ);
				cameraDir.normalize();
				m_Camera.x = cameraDir.x * CAMERA_DISTANCE;
				m_Camera.y = cameraDir.y * CAMERA_DISTANCE;
				m_Camera.z = cameraDir.z * CAMERA_DISTANCE;

				var upAxis_Normal:Number3D = Number3D.cross(new Number3D(-1, 0, 0), cameraDir);
				var upAxis_CaseX:Number3D = new Number3D(0, 0, 1);
				var ratio:Number = cameraDir.x;
				if(ratio < 0.0){ratio = -ratio;}
				var upAxis:Number3D = new Number3D(
					upAxis_Normal.x * (1.0-ratio) + upAxis_CaseX.x * ratio,
					upAxis_Normal.y * (1.0-ratio) + upAxis_CaseX.y * ratio,
					upAxis_Normal.z * (1.0-ratio) + upAxis_CaseX.z * ratio
				);
				upAxis.normalize();

				m_Camera.lookAt(DisplayObject3D.ZERO, upAxis);
			}

			//物理エンジンの更新
			m_WoWEngine.step();

			{//球体の位置を物理エンジンの計算結果に対応させる
				m_Sphere.x = m_Sphere_WoW.px;
				m_Sphere.y = m_Sphere_WoW.py;
				m_Sphere.z = m_Sphere_WoW.pz;
			}

			//描画
			m_Scene.renderCamera(m_Camera);
		}

		private function onStageResize(event:Event):void {
			m_Container.x = stage.stageWidth	/ 2;
			m_Container.y = stage.stageHeight / 2;
		}
		
		//=WiiFlash=

		//WiiFlash Serverに接続したときのリスナ
		private function handleConnect(e:Event):void
		{
			trace("Connect");
		}

	}
}