Yüzlerce 'oyun içi' karakteri nasıl yapılandırır / yönetiriz?


10

Unity'de Crusader Kings 2 gibi yüzlerce karakter içeren basit bir RTS oyunu yapıyorum. Onları saklamak için en kolay seçenek komut dosyası nesneleri kullanmak olacaktır, ancak bu, çalışma zamanında yenilerini oluşturamadığınız için iyi bir çözüm değildir.

Bu yüzden tüm verileri içeren "Karakter" adlı bir C # sınıfı oluşturdum. Her şey iyi çalışıyor, ancak oyun simüle ettikçe sürekli yeni karakterler yaratıyor ve bazı karakterleri öldürüyor (oyun içi etkinlikler gerçekleştikçe). Oyun sürekli olarak simüle ederken 1000 karakter oluşturur. Bir karakterin işlevini işlerken "Canlı" olduğundan emin olmak için basit bir kontrol ekledim. Bu yüzden performansa yardımcı olur, ancak öldüğü takdirde "Karakter" i kaldıramam çünkü aile ağacını oluştururken onun bilgilerine ihtiyacım var.

Bir liste oyunum için veri kaydetmenin en iyi yolu mu? Yoksa yaratılan bir karakterin 10000'leri olduğunda sorun yaratır mı? Olası bir çözüm, liste belirli bir miktara ulaştığında başka bir liste yapmak ve içindeki tüm ölü karakterleri taşımaktır.


9
Bir karakterin ölümünden sonra bir alt verisine ihtiyaç duyduğumda yaptığım bir şey, o karakter için bir "kaldırıldı olarak işaretleme" nesnesi oluşturmaktır. Mezar taşı daha sonra bakmam gereken bilgileri taşıyabilir, ancak daha küçük olabilir ve daha az tekrarlanabilir, çünkü yaşayan bir karakter gibi sürekli simülasyona ihtiyaç duymaz.
DMGregory


2
Oyun CK2 gibi mi, yoksa sadece çok fazla karaktere sahip olmanın bir parçası mı? Tüm oyunun CK2 gibi olduğunu anladım. Bu durumda, buradaki cevapların çoğu yanlış değildir ve iyi bilgi içerir, ancak sorunun amacını kaçırırlar. Bu CK2 bir denen yardım etmez , gerçek zamanlı strateji oyunu aslında bir zaman büyük strateji oyunu . Nitpicky gibi görünebilir, ancak karşılaştığınız sorunlarla çok ilgilidir.
Raphael Schmitz

1
Örneğin, "1000 karakterden" bahsettiğinizde, insanlar aynı anda ekranda 1000'lerin 3B modellerini veya spritelarını düşünüyorlar - yani Birlik'te 1000'lerin GameObjects. CK2'de aynı anda gördüğüm maksimum karakter sayısı mahkememe baktığımda ve orada 10-15 kişiyi gördüğümdü (çok uzak oynamadım). Aynı şekilde, 3000 askerden oluşan bir ordu sadece GameObject"3000" sayısını gösteren bir ordudur .
Raphael Schmitz

1
@ R.Schmitz Evet, bu Karakterin kendilerine eklenmiş gameobject özelliği bulunmadığını açıkça belirtmeliydim. Gerektiğinde karakteri bir noktadan diğerine taşımak gibi. Ai Mantığı ile bu Karakterin tüm bilgilerini içeren ayrı bir varlık oluşturulur.
paul p

Yanıtlar:


24

Göz önünde bulundurmanız gereken üç şey vardır:

  1. O mu aslında bir performans sorununa neden? 1000'ler aslında pek fazla değil. Modern bilgisayarlar son derece hızlıdır ve birçok şeyi halledebilir. Karakter işlemenin ne kadar zaman aldığını izleyin ve çok fazla endişelenmeden önce sorunun neden olup olmayacağını görün.

  2. Şu anda minimum düzeyde aktif olan karakterlerin doğruluğu. Yeni başlayan Oyun Programcılarının sık görülen bir hatası, ekran dışı karakterleri ekrandakilerle aynı şekilde güncellemek konusunda takıntı yapmaktır. Bu bir hatadır, kimse umursamaz. Bunun yerine, ekran dışı karakterlerin hala etkili olduğu izlenimini yaratmaya çalışmalısınız . Ekran dışında alınan güncelleme karakterlerinin miktarını azaltarak işlem sürelerini önemli ölçüde azaltabilirsiniz.

  3. Veri Odaklı Tasarım düşünün. 1000 karakter nesnesine sahip olmak ve her biri için aynı işlevi çağırmak yerine, 1000 karakter için bir dizi veriye sahip olun ve her biri sırayla güncellenen 1000 karakter üzerinde bir işlev döngüsüne sahip olun. Bu tür bir optimizasyon performansı önemli ölçüde artırabilir.


