Bileşen tabanlı bir varlık sistemindeki komut dosyası ve "yerel" bileşenleri işleme


11

Şu anda bir varlık temelde sadece bir kimlik ve bir oyun nesnesi oluşturmak için bileşenleri bir demet bağlayan bazı yardımcı yöntemler olduğu bir bileşen tabanlı varlık sistemi uygulamaya çalışıyorum. Bunun bazı hedefleri:

  1. Bileşenler yalnızca durum içerir (örn. Konum, sağlık, cephane sayısı) => mantık, bu bileşenleri ve durumlarını (ör. PhysicsSystem, RenderSystem, vb.) İşleyen "sistemlere" gider.
  2. Bileşenleri ve sistemleri hem saf C # 'da hem de komut dosyası (Lua) aracılığıyla uygulamak istiyorum. Temelde C # kaynağımı yeniden derlemek zorunda kalmadan doğrudan Lua içinde tamamen yeni bileşenler ve sistemler tanımlamak istiyorum.

Şimdi, bu verimli ve tutarlı bir şekilde nasıl ele alınacağına dair fikirler arıyorum, bu yüzden örneğin C # bileşenlerine veya Lua bileşenlerine erişmek için farklı sözdizimi kullanmama gerek yok.

Şu anki yaklaşımım, muhtemelen editöre varsayılan değerler ve şeyler hakkında bilgi veren bazı özniteliklerle süslenmiş düzenli, genel özellikleri kullanarak C # bileşenlerini uygulamak olacaktır. Sonra ben sadece bir Lua tablo dahili olarak bu tablo bir komut dosyası tarafından oluşturulan ve söz konusu bileşen türünün tüm durumunu tutan bir C # sınıfı "ScriptComponent" olurdu. Derleme zamanında, hangi ScriptComponents benim için hangi özellikleri ile kullanılabilir olacağını bilmiyordum gibi ben gerçekten C # tarafında bu duruma erişmek istemiyorum. Yine de, editörün buna erişmesi gerekecek, ancak aşağıdaki gibi basit bir arayüz yeterli olmalıdır:

public ScriptComponent : IComponent
{
    public T GetProperty<T>(string propertyName) {..}
    public void SetProperty<T>(string propertyName, T value) {..}
}

Bu, Lua tablosuna erişir ve Lua'dan bu özellikleri ayarlar ve alır ve saf C # bileşenlerine de kolayca dahil edilebilir (ancak daha sonra yansıma veya bir şey aracılığıyla normal C # özelliklerini kullanın). Bu, yalnızca oyun düzenleyicide kullanılır, normal oyun kodu tarafından kullanılmaz, bu nedenle performans burada o kadar önemli değildir. Belirli bir bileşen türünün gerçekte hangi özellikleri sunduğunu belgeleyen bazı bileşen açıklamaları oluşturmayı veya el yazmayı gerekli kılacaktır, ancak bu çok büyük bir sorun olmaz ve yeterince otomatik olabilir.

Ancak, bileşenlere Lua tarafından nasıl erişilir? Böyle bir şeye çağrı getComponentsFromEntity(ENTITY_ID)yaparsam, muhtemelen kullanıcı verileri olarak "ScriptComponent" de dahil olmak üzere bir grup yerli C # bileşeni alırım. Sarılmış Lua tablosundan değerlere erişmek,GetProperty<T>(..) erişmek, diğer C # bileşenlerinde olduğu gibi özelliklere doğrudan erişmek yerine yöntemi .

Belki özel bir yazı yaz getComponentsFromEntity() sadece yerel C # bileşenlerini kullanıcı verileri olarak döndüren Lua'dan çağrılacak yöntem yazın, bunun yerine "ScriptComponent" hariç, bunun yerine kaydırılan tabloyu döndürür. Ancak diğer bileşenlerle ilgili yöntemler olacak ve gerçekten tüm bu yöntemleri hem C # kodundan veya Lua komut dosyasından çağrılmak için çoğaltmak istemiyorum.

