paint-brush
Tekillik: Evrensel Arka Uç Çerçevesiyle Oyun Geliştirmeyi Kolaylaştırmaile@makhorin
450 okumalar
450 okumalar

Tekillik: Evrensel Arka Uç Çerçevesiyle Oyun Geliştirmeyi Kolaylaştırma

ile Andrei Makhorin13m2024/07/25
Read on Terminal Reader

Çok uzun; Okumak

Oyun tasarımı açısından “evrensel çerçeve” nedir? Neden gerekli veya faydalıdırlar? Gelişimi kolaylaştırmaya nasıl yardımcı olabilirler? Tüm bu soruları (artı daha fazlasını) yanıtlıyoruz ve çözümümüz olan Tekilliği gösteriyoruz.
featured image - Tekillik: Evrensel Arka Uç Çerçevesiyle Oyun Geliştirmeyi Kolaylaştırma
Andrei Makhorin HackerNoon profile picture


Merhaba! Ben Pixonic (MY.GAMES) Sunucu Geliştiricisi Andrey Makhorin. Bu makalede ekibimle birlikte arka uç geliştirme için nasıl evrensel bir çözüm oluşturduğumuzu paylaşacağım. Konsepti, sonucunu ve Tekillik adı verilen sistemimizin gerçek dünyadaki projelerde nasıl performans gösterdiğini öğreneceksiniz. Ayrıca karşılaştığımız zorlukların derinliklerine ineceğim.

Arka plan

Bir oyun stüdyosu kurulduğunda, ilgi çekici bir fikri hızlı bir şekilde formüle etmek ve uygulamak çok önemlidir: düzinelerce hipotez test edilir ve oyun sürekli değişikliklere uğrar; yeni özellikler eklenir ve başarısız çözümler revize edilir veya atılır. Ancak bu hızlı yineleme süreci, sıkı teslim tarihleri ve kısa planlama ufku ile birleştiğinde teknik borcun birikmesine yol açabilir.


Mevcut teknik borç nedeniyle eski çözümlerin yeniden kullanılması karmaşık olabilir, çünkü çeşitli sorunların bunlarla çözülmesi gerekir. Bu açıkçası optimal değil. Ancak başka bir yol daha var: “evrensel bir çerçeve”. Stüdyolar, genel, yeniden kullanılabilir bileşenler (ağ etkileşimlerini uygulayan düzen öğeleri, pencereler ve kitaplıklar gibi) tasarlayarak, yeni özellikler geliştirmek için gereken zamanı ve çabayı önemli ölçüde azaltabilir. Bu yaklaşım yalnızca geliştiricilerin yazması gereken kod miktarını azaltmakla kalmaz, aynı zamanda kodun zaten test edilmiş olmasını sağlar ve bu da bakım için daha az zaman harcanmasını sağlar.


Özellik geliştirmeyi bir oyun bağlamında tartıştık, ancak şimdi duruma başka bir açıdan bakalım: Herhangi bir oyun stüdyosu için, bir proje içindeki küçük kod parçalarını yeniden kullanmak, üretimi kolaylaştırmak için etkili bir strateji olabilir, ancak sonuçta, Yeni bir hit oyun yaratmamız gerekecek. Mevcut bir projedeki çözümlerin yeniden kullanılması teorik olarak bu süreci hızlandırabilir ancak iki önemli engel ortaya çıkar. Her şeyden önce, aynı teknik borç sorunları burada da geçerli ve ikincisi, eski çözümlerin büyük olasılıkla önceki oyunun özel gereksinimlerine göre uyarlanmış olması, onları yeni proje için uygunsuz hale getiriyor.


Bu sorunlara başka sorunlar da ekleniyor: veritabanı tasarımı yeni projenin gereksinimlerini karşılamayabilir, teknolojiler güncelliğini kaybetmiş olabilir ve yeni ekip gerekli uzmanlığa sahip olmayabilir.


Ayrıca, çekirdek sistem genellikle belirli bir tür veya oyun göz önünde bulundurularak tasarlanmakta ve bu da yeni bir projeye uyum sağlamayı zorlaştırmaktadır.