3
Varlık / Bileşen / Sistem bunun için iyi çalışır. İhtiyacınız olan her şey için "sistemi" oluşturun, binlerce veya on binlerce karakteri (bileşenlerini) saklamasını ve sisteme "karakter tanıtıcısını" sağlamasını sağlayın. Bu, farklı veri modellerini ayrı ve daha küçük tutmanıza olanak tanır ve ölü karakterleri bunlara ihtiyaç duymayan sistemlerden de kaldırabilirsiniz. (Şu anda kullanmıyorsanız, bir sistemi tamamen boşaltabilirsiniz.)
Der Kommissar

1
Ekran dışında alınan güncelleme karakterlerinin miktarını azaltarak işlem sürelerini önemli ölçüde artırabilirsiniz. Azaltmak istemiyor musun?
Tejas Kale

1
@TejasKale: Evet, düzeltildi.
Jack Aidley


1
Aslında kontrol etmek her zaman en iyisidir, ancak Romalıların birbirlerini hadım etmek isteyecekleri genellikle güvenli bir çalışma varsayımıdır;)
curiousdannii

11

Bu durumda Kompozisyon'u kullanmanızı öneririm :

Sınıfların, bileşimleriyle polimorfik davranış ve kodun yeniden kullanılmasını sağlaması ilkesi (istenen işlevselliği uygulayan diğer sınıfların örneklerini içererek)


Bu durumda, Charactersınıfınız tanrıya benziyor gibi görünüyor ve bir karakterin yaşam döngüsünün tüm aşamalarında nasıl çalıştığına dair tüm ayrıntıları içerir .

Örneğin, aile ağaçlarında kullanıldığı gibi, ölü karakterlerin hala gerekli olduğunu not edersiniz. Bununla birlikte, canlı karakterlerinizin tüm bilgi ve işlevlerinin, yalnızca onları bir soy ağacında görüntülemek için gerekli olması olası değildir. Örneğin, sadece isimlere, doğum tarihine ve bir portre ikonuna ihtiyaç duyabilirler.


Çözüm, ayrı parçalarınızı sahip olduğu Characteralt sınıflara ayırmaktır Character. Örneğin:

  • CharacterInfo adı, doğum tarihi, ölüm tarihi ve fraksiyonu ile basit bir veri yapısı olabilir,

  • Equipmentkarakterinizin tüm öğelerine veya mevcut varlıklarına sahip olabilir. Bunları fonksiyonlarda yöneten mantığa da sahip olabilir.

  • CharacterAIveya CharacterControllerkarakterin mevcut hedefi, fayda işlevleri vb. hakkında gerekli tüm bilgilere sahip olabilir.

Karakteri böldüğünüzde, artık güncelleme döngüsünde Canlı / Ölü bayrağını kontrol etmenize gerek yoktur.

Bunun yerine, bir olursun AliveCharacterObjectsahip olduğunu CharacterController, CharacterEquipmentve CharacterInfokomut ekli. Karakteri "öldürmek" için, artık ilgili olmayan parçaları (örneğin CharacterController) kaldırmanız yeterlidir; artık belleği veya işlem süresini boşa harcamaz.

CharacterInfoAile ağacı için gereken tek verinin nasıl olması gerektiğine dikkat edin. Sınıflarınızı daha küçük işlevsellik parçalarına ayırarak - bu küçük veri nesnesini ölümden sonra, AI odaklı karakterin tamamını tutmaya gerek kalmadan daha kolay tutabilirsiniz.


Bu paradigmanın, Birliğin kullanmak için inşa edildiği tek şey olduğunu belirtmek gerekir - ve bu yüzden birçok ayrı komut dosyası olan şeyleri işler. Büyük tanrı nesneleri oluşturmak nadiren verilerinizi Unity'de ele almanın en iyi yoludur.