Nihai amaç, yerel bileşenler ve Lua bileşenleri arasında, özellikle Lua tarafından farklı bir özel durum sözdizimi olmaksızın, tüm bileşen türlerini aynı şekilde ele almak olacaktır. Örneğin, böyle bir Lua betiği yazmak istiyorum:

entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")

nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3

Komut dosyası aslında ne tür bir bileşen aldığını umursamamalı ve bileşenleri almak, eklemek veya kaldırmak için C # tarafından kullanacağım yöntemleri kullanmak istiyorum.

Belki de senaryoyu böyle varlık sistemlerine entegre etmenin bazı örnek uygulamaları vardır?


1
Lua kullanmaya takılıp kaldınız mı? Ben çalışma sırasında diğer CLR dilleri ayrıştırılmakta olan bir C # uygulaması komut dosyası benzer şeyler yaptım (Boo, benim durumumda). Zaten yönetilen bir ortamda üst düzey kod çalıştırdığınız ve tüm IL kaputun altında olduğundan, varolan sınıfları "komut dosyası" tarafından alt sınıflara ayırmak ve bu sınıfların örneklerini ana bilgisayar uygulamasına geri aktarmak kolaydır. Benim durumumda, daha fazla yükleme hızı için derlenmiş bir komut dosyası derlemesini bile önbelleğe alırdım ve komut dosyaları değiştiğinde yeniden oluşturdum.
justinian

Hayır, Lua ile sıkışıp kalmadım. Kişisel olarak basitliği, küçük boyutu, hızı ve popülaritesi nedeniyle kullanmak istiyorum (bu yüzden zaten tanıdık olanların şansı daha yüksek görünüyor), ancak alternatiflere açıkım.
Mario

Neden C # ile komut dosyası ortamınız arasında eşit tanım yeteneklerine sahip olmak istediğinizi sorabilir miyim? Normal tasarım, C # 'da bileşen tanımı ve ardından kodlama dilinde' nesne montajı'dır. Bunun neden bir çözüm olduğunu merak ediyorum.
James

1
Amaç, bileşen sistemini C # kaynak kodunun dışından genişletilebilir hale getirmektir. Yalnızca XML veya komut dosyası dosyalarındaki özellik değerlerini ayarlayarak değil, yepyeni bileşenleri tamamen yeni özelliklerle ve bunları işleyen yeni sistemlerle tanımlayarak. Bu, büyük ölçüde Scott Bilas'ın Dungeon Siege'deki nesne sistemini tanımlamasından esinlenerek, "yerli" C ++ bileşenlerinin yanı sıra kendisi için sadece bir senaryo için bir sargı olan "GoSkritComponent" i de içeriyor: scottbilas.com/games/dungeon kuşatma
Mario

