iOS'ta Sonsuz Koşucu Nasıl Oluşturulur: Cocos2D, Otomasyon ve Daha Fazlası

Yayınlanan: 2022-03-11

iOS oyunları geliştirmek hem kişisel hem de finansal büyüme açısından zenginleştirici bir deneyim olabilir. Bu yılın başlarında, Cocos2D tabanlı bir oyun olan Bee Race'i App Store'a yerleştirdim. Oynanışı basittir: oyuncuların (bu durumda arıların) puan topladığı ve engellerden kaçındığı sonsuz bir koşucu. Demo için buraya bakın.

Bu eğitimde, Cocos2D'den yayınlamaya kadar iOS için oyun geliştirmenin arkasındaki süreci açıklayacağım. Başvuru için, kısa bir içindekiler tablosu:

  • Spritelar ve fiziksel nesneler
  • Cocos2D'ye kısa bir giriş
  • Cocos2D'yi storyboard'larla kullanma
  • Oynanış ve (kısa) proje açıklaması
  • İşleri otomatikleştirin. Araçları kullanın. Rahat ol.
  • Uygulama içi faturalandırma
  • Game Center ile çok oyunculu oyun
  • İyileştirme odası
  • Çözüm

Spritelar ve fiziksel nesneler

Cesur ayrıntılara girmeden önce, sprite ve fiziksel nesneler arasındaki farkı anlamak yardımcı olacaktır.

Sonsuz bir koşucu oyununun ekranında görünen herhangi bir varlık için, o varlığın grafiksel temsiline hareketli grafik adı verilirken, o varlığın fizik motorundaki poligonal temsiline fiziksel bir nesne adı verilir.

Böylece hareketli grafik, karşılık gelen fiziksel nesne tarafından desteklenerek ekrana çizilir ve daha sonra fizik motorunuz tarafından işlenir. Bu kurulum, ekranda sprite'ların fiziksel poligonal karşılıkları yeşil renkle özetlenmiş olarak görüntülendiği burada görselleştirilebilir:

iOS sonsuz koşucu oyunlarında, sprite'lar ve fiziksel nesneler bir arada bulunur.

Fiziksel nesneler varsayılan olarak kendi sprite'larına bağlı değildir; bu, iOS geliştiricisi olarak hangi fizik motorunun kullanılacağını ve sprite ve gövdelerin nasıl bağlanacağını seçebileceğiniz anlamına gelir. En yaygın yol, varsayılan hareketli grafiği alt sınıflara ayırmak ve ona somut bir fiziksel gövde eklemektir.

Bunu akılda tutarak…

Cocos2D iOS oyun geliştirme hakkında kısa bir eğitim

Cocos2D-iphone, donanım grafik hızlandırması için OpenGL kullanan ve Chipmunk ve Box2D fizik motorlarını destekleyen, iOS için açık kaynaklı bir çerçevedir.

Her şeyden önce, neden böyle bir çerçeveye ihtiyacımız var? Başlangıç ​​olarak, çerçeveler oyun geliştirmenin sık kullanılan bileşenlerini uygular. Örneğin, Cocos2D hareketli grafikleri (özellikle hareketli grafik sayfaları (neden?) yükleyebilir), bir fizik motorunu başlatabilir veya durdurabilir ve zamanlama ve animasyonu düzgün bir şekilde işleyebilir. Ve tüm bunları kapsamlı bir şekilde gözden geçirilmiş ve test edilmiş kodla yapıyor; neden daha düşük olası kodu yeniden yazmaya zaman ayırıyorsunuz?

Ancak belki de en önemlisi— Cocos2D oyun geliştirme, grafik donanım hızlandırmasını kullanır . Böyle bir hızlandırma olmadan, makul sayıda sprite içeren herhangi bir iOS sonsuz koşucu oyunu, özellikle düşük performansla çalışacaktır. Daha karmaşık bir uygulama yapmaya çalışırsak, muhtemelen ekranda bir “kurşun zamanı” efekti görmeye başlayacağız, yani canlandırmaya çalışırken her bir hareketli grafiğin birden çok kopyası.

Son olarak, Cocos2D, sprite'ları önbelleğe aldığı için bellek kullanımını optimize eder. Bu nedenle, herhangi bir çoğaltılmış hareketli grafik, oyunlar için açıkça yararlı olan minimum ek bellek gerektirir.

Cocos2D'yi storyboard'larla kullanma