8

İşlenecek büyük miktarda veriye sahip olduğunuzda ve her veri noktası gerçek bir oyun nesnesiyle temsil edilmediğinde, Birliğe özgü sınıflardan vazgeçmek ve sadece düz eski C # nesneleriyle gitmek kötü bir fikir değildir. Bu şekilde yükü en aza indirirsiniz. Yani burada doğru yoldasınız.

Yaşayan veya ölü tüm karakterleri tek bir Listede ( veya dizide ) saklamak yararlı olabilir, çünkü bu listedeki dizin standart karakter kimliği olarak kullanılabilir. Bir liste konumuna indeks ile erişmek çok hızlı bir işlemdir. Ancak, tüm canlı karakterlerin kimliklerinin ayrı bir listesini tutmak yararlı olabilir, çünkü bunları ölü karakterlere ihtiyaç duyduğunuzdan çok daha sık yinelemeniz gerekecektir.

Oyun mekaniğinizi uygulamanız ilerledikçe, en çok hangi tür aramaları yaptığınıza da bakmak isteyebilirsiniz. "Belirli bir konumdaki tüm canlı karakterler" veya "belirli bir karakterin tüm canlı veya ölü ataları" gibi. Bu tür sorgular için optimize edilmiş bazı ikincil veri yapıları oluşturmak faydalı olabilir. Her birinin güncel tutulması gerektiğini unutmayın. Bu, ek programlama gerektirir ve ek hata kaynağı olacaktır. Bu yüzden, ancak dikkate değer bir performans artışı bekliyorsanız yapın.

CKII " erik " kendi veritabanından karakterler kaynak tasarrufu için önemsiz olarak onları gördüğü. Ölü karakter yığınınız uzun süredir devam eden bir oyunda çok fazla kaynak tüketiyorsa, benzer bir şey yapmak isteyebilirsiniz (Bu "çöp toplama" demek istemiyorum. Belki "saygılı artış"?).

Eğer gerçekten oyunda her karakter için bir oyun nesne var, ardından yeni Birlik ECS ve İşler sistem sizin için yararlı olabilir. Çok sayıda benzer oyun nesnesini performansla işlemek için optimize edilmiştir. Ancak yazılım mimarinizi çok katı kalıplara zorlar.

Bu arada, CKII'yi ve binlerce benzersiz AI kontrollü karakterle bir dünyayı simüle etme şeklini gerçekten çok seviyorum, bu yüzden türü almayı dört gözle bekliyorum.