Bir komut dosyası veya eklenti arabirimi inşa etme tuzağına düşmekten kaçının, böylece orijinal aracın yeniden yazılmasına yaklaşır (bu durumda C # veya Visual Studio).
3Dave

Yanıtlar:


6

FxIII'ın cevabının bir sonucu, entegrasyon mantığınızın aslında modifikasyon için gerekli olduğundan emin olmak için önce her şey (modifiye edilebilir) komut dosyası dilinde (veya en azından çok iyi bir kısmı ) tasarlamanız gerektiğidir . Komut dosyası entegrasyonunuzun çok yönlü olduğundan eminseniz, gerekli bitleri C # 'da yeniden yazın.

Şahsen C # 4.0'daki dynamicözellikten nefret ediyorum (statik bir dil bağımlısıyım) - ancak bu şüphesiz kullanılması gereken ve gerçekten parladığı bir senaryodur. C # bileşenleriniz güçlü / genişletilmiş davranışsal nesneler olacaktır .

public class Villian : ExpandoComponent
{
    public void Sound() { Console.WriteLine("Cackle!"); }
}

//...

dynamic villian = new Villian();
villian.Position = new Vector2(10, 10); // Add a property.
villian.Sound(); // Use a method that was defined in a strong context.

Lua bileşenleriniz tamamen dinamik olacaktır (yukarıdaki aynı makale size bunu nasıl uygulayacağınız konusunda bir fikir vermelidir). Örneğin:

// LuaSharp is my weapon of choice.
public class LuaObject : DynamicObject
{
    private LuaTable _table;

    public LuaObject(LuaTable table)
    {
        _table = table;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        var methodName = binder.Name;
        var function = (LuaFunction)_table[methodName];
        if (function == null)
        {
            return base.TryInvokeMember(binder, args, out result);
        }
        else
        {
            var resultArray = function.Call(args);
            if (resultArray == null)
                result = null;
            else if (resultArray.Length == 1)
                result = resultArray[0];
            else
                result = resultArray;
            return true;
        }
    }

    // Other methods for properties etc.
}

Şimdi dynamickullandığınız için Lua nesnelerini sanki C # 'da gerçek sınıflarmış gibi kullanabilirsiniz.

dynamic cSharpVillian = new Villian();
dynamic luaVillian = new LuaObject(_script["teh", "villian"]);
cSharpVillian.Sound();
luaVillian.Sound(); // This will call into the Lua function.

İlk paragrafa tamamen katılıyorum, genellikle işimi bu şekilde yapıyorum. Daha düşük bir dilde yeniden uygulamak zorunda kalacağınızı bildiğiniz bir kod yazmak, faizler için ödemeniz gerektiğini bilecek bir kredi almak gibidir. BT tasarımcıları genellikle kredi alırlar: bunu yaptıklarını bildiklerinde ve borçlarını kontrol altında tuttuklarında iyi olurlar.
FxIII

2

Tam tersini yapmayı tercih ederim: dinamik bir dil kullanarak hepsini yazın ve ardından (varsa) bottleneks'i takip edin. Birini bulduğunuzda, C # veya (oldukça) C / C ++ kullanarak ilgili işlevleri seçerek yeniden uygulayabilirsiniz.

Bunu size söylüyorum, çünkü yapmaya çalıştığınız şey sisteminizin esnekliklerini C # bölümünde sınırlamak; sistem karmaşıklaştıkça C # motorunuz dinamik bir yorumlayıcı gibi görünmeye başlayacaktır, muhtemelen en iyisi değil. Soru şudur: Zamanınızın çoğunu genel bir C # motoru yazmaya veya oyununuzu genişletmeye yatırım yapmaya hazır mısınız?

Hedefiniz ikinciyse, inandığım gibi, muhtemelen zamanınızı en iyi oyun mekaniğine odaklanarak kullanacak ve deneyimli bir yorumlayıcının dinamik kodu çalıştırmasına izin vereceksiniz.


Evet, eğer her şeyi senaryo yazarak yaparsam, bazı temel sistemler için hala bu kötü performans korkusu var. Bu durumda, bu işlevselliği tekrar C # 'a (ya da her neyse) taşımak zorunda kaldım ... bu yüzden, bileşen sistemi ile C # tarafından arabirim kurmak için iyi bir yola ihtiyacım var. Buradaki temel sorun budur: hem C # hem de komut dosyasından eşit derecede kullanabileceğim bir bileşen sistemi oluşturmak.
Mario

C # C ++ veya C gibi şeyler için bir tür "hızlandırılmış komut dosyası benzeri" ortam olarak kullanıldığını düşündüm ? Her neyse, hangi dilde olacağınızdan emin değilseniz, Lua ve C # 'a mantık yazmaya başlayın. Bahse girerim, gelişmiş editör araçları ve hata ayıklama özellikleri nedeniyle çoğu zaman C # 'da daha hızlı olursunuz.
Imi

4
+1 Yine de buna katılıyorum - oyununuzun genişletilebilir olmasını istiyorsanız, kendi köpek mamasını yiyor olmalısınız: bir senaryo motorunu sadece potansiyel mod topluluğunuzun aslında hiçbir şey yapamayacağını bulmak için entegre edebilirsiniz. o.
Jonathan Dickinson
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.