이번에 남아공 월드컵이 있어 스타플(http://starpl.com) 에서 관련 이벤트를 하고 있습니다.


여기서는 개발자분이라면 다들 궁금해하시는 기술적(?) 설명을 드릴까 합니다.

축구게임은 Flash로 만들었습니다. Flash로 게임제작은 처음해보는 것이라 생각보다 여러 어려움이 있었습니다.
  • 제작툴 : Flash Builder 4.0 Eclipse plug-in 버전
  • 언어 : ActionScript 3.0 (Flex는 사용안함)
  • 동작환경 : Flash Player 10 이상
  • 사용한 공개라이브러리 : Papervision3d(3d엔진), jiglibflash(3D 물리엔진), TweenMax(트윈엔진)
  • 제작기간 : 10일
  • 키워드 : 축구게임엔진, 물리엔진, 3D, 게임상태변화, 사운드, 랭킹시스템, MVC

축구라는 소재를 어떻게 풀어가야할까 고민을 많이 했습니다. 그러다가 3D와 물리엔진을 도입할 필요를 느꼈고 간단하게 Away3d+jiglibflash 또는 Papervision3d+jiglibflash 로 테스트를 해봤습니다. 관련 내용은 이미 http://blog.jidolstar.com/689 에 공개했었고요.

하지만 게임이 만만치 않은게... 저 정도로 끝나지 않는다는 겁니다. 자원관리, 속도향상, 게임상태변화, 사운드등 여러가지 다양한 문제가 있더군요. 하나하나 해결해 가면서 완성해 나갔습니다. 가장 어려웠던 부분은 사운드였습니다. 디자인 부분이야 우리 회사에 막강한 캐릭터 디자이너가 있어서 문제가 없었는데... 사운드는 그런 사람이 전혀 없다보니 자원을 찾고 편집하는데 시간을 너무 소비했죠. 물론 무료를 찾느라고 시간을 다 보낸겁니다. 역시 개발자가 디자인, 음원편집을 할 수 없듯이 그에 적절한 사람을 알아두는 것도 좋을 것 같습니다.

전체적으로는 정말 간단한 게임입니다. 너무 단순해서 10일이나 걸려서 만든게 시간이 아깝다는 생각이 드네요. 다음에 만들면 더 속도를 낼 수 있겠죠? ^^

아래는 스크린 샷입니다. 구경하시죠.




하는 방법은 매우 단순합니다. 공을 찰 지점을 마우스로 선택하고 마우스 Down으로 힘조절을 한뒤 마우스 Up하면 공이 차지는 방식입니다. 공 차는 지점을 정확히 잡고 힘조절을 잘할 필요가 있습니다.

환경적 요소로 골대, 골키퍼, 수비수, 바람등이 있습니다. 레벨이 올라갈때마다 수비수가 늘어나며, 바람도 더 세집니다. 1골당 점수도 높아지죠. 골위치도 랜덤하게 되므로 높은 점수 얻는건 쉽지 않을겁니다. 스타플(http://starpl.com/)에 연계되어 최고점수를 기록하면 자신의 별에 기록되는 기능도 포함합니다.

이 게임은 2D 처럼 보이지만 그건 배경만 그렇고 실제 움직임은 전부 3D입니다. 골대도 안보이지만 3D 골대가 배치 되어 있죠. ^^ 만약 Papervision3D와 jiglibflash와 같은 공개라이브러리를 사용하지 않았다면 10일이라는 짧은 시간안에 이런 게임 만드는 것은 거의 불가능입니다. 나름 고급기술 사용했다는... ㅋ

하지만 Flash에서 3D 엔진과 물리엔진은 속도에 지극히 악영향을 줍니다. 그래서 적절한 합의점을 찾아가야 합니다. 완성본에는 공의 그림자도 모두 표현되어 있었지만 저급 PC에서 FPS가 나오지 않아서 제외시킨게 그 예이죠. 아쉽다는.... 그리고 Papervision3D 보다 Away3dlite를 이용했으면 더 좋았을 뻔 했습니다. 아무래도 Flash Player 10에서 3D API를 제공하기 때문에 이를 이용해서 만들어진 Away3dlite가 퍼포먼스 측면에서는 더 이득일 겁니다. 하지만 표현력에 있어서는 Papervision3D가 더 우세라는...

3D 물리엔진으로 jiglibflash를 사용했습니다. wow엔진이라는 것이 있는데... 이는 망한듯 합니다. 더 이상 업데이트도 안되고요. 그나마 jiglibflash가 튜토리얼도 있고 어느정도 예시도 있어서 사용했죠. jiglibflash는 papervision3d, away3d, away3dlite, sandy3d등 다양한 3D 엔진에 대응하여 물리엔진을 도입할 수 있도록 반쪽짜리 추상층을 제공합니다. 아무튼 이것을 이용하면 별 무리없이 물리엔진과 같은 효과를 만날 수 있죠. 하지만 FPS에 따라서 다르게 결과가 나오는 부분이 있어서 그다지 실용적이지는 못합니다. 말이 물리엔진이지... 그냥 눈속임이라는... 어쨌든 그나마 있어서 유용하게 썼습니다.

이 게임은 아쉽게도 1회성 이벤트 용입니다.

축구게임 이벤트 : http://starpl.com/#/main/event/big/ing/view/48

이벤트용이므로 게임을 즐기시려면 스타플(http://starpl.com)에 가입하셔야 합니다. ^^

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

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

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)

Flash, Flex, AIR, ActionScript 프로젝트를 진행하면서 모든 코드를 손수 만들어 사용하는 것은 그다지 추천할 만한 일은 아니다. 왜냐하면 아무리 실력있는 개발자라도 프로젝트에 필요한 코드모음을 전부 만드는 것은 너무 큰 노력이 들뿐만 아니라 코드의 유지보수에도 매우 많은 시간을 허비할 수 있기 때문이다.

Flash 기반 코드 라이브러리는 이미 수년간에 걸쳐 공개되어 있다. 조금만 노력을 기울이면 3D, 트윈, 물리엔진, 수학, 보안, 데이터관리, MVC,   as3corelib, Papervision3d, Away3d, Flex 컴포넌트, 각종 유틸등 ActionScript/Flex/AIR/Flash에 관련된 수십,수백가지 우량(?) 라이브러리들을 쉽게 사용할 수 있다.


위에 링크되어 있는 라이브러리만 해도 정말 현업에 바로 사용할 수 있는 것들이 정말 많다. 또한 단순히 공개차원을 넘어 지속적으로 업데이트가 되며 완성도가 높아지고 있다.

그럼 다시 생각해보자. 내가 3D 게임을 만들기 위해 3D 엔진을 만들어야할 것인가? 내가 트윈(tween)을 구현하기 위해 트윈엔진을 만들어야 할것인가? 각종유틸이 필요해서 그걸 다 내가 만들것인가? 만약 검증되지 않고 아직 별도로 공개되어 있지 않은 것이라면 당연히 만들어야할 것이다. 하지만 기본적으로 큰 노력없이 쉽게 사용할 수 있고 자동으로 업데이트 및 검증까지 해주는 사람들이 만든 훌륭한 라이브러리가 있는데... 왜!!! 그걸 다 만들 필요가 있는가이다.

재미있게도 위에서 소개한 라이브러리들은 대부분 Google 코드(http://code.google.com)에 공개되어 있다. 이는 무엇을 의미하는가? 업데이트할때마다 압축파일을 받아 라이브러리에 복사하는 수고가 필요없이 SVN으로 간단하게 업데이트 할 수 있다는 것을 말한다. 이것을 적극 활용하는 방법을 소개하는 것이 이 글을 쓰는 목적이다.

필자는 Flash 개발자라면 누구라도(?) 잘 알고 있는 Flash 3D 엔진인 Papervision3D과 Away3D를 SVN을 이용해 가져와 프로젝트에서 활용하는 방법을 언급하고자 한다. 개발환경은 Flash Builder 4를 기준으로 하겠다. (거의 모든 기능을 Flex Builder 3에서도 할 수 있다.)


1. Flex/Flash Builder에 SVN 플러그인 설치
이제부터 설명할 공개 라이브러리를 사용하기 위해 Flash builder에 SVN 플러그인을 설치해야한다. 방법은 어렵지 않다. 다음 링크를 참고한다.

Flex Builder 에서 Subversion(SVN) 사용하기


2. Google Code에서 Papervision3D SVN 주소 확인하기
Google 코드 사이트(http://code.google.com)로 들어가 papervision3d로 검색해서 Papervison3D 구글코드 페이지(http://code.google.com/p/papervision3d/)로 들어간다. Papervision3d의 라이센스와 각종 링크들을 보면 다른 정보들도 확인할 수 있다.

물론 화면 우측 Featured downloads에 있는 zip 및 swc 파일을 받아 직접 프로젝트에 복사해 사용할 수 있으나(많은 블로그에 이 방법을 소개하고 있다.) 앞서 말한데로 새로 업데이트되는 최신소스를 바로바로 사용해보려면 이 방법은 너무 불편하다. 게다가 공개 라이브러리를 사용하는게 4~5개 이상 넘어가면 지속적인 업데이트가 힘들다.

어떤 개발자는 너무 최신 버전이면 버그가 있을지도 모르지 않겠느냐? 라고 의심할 수 있을 것이다. 하지만 Papervision3D와 같이 이미 대중적(?)으로 인기를 모으고 있는 라이브러리라면 철저한 검증없이 함부로 최신화(commit) 하지 않는다.


위 Source 탭으로 들어가 Checkout 메뉴를 보면 아래처럼 SVN checkout 주소가 있다. 이 주소는 저장소의 주소라 불리운다. 여러분은 이 주소로 Papervision3D 소스 코드를 받아올 수 있겠다.




3. SVN을 이용해 Papervision3D 라이브러리 프로젝트 만들기
이제 본격적으로 Papervision3d를 SVN으로 받아와 라이브러리 프로젝트를 만들어보자.

Flash Builder를 실행한다. 실행되면 메뉴에서 File > Import 로 들어간다. 아래 처럼 창이 뜨면 SVN > Checkout Projects from SVN 을 선택한다음 Next 버튼을 누른다.



아래 처럼 보이면 새로운 저장소 위치를 만들기 위해 Create a new repository location을 선택후 Next 버튼을 누른다. 이는 새로운 저장소라는 것은 SVN을 접속할 주소를 의미한다고 보면 되겠다. 여기서는 Google 코드에서 봤던 주소 http://papervision3d.googlecode.com/svn/trunk 가 되겠다.

아래처럼 나오면 주소를 입력하고 Next 버튼을 누른다.



아래 화면처럼 나오면 http://papervision3d.googlecode.com/svn/trunk 저장소 내에 있는 Papervision3D 소스 경로를 볼 수 있다. 우리는 ActionScript 3.0 기반에서 제작하기 때문에 as3/trunk를 선택한다. 이때 선택하는 기준은 그 안에 src 폴더의 상위 폴더이어야 한다. 이 방식은 다른 라이브러리를 참조해서 하더라도 저장소내에 구조가 조금씩 다르지만 같은 방법을 사용하므로 기억하자.


위 창에서 Next 버튼을 누르면 다음 창으로 전환된다. 세부적으로 Check out 하는 방법을 설정할 수 있는데 우리는 최신 버전을 사용할 것이기 때문에 바로 Finish 버튼을 누른다.



이제 Papervison3D 라이브러리를 가져올 SVN 주소 설정을 끝냈으므로 이 라이브러리가 담길 프로젝트를 생성해야한다. 위 절차를 모두 끝내면 자동적으로 다음과 같이 New Project 창이 뜬다. 이때 Flex Library Project를 선택하면 되겠다.


다음 그림처럼 Project name을 Papervision3D(이름은 아무거나 써도 상관없다.)으로 설정하고 Flex SDK Version을 선택한 다음 Finish버튼을 누른다.


참고로 Flex SDK Version을 선택시에 라이브러리가 AIR 코드가 포함되어 있는 경우라면 Include Adobe AIR libraries를 선택해야한다.(예: as3corelib) 또한 라이브러리가 구동되기 위한 SDK가 무엇인지도 알아야한다. 가령 Flash Player 10부터 3D API를 사용한 라이브러리라면 그것을 지원하는 SDK를 사용해야한다는 것을 의미한다. SDK는 Adobe Labs에서 최신버전을 받아 사용하면 된다.(방법을 모르시면 댓글을 ^^)

아래처럼 Confirm Overwrite 메시지가 뜨면 그냥 OK 버튼을 누른다.



이제 구글 코드 저장소로부터 아래처럼 Papervision 3D 코드를 받아온다.


Package Explorer에 추가된 Papervion3D 라이브러리 프로젝트를 확인하자. 하지만 만약 에러메시지가 나온다면 다음과 같이 해보자. 해당 프로젝트를 선택하고 오른쪽 마우스 버튼을 눌러 컨텍스트 메뉴에서 Properties로 들어가보자. 아래와 같은 창이 나오면 왼쪽 메뉴에서 Flex Library Build Path를 누르고 우측 상단 탭에 Classes를 선택한다. 그리고 Select classes to include in the library를 선택한 다음 src 앞에 check박스를 체크한다. 그리고 OK 버튼을 누르면 다시 컴파일 되면서 컴파일 에러가 사라질 것이다.



여러분은 이제 최신버전의 Papervision3D를 사용할 수 있게 되었다. 아래처럼 해당 프로젝트를 선택하고 오른쪽 마우스 버튼을 눌러 컨텍스트 메뉴에서 Team > Update to Head를 선택하는 것만으로 최신버전으로 업데이트 할 수 있게 된다. 이러한 방법은 SVN으로 공유한 다른 라이브러리들을 항상 최신으로 유지하는데 쉬운 방법을 제공하게 된다.


한가지 언급하자면 위에서 소개하는 방법은 Papervision3D를 예로 들었기 때문에 다른 라이브러리의 공개 형태에 따라 수행하는 방법의 차이가 있을 수 있다는 점을 강조하고 싶다. 그러한 점은 개발자가 알아서 체크할 수 있어야 한다.


4. 조금만 더 가다듬어 보기 (관심있는 사람만 읽으면 됨)
3번까지 진행하는 것만으로도 개발하는데 전혀 문제없지만 아래 내용을 좀더 알고 가면 더욱 좋을 듯 싶다.

지금까지 구글 코드로부터 SVN 저장소를 통해 받아온 Papervison3d 라이브러리는 설정파일을 제외한 ActionScript 3.0 코드가 대부분이다. 그래서 실질적으로 프로젝트에 필요한 설정구성요소인 .actionScriptProperties나 .flexLibProperties등의 파일들은 Papervision3D 개발자가 처음부터 저장소에 공유하지 않는다. 왜냐하면 .actionScriptProperties, .flexLibProperties들은 Flex Builder나 Flash Builder와 같이 특정 개발툴에서 필요한 파일이기 때문에 다른 IDE에서는 무의미 할 수 있기 때문이다.

아래 처럼 Papervision3D의 프로젝트 이름에 SVN으로 공유되었으나 폴더그림에 * 표시가 되어 있다. 이 표시는 원래 SVN 저장소 내용과 프로젝트 내용이 다를때 나온다. 이 다른 내용이라는 것은 금방 언급했던 .actionScriptProperties, .flexLibProperties 등이다.


Flash Builder에서는 프로젝트내의 .actionScriptProperties, .flexLibProperties 파일을 직접 볼 수 없도록 되어 있다. 이는 Filter 처리가 되어 있기 때문이다. 아래 그림처럼 선택해 Filter 처리된 파일들을 전부 check out하자.



위 그림 처럼 Filter 처리된 파일을 check out하면 아래처럼 프로젝트 내에 .actionScriptProperties, .flexLibProperties 등을 볼 수 있게 된다. 이 파일의 아이콘에 ?로 표시된 것은 저장소에 이 파일이 존재하지 않음을 나타내는 것이다.

* 표시나 ? 표시를 없애기 위해서 이러한 파일들이 SVN 저장소와 공유되지 않도록 무시할 수 있도록 하면 된다. Flash Builder에 설치된 SVN 플러그인은 이러한 기능을 지원해준다. 메뉴에서 window > Preference로 들어가 창이 뜨면 Team > Ignored Resources로 들어가면 아래 처럼 나온다. 여기에 다음 내용을 Add Pattern하자.

.actionScriptProject
.actionScriptProperties
.flexProperties
.flexProject
.project
bin
.settings
참고 : http://labs.adobe.com/wiki/index.php/Flex_Builder:resources:plugins:subversion:filtering




이제 아래 그림처럼 * 표시나 ? 표시가 없어지고 저장소의 원본과 프로젝트의 소스가 일치함을 나타내는 표시로 바뀐다.




5. Away3D를 SVN으로 받아오기

Papervision3D 라이브러리를 이용해서 애플리케이션을 만드는 예제는 굳이 설명안해도 된다고 생각한다. 대신 Away3D를 활용하는 방법을 약간 언급하고자 한다.

Away3D도 Papervision3D처럼 구글코드(http://code.google.com/p/away3d/)를 통해 SVN으로 소스를 공개하고 있다. 하지만 Away3D는 Papervision3D와 다르게 Flash Player 9, Flash Player 10 버전을 함께 제공하면서 Flash Player 10 3D 라이브러리를 이용한 3D 엔진인 Away3DLite도 제공한다. 또한 몇가지 유용한 예제도 함께 제공하고 있다.


Away3D 저장소 주소(http://away3d.googlecode.com/svn/trunk)로 접속하면 아래처럼 나온다. fp9과 fp10은 각각 Flash Player 9, 10 버전을 의미한다. 아무래도 Flash Player 10 버전이 최신이면서 효율적으로 만들어졌으므로 fp10에 있는 공개 라이브러리와 예제소스를 참고할 것이다.

fp10에 보면 Away3D, Away3DLite 소스가 있음을 확인할 수 있다. 또한 그에 대한 예제는 Examples에 들어 있다.


아래는 해당 라이브러리와 예제의 저장소 주소이다.


여러분은 Papervision3D를 SVN으로 프로젝트를 만든 것처럼 Away3D에서도 그래도 하면 된다. 단. Examples의 경우 중간에 프로젝트 생성시 ActionScript 프로젝트를 선택하기 바란다. 물론 Flash Player 10 기준이라는 점도 명심하자.

예제 프로젝트에서는 해당 라이브러리 프로젝트의 경로를 포함시켜줘야한다. Away3D_Example은 Away3D 라이브러리를 Away3DLite_Examples는 Away3dLite 라이브러리를 포함해야한다. 방법은 간단하다. Away3D_Example와 Away3DLite_Examples내에 libs 폴더안에 각각의 라이브러리 프로젝트에서 만들어진 bin디렉토리에 있는 
swc를 복사하면 된다. 하지만 이는 애플리케이션을 전부 만들고 배포하고 난 다음에 백업을 위해 선택할 방법이고 실제로 개발할때는 다음과 같이 한다.

1. Away3D_Example 프로젝트를 선택한다.
2. 메뉴에서 Project > Properties를 선택한다.
3. 창이 뜨면 좌측 ActionScript Build Path(Flex의 경우 Flex Build Path)를 선택한다.
4. 상단 Library path 탭을 선택한다.
5. Add Project를 선택한 다음 Away3D 라이브러리를 선택한다.
6. OK 버튼을 누른다.
7. Away3D_Example의 .actionScriptProperties를 연다.
아래 그림처럼  default package에 있는 소스 들은 전부 애플리케이션들이다. 이 코드들이 진정한 애플리케이션으로서 동작하려면 .actionScriptProperties내에 application으로 등록해야한다. 수작업이긴 해도 전부 등록하자. 또는 해당 as 파일을 선택후 마우스 오른쪽 버튼을 눌러 Set as Default Application 항목을 선택해도 된다. 이는 애플리케이션으로 등록하는 동시에 Default 애플리케이션으로 잡아준다. Default 애플리케이션을 지칭하는 것은 .actionScriptProperties 파일 내에 actionScriptProperties 태그의 mainApplicationPath 속성이 그 역할을 한다.



위 처럼 설정하면 이제 예제 파일들을 아무 문제없이 동작시킬 수 있을 것이다.
8. Away3DLite_Example도 위와 비슷한 방법으로 하면 되겠다.

아래는 Away3DLite_Examples에 있는 Advanced_Mario1000.as 애플리케이션을 실행하는 화면이다.

아마 어떤 애플리케이션은 as3dmod 라이브러리가 필요하다는 에러가 나올것이다. 이것도 비슷한 방법으로 Google Code에서 가져올 수 있다. 단, 몇가지 다른 설정을 해야하는데 여기서 설명하지 않고 각자의 몫으로 돌리겠다.


정리하며

개발자라면 Google Code와 친해져야하지 않을까? 또한 기존 라이브러리가 있는가 검색을 해보는 성의가 있어야하지 않을까? 그리고 그 라이브러리를 잘 활용하는 방법을 스스로 깨달으려고 노력해야하지 않을까?

잘못된 사항이나 더 좋은 정보가 있다면 얼마든지 댓글이나 트랙백 부탁한다.


참고
bs-papervision : http://code.google.com/p/bs-papervision/ 
as3dmod : http://code.google.com/p/as3dmod/
sandy3d : http://www.flashsandy.org 
alternativa3D : http://alternativaplatform.com/en/alternativa3d/

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



 

Flash 3D에 관련된 3D 엔진 및 학습할 만한 사이트를 골라 네이버 오픈캐스트에 발행했습니다. 요즘 Flash 3D에 푹빠져 있는데 관심있는 분들과 함께 공부하고 싶네요. 전 3D 초급수준이라 공부할 것도 많네요. ㅎㅎ

 

http://opencast.naver.com/FL188/5

 

좋은 자료 되길 바랍니다.

 

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

 

Adobe AIR로 만들어진 상용프로그램 중 하나인 EarthBrowser를 소개한다.

 

EarthBrawser는 이름 그대로 지구에서 일어나는 주요 환경 변화, 사건등을 3D로 묘사한 세어웨어 AIR 프로그램이다. AIR로 만들어졌기 때문에 Mac OSX  및 Linux와 MS Window에 설치가 가능하다.

 

Adobe AIR Marketplace에 가면 프로그램을 설치할 수 있다. 단 유료라서 때문에 7일간만 Demo버전으로 이용할 수 있다.

 

아래는 EarthBrowser 공식 홈페이지 화면이다. http://www.earthbrowser.com/로 방문하면 된다.


홈페이지에서는 Free버전도 다운로드 받을 수 있지만 기능이 제한되어 있다.

 

EarthBrowser는 Flash Player 기반인 AIR 설치형 프로그램이기 때문에 다른 여느 3D 프로그램처럼 빠르진 않다. 하지만 Flash Player인데도 사용할 만한 속도와 각종 여러 정보를 한눈에 볼 수 있게 한 것이 매력이다.

 

아래 그림은 EarthBrowser를 실행한 화면이다. Google Earth와 같이 마우스 스크롤로 확대 축소가 가능하며 드래그를 하면 지구가 돌아간다. 아마도 Papervision3DAway3D 같은 프로그램으로 3D를 구현했을 것 같다.

 

 

 

아래는 축소한 모습이다.

 

 

아래는 확대한 모습이다. 한국의 서울까지 확대해 봤다.

백두산으로 가봤다. 화산표시가 되어 있고 그 표시를 누르니 AIR의 웹브라우져인 Webkit이 뜨면서 백두산에 대한 정보가 나오고 있다. 중국명칭인 장백산이 아닌 백두산이라고 나와서 그나마 다행이다… 설명에 중국-한국 경계라고 해서 좀 그렇지만..

 

EarthBrowser는 현재 2년간 자유롭게 업그레이드 할 수 있다는 조건에 $29.95로 가격이 책정되어 있다. Domo버전은 7일밖에 사용하지 못한다. 구입후 Code를 입력하면 사용할 수 있게 된다. Demo버전을 사용하면 주기적으로 이 창이 뜬다.

 

EarthBrowser는 주요위성의 위치, 각국 지역 날씨(온도,강수량,대기압,습도,풍향,바다온도,오존), 화산정보, 구름의 시각화, 오로라, 태풍정보, 나라별 국기, 지진정보,

아래는 대전의 1주일 날씨를 보여준다.

 

 

 

아래 그림은 나라별 국기 모습이다.

 

 

한국 국기를 누르니 아래처럼 한국 소개 브라우져가 나온다. 헉!! 일본해?!

 

각 지역별 온도분포를 보여주고 있다. 놀랍다…

 

 

아래는 강수량을 보여준다. 오늘 비왔는데… 잘 보여주는 듯 싶다.

 

각지역 웹캠으로 그 지역의 모습을 보여주기도 한다. 와우~~

 

우리나라 최초 우주인 이소연씨가 타고 왔던 국제우주정거장(ISS)의 위치도 잡힌다. 옆에 GALEX 위성도 보인다. 정말 멋지다!

 

정리하며…

예전에 Adobe Flex로 만들어진 각종 애플리케이션을 보면서 감탄을 했는데… 오늘 Adobe AIR로 만들어진 EarthBrowser를 보면서 놀라움을 금치 못했다. 그래픽 가속을 이용하지 않기 때문에 실행시 약간 버벅거림이 있는 것은 사실이지만 EarthBrowser는 Adobe RIA기술이 어디까지 확장할 수 있는지 잘 보여줄 수 있는 프로그램이라 생각한다. 단순히 Flash, AIR는 3D 가속이 안되기 때문에 느리다는 편견을 버리고 Cross Browse, Cross OS의 강점을 잘 살려서 그만의 독특한 프로그램을 만들어 내어 상용화까지 이르게 하는 창의적 생각을 하는 것이 중요하다고 생각했다.

 

개인적으로 천문프로그래밍에 관심이 많다. 스타플(http://starpl.com), 천문노트(http://astronote.org)를 개발하고 운영하면서 지금도 천문프로그래밍에 대한 좋은 아이디어를 찾고 있다.  Adobe AIR로 개발해서 Adobe AIR Marketplace 에 프로그램을 올리는 것도 나쁠 것 같지 않다.

 

Adobe AIR Marketplace가 나와서 말인데, 난 오늘 처음 이런게 있는지 알았다. App Store만 있다고 생각했는데 Adobe에서도 이런게 있었다니… 비록 App Store에 올라온 애플리케이션과 비교할 때, 40:1 수준으로 많이 부족하다. 정책 및 상용화에 대한 근본적인 차이가 있었을 것이라 생각한다. Adobe가 AIR를 개발하면서 어떻게 하면 상용화로 이끌 수 있을지 많이 고민했다면 App Store 수준까지는 아니더라도 지금 엄청나게 많은 AIR 애플리케이션이 쏟아졌을텐데… 안타깝다.

 

한국에서도 EarthBrowser와 같은 훌륭한 AIR 애플리케이션이 많이 나오길 희망한다.

Adobe AIR?

Adobe AIR는 여러 운영체제(Mac OSX, Windows, Linux)에서도 설치가 가능한 설치형 프로그램이다.  Cross Browser의 대명사인 Flash Player 기술이 브라우져에 종속의 한계를 넘어 Cross OS 설치형 프로그램을 가능하게한 것이 바로 Adobe AIR이다. AIR는  HTML/Javascript, Flash, Flex등으로 개발할 수 있다. 이러한 개발 방법의 다양화는 요즘 대세인 듯 싶다.  Adobe Alchemy로 C/C++코드를 Adobe Flash/Flex/AIR에서 사용할 수 있게 했듯이 말이다. ^^

참고사이트

예전부터 Flex4 프로젝트의 일환인 Gumbo를 다운로드를 받았지만 쓸 기회가 없어서 안쓰다가 Flash Player 10에서 3D 회전 예제가 있어서 한번 해봤다.

Flex Builder 3에서 작업을 하기 위해 수행해야할 일은 다음과 같다.

1. Gumbo 최신버전을 다운받는다. [다운로드 받으러가기]

2. 다운로드 받는 Flex 4 SDK를 Flex Builder 설치폴더안에 sdks 폴더에 압축하여 풀어준다.

3. Flex Builder 3를 실행해서 메뉴에 Windows->Preperences...에 들어가면 창이 뜬다. 창 왼쪽에 Flex->Installed Flex SDKs를 선택한 다음 아까 압축을 푼 SDK폴더를 등록한다.

4. File->New->ActionScript Project를 선택하면 창이 뜬다. 창 아래부분에 Use a specific SDK를 선택한다음 Flex 4를 선택하고 프로젝트 명을 CubeRotationExample로 프로젝트를 만든다.

5. 아래 소스를 복사해서 메인 소스에 덮어쓴다.

 

package{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
 
	public class CubeRotationExample extends Sprite {
		
		private static const EDGE	:Number = 40;		//edge length
		private var mtxWorld		:Matrix3D;			//world transform matrix
		private var vCube			:Vector.<Number>;	//cube points
		private var vFlatCube		:Vector.<Number>;	//projected cube points
		private var vUVT			:Vector.<Number>;	//uvt data
		private var vIndicies		:Vector.<int>;		//triangle indicies
		private var sprCube			:Sprite;			//canvas object
		
		public function CubeRotationExample(){
			// setup and initialize
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.BEST;			
			
			mtxWorld = new Matrix3D();
			vCube = Vector.<Number>([	EDGE,EDGE,-EDGE,
				 						EDGE,-EDGE,-EDGE, 
										-EDGE,-EDGE,-EDGE,
										-EDGE,EDGE,-EDGE, 
										EDGE,EDGE,EDGE,
										EDGE,-EDGE,EDGE,
										-EDGE,-EDGE,EDGE,
										-EDGE,EDGE,EDGE
										]);
			vFlatCube = new Vector.<Number>(16);
			vUVT = new Vector.<Number>(24);
			vIndicies = Vector.<int>([0,1,2, 2,3,0, 4,7,6, 6,5,4, 0,4,5, 5,1,0, 1,5,6, 6,2,1, 2,6,7, 7,3,2, 4,0,3, 3,7,4]);
			
			sprCube = new Sprite();
			addChild(sprCube);

			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onEnterFrame(eo:Event):void {
			// center the cube
			sprCube.x = stage.stageWidth/2;
			sprCube.y = stage.stageHeight/2;
			
			// rotate the transform matrix
			mtxWorld.appendRotation(2, new Vector3D(0,1,0));
			mtxWorld.appendRotation(2, new Vector3D(0,0,1));
			mtxWorld.appendRotation(2, new Vector3D(1,0,0));
			
			// project 3D data to 2D plane
			Utils3D.projectVectors(mtxWorld, vCube, vFlatCube, vUVT);
			
			// draw the cube using cube data
			sprCube.graphics.clear();
			sprCube.graphics.beginFill(0xFF00FF);
			sprCube.graphics.lineStyle(1,0xFF0000,1);
			sprCube.graphics.drawTriangles(vFlatCube, vIndicies, null, TriangleCulling.POSITIVE);
			sprCube.graphics.endFill();
		}
	} 
}

 

6. 저장하면 에러가 날거다. Flash Player 10에서 작동하므로 그에 따른 설정을 해주어야 한다. 메뉴에서 Project->Properties 로 들어가면 창이 뜬다. 왼쪽메뉴에 ActionScript Compiler를 선택하고 오른쪽 아래 Require Flash Player version을 10.0.0으로 맞춰준다.

7. 실행한다.(단 Flash Player 10이 설치되어 있어야 한다.)


[실행화면]


다시한번 언급하지만 Flash Player 10 환경에서만 된다.

위의 소스를 보면 예전 ActionScript 3.0에서 볼 수 없었던 것들이 보인다.
Vector, Matrix3D, Util3D등이다. Papervision3D와 같은 것을 쓰지않아도 쉽게 네이티브 코드로 3D를 구현할 수 있게 한 Flash Player 10. 아직 다양한 표현을 위한 많은 API를 가지고 있지 않지만 더욱 발전될 가능성이 크다고 생각한다. 기대해보겠다.

[소스출처]
http://dispatchevent.org/calebjohnston/flash-player-10-3d-example/

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

+ Recent posts