Merhaba, Cevabınız için teşekkürler. Tüm hesaplamalar Tek bir GameObject Yöneticisi tarafından yapılır. Oyun nesnelerini yalnızca gerektiğinde Bireysel Aktörlere atarım (Karakter Ordusu'nun bir konumdan diğerine hareket ettiğini göstermek gibi).
paul p

1
Like "all living characters in a specific location" or "all living or dead ancestors of a specific character". It might be beneficial to create some more secondary data-structures optimized for these kinds of queries.CK2 modifikasyonu ile ilgili deneyimlerime göre, bu CK2'nin verileri nasıl ele aldığına yakın. CK2, temelde temel veritabanı dizinleri olan ve belirli bir durum için karakter bulmayı daha hızlı hale getiren dizinler kullanıyor gibi görünüyor. Bir karakter listesi yerine, içerdiği tüm dezavantajları ve faydaları olan dahili bir karakter veritabanına sahip gibi görünüyor.
Morfildur

1

Müzikçaların yakınında yalnızca birkaç karakter varsa binlerce karakteri simüle etmeniz / güncellemeniz gerekmez. Sadece oyuncunun o anki gerçekte ne görebileceğini güncellemeniz gerekir, bu yüzden oynatıcıdan daha uzaktaki karakterler oyuncu onlara yaklaşana kadar askıya alınmalıdır.

Oyun mekaniğinizin zamanın geçişini göstermek için uzak karakterler gerektirdiği için bu işe yaramazsa, oyuncu yaklaştıkça bunları bir "büyük" güncellemede güncelleyebilirsiniz. Oyun mekaniğiniz, karakterin oyuncuya veya etkinliğe göre nerede olduğuna bakılmaksızın, her karakterin oyun içi olaylara gerçekte olduğu gibi yanıt vermesini gerektiriyorsa, oyuncudan daha uzak olan karakterlerin sıklığını azaltmak işe yarayabilir. güncellendi (yani, oyunun geri kalanıyla senkronize olarak güncelleniyorlar, ancak sık sık değiller, bu nedenle uzak karakterler bir etkinliğe yanıt vermeden önce küçük bir gecikme olacak, ancak bunun bir soruna neden olması veya oyuncu tarafından fark edilmesi olası değildir ). Alternatif olarak karma bir yaklaşım kullanmak isteyebilirsiniz,


bu bir RTS. Herhangi bir zamanda, dikkate değer sayıda birimin aslında ekranda olduğunu varsaymalıyız.
Tom

Bir RTS'de, oyuncu bakmıyorken dünyanın devam etmesi gerekir. Büyük bir güncelleme uzun sürecek, ancak kamerayı hareket ettirdiğinizde büyük bir patlama olacaktı.
PStag

1

Sorunun açıklığa kavuşturulması

Bir Birlik'te Haçlı Kralları 2 gibi yüzlerce karakter içeren basit bir RTS oyunu yapıyorum.

Bu cevapta, tüm oyunun sadece çok fazla karaktere sahip olmak yerine CK2 gibi olması gerektiğini varsayıyorum. CK2'de ekranda gördüğünüz her şeyin yapılması kolaydır ve performansınızı tehlikeye sokmaz veya Unity'de uygulanması karmaşık değildir. Arkasındaki veriler, karmaşıklaştığı yer.

İşlevsiz Charactersınıflar

Bu yüzden tüm verileri içeren "Karakter" adlı bir C # sınıfı oluşturdum.

İyi, çünkü oyunda bir karakter olan sadece veri. Ekranda gördüğünüz sadece bu verinin temsilidir. Bu Charactersınıflar oyunun kalbidir ve bu nedenle " tanrı nesneleri " olma tehlikesi altındadır . Bu yüzden buna karşı aşırı önlemler öneriyorum: Tüm sınıfları bu sınıflardan kaldırın. GetFullName()Adı ve soyadını birleştiren bir yöntem , Tamam, ama aslında "bir şeyler yapan" hiçbir kod yok. Put o bir eylem yapmak adanmış sınıfa kod; örneğin, Birtheryöntemi olan bir sınıf Character CreateCharacter(Character father, Character mother), Charactersınıfta bu işlevselliğe sahip olmaktan çok daha temiz olacaktır .

Verileri kodda saklamayın

Onları saklamak için en kolay seçenek komut dosyası nesneleri kullanmak olacaktır

Hayır. Unity'nin JsonUtility yazılımını kullanarak JSON formatında saklayın. Bu işlevsellik yok Charactersınıfları ile yapılması önemsiz olmalıdır. Bu, oyunun ilk kurulumu ve kaydetme oyunlarında saklanması için işe yarayacaktır. Ancak, hala yapmak sıkıcı bir şey, bu yüzden durumunuzdaki en kolay seçeneği verdim. Ayrıca, bir metin dosyasında saklandığında insanlar tarafından okunabildiği sürece, XML veya YAML veya herhangi bir formatı gerçekten kullanabilirsiniz. CK2 de aynısını yapar, aslında çoğu oyun bunu yapar. Ayrıca, insanların oyununuzu modifiye etmesine izin vermek için mükemmel bir kurulum, ancak bu daha sonra düşünülüyor.

Soyut düşün

[...] işlerken karakterin "Canlı" olduğundan emin olmak için basit bir kontrol ekledim, ancak öldüğünde "Karakter" i kaldıramıyorum çünkü soy ağacını oluştururken onun bilgisine ihtiyacım var.

Bunu söylemek yapmaktan daha kolaydır, çünkü genellikle doğal düşünme biçimiyle çarpışır. "Doğal" bir şekilde, "karakter" olarak düşünüyorsunuz. Ancak, oyununuz açısından, "bir karakter" olan en az 2 farklı veri türü varmış gibi görünüyor: Ben buna diyeceğim ActingCharacterve FamilyTreeEntry. Ölü bir karakterin FamilyTreeEntrygüncellenmesi gerekmez ve muhtemelen aktif olandan çok daha az veriye ihtiyaç duyar ActingCharacter.


0

Katı bir OO tasarımından Entity-Component-System (ECS) tasarımına kadar biraz deneyimden bahsedeceğim.

Bir süre önce tıpkı sizin gibiydim , benzer özelliklere sahip bir dizi farklı şeyim vardı ve çeşitli nesneler inşa ettim ve çözmek için miras kullanmaya çalıştım. Çok zeki bir insan bana bunu yapmadığını ve bunun yerine Entity-Component-System'ı kullandığını söyledi.

Şimdi, ECS büyük bir kavram ve doğru olmak zor. Varlıkları, bileşenleri ve sistemleri düzgün bir şekilde inşa eden çok fazla iş var. Bunu yapmadan önce terimleri tanımlamamız gerekir.

  1. Varlık : bu şey , oyuncu, hayvan, NPC, her neyse . Ekli bileşenlere ihtiyaç duyan bir şey.
  2. Bileşen : Bu, durumunuzdaki "Ad" veya "Yaş" veya "Ebeveynler" gibi özellik veya özelliktir .
  3. Sistem : Bu, bir bileşenin veya davranışın arkasındaki mantıktır . Genellikle, bileşen başına bir sistem oluşturursunuz, ancak bu her zaman mümkün değildir. Ayrıca, bazen sistemlerin diğer sistemleri de etkilemesi gerekir .

İşte bununla nereye gideceğim:

Her şeyden önce, IDkarakterleriniz için bir oluşturun . An int, Guidne istersen. Bu "Varlık" tır.

İkincisi, yaşadığınız farklı davranışları düşünmeye başlayın. "Aile Ağacı" gibi şeyler - bu bir davranış. Varlık üzerinde öznitelik olarak modellemek yerine, tüm bu bilgileri içeren bir sistem oluşturun . Sistem daha sonra onunla ne yapacağına karar verebilir.

Benzer şekilde, "Karakter canlı mı, ölü mü?" Bu, tasarımınızdaki en önemli sistemlerden biridir, çünkü diğerlerini etkiler. Bazı sistemler "ölü" karakterleri ("hareketli grafik" sistemi gibi) silebilirken, diğer sistemler yeni durumu daha iyi desteklemek için işleri dahili olarak yeniden düzenleyebilir.

Örneğin, bir "Sprite" veya "Çizim" veya "Oluşturma" sistemi oluşturacaksınız. Bu sistem, karakterin hangi hareketli grafikle gösterilmesi gerektiğini ve nasıl gösterileceğini belirleme sorumluluğuna sahip olacaktır. Ardından, bir karakter öldüğünde onları kaldırın.

Ek olarak, bir karaktere ne yapacağını, nereye gideceğini vb. Söyleyebilen bir "AI" sistemi. Bu, diğer birçok sistemle etkileşime girmeli ve bunlara dayalı kararlar almalıdır. Yine, ölü karakterler muhtemelen bu sistemden kaldırılabilir, çünkü artık hiçbir şey yapmıyorlar.

"Ad" sisteminiz ve "Aile Ağacı" sisteminiz muhtemelen karakteri (canlı veya ölü) bellekte tutmalıdır. Bu sistemin, karakterin durumu ne olursa olsun bu bilgiyi hatırlaması gerekir. (Jim onu ​​gömdükten sonra bile hala Jim'dir.)

Bu aynı zamanda bir sistem daha verimli tepki verdiğinde değiştirme avantajı sağlar : sistemin kendi zamanlayıcısı vardır. Bazı sistemlerin hızlı bir şekilde ateş etmesi gerekir, bazılarının ateş etmemesi gerekir. Burası, bir oyunu verimli bir şekilde çalıştıran şeyin içine girmeye başladığımız yerdir. Havayı her milisaniyede bir yeniden hesaplamamız gerekmiyor, muhtemelen her 5 ya da daha fazla.

Aynı zamanda size daha yaratıcı bir kaldıraç sağlar: A'dan B'ye bir yolun hesaplanmasını işleyebilen ve gerektiğinde güncelleyebilen bir "Yol Bulucu" sistemi oluşturabilir, Hareket sisteminin "nerede yapmam gerekir? sonrakine git?" Artık bu endişeleri ve nedenlerini daha etkin bir şekilde ayırabiliriz. Hareketin yolu bulmasına gerek yok, sadece oraya gitmeniz gerekiyor.

Bir sistemin bazı kısımlarını dışarıya maruz bırakmak isteyeceksiniz. Senin içinde Pathfindersistemin muhtemelen isteyeceksiniz Vector2 NextPosition(int entity). Bu şekilde, bu öğeleri sıkı denetimli dizilerde veya listelerde tutabilirsiniz. structBileşenleri, sistem güncellemelerini çok daha hızlı hale getirebilecek daha küçük, bitişik bellek bloklarında tutmanıza yardımcı olabilecek daha küçük türler kullanabilirsiniz . (Özellikle bir sistem üzerindeki dış etkiler asgari düzeyde ise, şimdi yalnızca bunun iç durumunu dikkate alması gerekir Name.)

Ama, bunu yeterince vurgulayamıyorum, şimdi an Entitysadece IDfayans, nesne vb. İçeren bir. Bir varlık bir sisteme ait değilse, sistem onu ​​izlemez. Bu, "Ağaç" nesnelerimizi oluşturabildiğimiz, bunları Spriteve Movementsistemlerinde saklayabileceğimiz anlamına gelir (ağaçlar hareket etmez, ancak "Konum" bileşenine sahiptir) ve bunları diğer sistemlerin dışında tutabiliriz. Artık ağaç için özel bir listeye ihtiyacımız yok, çünkü bir ağaç oluşturmak, kağıt doldurmanın yanı sıra bir karakterden farklı değildir. ( SpriteSistemin kontrol edebileceği veya Paperdollsistemin kontrol edebileceği.) Şimdi NextPositionbiraz yeniden yazılabilir: Vector2? NextPosition(int entity)ve nullumursamadığı varlıklar için bir konum döndürebilir . Bunu da kendimize NameSystem.GetName(int entity)uyguluyoruz null, ağaçlar ve kayalar için geri dönüyor .


Bunu sonuna kadar çizeceğim, ancak burada fikir size ECS hakkında biraz arka plan vermek ve oyununuzda daha iyi bir tasarım sağlamak için gerçekten nasıl kullanabileceğinizdir. Performansı artırabilir, alakasız öğeleri çözebilir ve işleri daha düzenli bir şekilde tutabilirsiniz. (Eğer varsa ben çok F # kontrol tavsiye F # ve LINQ gibi bu da çiftleri de fonksiyonel dilleri / kurulumları, değil zaten, bu çiftleri çok iyi C # ile size birlikte kullanıldıklarında.)


Merhaba, Böyle ayrıntılı bir yanıt için teşekkürler. Ben sadece tüm diğer oyun içi karakter referans içeren bir GameObject Yöneticisi kullanıyorum.
paul p

Birlik'te gelişmek, GameObjectfazla bir Componentşey yapmayan, ancak gerçek işi yapan sınıfların bir listesine sahip denilen varlıklar etrafında döner . Orada on yıl önce dolambaçlı ECSS ilişkin bir paradigma kayması ayrı bir sistem sınıflarında oyunculuk kodunu koyarak olarak temizdir,. Birlik yakın zamanda böyle bir sistemi de uygulamaya koydu, fakat onların GameObjectsistemi bir ECS idi ve her zaman çok fazlaydı. OP zaten bir ECS kullanıyor.
Raphael Schmitz

-1

Bunu Birlik'te yaptığınız için en kolay yaklaşım şudur:

  • karakter veya birim türü başına bir oyun nesnesi oluşturma
  • bunları prefabrik olarak kaydet
  • daha sonra gerektiğinde bir prefabrik başlatabilirsiniz
  • bir karakter öldürüldüğünde, gameobject'i yok edin ve artık herhangi bir CPU veya bellek kaplamıyor

Kodunuzda, her zaman Find () ve varyasyonlarını kullanmaktan kurtulmak için Liste gibi bir şeydeki nesnelere başvuruları saklayabilirsiniz. Bellek için CPU döngüleri alıyorsunuz, ancak bir işaretçi listesi oldukça küçüktür, bu yüzden birkaç bin nesne olsa bile çok fazla sorun olmamalıdır.

Oyun boyunca ilerledikçe, bireysel oyun nesnelerine sahip olmanın size navigasyon ve AI dahil tonlarca avantaj sağladığını göreceksiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.