iOS에서 무한 러너를 구축하는 방법: Cocos2D, 자동화 등
게시 됨: 2022-03-11iOS 게임 개발은 개인적 성장과 재정적 성장 모두에서 풍요로운 경험이 될 수 있습니다. 올해 초 저는 Cocos2D 기반 게임인 Bee Race를 App Store에 배포했습니다. 게임 플레이는 간단합니다. 플레이어(이 경우 꿀벌)가 포인트를 수집하고 장애물을 피하는 무한 러너입니다. 데모는 여기를 참조하십시오.
이 튜토리얼에서는 Cocos2D에서 퍼블리싱까지 iOS용 게임을 개발하는 과정을 설명합니다. 참고로 다음은 짧은 목차입니다.
- 스프라이트 및 물리적 개체
- Cocos2D에 대한 간략한 소개
- 스토리보드와 함께 Cocos2D 사용
- 게임 플레이 및 (간단한) 프로젝트 설명
- 작업을 자동화합니다. 도구를 사용합니다. 침착해.
- 인앱 결제
- Game Center로 멀티플레이어 게임 플레이
- 개선의 여지
- 결론
스프라이트 및 물리적 개체
세부적인 내용을 다루기 전에 스프라이트와 물리적 개체의 차이점을 이해하는 것이 도움이 될 것입니다.
무한 러너 게임의 화면에 나타나는 특정 개체의 경우 해당 개체의 그래픽 표현을 스프라이트 라고 하는 반면 물리 엔진에서 해당 개체의 다각형 표현을 물리적 개체 라고 합니다.
따라서 스프라이트가 화면에 그려지고 해당 물리적 개체가 지원되며 물리 엔진에서 처리됩니다. 그 설정은 여기에서 시각화할 수 있습니다. 여기서 스프라이트는 녹색으로 윤곽선이 표시된 물리적 다각형과 함께 화면에 표시됩니다.
물리적 개체는 기본적으로 해당 스프라이트에 연결되지 않습니다. 즉, iOS 개발자는 사용할 물리 엔진과 스프라이트와 몸체를 연결하는 방법을 선택할 수 있습니다. 가장 일반적인 방법은 기본 스프라이트를 하위 분류하고 여기에 구체적인 물리적 본체를 추가하는 것입니다.
이를 염두에 두고…
Cocos2D iOS 게임 개발에 대한 간단한 튜토리얼
Cocos2D-iphone은 하드웨어 그래픽 가속을 위해 OpenGL을 사용하고 Chipmunk 및 Box2D 물리 엔진을 지원하는 iOS용 오픈 소스 프레임워크입니다.
우선, 왜 그러한 프레임워크가 필요합니까? 우선 프레임워크는 게임 개발에서 자주 사용되는 구성 요소를 구현합니다. 예를 들어, Cocos2D는 스프라이트(특히 스프라이트 시트(왜?))를 로드하고, 물리 엔진을 시작하거나 중지하고, 타이밍과 애니메이션을 적절하게 처리할 수 있습니다. 그리고 광범위하게 검토되고 테스트된 코드로 이 모든 작업을 수행합니다. 열등할 가능성이 있는 코드를 다시 작성하는 데 시간을 할애하는 이유는 무엇입니까?
그러나 가장 중요한 것은 Cocos2D 게임 개발이 그래픽 하드웨어 가속을 사용한다는 것 입니다. 이러한 가속이 없으면 적절한 수의 스프라이트가 있는 iOS 무한 러너 게임도 현저하게 낮은 성능으로 실행됩니다. 더 복잡한 응용 프로그램을 만들려고 하면 화면에 "불릿 시간" 효과, 즉 애니메이션을 시도할 때 각 스프라이트의 여러 복사본이 표시되기 시작할 것입니다.
마지막으로 Cocos2D는 스프라이트를 캐시하므로 메모리 사용을 최적화합니다. 따라서 복제된 스프라이트에는 최소한의 추가 메모리가 필요하며 이는 게임에 분명히 유용합니다.
스토리보드와 함께 Cocos2D 사용
Cocos2D에 대한 모든 찬사를 받은 후에도 스토리보드 사용을 제안하는 것은 비논리적으로 보일 수 있습니다. Cocos2D 등으로 개체를 조작하지 않는 이유는 무엇입니까? 음, 솔직히 말해서, 정적 창의 경우 Xcode의 Interface Builder와 Storyboard 메커니즘을 사용하는 것이 더 편리한 경우가 많습니다.
첫째, 끝없는 러너 게임의 모든 그래픽 요소를 마우스로 끌어서 배치할 수 있습니다. 둘째, Storyboard API는 매우 유용합니다. (예, Cocos Builder에 대해 알고 있습니다).
다음은 내 스토리보드를 간략히 살펴보겠습니다.
게임의 메인 뷰 컨트롤러에는 맨 위에 HUD 요소가 있는 Cocos2D 장면만 포함되어 있습니다.
흰색 배경에 주의하십시오. Cocos2D 장면으로 런타임에 필요한 모든 그래픽 요소를 로드합니다. 다른 보기(라이브 표시기, 민들레, 버튼 등)는 모두 Interface Builder를 사용하여 화면에 추가된 표준 Cocoa 보기입니다.
자세한 내용은 다루지 않겠습니다. 관심이 있는 경우 GitHub에서 예제를 찾을 수 있습니다.
게임 플레이 및 (간단한) 프로젝트 설명
(좀 더 동기를 부여하기 위해 내 끝없는 러너 게임에 대해 조금 더 자세히 설명하고 싶습니다. 기술 토론을 진행하려면 이 섹션을 건너뛰어도 됩니다.)
라이브 게임 플레이 중에는 벌이 움직이지 않고 들판 자체가 실제로 돌진하며 다양한 위험(거미와 독 꽃)과 특전(민들레와 그 씨앗)을 가져옵니다.
Cocos2D에는 캐릭터를 따라가도록 설계된 카메라 객체가 있습니다. 실제로는 게임 세계가 포함된 CCLayer를 조작하는 것이 덜 복잡했습니다.
컨트롤은 간단합니다. 화면을 탭하면 벌이 위로 이동하고 한 번 더 탭하면 아래로 이동합니다.
세계 레이어 자체에는 실제로 두 개의 하위 레이어가 있습니다. 게임이 시작되면 첫 번째 하위 레이어가 0에서 BUF_LEN까지 채워지고 처음에 표시됩니다. 두 번째 하위 계층은 BUF_LEN에서 2*BUF_LEN까지 미리 채워집니다. 꿀벌이 BUF_LEN에 도달하면 첫 번째 하위 계층이 정리되고 즉시 2*BUF_LEN에서 3*BUF_LEN으로 다시 채워지고 두 번째 하위 계층이 표시됩니다. 이런 식으로 우리는 메모리 누수를 피하는 데 중요한 부분인 더 이상 사용되지 않는 개체를 유지하지 않고 레이어를 번갈아 가며 사용합니다.
물리 엔진 측면에서 나는 두 가지 이유로 Chipmunk를 사용했습니다.
- 순수한 Objective-C로 작성되었습니다.
- 이전에 Box2D로 작업한 적이 있으므로 둘을 비교하고 싶었습니다.
물리 엔진은 실제로 충돌 감지에만 사용되었습니다. 가끔은 "왜 충돌 감지 기능을 직접 작성하지 않았습니까?"라는 질문을 받습니다. 실제로는 별로 의미가 없습니다. 물리 엔진은 바로 그 목적을 위해 설계되었습니다. 복잡한 모양의 물체 사이의 충돌을 감지하고 해당 프로세스를 최적화할 수 있습니다. 예를 들어, 물리 엔진은 종종 세계를 셀로 분할하고 동일하거나 인접한 셀에 있는 본체에 대해서만 충돌 검사를 수행합니다.
작업을 자동화합니다. 도구를 사용합니다. 침착해.
인디 무한 러너 게임 개발의 핵심 구성 요소는 작은 문제에 걸려 넘어지지 않도록 하는 것입니다. 시간은 앱을 개발할 때 중요한 자원이며 자동화는 시간을 엄청나게 절약할 수 있습니다.
그러나 때로는 자동화가 완벽주의와 기한 준수 사이의 타협이 될 수도 있습니다. 이런 의미에서 완벽주의는 Angry Birds 킬러가 될 수 있습니다.
예를 들어, 현재 개발 중인 다른 iOS 게임에서 특별한 도구(GitHub에서 사용 가능)를 사용하여 레이아웃을 생성하는 프레임워크를 구축했습니다. 이 프레임워크에는 한계가 있지만(예: 장면 간에 전환이 잘 되지 않음) 이 프레임워크를 사용하면 10분의 1로 장면을 만들 수 있습니다.
따라서 특별한 슈퍼툴로 자신만의 슈퍼프레임워크를 구축할 수는 없지만 이러한 작은 작업을 가능한 한 많이 자동화할 수 있고 또 해야 합니다.
이 무한 러너를 구축할 때 자동화가 다시 한 번 핵심이었습니다. 예를 들어, 내 아티스트는 특별한 Dropbox 폴더를 통해 고해상도 그래픽을 나에게 보냅니다. 시간을 절약하기 위해 App Store에서 요구하는 다양한 대상 해상도에 대한 파일 세트를 자동으로 빌드하는 스크립트를 작성하고 -hd 또는 @2x도 추가했습니다(해당 스크립트는 ImageMagick 기반).
추가 도구 측면에서 TexturePacker는 매우 유용하다는 것을 알았습니다. 모든 스프라이트가 단일 파일에서 읽히므로 앱이 메모리를 덜 소비하고 더 빨리 로드할 수 있도록 스프라이트 시트에 스프라이트를 패킹할 수 있습니다. 또한 거의 모든 가능한 프레임워크 형식으로 텍스처를 내보낼 수 있습니다. (TexturePacker는 무료 도구가 아니지만 그만한 가치가 있다고 생각합니다. ShoeBox와 같은 무료 대안도 확인할 수 있습니다.)
게임 물리학과 관련된 주요 어려움은 각 스프라이트에 적합한 다각형을 만드는 것입니다. 즉, 모호한 모양의 꿀벌이나 꽃을 다각형으로 표현하는 것입니다. 손으로 이 작업을 수행하려고 하지도 마십시오. 항상 특수 응용 프로그램을 사용하십시오. 그 중 많은 응용 프로그램이 있습니다. Inkspace로 벡터 마스크를 만든 다음 게임으로 가져오는 것과 같이 일부는 매우 이국적입니다.
끝없는 러너 게임 개발을 위해 Andengine Vertex Helper라고 하는 이 프로세스를 자동화하는 도구를 만들었습니다. 이름에서 알 수 있듯이 처음에는 Andengine 프레임워크용으로 설계되었지만 요즘에는 여러 형식에서 적절하게 작동합니다.

