Cocos2D-HTML5 게임의 Box2DWeb
PUBLISHED
소개
이 문서의 목적은 개발자가 Box2dWeb에 친숙해지고 Tizen 애플리케이션에서 사용하는 방법을 설명하는 것입니다. 이러한 주제는 샘플 애플리케이션을 사용해서 설명합니다. Cocos2D-HTML5 게임에 대한 시리즈 문서(Cocos2d-html5로 Tiled Map Editor를 사용하는 방법)의 추가분입니다.
"Run Snail Run"이라는 샘플 게임이 물리 및 그래픽 라이브러리 사용법을 설명하기 위해 제공됩니다. 추가된 충돌 감지를 기반으로 한 Box2dWeb과 함께 이전 문서에서 사용된 애플리케이션의 새 버전입니다.
Box2dWeb
Box2d는 2d 게임용 물리 엔진입니다. Zlib 라이선스 하에 배포된 오픈 소스 라이브러리입니다. Java, Adobe Flash, C# 및 JavaScript를 포함한 다양한 프로그래밍 언어와 환경에 포팅되었습니다.
이 샘플 애플리케이션에서는 JavaScript에 대해 두 개의 주요 Box2d 포트 중 하나인 Box2dWeb(http://code.google.com/p/box2dweb/)을 사용합니다. 다른 주요 포트인 Box2dJS(http://box2d-js.sourceforge.net/) 보다 최신입니다. 또 다른 장점은 단일 파일에 저장할 수 있다는 것입니다.
Box2dWeb를 위한 전용 문서는 없지만 Box2dFlash 문서(http://www.box2dflash.org/docs/2.1a/reference/)를 사용할 수 있습니다. Box2dWeb은 Box2dFlash에서 컴파일되어 있고 JavaScript 1.6과 ActionScript 2.0 모두 동일한 ECMAScript 표준을 기반으로 합니다. 또한 Box2d(http://www.box2d.org/manual.html)에 매우 유용하고 포괄적인 자습서가 있습니다.
Box2dWeb은 물리 엔진입니다. 샘플 애플리케이션의 모든 그래픽은 캔버스를 사용하여 만들어집니다. 라이브러리는 강체 운동과 상호 작용을 시뮬레이션합니다. 몸체는 다각형, 원, 가장자리 모양으로 구성될 수 있습니다. 중력, 마찰 및 복원과 같은 힘에 적용할 수 있습니다.
Tizen 애플리케이션에 Box2dWeb 추가
Tizen 애플리케이션에 Box2dWeb를 포함하는 것은 매우 쉽습니다. 수행해야 하는 작업은 프로젝트로 라이브러리 파일을 가져오고 index.html 파일에서 선언하는 것입니다.
이제 JavaScript 파일에서 Box2dWeb 함수에 액세스할 수 있습니다.
Box2dWeb 세계 만들기
첫 번째 단계는 Box2dWeb 세계 개체를 만드는 것입니다. 메모리, 개체 및 시뮬레이션을 관리합니다. 샘플 애플리케이션에서 Box2dWeb 세계를 별도 게임 모듈에 만들고 관리합니다.
var world = new b2World(new b2Vec2(0, 0) // gravity , true // allow sleep );
생성자는 gravity 및 allowSleep의 두 매개 변수를 사용합니다. RunSnailRun 게임에서 충돌 감지에 대한 Box2dWeb을 사용합니다. 몸체 이동은 게임 로직에 의해 제어되므로 중력 벡터를 0으로 설정합니다. 두 번째 매개 변수 allowSleep은 움직임을 멈출 때 몸체가 수면 상태로 전환하는 것을 허용할지 여부를 결정합니다. 지나친 CPU 오버헤드를 방지합니다.
몸체 만들기
Box2dWeb 몸체는 라이브러리에서 사용하는 기본 개체입니다. 위치와 모양을 비롯하여 마찰이나 밀도와 같은 다른 매개 변수를 정의할 수 있습니다.
몸체를 만들려면 다음 단계를 수행해야 합니다.
1. 몸체 정의 만들기
달팽이와 고슴도치에 대해 동적 몸체를 사용합니다.
var bodyDef = new Box2D.Dynamics.b2BodyDef; bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;
2. 고정물 정의
고정물은 셰이프를 몸체에 부착하고 밀도, 마찰 및 복원과 같은 물질 속성을 추가합니다. 하나의 고정물은 단일 셰이프를 유지합니다. 몸체는 다양한 고정물을 가질 수 있습니다.
고슴도치에 대한 고정물 만들기는 다음 코드 샘플에 표시됩니다. 고슴도치는 동그랗게 생겼으므로 원형 셰이프를 사용할 수 있습니다.
//create fixture object var fixDef = new Box2D.Dynamics.b2FixtureDef; //define basic parameters fixDef.density = 1.0; fixDef.friction = 0.5; fixDef.restitution = 0.2; fixDef.isSensor = true; // canvas width and height in meters var height = document.documentElement.clientHeight / game.config.box2dScale; var width = document.documentElement.clientWidth / game.config.box2dScale; //define shape fixDef.shape = new Box2D.Collision.Shapes.b2CircleShape(height / 45);
셰이프 치수는 캔버스 너비와 높이를 기준으로 합니다. 고정물 정의에서 isSensor 매개 변수를 주목합니다. True 값은 몸체가 충돌하고 방향을 변경하지 않아도 몸체가 겹쳐질 때 알려줄 것을 의미합니다.
3. 고정물을 몸체에 바인딩하고 세계에 추가
마지막으로 몸체 위치를 설정하고, 고정물을 몸체 정의에 바인딩하고, 몸체를 세계에 추가해야 합니다.
bodyDef.position.x = width / 2; bodyDef.position.y = -height / 2; this.hedgehogBody = game.getBox2dWorld().CreateBody(bodyDef); this.hedgehogBody.CreateFixture(fixDef);
사용자 지정 셰이프 만들기
때로 기본 사각형 및 원형 셰이프는 정확하게 게임 개체를 정의할 수 없습니다. 고슴도치 개체는 거의 완벽하게 둥글지만, 달팽이 모양은 쉽게 만들 수 없습니다. Box2dWeb에서 자체 다각형 셰이프를 정의할 수 있습니다. 볼록 다각형만 있습니다. 즉 다음과 같습니다.
- 모든 내부 각도가 180도보다 작거나 같습니다.
- 꼭짓점 두 개 사이의 모든 선 세그먼트가 계속 내부 또는 다각형 경계선에 있습니다.
다각형 셰이프를 만들려면 해당 좌표를 제공해야 합니다. 테이블을 찾으려면 도구를 사용할 수 있습니다. 인기 있는 애플리케이션 VertexHelper Pro(http://itunes.apple.com/us/app/vertexhelper-pro/id411684411?mt=12)가 있지만 유료이며 Mac에서만 사용할 수 있습니다. 대신 Andengine Vertex Helper(http://www.andengine.org/forums/features/vertex-helper-t1370.html)와 같은 커뮤니티 생성 도구 중 하나를 사용할 수 있습니다. 꼭짓점 테이블을 쉽게 변경하기 위해 기본 패턴 벡터를 % +.5f, %+.5f로 변경할 수 있습니다.
Andengine Vertex Helper
오른쪽 좌표를 발견하면 b2Vec2 개체의 벡터를 만들 수 있습니다. PtmRatio(픽셀-미터 비율)로 좌표 값을 Box2dWorld로 변환할 수 있습니다. B2Polygon 셰이프가 다음 코드 샘플과 같이 생성될 수 있습니다.
var ptmRatio = height / 22;// scale snail shape to graphics size on the screen var v = [ [ -0.537508 * ptmRatio, -0.40000 * ptmRatio ], [ 0.35833 * ptmRatio, -0.36250 * ptmRatio ], [ 0.52500 * ptmRatio, 0.37083 * ptmRatio ], [ -0.42917 * ptmRatio, 0.37500 * ptmRatio ] ];// vector defining shape of the snail, coordinates determined using Andengine Vertex Helper tool var vecs = []; for ( var j = 0; j < v.length; j++) { var tmp = new Box2D.Common.Math.b2Vec2(); dd.Set(v[j][0], v[j][1]); vecs[j] = tmp; } fixDef.shape = new Box2D.Collision.Shapes.b2PolygonShape; fixDef.shape.SetAsArray(vecs, vecs.length);
추가 데이터 저장
Box2dWeb 몸체는 일부 추가 사용자 데이터를 저장할 수 있습니다. 샘플 애플리케이션에서는 개체 유형(달팽이 또는 고슴도치)에 대한 정보를 유지하기 위해 이를 사용합니다.
this.hedgehogBody = game.getBox2dWorld().CreateBody(bodyDef); this.hedgehogBody.SetUserData("hedgehog");
사용자 데이터를 가져오려면 GetUserData() 함수를 사용할 수 있습니다.
this.hedgehogBody.GetUserData(); //returns "hedgehog"
디버그 드로우 설정
Box2dWeb은 몸체를 표시하기 위한 함수를 제공하지 않습니다. 몸체와 상호 작용의 위치 계산을 수행할 수 있습니다. RunSnailRun 게임에서 게임 로직에 의해 개체 위치가 계산되며 충돌 감지에 대해서만 Box2dWeb이 사용됩니다.
디버그 모드
또한 투명한 Box2dWeb 몸체를 볼 수 있는 디버그 모드가 있습니다. 모든 몸체가 보고 행동할지 여부를 확인할 수 있기 때문에 디버깅 목적에서 매우 유용합니다. 이 샘플 애플리케이션에서 디버그 모드는 game.js 파일에서 config.box2dDebug 값을 사용하여 설정/해제할 수 있습니다. Box2dWeb 디버그 데이터를 렌더링하면 더욱 빨리 작동하므로 시뮬레이터에서 디버그 모드를 사용하는 것이 좋습니다.
RunSnailRun은 WebGL을 사용하여 그래픽을 렌더링합니다. 디버그 데이터를 표시하려면 전체 게임 보드를 다루는 별도 캔버스를 사용합니다. Box2dWeb에 대해 2d 컨텍스트에서 사용될 수 있습니다.
Box2dWeb 디버그 모드는 데카르트 좌표계를 사용하지 않습니다. x축 값이 오른쪽으로 증가하고, y축 값이 아래쪽으로 증가합니다. 그래서 Cocos2d 데카르트 좌표를 Box2dWeb 세계로 변환해야 합니다. 이 문서의 "세계 시뮬레이션" 부분에서 자세히 설명되어 있습니다.
setBox2dDebug : function() { if (this.config.box2dDebug) { var b2DebugDraw = Box2D.Dynamics.b2DebugDraw; var debugDraw = new b2DebugDraw(); debugDraw.SetSprite(document.getElementById("box2d").getContext("2d")); debugDraw.SetDrawScale(this.config.box2dScale); debugDraw.SetFillAlpha(0.8); debugDraw.SetLineThickness(1.0); debugDraw.SetFlags(b2DebugDraw.e_shapeBit); world.SetDebugDraw(debugDraw); } }
위의 샘플 코드에서 배율 값을 확인합니다. config.box2dScale(여기서 30과 동일)에서 설정됩니다. Box2dWeb은 자체 단위 체계를 사용합니다. 그래픽을 렌더링할 때 픽셀 좌표로 변환해야 합니다. 배율 값 30은 1미터 = 30픽셀을 의미합니다.
디버그 정보는 DrawDebugData() 함수(이 문서에서 "세계 시뮬레이션" 부분 참조)를 사용하여 업데이트 함수의 캔버스에 그려집니다.
충돌 감지
정확한 충돌 감지의 주요 목적은 이 샘플 애플리케이션의 Box2dWeb를 사용하는 것입니다. RunSnailRun의 기본 버전은 교차하는 사각형의 빠른 솔루션을 사용했습니다("Tizen 애플리케이션의 Cocos2D-HTML5 게임 프레임워크: 추가 작업" 참조). B2PolygonShape를 사용하면 정확한 셰이프 경계를 정의할 수 있습니다.
Box2dWeb은 접촉 감지에 대한 수신기(Dynamics.b2ContactListener)를 제공합니다. 4개의 함수가 있습니다.
- begin contact event,
- end contact event,
- pre-solve event
- post-solve event.
이 접촉 수신기를 설정하기 위해 SetContactListener 함수가 사용됩니다.
// Add listeners for contact var listener = new Box2D.Dynamics.b2ContactListener; var that = this; listener.BeginContact = function(contact) { if ((contact.GetFixtureA().GetBody().GetUserData() == 'hedgehog' && contact.GetFixtureB().GetBody().GetUserData() == 'snail') || (contact.GetFixtureA().GetBody().GetUserData() == 'snail' && contact.GetFixtureB().GetBody().GetUserData() == 'hedgehog')) { for ( var i = 0; i < that.snailsBodies.length; i++) { // check if it is a snail-hedgehog collision (if yes, remove the snail) if (that.snailsBodies[i] === contact.GetFixtureA().GetBody() || that.snailsBodies[i] === contact.GetFixtureB().GetBody()) { that.snailToRemove = i; // mark snail to remove } } } } // set contact listener to the world game.getBox2dWorld().SetContactListener(listener);
샘플 애플리케이션에서 접촉 시작 이벤트에만 사용됩니다. b2Contact 개체를 이벤트 처리기 함수 매개 변수로 가져옵니다. 이 개체에는 A와 B라는 두 개의 충돌 고정물이 포함되어 있습니다. 첫 번째로 수행해야 할 작업은 고슴도치-달팽이 충돌(달팽이-달팽이가 아님)인지 결정하는 것입니다. 그런 다음 충돌에 관련된 달팽이를 찾습니다. 수신기에 전달된 몸체가 잠겨 있기 때문에 수신기 내부에서 Box2d 몸체를 제거할 수 없습니다. this.snailForRemoval 변수에서 제거하려는 달팽이의 인덱스를 저장합니다. 주기적으로 업데이트 함수가 확인됩니다.
update : function(dt) { ... if (this.snailToRemove != null) { this.removeSnail(this.snailToRemove); this.snailToRemove = null; } },
해당 값이 null인 경우 removeSnail 함수로 달팽이가 제거됩니다.
removeSnail : function(index) { this.removeChild(this.snails[index]); this.numberOfSnails--; this.snails.splice(index, 1); game.getBox2dWorld().DestroyBody(this.snailsBodies[index]); this.removeChild(this.snailsBodies[index]); this.snailsBodies.splice(index, 1); if (this.numberOfSnails === 0) { ... // end game level } },
달팽이를 제거하면 Box2d 없이 애플리케이션의 첫 번째 버전에서와 같이 정확히 동일한 방식으로 작동합니다. 또한 달팽이의 몸체를 삭제해야 합니다.
세계 시뮬레이션
적절한 충돌 감지에 대해 일정 시간 간격으로 몸체 위치를 업데이트해야 합니다. 일반적으로 애플리케이션은 Box2dWeb 물리학 세계를 사용하여 개체를 이동해야 하는 방법 결정합니다(Tizen의 사용자 지정 3D 그래픽 문서 참조). RunSnailRun에서 게임 로직에 의해 수행됩니다. setPosition 함수를 사용하여 계산된 위치를 Box2dWeb 몸체에 설정합니다. 인수로 b2Vec2 개체를 인수로 사용합니다.
이 문서의 "디버그 드로우 설정" 부분에서 설명했듯이 Box2dWeb은 Cocos2d가 아닌 다른 좌표계를 사용하므로 좌표를 변환해야 합니다. 모든 픽셀 값은 Box2dWeb 배율 값인 game.config.box2dScale로 분할됩니다. 출력 값은 미터 단위입니다. 또한 세로 위치 값을 변경해야 합니다. Box2dWeb에서 y축이 아래쪽으로 증가하고, Cocox2d에서는 위쪽으로 증가합니다. 계산은 다음 코드 조각에 표시됩니다.
// set new box2dweb bodies positions this.hedgehogBody.SetPosition(new Box2D.Common.Math.b2Vec2(this.hedgehog.getX() / game.config.box2dScale, game.config.heightInMeters - (this.hedgehog.getY() / game.config.box2dScale))); for ( var i = 0; i < this.snails.length; i++) { this.snailsBodies[i].SetPosition(new Box2D.Common.Math.b2Vec2(this.snails[i].getX() / game.config.box2dScale, game.config.heightInMeters - (this.snails[i].getY() / game.config.box2dScale))); }
Box2dWeb은 일정한 주파수로 세계 매개 변수를 새로 고칩니다. world.Step 함수에서 설정할 수 있습니다.
함수 구문: world.Step(timeStep, velocityIterations, positionIterations
속도 반복과 위치 반복의 두 매개 변수를 사용합니다.
world.Step(1 / 60, 10, 10);
위의 값은 라이브러리와 함께 배포된 데모에서 가져온 값입니다. 자체 매개 변수를 선택할 수 있습니다. 이러한 값을 늘리면 Box2dWeb에 의한 계산이 더 정확해지지만 성능에 영향을 미칩니다. 더 자세한 내용은 Box2d 설명서 또는 Tizen의 사용자 지정 3D 그래픽 문서를 참조하십시오. RunSnailRun 게임에서 몸체 이동 시뮬레이션에 대해 Box2dWeb을 사용하지 않으므로 이러한 함수는 중요하지 않습니다.
디버그 모드에 있는 경우 세계 시뮬레이션의 각 반복에서 디버그 데이터를 그립니다.
world.DrawDebugData();
world.Step 및 world.DrawDebugData 함수 모두 게임 모듈에서 단일 함수(updateBox2dWeb)에 배치됩니다. 수준 모듈에서 일정 시간 간격으로 호출됩니다.
window.setInterval(this.updateBox2d, 1000 / 120, game.getBox2dWorld());
정리
각 수준 완료 후 Box2dWeb 세계를 청소해야 합니다. 고슴도치 본체 및 각 남은 달팽이 몸체에서 단일 DestroyBody 함수를 사용하여 수행할 수 있습니다.
cleanGameObjects : function() { for ( var i = 0; i < this.snails.length; i++) { ... game.getBox2dWorld().DestroyBody(this.snailsBodies[i]); this.removeChild(this.snailsBodies[i]); ... } game.getBox2dWorld().DestroyBody(this.hedgehogBody); },
요약
Cocos2d-html5로 게임 개발에 대한 문서 시리즈의 마지막 부분입니다. 이 문서에서는 Tizen 웹 애플리케이션에서 Box2dWeb을 사용하는 방법을 보여줍니다. Box2dWeb 기본 사항에 대해 설명하고 충돌 감지에 사용하는 것에 중점을 둡니다. RunSnailRun 게임인 샘플 애플리케이션은 설명한 항목을 보여줍니다.