İşte burada yine evrensel bir çerçeve devreye giriyor ve birbirinden çok farklı oyunlar yaratmak aşılmaz bir zorluk gibi görünse de bu sorunu başarıyla çözen platform örnekleri var: PlayFab, Photon Engine ve benzeri platformlar geliştiricilerin altyapı yerine oyun oluşturmaya odaklanmasına olanak tanıyarak geliştirme süresini önemli ölçüde azaltma yeteneklerini gösterdiler.


Şimdi hikayemize geçelim.

Tekillik ihtiyacı

Çok oyunculu oyunlar için sağlam bir arka uç şarttır. Örnek olarak: amiral gemisi oyunumuz War Robots. Bu mobil bir PvP nişancı oyunudur, 10 yılı aşkın bir süredir piyasadadır ve arka uç desteği gerektiren çok sayıda özelliği bünyesinde barındırmaktadır. Sunucu kodumuz projenin özelliklerine göre uyarlanırken, modası geçmiş teknolojileri kullanıyordu.


Yeni bir oyun geliştirme zamanı geldiğinde War Robots'un sunucu bileşenlerini yeniden kullanmaya çalışmanın sorunlu olacağını fark ettik. Kod fazlasıyla projeye özeldi ve yeni ekibin sahip olmadığı teknolojilerde uzmanlık gerektiriyordu.


Ayrıca yeni projenin başarısının garanti olmadığının ve başarılı olsa bile eninde sonunda yeni bir oyun yaratmamız gerekeceğinin ve aynı "boş sayfa" sorunuyla karşı karşıya kalacağımızın da farkındaydık. Bunu önlemek ve geleceğe yönelik bir hazırlık yapmak için oyun geliştirme için gereken temel bileşenleri belirlemeye ve ardından gelecekteki tüm projelerde kullanılabilecek evrensel bir çerçeve oluşturmaya karar verdik.


Amacımız, geliştiricilere arka uç mimarilerini , veritabanı şemalarını, etkileşim protokollerini ve belirli teknolojileri tekrar tekrar tasarlama ihtiyacını ortadan kaldıracak bir araç sağlamaktı. İnsanları yetkilendirme, ödeme işleme ve kullanıcı bilgileri depolama uygulama yükünden kurtarmak ve onların oyunun temel yönlerine (oynanış, tasarım, iş mantığı ve daha fazlası) odaklanmalarına olanak sağlamak istedik.


Ek olarak, yeni çerçevemizle yalnızca gelişimi hızlandırmayı değil, aynı zamanda istemci programcıların ağ oluşturma, DBMS veya altyapı konusunda derin bilgi sahibi olmadan sunucu tarafı mantığı yazabilmelerini de sağlamak istedik.


Bunun ötesinde, bir dizi hizmeti standartlaştırarak DevOps ekibimiz, yalnızca IP adreslerini değiştirerek tüm oyun projelerini benzer şekilde ele alabilecektir. Bu, yeniden kullanılabilir dağıtım komut dosyası şablonları ve izleme kontrol panelleri oluşturmamıza olanak tanır.


Süreç boyunca arka ucu gelecekteki oyunlarda yeniden kullanma olasılığını hesaba katan mimari kararlar aldık. Bu yaklaşım, çerçevemizin esnek, ölçeklenebilir ve çeşitli proje gereksinimlerine uyarlanabilir olmasını sağladı.


(Çerçevenin geliştirilmesinin bir ada olmadığını, başka bir projeye paralel olarak oluşturulduğunu da belirtmekte fayda var.)

Platformun oluşturulması

Singularity'ye bir oyunun türünden, ortamından veya temel oynanışından bağımsız bir dizi işlev vermeye karar verdik:

  • Kimlik doğrulama
  • Kullanıcı Veri Depolama
  • Oyun Ayarları ve Denge Ayrıştırma
  • Odeme yapiliyor
  • AB Test Dağıtımı
  • Analitik Hizmet Entegrasyonu
  • Sunucu Yönetici Paneli