Cocos2D'de aldığım onca övgüden sonra, Storyboard'ları kullanmayı önermek mantıksız görünebilir. Neden nesnelerinizi Cocos2D vb. ile değiştirmiyorsunuz? Dürüst olmak gerekirse, statik pencereler için Xcode'un Arayüz Oluşturucusunu ve Storyboard mekanizmasını kullanmak genellikle daha uygundur.

İlk olarak, sonsuz koşucu oyunum için tüm grafik öğelerimi faremle sürükleyip konumlandırmama izin veriyor. İkincisi, Storyboard API'si çok ama çok kullanışlıdır. (Ve evet, Cocos Builder'ı biliyorum).

İşte Storyboard'uma kısa bir bakış:

Sonsuz bir koşu oyununu nasıl yapacağınızı öğrenmek için iyi bir Storyboard ile başlayın.

Oyunun ana görünüm denetleyicisi, üzerinde bazı HUD öğeleri bulunan bir Cocos2D sahnesi içeriyor:

Cocos2D eğitimimizin başlangıcı, görünüm denetleyicisindedir.

Beyaz arka plana dikkat edin: Bu, çalışma zamanında gerekli tüm grafik öğelerini yükleyecek bir Cocos2D sahnesidir. Diğer görünümler (canlı göstergeler, karahindibalar, düğmeler vb.), Arayüz Oluşturucu kullanılarak ekrana eklenen standart Kakao görünümleridir.

Ayrıntılar üzerinde durmayacağım - ilgileniyorsanız, GitHub'da örnekler bulunabilir.

Oynanış ve (kısa) proje açıklaması

(Daha fazla motivasyon sağlamak için sonsuz koşucu oyunumu biraz daha detaylı anlatmak istiyorum. Teknik tartışmaya geçmek istiyorsanız bu bölümü atlamaktan çekinmeyin.)

Canlı oyun sırasında, arı hareketsizdir ve tarlanın kendisi çeşitli tehlikeleri (örümcekler ve zehirli çiçekler) ve avantajları (karahindiba ve tohumları) beraberinde getirerek aslında acele eder.

Cocos2D, karakteri takip etmek için tasarlanmış kamera nesnesine sahiptir; pratikte, oyun dünyasını içeren CCLayer'ı manipüle etmek daha az karmaşıktı.

Kontroller basittir: ekrana dokunmak arıyı yukarı hareket ettirir ve başka bir dokunuş onu aşağı hareket ettirir.

Dünya katmanının aslında iki alt katmanı vardır. Oyun başladığında, ilk alt katman 0'dan BUF_LEN'e doldurulur ve başlangıçta görüntülenir. İkinci alt katman, BUF_LEN'den 2*BUF_LEN'e önceden doldurulur. Arı BUF_LEN'e ulaştığında, ilk alt katman temizlenir ve anında 2*BUF_LEN'den 3*BUF_LEN'e yeniden doldurulur ve ikinci alt katman sunulur. Bu şekilde, bellek sızıntılarından kaçınmanın önemli bir parçası olan eski nesneleri asla tutmadan katmanlar arasında geçiş yaparız.

Sonsuz koşucu oyunum çok katmanlı bir dünyadan oluşuyor.

Fizik motorları açısından Chipmunk'ı iki nedenden dolayı kullandım:

  1. Saf Objective-C ile yazılmıştır.
  2. Box2D ile daha önce çalıştım, bu yüzden ikisini karşılaştırmak istedim.

Fizik motoru gerçekten sadece çarpışma tespiti için kullanıldı. Bazen, “Neden kendi çarpışma algılamanızı yazmadınız?” diye soruluyor. Gerçekte, bunun pek bir anlamı yok. Fizik motorları tam da bu amaçlar için tasarlandı: karmaşık şekillerdeki cisimler arasındaki çarpışmaları algılayabilir ve bu süreci optimize edebilirler. Örneğin, fizik motorları genellikle dünyayı hücrelere böler ve yalnızca aynı veya bitişik hücrelerdeki gövdeler için çarpışma kontrolleri gerçekleştirir.

İşleri otomatikleştirin. Araçları kullanın. Rahat ol.

Bağımsız sonsuz koşucu oyun geliştirmenin önemli bir bileşeni, küçük sorunlarda tökezlemekten kaçınmaktır. Bir uygulama geliştirirken zaman çok önemli bir kaynaktır ve otomasyon inanılmaz derecede zaman kazandırabilir.

Ancak bazen otomasyon, mükemmeliyetçilik ile son teslim tarihini karşılama arasında bir uzlaşma olabilir. Bu anlamda mükemmeliyetçilik, Angry Birds katili olabilir.