우리의 경우 plist 패턴을 사용해야 합니다.
<real>%.5f</real><real>%.5f</real>
다음으로 객체 설명이 포함된 plist 파일을 만듭니다.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>jet_ant</key> <dict> <key>vertices</key> <array> <real>-0.18262</real><real>0.08277</real> <real>-0.14786</real><real>-0.22326</real> <real>0.20242</real><real>-0.55282</real> <real>0.47047</real><real>0.41234</real> <real>0.03823</real><real>0.41234</real> </array> </dict> </dict> </plist>
그리고 객체 로더:
- (void)createBodyAtLocation:(CGPoint)location{ float mass = 1.0; body = cpBodyNew(mass, cpMomentForBox(mass, self.sprite.contentSize.width*self.sprite.scale, self.sprite.contentSize.height*self.sprite.scale)); body->p = location; cpSpaceAddBody(space, body); NSString *path =[[NSBundle mainBundle] pathForResource:@"obj _descriptions" ofType:@"plist"]; // <- load plist NSDictionary *objConfigs = [[[NSDictionary alloc] initWithContentsOfFile:path] autorelease]; NSArray *vertices = [[objConfigs objectForKey:namePrefix] objectForKey:@"vertices"]; shape = [ChipmunkUtil polyShapeWithVertArray:vertices withBody:body width:self.sprite.contentSize.width height:self.sprite.contentSize.height]; shape->e = 0.7; shape->u = 1.0; shape->collision_type = OBJ_COLLISION_TYPE; cpSpaceAddShape(space, shape); }
스프라이트가 물리적 몸체에 어떻게 대응하는지 테스트하려면 여기를 참조하십시오.
훨씬 낫죠?
요약하면, 가능하면 항상 자동화하십시오. 간단한 스크립트로도 많은 시간을 절약할 수 있습니다. 그리고 중요한 것은 그 시간을 마우스 클릭 대신 프로그래밍에 사용할 수 있다는 것입니다. (추가 동기 부여를 위해 XKCD 토큰이 있습니다.)
인앱 결제
게임에서 수집된 블로우볼은 인앱 통화로 작동하여 사용자가 꿀벌의 새 스킨을 구매할 수 있도록 합니다. 그러나 이 통화는 실제 돈으로도 구입할 수 있습니다. 인앱 결제와 관련하여 주목해야 할 중요한 점은 구매 유효성을 위해 서버 측 확인을 수행해야 하는지 여부입니다. 구매 가능한 모든 상품은 게임 플레이 측면에서 본질적으로 동일하기 때문에(단지 벌 모양만 변경) 구매 유효성을 위해 서버 확인을 수행할 필요가 없습니다. 그러나 많은 경우에 반드시 그렇게 해야 합니다.
더 많은 정보를 위해 Ray Wenderlich는 완벽한 인앱 결제 튜토리얼을 제공합니다.
Game Center로 멀티플레이어 게임 플레이
모바일 게임에서 소셜라이징 은 단순히 Facebook '좋아요' 버튼을 추가하거나 순위표를 설정하는 것 이상입니다. 게임을 더 흥미롭게 만들기 위해 멀티플레이어 버전을 구현했습니다.
어떻게 작동합니까? 먼저 iOS Game Center의 실시간 매치메이킹을 통해 두 명의 플레이어가 연결됩니다. 플레이어가 실제로 동일한 무한 러너 게임을 하고 있기 때문에 게임 개체 집합이 하나만 있으면 됩니다. 즉, 한 플레이어의 인스턴스는 개체를 생성해야 하고 다른 플레이어의 인스턴스는 해당 개체를 읽어 들입니다. 즉, 두 플레이어의 장치가 게임 개체를 생성하는 경우 경험을 동기화하기가 어렵습니다.
이를 염두에 두고 연결이 설정된 후 두 플레이어는 서로에게 임의의 번호를 보냅니다. 숫자가 더 높은 플레이어가 "서버" 역할을 하여 게임 개체를 생성합니다.
분할 세계 생성에 대한 논의를 기억하십니까? 0에서 BUF_LEN까지 하나와 BUF_LEN에서 2*BUF_LEN까지의 두 개의 하위 계층이 있는 곳은 어디입니까? 이 아키텍처는 우연히 사용된 것이 아니라 지연된 네트워크에서 부드러운 그래픽을 제공하기 위해 필요했습니다. 객체의 일부가 생성되면 plist로 패킹되어 다른 플레이어에게 전송됩니다. 버퍼는 네트워크 지연이 있더라도 두 번째 플레이어가 플레이할 수 있을 만큼 충분히 큽니다. 두 선수는 0.5초 간격으로 현재 위치를 서로 보내고 즉시 위아래 움직임을 보냅니다. 원활한 경험을 위해 위치와 속도는 부드러운 애니메이션으로 0.5초마다 수정되므로 실제로는 다른 플레이어가 점차적으로 움직이거나 가속하는 것처럼 보입니다.
멀티플레이어 끝없는 실행 게임 플레이와 관련하여 고려해야 할 사항이 분명히 더 있지만, 이것이 관련된 도전 유형에 대한 이해를 제공하기를 바랍니다.
개선의 여지
게임은 끝나지 않습니다. 물론 다음과 같이 내 자신을 개선하고 싶은 몇 가지 영역이 있습니다.
- 제어 문제: 탭하는 것은 슬라이드를 선호하는 플레이어에게 종종 직관적이지 않은 제스처입니다.
CCMoveBy 액션을 사용하여 월드 레이어를 이동합니다. CCMoveBy 액션이 CCRepeatForever와 함께 순환되었기 때문에 월드 레이어의 속도가 일정할 때는 문제가 없었습니다.
-(void) infiniteMove{ id actionBy = [CCMoveBy actionWithDuration: BUFFER_DURATION position: ccp(-BUFFER_LENGTH, 0)]; id actionCallFunc = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, actionCallFunc, nil]; id repeateForever = [CCRepeatForever actionWithAction:actionSequence]; [self.bufferContainer runAction:repeateForever]; }
그러나 나중에 게임이 진행됨에 따라 게임을 더 어렵게 만들기 위해 월드 속도 증가를 추가했습니다.
-(void) infiniteMoveWithAccel { float duration = BUFFER_DURATION-BUFFER_ACCEL*self.lastBufferNumber; duration = max(duration, MIN_BUFFER_DURATION); id actionBy = [CCMoveBy actionWithDuration: duration position: ccp(-BUFFER_LENGTH, 0)]; id restartMove = [CCCallFunc actionWithTarget:self selector:@selector(infiniteMoveWithAccel)]; id fillBuffer = [CCCallFunc actionWithTarget:self selector:@selector(requestFillingNextBuffer)]; id actionSequence = [CCSequence actions: actionBy, restartMove, fillBuffer, nil]; [self.bufferContainer runAction:actionSequence]; }
이 변경으로 인해 애니메이션이 다시 시작할 때마다 애니메이션이 느슨해졌습니다. 문제를 해결하려고 했지만 소용이 없었습니다. 그러나 내 베타 테스터는 이 동작을 알아차리지 못했고 수정을 연기했습니다.
- 한편으로는 Game Center를 사용하거나 자체 게임 서버를 실행할 때 멀티플레이어에 대한 자체 인증을 작성할 필요가 없었습니다. 반면에 봇을 생성할 수 없게 되었는데, 이는 제가 변경하고 싶은 부분입니다.
결론
자신만의 인디 무한 러너 게임을 만드는 것은 좋은 경험이 될 수 있습니다. 그리고 출판 과정의 단계에 이르면 자신의 창작물을 야생으로 공개할 때 기분이 좋을 수 있습니다.
검토 프로세스는 며칠에서 몇 주까지 걸릴 수 있습니다. 자세한 내용은 크라우드 소싱 데이터를 사용하여 현재 검토 시간을 추정하는 유용한 사이트가 있습니다.
또한 AppAnnie를 사용하여 App Store의 모든 응용 프로그램에 대한 다양한 정보를 검토할 것을 권장하며 Flurry Analytics와 같은 일부 분석 서비스에 등록하는 것도 도움이 될 수 있습니다.
그리고 이 게임에 흥미를 느꼈다면 스토어에서 Bee Race를 확인하십시오.