Bu işlevler çok kullanıcılı herhangi bir mobil proje için temeldir (en azından Pixonic'te geliştirilen projelerle ilgilidir).


Bu temel işlevlere ek olarak Singularity, iş mantığına daha yakın, projeye özgü özellikleri barındıracak şekilde tasarlandı. Bu yetenekler soyutlamalar kullanılarak oluşturulmuştur ve bu da onları farklı projelerde yeniden kullanılabilir ve genişletilebilir hale getirir.


Bazı örnekler şunları içerir:

  • Görevler
  • Teklifler
  • Arkadaş listesi
  • Çöpçatanlık
  • Derecelendirme tabloları
  • Oyuncuların çevrimiçi durumu
  • Oyun içi bildirimler



Teknik olarak Singularity platformu dört bileşenden oluşur:

  • Sunucu SDK'sı: Bu, oyun programcılarının sunucularını geliştirebileceklerini temel alan bir dizi kitaplıktır.
  • İstemci SDK'sı: Ayrıca bir dizi kitaplıktır, ancak mobil uygulama geliştirmek içindir.
  • Bir dizi hazır mikro hizmet: Bunlar değişiklik gerektirmeyen hazır sunuculardır. Bunlar arasında kimlik doğrulama sunucusu, denge sunucusu ve diğerleri bulunur.
  • Uzantı kitaplıkları: Bu kitaplıklar halihazırda teklifler, görevler vb. gibi çeşitli özellikleri uygulamaktadır. Oyun programcıları, oyunları gerektiriyorsa bu uzantıları etkinleştirebilirler.


Şimdi bu bileşenlerin her birini inceleyelim.


Sunucu SDK'sı

Profil hizmeti ve eşleştirme gibi bazı hizmetler oyuna özel iş mantığı gerektirir. Bunu karşılamak için bu hizmetleri kütüphaneler halinde dağıtılacak şekilde tasarladık. Geliştiriciler bu kitaplıkların üzerine inşa ederek komut işleyicileri, eşleştirme mantığını ve diğer projeye özgü bileşenleri içeren uygulamalar oluşturabilirler.


Bu yaklaşım, çerçevenin düşük seviyeli HTTP protokolü işlevselliği sağladığı ve bu arada geliştiricinin iş mantığını içeren denetleyiciler ve modeller oluşturmaya odaklanabildiği bir ASP.NET uygulaması oluşturmaya benzer.


Örneğin oyun içerisinde kullanıcı adlarını değiştirme özelliğini eklemek istediğimizi varsayalım. Bunu yapmak için programcıların yeni kullanıcı adını ve bu komut için bir işleyiciyi içeren bir komut sınıfı yazmaları gerekir.


Aşağıda bir ChangeNameCommand örneği verilmiştir:

 public class ChangeNameCommand : ICommand { public string Name { get; set; } }


Bu komut işleyicisinin bir örneği:

 class ChangeNameCommandHandler : ICommandHandler<ChangeNameCommand> { private IProfile Profile { get; } public ChangeNameCommandHandler(IProfile profile) { Profile = profile; } public void Handle(ICommandContext context, ChangeNameCommand command) { Profile.Name = command.Name; } }


Bu örnekte işleyicinin, bağımlılık ekleme yoluyla otomatik olarak işlenen bir IProfile uygulamasıyla başlatılması gerekir. IProfile, IWallet ve IInventory gibi bazı modeller ek adımlara gerek kalmadan uygulamaya uygundur. Ancak bu modellerin soyut doğaları, veri sağlamaları ve belirli proje ihtiyaçlarına uygun olmayan argümanları kabul etmeleri nedeniyle üzerinde çalışmak pek uygun olmayabilir.


İşleri kolaylaştırmak için projeler kendi etki alanı modellerini tanımlayabilir, bunları işleyicilere benzer şekilde kaydedebilir ve gerektiğinde yapıcılara enjekte edebilir. Bu yaklaşım, verilerle çalışırken daha özelleştirilmiş ve kullanışlı bir deneyim sağlar.


Aşağıda bir etki alanı modeli örneği verilmiştir:

 public class WRProfile { public readonly IProfile Raw; public WRProfile(IProfile profile) { Raw = profile; } public int Level { get => Raw.Attributes["level"].AsInt(); set => Raw.Attributes["level"] = value; } }


Varsayılan olarak oynatıcı profili Level özelliğini içermez. Ancak projeye özel bir model oluşturularak bu tür bir özellik eklenebilir ve komut işleyicilerdeki oyuncu düzeyindeki bilgiler kolayca okunabilir veya değiştirilebilir.


Etki alanı modelini kullanan bir komut işleyici örneği:

 class LevelUpCommandHandler : ICommandHandler<LevelUpCommand> { private WRProfile Profile { get; } public LevelUpCommandHandler(WRProfile profile) { Profile = profile; } public void Handle(ICommandContext context, LevelUpCommand command) { Profile.Level += 1; } }


Bu kod, belirli bir oyunun iş mantığının, temeldeki aktarım veya veri depolama katmanlarından yalıtılmış olduğunu açıkça göstermektedir. Bu soyutlama, programcıların işlemsellik, yarış koşulları veya diğer yaygın arka uç sorunları hakkında endişelenmeden temel oyun mekaniğine odaklanmasına olanak tanır.


Dahası, Singularity oyun mantığını geliştirmek için kapsamlı bir esneklik sunuyor. Oyuncunun profili, oyun tasarımcılarının tam da hayal ettikleri gibi herhangi bir özelliği kolayca eklemelerine olanak tanıyan "anahtarla yazılan değer" çiftlerinden oluşan bir koleksiyondur.


Profilin ötesinde, Singularity'deki oyuncu varlığı, esnekliği korumak için tasarlanmış çeşitli temel bileşenlerden oluşur. Özellikle, bu, içindeki her para biriminin miktarını izleyen bir cüzdanın yanı sıra oyuncunun öğelerini listeleyen bir envanteri de içerir.


İlginçtir ki, Singularity'deki öğeler profillere benzer soyut varlıklardır; her öğenin benzersiz bir tanımlayıcısı ve bir dizi anahtar tipi değer çifti vardır. Yani bir öğenin oyun dünyasında mutlaka silah, kıyafet veya kaynak gibi somut bir nesne olması gerekmez. Bunun yerine, bir görev veya teklif gibi, oyunculara özel olarak verilen herhangi bir genel tanımı temsil edebilir. Aşağıdaki bölümde bu kavramların belirli bir oyun projesinde nasıl uygulandığını ayrıntılarıyla anlatacağım.


Tekillikteki temel farklardan biri, kalemlerin bilançoda genel bir açıklamaya referans saklamasıdır. Bu açıklama statik kalsa da verilen bireysel öğenin özellikleri değişebilir. Örneğin oyunculara silah görünümlerini değiştirme yeteneği verilebiliyor.


Ayrıca oyuncu verilerini taşımak için güçlü seçeneklerimiz var. Geleneksel arka uç geliştirmede, veritabanı şeması genellikle iş mantığıyla sıkı bir şekilde bağlantılıdır ve bir varlığın özelliklerinde yapılan değişiklikler genellikle doğrudan şema değişikliklerini gerektirir.


Ancak geleneksel yaklaşım Singularity için uygun değildir çünkü çerçeve, bir oyuncu varlığıyla ilişkili iş özelliklerine ilişkin farkındalıktan yoksundur ve oyun geliştirme ekibinin veritabanına doğrudan erişimi yoktur. Bunun yerine geçişler, doğrudan depo etkileşimi olmadan çalışan komut işleyicileri olarak tasarlanıp kaydedilir. Bir oyuncu sunucuya bağlandığında verileri veritabanından alınır. Sunucuda kayıtlı herhangi bir geçiş henüz bu oynatıcıya uygulanmadıysa, bunlar yürütülür ve güncellenen durum veritabanına geri kaydedilir.


Uygulanan geçişlerin listesi aynı zamanda bir oynatıcı özelliği olarak da depolanır ve bu yaklaşımın başka bir önemli avantajı vardır: geçişlerin zaman içinde kademeli olarak yapılmasına olanak tanır. Bu, tüm oynatıcı kayıtlarına yeni bir özellik eklemek ve bunu varsayılan bir değere ayarlamak gibi büyük veri değişikliklerinin neden olabileceği kesinti sürelerinden ve performans sorunlarından kaçınmamıza olanak tanır.

İstemci SDK'sı

Singularity, arka uç etkileşimi için basit bir arayüz sunarak proje ekiplerinin protokol veya ağ iletişim teknolojileri sorunları hakkında endişelenmeden oyun geliştirmeye odaklanmasına olanak tanır. (Bununla birlikte SDK, gerekirse projeye özel komutlar için varsayılan serileştirme yöntemlerini geçersiz kılma esnekliği sağlar.)


SDK, API ile doğrudan etkileşime olanak tanır, ancak aynı zamanda rutin görevleri otomatikleştiren bir sarmalayıcı da içerir. Örneğin, profil hizmetinde bir komutun yürütülmesi, oyuncunun profilindeki değişiklikleri gösteren bir dizi olay üretir. Sarmalayıcı bu olayları yerel duruma uygulayarak istemcinin profilin geçerli sürümünü korumasını sağlar.


İşte bir komut çağrısı örneği:

 var result = _sandbox.ExecSync(new LevelUpCommand())


Hazır Mikro Hizmetler

Singularity'deki hizmetlerin çoğu çok yönlü olacak şekilde tasarlanmıştır ve belirli projeler için özelleştirme gerektirmez. Bu hizmetler önceden oluşturulmuş uygulamalar olarak dağıtılır ve çeşitli oyunlarda kullanılabilir.


Hazır hizmet paketi şunları içerir:

  • Müşteri istekleri için bir ağ geçidi
  • Bir kimlik doğrulama hizmeti
  • Ayarları ve denge tablolarını ayrıştırmaya ve depolamaya yönelik bir hizmet
  • Çevrimiçi durum hizmeti
  • Bir arkadaş servisi
  • Liderlik tablosu hizmeti


Kimlik doğrulama hizmeti ve ağ geçidi gibi bazı hizmetler platform için temel öneme sahiptir ve dağıtılması gerekir. Arkadaşlık hizmeti ve sıralama tablosu gibi diğerleri isteğe bağlıdır ve bunları gerektirmeyen oyun ortamından hariç tutulabilir.

Çok sayıda hizmetin yönetilmesi ile ilgili konulara daha sonra değineceğim ancak şimdilik opsiyonel hizmetlerin opsiyonel kalması gerektiğini vurgulamakta yarar var. Hizmetlerin sayısı arttıkça yeni projelerin karmaşıklığı ve katılım eşiği de artıyor.


Uzantı Kitaplıkları

Singularity'nin çekirdek çerçevesi oldukça yetenekli olsa da, önemli özellikler proje ekipleri tarafından çekirdeği değiştirmeden bağımsız olarak uygulanabilir. İşlevselliğin birden fazla proje için potansiyel olarak yararlı olduğu belirlendiğinde, çerçeve ekibi tarafından geliştirilebilir ve ayrı uzantı kitaplıkları olarak yayınlanabilir. Bu kütüphaneler daha sonra oyun içi komut işleyicilerine entegre edilebilir ve kullanılabilir.


Burada geçerli olabilecek bazı örnek özellikler, görevler ve tekliflerdir. Çekirdek çerçevenin perspektifinden bakıldığında, bu varlıklar yalnızca oyunculara atanan öğelerdir. Bununla birlikte, uzantı kitaplıkları bu öğelere belirli özellikler ve davranışlar aşılayarak bunları görevlere veya tekliflere dönüştürebilir. Bu yetenek, öğe özelliklerinin dinamik olarak değiştirilmesine, görev ilerlemesinin izlenmesine veya oyuncuya bir teklifin sunulduğu son tarihin kaydedilmesine olanak tanır.


Şu ana kadarki sonuçlar

Singularity, küresel olarak mevcut en yeni oyunlarımızdan biri olan Little Big Robots'ta başarıyla uygulandı ve bu, istemci geliştiricilere sunucu mantığını kendilerinin yönetme gücünü verdi. Ek olarak, platform ekibinin doğrudan desteğine ihtiyaç duymadan mevcut işlevleri kullanan prototipler oluşturabildik.


Ancak bu evrensel çözümün zorlukları da yok değil. Özelliklerin sayısı arttıkça platformla etkileşimin karmaşıklığı da arttı. Singularity, basit bir araçtan gelişmiş, karmaşık bir sisteme dönüştü; bazı yönlerden basit bir tuşlu telefondan tam özellikli bir akıllı telefona geçişe benzer.


Singularity, geliştiricilerin veritabanları ve ağ iletişiminin karmaşıklığına dalma ihtiyacını hafifletirken, aynı zamanda kendi öğrenme eğrisini de ortaya koydu. Geliştiricilerin artık Singularity'nin nüanslarını anlaması gerekiyor, bu da önemli bir değişim olabilir.


Geliştiricilerden altyapı yöneticilerine kadar birçok kişi bu zorluklarla karşı karşıyadır. Bu profesyoneller genellikle Postgres ve Kafka gibi iyi bilinen çözümlerin dağıtımı ve bakımı konusunda derin uzmanlığa sahiptir. Ancak Singularity, yeni beceriler kazanmalarını gerektiren dahili bir üründür: Singularity kümelerinin inceliklerini öğrenmeleri, gerekli ve isteğe bağlı hizmetler arasında ayrım yapmaları ve hangi ölçümlerin izleme için kritik olduğunu anlamaları gerekir.


Bir şirket içinde geliştiricilerin tavsiye almak için platformun yaratıcılarına her zaman ulaşabilecekleri doğru olsa da, bu süreç kaçınılmaz olarak zaman gerektirir. Amacımız giriş engelini mümkün olduğunca en aza indirmektir. Bunu başarmak, her yeni özellik için kapsamlı belgelendirmeyi gerektirir; bu da geliştirmeyi yavaşlatabilir, ancak yine de platformun uzun vadeli başarısına bir yatırım olarak kabul edilir. Ayrıca, sağlam birim ve entegrasyon testi kapsamı, sistem güvenilirliğini sağlamak için gereklidir.


Tekillik büyük ölçüde otomatik testlere dayanır çünkü manuel test ayrı oyun örnekleri geliştirmeyi gerektirir ve bu da pratik değildir. Otomatik testler hataların büyük çoğunluğunu, yani %99'unu yakalayabilir. Ancak, yalnızca belirli oyun testleri sırasında ortaya çıkan sorunların her zaman küçük bir yüzdesi vardır. Singularity ekibi ve proje ekipleri genellikle eşzamansız çalıştığı için bu durum yayın zaman çizelgelerini etkileyebilir. Uzun zaman önce yazılan kodda bir engelleme hatası bulunabilir ve platform geliştirme ekibi başka bir kritik görevle meşgul olabilir. (Bu zorluk Singularity'ye özgü değildir ve özel arka uç geliştirmede de ortaya çıkabilir.)