Örneğin, şu anda geliştirmekte olduğum başka bir iOS oyununda, özel bir araç (GitHub'da mevcut) kullanarak mizanpajlar oluşturmak için bir çerçeve oluşturdum. Bu çerçevenin sınırlamaları var (örneğin, sahneler arasında hoş geçişler yok), ancak onu kullanmak, sahnelerimi zamanın onda birinde yapmamı sağlıyor.

Bu nedenle, özel süper araçlarla kendi süper çerçevenizi oluşturamazken, yine de bu küçük görevlerin mümkün olduğunca çoğunu otomatikleştirebilirsiniz ve yapmalısınız.

Mükemmeliyetçilik, Angry Birds katili olabilir. iOS oyun geliştirmede zaman çok önemli bir kaynaktır.
Cıvıldamak

Bu sonsuz koşucuyu oluştururken otomasyon bir kez daha kilit rol oynadı. Örneğin, sanatçım bana özel bir Dropbox klasörü aracılığıyla yüksek çözünürlüklü grafikler gönderirdi. Zaman kazanmak için, App Store'un gerektirdiği çeşitli hedef çözünürlükler için otomatik olarak dosya kümeleri oluşturmak üzere bazı komut dosyaları yazdım, -hd veya @2x de ekledim (söz konusu komut dosyaları ImageMagick'e dayanmaktadır).

Ek araçlar açısından, TexturePacker'ı çok yararlı buldum - tüm hareketli grafikleriniz tek bir dosyadan okunacağından, uygulamanızın daha az bellek tüketmesi ve daha hızlı yüklenmesi için hareketli grafikleri hareketli grafik sayfalarına paketleyebilir. Ayrıca dokuları neredeyse tüm olası çerçeve formatlarında dışa aktarabilir. (DokuPacker'ın ücretsiz bir araç olmadığını unutmayın, ancak fiyatına değer olduğunu düşünüyorum. ShoeBox gibi ücretsiz alternatiflere de göz atabilirsiniz.)

Oyun fiziği ile ilgili ana zorluk, her bir hareketli karakter için uygun çokgenler oluşturmaktır. Başka bir deyişle, belirsiz şekilli bir arı veya çiçeğin çokgen bir temsilini oluşturmak. Bunu elle yapmaya bile çalışmayın - her zaman çok sayıda özel uygulamalar kullanın. Hatta bazıları oldukça egzotiktir; Inkspace ile vektör maskeleri oluşturmak ve ardından bunları oyuna aktarmak gibi.

Kendi sonsuz koşucu oyun geliştirmem için bu süreci otomatikleştirmek için Andengine Vertex Helper adını verdiğim bir araç yarattım. Adından da anlaşılacağı gibi, başlangıçta Andengine çerçevesi için tasarlandı, ancak bu günlerde bir dizi formatla uygun şekilde çalışacak.

Bizim durumumuzda, plist modelini kullanmamız gerekiyor:

 <real>%.5f</real><real>%.5f</real>

Ardından, nesne açıklamalarıyla bir plist dosyası oluşturuyoruz:

 <?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>

Ve bir nesne yükleyici:

 - (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); }

Sprite'ların fiziksel bedenlerine nasıl karşılık geldiğini test etmek için buraya bakın.

Çok daha iyi, değil mi?

Özetle, mümkün olduğunda daima otomatikleştirin. Basit komut dosyaları bile size tonlarca zaman kazandırabilir. Ve daha da önemlisi, bu süre fare tıklaması yerine programlama için kullanılabilir. (Ek motivasyon için, işte bir XKCD jetonu.)

Uygulama içi faturalandırma

Oyunda toplanan balonlar, uygulama içi para birimi işlevi görerek, kullanıcıların arıları için yeni görünümler satın almalarına olanak tanır. Ancak bu para birimi gerçek para ile de satın alınabilir. Uygulama içi faturalandırmayla ilgili olarak dikkat edilmesi gereken önemli bir nokta, satın alma geçerliliği için sunucu tarafı kontrolleri yapmanız gerekip gerekmediğidir. Tüm satın alınabilir ürünler oyun açısından esasen eşit olduğundan (sadece arının görünümünü değiştirerek), satın alma geçerliliği için bir sunucu kontrolü yapmaya gerek yoktur. Ancak, çoğu durumda kesinlikle bunu yapmanız gerekecektir.

Daha fazlası için Ray Wenderlich, mükemmel bir uygulama içi faturalandırma eğitimine sahiptir.

Game Center ile çok oyunculu oyun

