한창 축구게임을 만들고 있다. 이에 관련되어 테스트 해본 코드를 공유해본다.

3D를 표현하기 위해 Papervision3d와 물리엔진을 표현하기 위해 jiglibflash를 이용했다. 공을 클릭하면 임의의 힘이 주어지고 클릭한 부분을 참조하여 방향이 결정된다. 키보드 A,S,D,W로 카메라 조정이 된다.

원더플(wonderfl.net)에 소스를 올려두었으니 부디 많은 Fork가 이뤄져서 더 좋은 모습으로 탈바꿈 되는 모습을 보고 싶다. ^^

http://wonderfl.net/c/j8ly

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.BlurFilter;
	import flash.geom.Vector3D;
	import flash.system.IME;
	import flash.system.IMEConversionMode;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;
	import flash.utils.getTimer;
	
	import jiglib.cof.JConfig;
	import jiglib.geometry.JBox;
	import jiglib.geometry.JCapsule;
	import jiglib.geometry.JPlane;
	import jiglib.geometry.JSphere;
	import jiglib.geometry.JTerrain;
	import jiglib.math.JMatrix3D;
	import jiglib.physics.RigidBody;
	import jiglib.plugin.papervision3d.Papervision3DPhysics;
	import jiglib.plugin.papervision3d.Pv3dMesh;
	import jiglib.plugin.papervision3d.pv3dTerrain;
	
	import net.hires.debug.Stats;
	
	import org.papervision3d.Papervision3D;
	import org.papervision3d.core.utils.InteractiveSceneManager;
	import org.papervision3d.core.utils.Mouse3D;
	import org.papervision3d.events.FileLoadEvent;
	import org.papervision3d.events.InteractiveScene3DEvent;
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.materials.BitmapMaterial;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.MovieAssetMaterial;
	import org.papervision3d.materials.MovieMaterial;
	import org.papervision3d.materials.WireframeMaterial;
	import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
	import org.papervision3d.materials.shaders.FlatShader;
	import org.papervision3d.materials.shaders.GouraudShader;
	import org.papervision3d.materials.shaders.LightShader;
	import org.papervision3d.materials.shaders.PhongShader;
	import org.papervision3d.materials.shaders.ShadedMaterial;
	import org.papervision3d.materials.special.FogMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.parsers.Collada;
	import org.papervision3d.objects.parsers.DAE;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Cylinder;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.view.BasicView;
	import org.papervision3d.view.layer.ViewportLayer;
	import org.papervision3d.view.layer.util.ViewportLayerSortMode;
	
	[SWF(width="465", height="465", backgroundColor="#000000", frameRate="24")]
	/**
	 * Papervision3D + jiglibflash 을 이용한 축구게임 프로토 타입 
	 * 간단하게 만들어 봤습니다. Fork를 통해 향상되고 멋진 기능을 가진 게임이 탄생하기 바랍니다. 
	 * 1. click ball
	 * 2. press key w,a,s,d
	 * 
	 * author : Yongho Ji, jidolstar@gmail.com
	 */ 
	public class SoccerGamePrototype extends BasicView {
		private var _physics:Papervision3DPhysics;
		private var _light:PointLight3D;
		private var _vpBallLayer:ViewportLayer;
		
		private var _ground:Plane;
		private var _ball:RigidBody;
		private var _ballRadius:Number = 30;
		
		private var _isShooting:Boolean = false;
		private var _isIniting:Boolean = false;
		private var _firstTime:Number;
		private var _ballX:Number;
		private var _windForce:Vector3D;
				
		private var _keyForward:Boolean=false;
		private var _keyBackward:Boolean=false;
		private var _keyLeft:Boolean=false;
		private var _keyRight:Boolean=false;
		
		/**
		 * 생성자 
		 */ 
		public function SoccerGamePrototype() {
			super(465, 465, true, true, "Target");
			configStage();
			configScene();
			initPhysics();
			createBall();
			createGround();
			createGoalNet();
			createGoalPost();
			createBox();
			startRendering();
			addEventHandler();
			addChild(new Stats);
		}
		
		/**
		 * 스테이지 환경 설정 
		 */ 
		private function configStage():void {
			stage.scaleMode=StageScaleMode.NO_SCALE;
			stage.align=StageAlign.TOP_LEFT;
			stage.quality=StageQuality.BEST;
			stage.frameRate = 24;
			stage.showDefaultContextMenu = false;
		}
		
		/**
		 * 이벤트 핸들러 함수 등록 
		 */ 
		private function addEventHandler():void {
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
		}
		
		/**
		 * 3D 환경 기본 설정 
		 */ 
		private function configScene():void {
			viewport.containerSprite.sortMode=ViewportLayerSortMode.INDEX_SORT;
			viewport.containerSprite.addLayer(_vpBallLayer=new ViewportLayer(viewport, null));
			_vpBallLayer.sortMode=ViewportLayerSortMode.Z_SORT;	

			camera.x=-500;
			camera.y=200;
			camera.z=0;
			
			_light=new PointLight3D(true, false);
			_light.x=-400;
			_light.y=300;
			_light.z=-100;
		}
		
		/**
		 * 물리엔진 기본 설정 
		 */ 
		private function initPhysics():void {
			_physics=new Papervision3DPhysics(scene, 10);
		}
		
		/**
		 * 바닥 생성  
		 */ 
		private function createGround():void {
			_ground = new Plane( new WireframeMaterial(0xff0000), 1000, 1000, 10, 10 );
			scene.addChild(_ground);
			var jGround:JPlane = new JPlane(new Pv3dMesh(_ground));
			jGround.setOrientation(JMatrix3D.getRotationMatrixAxis(90));
			_physics.addBody(jGround);
			jGround.y = 0;
			jGround.friction = 10;
		}
		
		/**
		 * 그물망 생성
		 */ 
		private function createGoalNet():void {
			var i:Number, dx:Number = 15, dy:Number = 15, dd:Number = 15;
			var w:Number = 600, h:Number = 300, d:Number = 100;
			var movie : Sprite = new Sprite();
			var g:Graphics = movie.graphics;
			var jGoalNet:RigidBody;
			var material:MovieMaterial,materials:MaterialsList;
			
			//Back
			g.lineStyle(3,0xaaaaaa,2);
			for( i = 0; i <= w; i+=dx ) {
				g.moveTo(i,0);
				g.lineTo(i,h);
			}
			for( i = 0; i <= h; i+=dy ) {
				g.moveTo(0,i);
				g.lineTo(w,i);
			}
			material = new MovieMaterial(movie,true,false,false,null);
			material.oneSide = false;
			materials=new MaterialsList();
			materials.addMaterial(material, "back");
			jGoalNet = _physics.createCube( materials, w, 100, h, 5, 1, 5, 0, 0 );
			jGoalNet.movable = false;
			jGoalNet.rotationY = 90;
			jGoalNet.material.restitution = 0.01;
			jGoalNet.moveTo(new Vector3D(600+50,150,0,0));
			
			//Top
			movie = new Sprite;
			g = movie.graphics;
			g.lineStyle(3,0xaaaaaa,2);
			for( i = 0; i <= w; i+=dx ) {
				g.moveTo(i,0);
				g.lineTo(i,d);
			}
			for( i = 0; i <= d; i+=dd ) {
				g.moveTo(0,i);
				g.lineTo(w,i);
			}			
			jGoalNet = _physics.createCube( materials, w, 1, d, 3, 1, 1, 0, 0 );
			jGoalNet.movable = false;
			jGoalNet.rotationY = 90;
			jGoalNet.rotationZ = 90;
			jGoalNet.material.restitution = 0.01;
			jGoalNet.moveTo(new Vector3D(550,300,0,0));
			
			//Left
			movie = new Sprite;
			g = movie.graphics;
			g.lineStyle(3,0xaaaaaa,2);
			for( i = 0; i <= d; i+=dd ) {
				g.moveTo(i,0);
				g.lineTo(i,h);
			}
			for( i = 0; i <= h; i+=dy ) {
				g.moveTo(0,i);
				g.lineTo(d,i);
			}			
			jGoalNet = _physics.createCube( materials, d, 1, h, 1, 1, 1, 0, 0 );
			jGoalNet.movable = false;
			jGoalNet.material.restitution = 0.01;
			jGoalNet.rotationY = -180;
			jGoalNet.moveTo(new Vector3D(550,h/2,-w/2,0));
			
			//Right
			jGoalNet = _physics.createCube( materials, d, 1, h, 1, 1, 1, 0, 0 );
			jGoalNet.movable = false;
			jGoalNet.material.restitution = 0.01;
			jGoalNet.moveTo(new Vector3D(550,h/2,w/2,0));
			
		}
		
		/**
		 * 볼 포스트 생성 - Cylinder 3개로 생성 
		 */ 
		private function createGoalPost():void {
			function createCapsule($radius:Number,$height:Number):JCapsule {
				var material:FlatShadeMaterial = new FlatShadeMaterial(_light, 0xFFFFFF, 0x555555, 5);
				var cylinder:Cylinder = new Cylinder(material,$radius,$height,15,1,-1,true,false);
				var jCapsule:JCapsule = new JCapsule(new Pv3dMesh(cylinder),$radius,$height);
				jCapsule.movable = false;
				jCapsule.material.restitution = 0.9;
				scene.addChild(cylinder);
				_physics.addBody( jCapsule );
				return jCapsule;
			}	
			
			var jCapsule:JCapsule;
			jCapsule = createCapsule(10,300);
			jCapsule.moveTo(new Vector3D(500,150,-300,0));
			jCapsule = createCapsule(10,300);
			jCapsule.moveTo(new Vector3D(500,150,300,0));
			jCapsule = createCapsule(10,610);
			jCapsule.rotationX = 90;
			jCapsule.moveTo(new Vector3D(500,300-5,0,0));
		}
		
		/**
		 * 4개의 Box들 생성 
		 */ 
		private function createBox():void {
			var materials:MaterialsList=new MaterialsList();
			materials.addMaterial(new FlatShadeMaterial(_light, 0x00FF00, 0x555555, 5), "all");
			var box:RigidBody;
			for (var i:int=1; i < 5; i++) {
				box=_physics.createCube(materials, 100, 100, 100, 3, 3, 3);
				box.mass=1;
				box.x = 300;
				box.y=300 * i;
				_vpBallLayer.addDisplayObject3D(_physics.getMesh(box));
			}
		}
		
		/**
		 * 축구공 생성 
		 */ 
		private function createBall():void {
			var material:FlatShadeMaterial = new FlatShadeMaterial(_light, 0xFFFFFF, 0x555555, 105);
			_ball=_physics.createSphere(material, _ballRadius, 10, 8);
			material.interactive = true;
			_vpBallLayer.addDisplayObject3D(_physics.getMesh(_ball));
			_ball.x=-300;
			_ball.y=300;
			_ball.mass=1;
			_ball.friction=10;
			
			var mesh:DisplayObject3D = _physics.getMesh(_ball);
			mesh.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press);
			
			//공을 누른다.
			function press($e:InteractiveScene3DEvent=null):void {
				mesh.removeEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press );
				mesh.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release );
			}
			//공을 찬다!
			function release($e:InteractiveScene3DEvent=null):void {
				mesh.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press );
				mesh.removeEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release );				
				shootBall();
			}			
		}
		
		/**
		 * 축구공을 찬다.
		 */ 
		private function shootBall():void {
			_isShooting = true;
			_ballX = _ball.x;
			_firstTime = getTimer();
			
			var mesh:DisplayObject3D = Pv3dMesh(_ball.skin).mesh;
			mesh.calculateScreenCoords(camera); //공의 스크린 좌표 계산. 3D->2D
			var kickPower:Number = Math.random()*500+200;
			var kickPosZTheta:Number = Math.tan( (stage.mouseX-mesh.screen.x-viewport.width/2)/_ballRadius )
			var kickPosYTheta:Number = Math.tan( (stage.mouseY-mesh.screen.y-viewport.height/2)/_ballRadius );
			var kickPowerX:Number = kickPower * Math.cos(kickPosYTheta)*Math.cos(kickPosZTheta);
			var kickPowerY:Number = kickPower * Math.sin(kickPosYTheta);
			var kickPowerZ:Number = kickPower * Math.cos(kickPosYTheta)*Math.sin(kickPosZTheta);
			_ball.addWorldForce(new Vector3D(kickPowerX,kickPowerY,kickPowerZ),_ball.currentState.position);
			_ball.addBodyTorque(new Vector3D(0, 0, 0));
			
			_windForce = new Vector3D( 0, 0, Math.random()*10-5);
		}
				
		/**
		 * 3D 렌더링, 물리엔진 가동, 키보드에 따른 카메라 이동, 축구공 액션 
		 */ 
		protected override function onRenderTick(event:Event=null):void {
			_physics.step(); 
			if( _isShooting ) {
				_ball.addWorldForce(_windForce,_ball.currentState.position);
				if( _ball.x > 500 || _ball.x < _ballX || getTimer() - _firstTime > 2000 ) {
					_isShooting = false;
					_isIniting = true;
					_firstTime = getTimer();
				}
				_ballX = _ball.x;
			}
			if( _isIniting ) {
				if( getTimer() - _firstTime > 2000 ) {
					_isIniting = false;
					_ball.x=-300;
					_ball.y=400;
					_ball.z = 0;
					_ball.setVelocity( new Vector3D(0,0,0) );
				}
			}
			moveCamera();
			super.onRenderTick(event);
		}
		
		/**
		 * 카메라 이동 
		 */ 	
		private function moveCamera():void {
			var dist:Number = 30;
			if (_keyForward) {
				camera.moveForward(dist);
			}
			if (_keyBackward) {
				camera.moveBackward(dist);
			}
			if (_keyLeft) {
				camera.moveLeft(dist);
			}
			if (_keyRight) {
				camera.moveRight(dist);
			}
		}
		
		/**
		 * 키보드 Down 핸들러 
		 */ 
		private function keyDownHandler(e:KeyboardEvent):void {
			if (e.keyCode == 229) {
				IME.conversionMode=IMEConversionMode.ALPHANUMERIC_HALF;
			}
			switch (e.keyCode) {
				case 87:
					_keyForward=true;
					break;
				case 83:
					_keyBackward=true;
					break;
				case 65:
					_keyLeft=true;
					break;
				case 68:
					_keyRight=true;
					break;
				
			}
		}
	
		/**
		 * 키보드 Up 핸들러 
		 */ 
		private function keyUpHandler(e:KeyboardEvent):void {
			switch (e.keyCode) {
				case 87:
					_keyForward=false;
					break;
				case 83:
					_keyBackward=false;
					break;
				case 65:
					_keyLeft=false;
					break;
				case 68:
					_keyRight=false;
					break;
			}
		}

	}
}




글쓴이 : 지돌스타(http://blog.jidolstar.com/689)

+ Recent posts