Oyun aynı anda tüm karakterleri nasıl idare edebilir?


31

Bu soru sadece bir oyunun aynı anda bu kadar çok karakteri nasıl idare edebileceği hakkında bilgi edinmektir. Ben oyunda yeniyim, bu yüzden önceden özür dilerim.

Örnek

Kulelerin yapıldığı ve her kulenin belirli bir oranda mermi fırlattığı 15 kule yuvası olduğu bir kule savunma oyunu yaratıyorum ; sağlar ki her saniye, 2 mermi kulelerin her biri tarafından oluşturulur ve orada savaş alanında yürüyen düşmanlar, 70 diyelim onlar etrafında hareket ederken değişecektir hangi vb HP, mana gibi niteliklerin 10 türleri ile (her savaş).

özet

Kule Sayısı =
Saniyede Her Kule Tarafından Oluşturulan 15 Mermi = 2
Saniyede Oluşturulan Toplam Mermi
Sayısı = Savaş Alanı Sayımında 30 Birim = 70

Şimdi, oyun olanlar ele alır 30 mermi ve 70 birimleri üzerinde ele alarak 100 farklı iplikler (PC için çok fazla) ya da hepsi hamle, vb onların değerini, azalttığını 1 parçacığı (tür yavaş olacaktır hangi , Bence)?

Bununla ilgili bir ipucum yok, bu yüzden işlerin nasıl sonuçlanacağına dair bana rehberlik edebilecek biri var mı?


Yorumlar genişletilmiş tartışmalar için değildir; bu konuşma sohbete taşındı .
MichaelHouse

Diğer cevaplara ekleyerek ... bazı büyük oyunların bir örneği. Skyrim, oyun mantığı güncellemesinin çoğunu tek bir iş parçacığında buldu. Bunu çok iyi yönetme şekli, uzak NPC'lerin (mil uzakta olan NPC'ler) programlarına göre yaklaştırılmasıdır. MMO'ların çoğu oyun mantığını tek bir iş parçacığında günceller, ancak bir haritanın her bir kısmı farklı bir iş parçacığında ya da sunucu rafında bulunur.
moonshineTheleocat

Yanıtlar:


77

Şimdi, oyun, bu 30 Mermi ve 70 birimi 100 farklı ipte ele alarak nasıl idare ediyor?