Mobil oyunlarda sosyalleşme , Facebook'a 'Beğen' düğmesi eklemekten veya skor tabloları oluşturmaktan daha fazlasıdır. Oyunu daha heyecanlı hale getirmek için çok oyunculu bir sürüm uyguladım.

O nasıl çalışır? İlk olarak, iOS Game Center'ın gerçek zamanlı eşleştirmesini kullanarak iki oyuncu birbirine bağlanır. Oyuncular gerçekten aynı sonsuz koşucu oyununu oynadığı için, yalnızca tek bir oyun nesnesi seti olması gerekir. Bu, bir oyuncunun örneğinin nesneleri üretmesi gerektiği ve diğer oyunun onları okuyacağı anlamına gelir. Başka bir deyişle, her iki oyuncunun cihazı da oyun nesneleri oluşturuyorsa, deneyimi senkronize etmek zor olurdu.

Bunu akılda tutarak, bağlantı kurulduktan sonra her iki oyuncu da birbirine rastgele bir sayı gönderir. Daha yüksek sayıya sahip olan oyuncu, oyun nesneleri yaratan “sunucu” olarak hareket eder.

Parçalanmış dünya nesli tartışmasını hatırlıyor musunuz? Biri 0'dan BUF_LEN'e, diğeri BUF_LEN'den 2*BUF_LEN'e kadar iki alt katmanımız vardı? Bu mimari tesadüfen kullanılmadı; gecikmeli ağlar üzerinden düzgün grafikler sağlamak gerekiyordu. Nesnelerin bir kısmı oluşturulduğunda, bir plist içine paketlenir ve diğer oyuncuya gönderilir. Arabellek, ikinci oyuncunun bir ağ gecikmesiyle bile oynamasına izin verecek kadar büyüktür. Her iki oyuncu da yarım saniyelik bir periyotla mevcut pozisyonlarını birbirlerine gönderirler, aynı zamanda yukarı-aşağı hareketlerini de hemen gönderirler. Deneyimi yumuşatmak için, konum ve hız düzgün bir animasyonla her 0,5 saniyede bir düzeltilir, bu nedenle pratikte diğer oyuncu yavaş yavaş hareket ediyor veya hızlanıyormuş gibi görünür.

Çok oyunculu sonsuz koşu oyunuyla ilgili olarak kesinlikle dikkate alınması gereken daha çok şey var, ancak umarım bu size ilgili zorluk türleri hakkında bir fikir verir.

İyileştirme odası

Oyunlar asla bitmez. Kuşkusuz, kendimi geliştirmek istediğim birkaç alan var, yani:

  1. Kontrol sorunları: Dokunma, kaymayı tercih eden oyuncular için genellikle sezgisel olmayan bir harekettir.
  2. Dünya katmanı, CCMoveBy eylemi kullanılarak taşınır. CCMoveBy eylemi CCRepeatForever ile çevrildiğinden, dünya katmanının hızı sabit olduğunda bu iyiydi:

     -(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]; }

    Ancak daha sonra, oyunu ilerledikçe daha da zorlaştırmak için bir dünya hızı artışı ekledim:

     -(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]; }

    Bu değişiklik, her eylemin yeniden başlatılmasında animasyonun parçalanmasına neden oldu. Sorunu çözmeye çalıştım, boşuna. Ancak, beta testçilerim davranışı fark etmedi, bu yüzden düzeltmeyi erteledim.

  3. Bir yandan, Game Center'ı kullanırken veya kendi oyun sunucumu çalıştırırken çok oyunculu için kendi yetkimi yazmama gerek yoktu. Öte yandan, değiştirmek isteyebileceğim bir şey olan bot oluşturmayı imkansız hale getirdi.

Çözüm

Kendi indie sonsuz koşu oyununuzu yaratmak harika bir deneyim olabilir. Ve sürecin yayınlama aşamasına geldiğinizde, kendi eserinizi vahşi doğaya salıvermek harika bir duygu olabilir.

İnceleme süreci birkaç günden birkaç haftaya kadar değişebilir. Daha fazlası için, mevcut inceleme sürelerini tahmin etmek için kitle kaynaklı verileri kullanan yararlı bir site var.

Ek olarak, App Store'daki tüm uygulamalarla ilgili çeşitli bilgileri incelemek için AppAnnie'yi kullanmanızı ve Flurry Analytics gibi bazı analiz hizmetlerine kaydolmanızı tavsiye ederim.

Ve bu oyun ilginizi çektiyse, mağazadaki Bee Race'e göz atmayı unutmayın.