球と面の衝突判定実装ログ

現状

3次元配列をもとに、ブロック(正確にはその面)を表示するところまで完了。意外と時間がかかった。
明日から、ようやく3次元の衝突判定の実装開始。

どうも会社からftpでサーバに入れないので、swfはあとで家から上げる予定。

動作

上げてみた。

コード

forEachの内部など、コードが汚いところがあるので、そこらへんを綺麗にしつつ衝突処理を実装予定。

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

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

	//Debug
	import flash.text.*;
	import flash.ui.*;
	import flash.utils.*;

	//Input
	import flash.ui.Keyboard;

	//Class
	public class Main extends Sprite {
		//Block
		private var m_BlockManager:BlockManager;

		//Input
		private var m_InputL:int = 0;
		private var m_InputR:int = 0;
		private var m_InputU:int = 0;
		private var m_InputD:int = 0;

		//Param
		private const PLAYER_VEL:Number = 160.0;
		private const GRAVITY:Number = 0.0;//-1000.0;

		//Debug
		public static var m_Label:TextField;


		//Constructor
		public function Main():void {
			{//Init Block
				m_BlockManager = new BlockManager();
				addChild(m_BlockManager);

//				m_BlockManager.m_PlayerSphere.m_CollisionAcc.y = GRAVITY;
			}

			{//Init Input
				stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
				stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
			}

			{//Init Common
				//リサイズ対応
//				stage.addEventListener(Event.RESIZE, m_BlockManager.onStageResize);

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

			{//Init Debug
				m_Label = new TextField();
				m_Label.autoSize = TextFieldAutoSize.LEFT;
				m_Label.selectable=false
				addChild(m_Label);
			}
		}


		//=Common=

		private function update( event:Event ):void {
			//プレイヤーの移動速度をセット
//			m_BlockManager.m_PlayerSphere.m_CollisionVel.x = PLAYER_VEL * (m_InputR - m_InputL);
//			if(m_InputU > 0){
//				m_BlockManager.m_PlayerSphere.m_CollisionVel.y = -PLAYER_VEL;
//			}

			//コリジョンを元に位置更新
			m_BlockManager.UpdateCollision();

			//描画
			m_BlockManager.Render();

			//更新した位置を表示に反映
//			m_Player.x = m_BlockManager.m_PlayerSphere.m_CollisionPos.x;
//			m_Player.y = m_BlockManager.m_PlayerSphere.m_CollisionPos.y;
		}

		//=Input=
		private function onKeyDown(event:KeyboardEvent):void{
			if(event.keyCode == Keyboard.LEFT){
				m_InputL = 1;
			}
			if(event.keyCode == Keyboard.RIGHT){
				m_InputR = 1;
			}
			if(event.keyCode == Keyboard.UP){
				m_InputU = 1;
			}
			if(event.keyCode == Keyboard.DOWN){
				m_InputD = 1;
			}
		}
		private function onKeyUp(event:KeyboardEvent):void{
			if(event.keyCode == Keyboard.LEFT){
				m_InputL = 0;
			}
			if(event.keyCode == Keyboard.RIGHT){
				m_InputR = 0;
			}
			if(event.keyCode == Keyboard.UP){
				m_InputU = 0;
			}
			if(event.keyCode == Keyboard.DOWN){
				m_InputD = 0;
			}
		}
	}
}


//=Local Class=

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

//Debug
import flash.text.*;
import flash.ui.*;
import flash.utils.*;

//Input
import flash.ui.Keyboard;

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



//=3次元ベクトル=
class CVector
{
	//メンバ
	public var x:Number = 0;
	public var y:Number = 0;
	public var z:Number = 0;

	//コンストラクタ
	public function CVector(in_x:Number = 0, in_y:Number = 0, in_z:Number = 0):void{
		x = in_x;
		y = in_y;
		z = in_z;
	}


	//長さ
	public function Length():Number{
		return Math.sqrt(x*x + y*y + z*z);
	}


	//=各種オペレータ=
	//本体には影響を与えない
	//vecA + vecB の代わりに vecA.Plus(vecB) とやる感じ
	//あるいは vecA.Dot(vecB) の延長で vecA.Plus(vecB) がある感じ

	//和
	public function Plus(rhs:CVector):CVector{
		return new CVector(x + rhs.x, y + rhs.y, z + rhs.z);
	}

	//差
	public function Minus(rhs:CVector):CVector{
		return new CVector(x - rhs.x, y - rhs.y, z - rhs.z);
	}

	//内積
	public function Dot(rhs:CVector):Number{
		return x * rhs.x + y * rhs.y + z * rhs.z;
	}

	//外積
	public function Cross(rhs:CVector):CVector{
		return new CVector(y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);
	}

	//スケール
	public function Scale(scl:Number):CVector{
		return new CVector(x * scl, y * scl, z * scl);
	}

	//正規化
	public function Normal():CVector{
		var distance:Number = Length();
		if(distance > 0.0){
			return new CVector(x/distance, y/distance, z/distance);
		}
		return new CVector(0, 0, 0);//err
	}

	//線形補間
	public function Lerp(rhs:CVector, ratio:Number):CVector{
		//this * (1 - ratio) + rhs * ratio
		return Scale(1.0 - ratio).Plus(rhs.Scale(ratio));
	}

	//値の比較
	public function Equals(rhs:CVector):Boolean{
		return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
	}
}