Hayır, bunu asla yapma. Asla kaynak başına yeni bir iş parçacığı oluşturmayın, bu ağ oluşturmada ölçeklenmez, varlıkların güncellenmesi de gerçekleşmez. (Java'da soket başına okumak için bir iş parçacığının olduğu zamanları hatırlayan var mı?)

Hepsini hareket ettiren 1 iş parçacığı değerlerini vb.

Evet, yeni başlayanlar için, bu şekilde gitmek. "Büyük motorlar", iş parçacığı arasında bazı işleri böler, ancak bu, bir kule savunma oyunu gibi basit bir oyuna başlamak için gerekli değildir. Muhtemelen, bu konuya gireceğiniz her kene yapmak için daha fazla iş vardır. Oh evet ve elbette render.

(bence biraz yavaş olacak)

Peki ... yavaş tanımıyorsun ? 100 işletme için kod kalitenize ve üzerinde çalıştığınız dile bağlı olarak, muhtemelen daha da az yarım milisaniyeden fazla sürmemelidir. Tam iki milisaniye sürse bile, 60 tps'ye ulaşacak kadar iyidir (bu durumda saniye başına işaretler, çerçeveler hakkında konuşmazsınız).


34
Garip bir şey yapmazsan daha çok yarım saniye. Ancak en önemlisi, çalışmayı birden fazla iş parçacığına bölmek her şeyi daha iyi değil daha kötü hale getirecek . Multithreading'in son derece zor olduğunu söylemeye gerek yok .
Luaan

13
+1 Modern oyun motorlarının çoğu, gerçek zamanlı olarak binlerce hatta on binlerce çokgen oluşturuyor; bu da bellekteki sadece 100 nesnenin hareketini izlemekten çok daha yoğun.
phyrfox

1
"Bir kule savunma oyunu gibi basit bir oyun." Hmm ... Hiç Savunma Izgarası: Uyanış ya da devam filmini oynadın mı?
Mason Wheeler

4
“Asla kaynak başına yeni bir iş parçacığı oluşturma, bu ağ oluşturmada ölçeklenmiyor …” ahem bazı çok ölçeklenebilir mimariler tam olarak bunu yapıyor!
NPSF3000,

2
@BarafuAlbino: Söylemesi garip bir şey. Kullanılabilir çekirdeklerden daha fazla iş parçacığı oluşturmak için birçok geçerli neden vardır. Diğer herhangi bir tasarım kararında olduğu gibi karmaşıklık / performans / vb.
Dietrich Epp

39

Çoklu okumanın bir numaralı kuralı şudur: Performans veya yanıt verme için birden fazla CPU çekirdeği üzerinde paralelleştirme yapmanız gerekmediği sürece kullanmayın . Çoklu okuma kullanmak için "x ve y'nin kullanıcılar açısından aynı anda gerçekleşmesi" gerekliliği henüz yeterli değildir.

Niye ya?

Çoklu okuma zor. Her bir ipliğin ne zaman yürütüldüğünü kontrol edemezsiniz, bu da sorunları yeniden üretmek için her türlü imkansızlığa neden olabilir ("yarış koşulları"). Bunu önlemenin yöntemleri vardır (senkronizasyon kilitleri, kritik bölümler), ancak bunlar kendi problem setleriyle ("kilitlenmeler") birlikte gelir.

Genellikle sadece birkaç yüz kadar düşük sayıdaki nesnelerle uğraşan oyunlar (evet, bu oyun geliştirmede bu kadar fazla değildir) genellikle bunları ortak bir fordöngü kullanarak her mantıkla tiklayarak seri şekilde işler .

Nispeten zayıf akıllı telefon işlemcileri bile saniyede milyarlarca talimat verebilir . Bu, nesnelerinizin güncelleme mantığı karmaşık olduğunda ve nesne başına yaklaşık 1000 talimat aldığında ve işaretlendiğinde ve saniyede cömert 100 tene atmayı hedeflediğinizde bile, on binlerce nesne için yeterli CPU kapasitesine sahip olduğunuz anlamına gelir. Evet, bu büyük ölçüde basitleştirilmiş zarf arkası hesaplamasıdır, ancak size bir fikir verir.

Ayrıca, oyun geliştirmedeki yaygın bilgelik, oyun mantığının oyunun çok az tıkanması olduğudur. Performans açısından kritik olan kısım neredeyse her zaman grafiktir. Evet, 2d oyunlar için bile.


1
"Çok okuyucunun bir numaralı kuralı şudur: Performans veya yanıt verme için birden fazla CPU çekirdeğine paralel hale getirmeniz gerekmediği sürece kullanmayın." Olabilir (ama ben bile şüphe) Oyun geliştirme için de geçerli. Gerçek zamanlı sistemlerle çalışırken, konu eklemenin birincil nedeni son başvuru tarihlerini ve mantıksal sadeliği bulmaktır.
Sam,

6
@Sam Gerçek zamanlı sistemlerde son tarihler toplantı yanıtlama için çok okumaya ihtiyaç duyduğunuz bir durumdur. Ancak orada diş çekme yoluyla göründüğünüz mantıksal basitlik çoğu zaman haindir, çünkü çıkmazlar, yarış koşulları ve kaynak açlığı şeklinde gizli karmaşıklık yaratır.
Philipp

Maalesef, birçok kez oyun mantığının yol bulma problemleri varsa oyunun tamamında ilerlemesini gördüm.
Loren Pechtel

1
@LorenPechtel Bunu da gördüm. Ancak, genellikle gereksiz yol hesaplamaları yapmamak (her tek kene üzerindeki her tek yolu yeniden hesaplamak gibi), sıkça istenen yolları önbelleğe almak, çok katmanlı yol bulma özelliğini kullanarak ve daha uygun yol bulma algoritmalarını kullanarak çözülebilir. Bu, yetenekli bir programcının genellikle çok fazla optimizasyon potansiyeli bulabileceği bir şeydir.
Philipp

1
@LorenPechtel Örneğin bir kule savunma oyununda, tipik olarak sadece bir avuç hedef noktası olduğu gerçeğini kullanabilirsiniz. Böylece, tüm üniteleri yönlendiren bir yön haritası hesaplamak için Dijkstra'nın her bir hedef için algoritmasını çalıştırabilirsiniz . Bu haritaları her karede yeniden hesaplamanız gereken dinamik bir ortamda bile, bu hala uygun fiyatlı olmalıdır.
CodesInChaos

26

Diğer cevaplar modern bilgisayarların iş parçacığını ve gücünü ele almıştır. Yine de daha büyük soruyu ele almak için, burada yapmaya çalıştığınız şey "kare olmayan" durumlardan kaçınmaktır.

Örneğin, 1000 mermi ve 1000 düşmanınız varsa, saf çözüm, hepsini birbirlerine karşı kontrol etmektir.

Bu, p * e = 1,000 * 1,000 = 1,000,000 farklı çekle sonuçlandığınız anlamına gelir! Bu O (n ^ 2).

Öte yandan, verilerinizi daha iyi düzenlerseniz, bundan çok kaçınabilirsiniz.

Örneğin, şebekenin her karesinde hangi karede düşman olduğunu gösterirseniz, 1000 merminiz üzerinden döngü geçirebilir ve ızgaradaki kareyi kontrol edebilirsiniz. Şimdi her mermiyi kareye göre kontrol etmeniz gerekiyor, bu O (n). Milyonlarca kontrol yerine her kareye yalnızca bin tane ihtiyacınız var.

Verilerinizi organize etmeyi düşünün ve bu organizasyondan dolayı verimli bir şekilde işleyin, yapabileceğiniz en büyük tek optimizasyondur.


1
Sadece birkaç elementi izlemek için tüm şebekeyi belleğe kaydetmeye alternatif olarak, her eksen için bir tane olan b-ağaçları da olası çarpışma adaylarını hızlıca aramak için kullanabilirsiniz. "; hit bölgeleri belirlersiniz ve bir çarpışma listesi istersiniz ve kütüphane size verir. Bu, geliştiricilerin sıfırdan yazmak yerine (mümkünse, elbette) bir motor kullanmak zorunda olmasının birçok nedeninden biridir.
phyrfox

@phyrfox Kesinlikle, bunu yapmanın çok farklı yolları vardır - kullanım durumunuza bağlı olarak daha iyi olan değişiklik gösterebilir.
Tim B

17

Kaynak / nesne başına değil, program mantığınızın her bir bölümü için iş parçacığı oluşturmayın. Örneğin:

  1. Birimleri ve mermileri güncellemek için konu - mantık ipliği
  2. Ekranı işlemek için kullanılan konu - GUI thread
  3. Ağ için iş parçacığı (örneğin, çok oyunculu) - IO iş parçacığı

Bunun avantajı, mantığınız yavaşsa GUI'nizin (örneğin, düğmelerin) mutlaka sıkışıp kalmamasıdır. Kullanıcı oyunu durdurabilir ve kaydedebilir. Ayrıca, oyununuzu çok oyunculu için hazırlamak için de iyi, şimdi grafiği mantıktan ayırıyorsunuz.


1
Yeni başlayanlar için ayrı grafik ve mantık dizileri kullanmanızı tavsiye etmem, çünkü gerekli verileri kopyalamazsanız, oyun durumunu göstermek oyun durumuna okuma erişimi gerektirir, bu yüzden çizerken oyun durumunu değiştiremezsiniz.
CodesInChaos

1
Çok sık çizilmemesi (örneğin, saniyede 50 defadan fazla), çok önemlidir ve bu soru performansla ilgiliydi. Programı bölmek, gerçek bir performans avantajı için yapılacak en basit şeydir. Bu, konu hakkında biraz bilgi gerektirdiği gerçeğidir, ancak bu bilgiyi edinmeye değer.
Tomáš Zato - Monica'yı

Bir Programın birden fazla Konuya bölünmesi , bir programcının yapması en zor şeydir. En can sıkıcı hataların çoğu çoklu iş parçacılığından kaynaklanmaktadır ve dev bir sıkıntıdır ve çoğu zaman buna değmez - İlk kural: Performans probleminiz olup olmadığını kontrol edin, THEN optimize edin. Ve darboğazı olduğu yerde sağ optimize edin. Belki de karmaşık bir algoritma için tek bir harici iş parçacığı. Ama o zaman bile, bu algoritmanın bitmesi 3 saniye sürdüğünde oyun mantığınızın nasıl ilerleyeceğini düşünmek zorundasınız ...
Falco

@Falco Bu modelin uzun vadeli avantajlarını denetliyorsunuz - hem proje hem de programcı deneyimi için. En zor olduğunu düşündüğünüz iddiası gerçekten ele alınamaz, bu sadece bir fikir. Bana göre GUI tasarımı çok daha korkunç. Tüm gelişmiş dillerin (C ++, Java) çok net okuyuculu modelleri vardır. Ve eğer gerçekten emin değilseniz, acemi çoklu kullanım hatalarından muzdarip olmayan aktör modelini kullanabilirsiniz. Çoğu uygulamanın benim önerdiğim gibi tasarlanmasının bir nedeni olduğunu biliyorsunuz, ancak daha fazla tartışmaktan çekinmeyin.
Tomáš Zato - Monica’yı

4

Space Invaders bile düzinelerce etkileşen nesneyi yönetti. HD H264 videonun bir karesini deşifre ederken yüz milyonlarca aritmetik işlem içerir. Bir var çok işlem gücü mevcut.

Bu, eğer israf edersen hala yavaşlatabilirsin dedi. Sorun, yapılan çarpışma testi sayısı kadar nesne sayısı değil; Her bir nesneyi diğer nesneye karşı kontrol etmenin basit yaklaşımı, gerekli hesaplama sayısını kareler . 1001 nesneyi çarpışmalar için bu şekilde test etmek milyonlarca karşılaştırma yapılmasını gerektirir. Genellikle bu, mermilerin birbirleriyle çarpışma için kontrol edilmemesi ile ele alınmaktadır.


2
Space Invaders'in bu konuda en iyi karşılaştırma olduğuna emin değilim. Düşmanları öldürürken yavaşlaması ve hızlanmasının sebebi, bu şekilde tasarlanması değil, donanımın o kadar çok düşmanı aynı anda gerçekleştirememesidir. en.wikipedia.org/wiki/Space_Invaders#Hardware
Mike Kellogg

Peki ya, her nesne bir sonraki saniyede kendileriyle çarpışabilecek kadar yakın olan, saniyede bir kez veya her yön değiştirdiğinde tüm nesnelerin listesini tutar?
Random832,

Neyi modellediğine bağlı. Uzay bölümleme çözümleri başka bir yaygın yaklaşımdır: dünyayı bölgelere ayırın (örneğin, renderleme amaçları için yapmanız gerekebilecek BSP veya dörtlü), o zaman sadece aynı bölgedeki nesnelerle çarpışabilirsiniz.
pjc50

3

Buradaki diğer cevapların bazılarına katılmıyorum. Ayrı mantık iplikleri sadece iyi bir fikir değil aynı zamanda işlem hızına da oldukça faydalıdır - eğer mantığınız kolayca ayrılabilirse .

Sorunuz üzerine ek bir mantık ekleyebiliyorsanız, muhtemelen ayrılabilir bir mantık örneğidir. Örneğin, iplikleri belirli alan bölgelerine kilitleyerek veya ilgili nesnelerin sesini kısarak birkaç isabet algılama ipliği çalıştırabilirsiniz.

Muhtemelen her olası çarpışma için bir iplik istemezsiniz, çünkü bunun programlayıcıyı yavaşlatması muhtemeldir; iplik oluşturma ve yok etmenin bir maliyeti de vardır. Sistemin çekirdeklerinin etrafında birkaç iplik oluşturmak (ya da eskisi gibi bir metrik kullanmak #cores * 2 + 4) daha iyidir, daha sonra işlemleri bittiğinde tekrar kullanın.

Yine de, tüm mantık kolayca ayrılabilir değildir. Bazen işlemleriniz tüm oyun verilerine aynı anda erişebilir ve bu da diş açmayı gereksiz kılar (aslında, zararlı, çünkü iş parçacığı sorunlarından kaçınmak için kontroller eklemeniz gerekir). Ayrıca, birden fazla mantık aşaması, belirli sıralarda meydana gelen birbirine büyük ölçüde bağımlıysa, sıraya bağlı sonuçlar vermeyecek şekilde ipliklerin yürütülmesini kontrol etmeniz gerekecektir. Ancak, bu konu, iş parçacığı kullanılmadan ortadan kaldırılmaz, iş parçacığı sadece onu daha da kötüleştirir.

Çoğu oyun bunu basitçe yapmaz, çünkü ortalama oyun geliştiricisinin genellikle ilk etapta tıkanıklık olmayan şeyleri idare etmeye istekli / yapabildiğinden daha karmaşıktır. Oyunların büyük çoğunluğu GPU sınırlıdır, CPU sınırlıdır. CPU hızını artırmak genel olarak yardımcı olabilirken, genellikle odak noktası değildir.

Bununla birlikte, fizik motorları sıklıkla birden fazla iş parçacığı kullanır ve birden fazla mantık iş parçacığından faydalanabileceğini düşündüğüm birkaç oyunu adlandırabilirim (örneğin, HOI3 ve benzeri Paradox RTS oyunları).

Yararlı olsa bile , muhtemelen bu özel örnekte iş parçacığı kullanmaya gerek duymayacağınız diğer paylaşımlara katılıyorum . Diş açma, diğer yöntemlerle optimize edilemeyen aşırı CPU yüküne sahip olduğunuz durumlar için ayrılmalıdır. Bu çok büyük bir taahhüt ve motorun temel yapısını etkileyecektir; Bu gerçekte sonra yapabileceğiniz bir şey değil.


2

Bence diğer cevaplar, sorunun çok fazla kısmına odaklanarak sorunun önemli bir bölümünü özlediğini düşünüyorum.

Bilgisayar bir oyunda tüm nesneleri aynı anda işlemez. Onları sırayla işler.

Bir bilgisayar oyunu ayrık zaman adımlarında ilerler. Oyuna ve PC'nin hızına bağlı olarak, bu adımlar genellikle saniyede 30 veya 60 adım veya PC'nin hesaplayabildiği kadar çok / birkaç adımdır.

Böyle bir adımda, bir bilgisayar oyun nesnelerinin her birinin bu adımda ne yapacağını hesaplar ve bunları birbiri ardına günceller. Paralel olarak bile, daha hızlı olması için iplikleri kullanmak bile mümkün olabilirdi, ancak yakında göreceğimiz gibi hız hiç de önemli değil.

Ortalama bir CPU 2 GHz veya daha hızlı olmalıdır, bu da saniyede 10 9 saat döngüsü anlamına gelir . Biz saniyede 60 dilimler, bu yapraklar 10 hesaplamak ise 9 /60 saat çevrimi = 16.666.666 saat çevrimleri zaman başına aşama. 70 ünite ile, geriye kalan birim başına yaklaşık 2.400.000 saat döngüsüne sahibiz. İyileştirmek zorunda olsaydık, her bir birimi oyun mantığının karmaşıklığına bağlı olarak 240 döngü kadar kısa bir sürede güncelleyebilirdik. Gördüğünüz gibi, bilgisayarımız bu görev için olması gerekenden yaklaşık 10.000 kat daha hızlı.


0

Feragatname: Tüm zamanların en sevdiğim oyun türü metin tabanlıdır ve bunu eski bir MUD'un uzun süredir programcısı olarak yazıyorum.

Kendine sorman gereken önemli bir soru şudur: Konuya ihtiyacın var mı? Grafiksel bir oyunun muhtemelen MT’leri daha fazla kullandığını biliyorum, ancak bunun oyunun mekaniğine de bağlı olduğunu düşünüyorum. (GPU'lar, CPU'lar ve bugün sahip olduğumuz diğer tüm kaynaklar sayesinde, kaynaklarınızla ilgili endişelerinizi sizin için göründüğü kadar sorunlu kılan, gerçekte 100 nesne neredeyse sıfırdır. Aynı zamanda 'aynı anda tüm karakterleri' nasıl tanımladığınıza da bağlıdır. Aynı anda mı demek istiyorsun? Peter'ın haklı olarak işaret ettiği gibi, bir kerede kelimenin tam anlamıyla alakasız olduğu; sadece bu şekilde görünür.

Eğer konuya gireceğinizi farz edersiniz: Kesinlikle 100 konu düşünmemelisiniz (işlemciniz için çok fazla olup olmadığına bile girmeyeceğim bile; bunun sadece komplikasyonlarına ve pratikliğine atıfta bulunuyorum).

Ancak şunu unutmayın: çoklu iş parçacığı kolay değildir (Philipp'in işaret ettiği gibi) ve birçok sorunu var. Diğerleri, MT ile yaptığımdan çok daha fazla deneyime sahip (fakat benim açımdan daha yetenekli olsalar bile), özellikle benim açımdan pratik yapmadan), aynı şeyi önereceklerini söyleyebilirim.

Bazıları iş parçacıklarının yararlı olmadığını, bazıları ise her nesnenin bir iş parçacığı olması gerektiğini savunuyorlar. Ancak (ve yine tüm bunlar metindir, ancak Philipp'in oyunların listelerde yinelenme eğiliminde olduğunu belirttiğinden, her bir nesne için birden fazla iş parçacığı düşünseniz bile - ve her bir nesne için düşünmemeniz gerekir). Ancak bu, sadece (çok az nesnenin parametrelerine karşılık verdiğini fark etmeme rağmen) önerdiği gibi değil. MUD'da programcı olduğum için aşağıdakilere sahibiz (ve bu gerçek zamanlı olarak gerçekleşen tüm faaliyetler değil, bu yüzden bunu aklınızda bulundurun):

(Örneklerin sayısı elbette değişir - daha yüksek ve daha düşük)

Cep telefonları (NPC yani oyuncu olmayan karakter): 2614; prototipler: 1360 Nesneler: 4457; prototipler: 2281 Oda sayısı: 7983; prototipler: 7983. Her odanın genellikle kendine ait bir örneği vardır, fakat aynı zamanda bir oda içindeki odaları söyleyen dinamik odalarımız da vardır; veya bir cep telefonu içindeki odalar, örneğin bir ejderhanın midesi; veya nesnelerdeki odalar, örneğin büyülü bir nesneye girersiniz). Bu dinamik odaların, kendilerini tanımlayan nesne / oda / mobil başına mevcut olduğunu unutmayın. Evet, bu World of Warcraft'ınkine çok benziyor (oynamıyorum ama bir süre Windows makinem varken bir arkadaşım oynamıştı), World of Warcraft'ın varlığından çok önce sahip olduğumuz durumlar dışında bir fikirdi.

Scriptler: 868 (şu anda) (garip bir şekilde istatistik komutumuz ne kadar prototipimiz olduğunu göstermiyor, bu yüzden bunu ekleyeceğim). Bunların hepsi bölgelerde / bölgelerde düzenleniyor ve bunlardan 103'ümüz var. Ayrıca farklı zamanlarda işleyen özel prosedürlerimiz de var. Başka etkinliklerimiz de var. O zaman biz de soket bağladık. Cep telefonları dolaşıyor, farklı etkinlikler yapıyor (savaş dışında), oyuncularla etkileşime giriyor vb. (Öyleyse diğer varlık türlerini de yapın).

Bütün bunları gecikmeden nasıl hallederiz?

  • soketler: select (), kuyruklar (giriş, çıkış, olaylar, diğer şeyler), arabellek (giriş, çıkış, diğer şeyler) vb. Bunlar saniyede 10 kez yoklanır.

  • karakterler, nesneler, odalar, savaş, her şey: hepsi farklı darbelerde merkezi bir döngüde.

Ayrıca (kurucu / diğer programcı ve kendim arasındaki tartışmaya dayanan uygulamam) kapsamlı bir bağlantılı liste izleme ve işaretçi geçerliliği testine sahibiz ve aslında ihtiyaç duymamız halinde yeterli kaynaktan daha fazlasına sahibiz. Bütün bunlar (dünyayı genişlettiğimiz hariç) yıllar önce daha az RAM, CPU gücü, sabit disk alanı vb. Varken vardı. Ve aslında o zaman bile sorun yaşamadık. Açıklanan döngülerde (senaryolar bunun nedenleri sıfırlar / diğer şeylerin çoğalmasına neden olur) canavarlar, nesneler (eşyalar) ve başka şeyler yaratılır, serbest bırakılır vb. Bağlantılar da kabul edilir, sorgulanır ve beklediğiniz diğer her şey.

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.