Cevap her zaman bir dizi veya std :: vector kullanmaktır. Bağlantılı bir liste veya std :: map gibi türler genellikle oyunlarda kesinlikle korkunçtur ve bu kesinlikle oyun nesneleri koleksiyonu gibi vakaları içerir.
Nesneleri dizide / vektörde (işaretçiler değil) kendileri saklamalısınız.
Sen istediğiniz bitişik bellek. Gerçekten istiyorsun. Bitişik olmayan bellekte herhangi bir veri üzerinde yineleme yapmak, genel olarak çok sayıda önbellek özlüsü getirir ve derleyici ve CPU'nun etkili önbellek önceden getirme yeteneğini ortadan kaldırır. Bu tek başına performansı öldürebilir.
Ayrıca bellek ayırmalarından ve yeniden konumlandırmalardan kaçınmak istersiniz. Hızlı bir bellek ayırıcıyla bile çok yavaştırlar. Oyunların her karede birkaç yüz bellek ayırmasını kaldırarak 10x FPS yumru aldığını gördüm. Bu kadar kötü olmalı gibi görünmüyor, ama olabilir.
Son olarak, oyun nesnelerini yönetmek için önem verdiğiniz çoğu veri yapısı, bir dizi veya vektör üzerinde bir ağaç veya liste ile olduğundan çok daha verimli bir şekilde uygulanabilir.
Örneğin, oyun nesnelerini kaldırmak için takas ve pop kullanabilirsiniz. Gibi bir şey ile kolayca uygulanır:
std::swap(objects[index], objects.back());
objects.pop_back();
Ayrıca, nesneleri silindi olarak işaretleyebilir ve bir dahaki sefere yeni bir nesne oluşturmanız gerektiğinde dizinlerini ücretsiz bir listeye koyabilirsiniz, ancak takas ve pop'u yapmak daha iyidir. Döngünün kendisinden dallanma olmadan tüm canlı nesneler üzerinde basit bir döngü yapmanızı sağlar. Mermi fiziği entegrasyonu ve benzerleri için bu önemli bir performans artışı olabilir.
Daha da önemlisi, sen-ebilmek bulmak nesneleri ile basit bir tablo aramalar istikrarlı bir benzersiz benzersiz yuvası harita yapısı kullanmaktır.
Oyun nesnelerinizin ana dizilerinde bir dizin vardır. Sadece bu indeksle çok verimli bir şekilde aranabilirler (bir haritadan veya bir karma tablodan çok daha hızlı). Ancak, nesne kaldırılırken takas ve pop nedeniyle dizin sabit değildir.
Bir yuva haritası iki dolaylı katman gerektirir, ancak her ikisi de sabit indekslere sahip basit dizi aramalarıdır. Onlar hızlı . Çok hızlı.
Temel fikir, üç dizinizin olması: ana nesne listeniz, dolaylı listeniz ve dolaylı liste için ücretsiz bir liste. Ana nesne listeniz, her nesnenin kendi benzersiz kimliğini bildiği gerçek nesnelerinizi içerir. Benzersiz kimlik bir dizin ve sürüm etiketinden oluşur. Dolaylı liste basitçe ana nesne listesine bir indeks dizisidir. Ücretsiz liste, dolaylı listeye bir indeks yığınıdır.
Ana listede bir nesne oluşturduğunuzda, dolaylı listede kullanılmayan bir giriş bulursunuz (ücretsiz listeyi kullanarak). Dolaylı listedeki giriş, ana listede kullanılmayan bir girişi gösterir. Nesnenizi bu konumda başlatırsınız ve benzersiz kimliğini seçtiğiniz dolaylı liste girişinin dizinine ve ana liste öğesindeki mevcut sürüm etiketine ve bir tanesine ayarlarsınız.
Bir nesneyi yok ettiğinizde, takas ve pop'u normal şekilde yaparsınız, ancak sürüm numarasını da artırırsınız. Daha sonra ücretsiz listeye dolaylı liste dizinini (nesnenin benzersiz kimliğinin bir parçası) da eklersiniz. Bir nesneyi takas ve pop'un bir parçası olarak taşırken, aynı zamanda dolaylı listedeki girişini yeni konumuna da güncellersiniz.
Örnek sözde kod:
Object:
int index
int version
other data
SlotMap:
Object objects[]
int slots[]
int freelist[]
int count
Get(id):
index = indirection[id.index]
if objects[index].version = id.version:
return &objects[index]
else:
return null
CreateObject():
index = freelist.pop()
objects[count].index = id
objects[count].version += 1
indirection[index] = count
Object* object = &objects[count].object
object.initialize()
count += 1
return object
Remove(id):
index = indirection[id.index]
if objects[index].version = id.version:
objects[index].version += 1
objects[count - 1].version += 1
swap(objects[index].data, objects[count - 1].data)
Dolaylı katman, sıkıştırma sırasında hareket edebilen bir kaynak (ana nesne listesi) için kararlı bir tanımlayıcıya (girdilerin hareket etmediği dolaylı katmana indeks) sahip olmanızı sağlar.
Sürüm etiketi, silinebilecek bir nesneye bir kimlik depolamanıza olanak tanır. Örneğin, (10,1) kimliğine sahipsiniz. Dizin 10'a sahip nesne silinir (örneğin, merminiz bir nesneye çarpar ve yok edilir). Bu durumda, ana nesne listesindeki belleğin konumundaki nesne, sürüm numarasına çarparak verir (10,2). Eski bir kimlikten tekrar (10,1) aramaya çalışırsanız, arama bu nesneyi dizin 10 aracılığıyla döndürür, ancak sürüm numarasının değiştiğini görebilir, böylece kimlik artık geçerli olmaz.
Bu, nesnelerin bellekte hareket etmesini sağlayan sabit bir kimlikle sahip olabileceğiniz en hızlı veri yapısıdır ve bu da veri konumu ve önbellek tutarlılığı için önemlidir. Bu, mümkün olan bir karma tablonun uygulanmasından daha hızlıdır; bir hash tablosunun en azından bir karma değerini hesaplaması gerekir (tablo aramasından daha fazla talimat) ve sonra karma zincirini (std :: unordered_map'nin korkunç durumunda bağlı bir liste veya açık adresli bir liste) izlemesi gerekir. bir karma tablonun aptal olmayan bir şekilde uygulanması) ve ardından her anahtarda bir değer karşılaştırması yapması gerekir (sürüm etiketi kontrolünden daha pahalı değil, ancak daha az pahalı olabilir). Çok iyi bir karma tablosu (STL'nin herhangi bir uygulamasındaki tablo değil, STL bir oyun nesnesi listesi için oyundan farklı kullanım durumlarını optimize eden bir karma tabloyu zorunlu kıldığı için) bir dolaylı kayıttan tasarruf edebilir,
Temel algoritmada yapabileceğiniz çeşitli iyileştirmeler vardır. Örneğin ana nesne listesi için std :: deque gibi bir şey kullanmak; fazladan bir dolaylı katman içerir, ancak nesnelerin alan haritasından aldığınız geçici işaretçileri geçersiz kılmadan tam listeye eklenmesine olanak tanır.
Ayrıca, dizin nesnenin bellek adresinden (this - nesneler) hesaplanabileceğinden ve daha iyisi yalnızca nesneyi kaldırırken daha iyi bir şeydir (bu durumda zaten nesnenin kimliğine sahip olmanız gerekir (ve dolayısıyla endeksi) parametresini kullanın.
Yazma özürleri; Bunun olabileceği en açık açıklama olduğunu düşünmüyorum. Geç oldu ve kod örnekleri üzerinde olduğundan daha fazla zaman harcamadan açıklamak zor.