Bir diğer önemli zorluk ise Singularity'yi kullanan tüm projelerdeki güncellemeleri yönetmektir. Tipik olarak, sürekli bir özellik istekleri ve iyileştirme akışıyla çerçevenin gelişimini yönlendiren bir amiral gemisi projesi vardır. Bu projenin ekibiyle etkileşim sıkı sıkıya bağlıdır; ihtiyaçlarını ve sorunlarını çözmek için platformumuzdan nasıl yararlanabileceklerini anlıyoruz.


Bazı önemli projeler çerçeve ekibiyle yakından ilişkiliyken, erken geliştirme aşamalarındaki diğer oyunlar genellikle bağımsız olarak çalışır ve yalnızca mevcut işlevsellik ve belgelere dayanır. Geliştiriciler belgeleri yanlış anlayabileceğinden veya mevcut özellikleri yanlış kullanabileceğinden, bu bazen gereksiz veya yetersiz çözümlere yol açabilir. Bunu azaltmak için sunumlar, buluşmalar ve ekip değişimleri yoluyla bilgi paylaşımını kolaylaştırmak çok önemlidir, ancak bu tür girişimler önemli miktarda zaman yatırımı gerektirse de.

Gelecek

Singularity, değerini zaten oyunlarımızda gösterdi ve daha da gelişmeye hazırlanıyor. Yeni özellikler sunmayı planlasak da şu anda öncelikli odak noktamız bu geliştirmelerin platformun proje ekipleri için kullanılabilirliğini zorlaştırmamasını sağlamaktır.

Bunun yanı sıra, giriş engelini azaltmak, dağıtımı basitleştirmek, analitik açısından esneklik eklemek, projelerin kendi çözümlerini birbirine bağlamasına izin vermek gerekiyor. Bu, ekip için bir zorluktur, ancak çözümümüze harcanan çabaların kesinlikle karşılığını alacağına inanıyor ve pratikte görüyoruz!