//=ブロックの配置=
var BLOCK_DATA:Array = [
	[
		[0, 0, 1, 0, 0],
		[0, 1, 1, 1, 0],
		[1, 1, 1, 1, 1],
		[0, 1, 1, 1, 0],
		[0, 0, 1, 0, 0],
	],
	[
		[0, 0, 0, 0, 0],
		[0, 0, 1, 0, 0],
		[0, 1, 1, 1, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
	],
/*
	[
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
		[0, 0, 0, 0, 0],
	],
//*/
];


//=ブロックの管理と衝突管理(できれば分けたい)=
class BlockManager 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;

	//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 = 10;
	private const SPHERE_INIT_Z:Number = 20;

	private const SPHERE_RAD:Number = 8;

	private const CAMERA_DISTANCE:Number = 400;

	private const GRAVITY:Number = 10.0;

	//ブロックの幅
	private const W:int = 20;
	private const H:int = 20;
	private const D:int = 20;


	//Constructor
	public function BlockManager():void {
		{//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.x = 50;
				m_Camera.y = 50;
				m_Camera.z = 0;
				m_Camera.focus = 500;
				m_Camera.zoom = 1;
			}

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

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

			//ブロックの描画の登録と、その境界線上にコリジョン配置
			//配列の中心が原点に来るようにする
			var centerPos:CVector = new CVector(0.5*W * BLOCK_DATA[0][0].length, 0.5*H * BLOCK_DATA.length, 0.5*D * BLOCK_DATA[0].length);
			BLOCK_DATA.forEach(
				function(innerArray:Array, indexY:int, array:Array):void{
					innerArray.forEach(
						function(innerArray2:Array, indexZ:int, array:Array):void{
							innerArray2.forEach(
								function(val:int, indexX:int, array2:Array):void{
									//境界線上に描画面とコリジョンを配置(端が抜けているが、今回は気にしない)
									var pos:CVector;
									var axis:CVector;
									if(indexX > 0){
										if(BLOCK_DATA[indexY][indexZ][indexX-1] != val){
											pos = (new CVector(W * (indexX), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
											axis = new CVector(0, 1, 0);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, H));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
														obj.rotationY = 90;
													}else{
														obj.rotationY = -90;
													}
													return obj;
												}()
											);

										}
										if(indexX == BLOCK_DATA[0][0].length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+1), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
												axis = new CVector(0, 1, 0);
	//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationY = -90;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX), H * (indexY+0.5), D * (indexZ+0.5))).Minus(centerPos);
											axis = new CVector(0, 1, 0);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, H));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCCCFF), D, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													obj.rotationY = 90;
													return obj;
												}()
											);

										}
									}

									if(indexY > 0){
										if(BLOCK_DATA[indexY-1][indexZ][indexX] != val){
											pos = (new CVector(W * (indexX+0.5), H * (indexY), D * (indexZ+0.5))).Minus(centerPos);
											axis = new CVector(1, 0, 0);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
														obj.rotationX = 90;
													}else{
														obj.rotationX = -90;
													}
													return obj;
												}()
											);
										}
										if(indexY == BLOCK_DATA.length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+0.5), H * (indexY+1), D * (indexZ+0.5))).Minus(centerPos);
												axis = new CVector(1, 0, 0);
	//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationX = -90;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX+0.5), H * (indexY), D * (indexZ+0.5))).Minus(centerPos);
											axis = new CVector(1, 0, 0);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xCCFFCC), W, D, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													obj.rotationX = 90;
													return obj;
												}()
											);

										}
									}

									if(indexZ > 0){
										if(BLOCK_DATA[indexY][indexZ-1][indexX] != val){
											pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ))).Minus(centerPos);
											axis = new CVector(0, 0, 1);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
													if(val == 1){
													}else{
														obj.rotationX = 180;
													}
													return obj;
												}()
											);
										}
										if(indexZ == BLOCK_DATA[0].length-1){
											if(val == 1){
												pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ+1))).Minus(centerPos);
												axis = new CVector(0, 0, 1);
	//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

												//描画用の面
												m_RootNode.addChild(
													function():DisplayObject3D
													{
														var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
														obj.x = pos.x;
														obj.y = pos.y;
														obj.z = pos.z;
														obj.rotationX = -180;
														return obj;
													}()
												);
											}
										}
									}else{
										if(val == 1){
											pos = (new CVector(W * (indexX+0.5), H * (indexY+0.5), D * (indexZ))).Minus(centerPos);
											axis = new CVector(0, 0, 1);
//											m_PlaneCollisionList.push(new CollisionLine(pos, axis, W));

											//描画用の面
											m_RootNode.addChild(
												function():DisplayObject3D
												{
													var obj:DisplayObject3D = new Plane(new ColorMaterial(0xFFCCCC), W, H, 8);
													obj.x = pos.x;
													obj.y = pos.y;
													obj.z = pos.z;
//													obj.rotationX = 180;
													return obj;
												}()
											);

										}
									}

								}
							);
						}
					);
				}
			);

		}

	}

	public function UpdateCollision():void{
	}

	private var m_CameraRot:Number = 0;//カメラ回転用
	//描画
	public function Render():void{
		//カメラを回転させてみる
		m_CameraRot += 0.1;
		m_Camera.x = 50 * Math.cos(m_CameraRot);
		m_Camera.z = 50 * Math.sin(m_CameraRot);
		//描画
		m_Scene.renderCamera(m_Camera);
	}

}