TL; DR
Bu cevap biraz deliriyor. Ancak, yeteneklerinizi "Komutlar" olarak uygulamaktan bahsettiğinizi görüyorum, bu da C ++ / Java / .NET tasarım desenlerini ima eden ve kod ağırlıklı bir yaklaşım anlamına geliyor. Bu onay geçerli, ama daha iyi bir yol var. Belki zaten başka bir şey yapıyorsun. Eğer öyleyse, oh iyi. Umarım diğerleri bu durumda faydalı bulurlar.
Kovalamak için aşağıdaki Veriye Dayalı Yaklaşım'a bakın. Jacob Pennock'un CustomAssetUility'sini buradan edinin ve bu konudaki yayınını okuyun .
Unity ile Çalışma
Diğerlerinin de belirttiği gibi, 100-300 öğelik bir listeyi dolaşmak düşündüğünüz kadar büyük bir anlaşma değildir. Bu sizin için sezgisel bir yaklaşımsa, bunu yapın. Beyin verimliliği için optimize edin. @Norguard gösterildiği Ama Sözlük, onun cevap , sen sabit zamanlı ekleme ve alma almak beri bu sorunu ortadan kaldırmak için kolay no-beyin gücü-Gerekli yoludur. Muhtemelen kullanmalısınız.
Bunun Birlik içinde iyi çalışmasını sağlamak için, bağırsaklarım bana yetenek başına bir MonoBehaviour'un aşağı inmek için tehlikeli bir yol olduğunu söylüyor. Yeteneklerinizden biri yürüttükleri zaman içinde durumu koruyorsa, bu durumu sıfırlamak için bir yol sağlamanız gerektiğini yönetmeniz gerekir. Coroutines bu sorunu hafifletir, ancak yine de bu komut dosyasının her güncelleme çerçevesinde bir IEnumerator referansı yönetiyorsunuz ve eksik ve bir döngü halinde sıkışmış yetenekleri sıfırlamak için kesin bir yolunuz olduğundan emin olmalısınız. yetenekler fark edilmeden sessizce oyununuzun dengesini bozmaya başlar. "Elbette bunu yapacağım!" "Ben" İyi Bir Programcıyım "diyorsunuz. Ama gerçekten, biliyorsunuz, hepimiz nesnel olarak korkunç programcılarız ve en büyük AI araştırmacıları ve derleyici yazarları her zaman işleri mahvediyor.
Unity'de komut örnekleme ve erişimini uygulayabileceğiniz tüm yollardan ikisini düşünebilirim: biri iyi ve size bir anevrizma vermeyecek ve diğeri SINIRSIZ BÜYÜ YARATICILIĞINA izin veriyor . Bir çeşit.
Kod Merkezli Yaklaşım
Birincisi, çoğunlukla kod içinde bir yaklaşımdır. Ne tavsiye her bir BaseCommand abtract sınıf devralan ya da bir ICommand arabirimi uygulayan basit bir sınıf yapmak (Bu Komutlar sadece karakter yetenekleri olacak kısalık uğruna varsayıyorum, dahil etmek zor değil) diğer kullanımlar). Bu sistem, her komutun bir ICommand olduğunu, parametre almayan ve etkin durumdayken her karenin güncellenmesini gerektiren bir ortak yapıcıya sahip olduğunu varsayar.
Soyut bir temel sınıf kullanırsanız işler daha kolaydır, ancak sürümüm arabirimler kullanıyor.
MonoBehaviours'larınızın belirli bir davranışı veya yakından ilişkili davranışlar sistemini kapsaması önemlidir. Düzgün C # sınıflarına etkili bir şekilde vekalet eden çok sayıda MonoBehaviour'a sahip olmak iyidir, ancak kendinizi çok iyi bulursanız, XNA oyunu gibi görünmeye başladığı noktaya kadar her türlü farklı nesneye yapılan çağrıları güncelleyebilirsiniz. ciddi bir belada ve mimarinizi değiştirmeniz gerekiyor.
// ICommand.cs
public interface ICommand
{
public void Execute(AbilityActivator originator, TargetingInfo targets);
public void Update();
public bool IsActive { get; }
}
// CommandList.cs
// Attach this to a game object in your loading screen
public static class CommandList
{
public static ICommand GetInstance(string key)
{
return commandDict[key].GetRef();
}
static CommandListInitializerScript()
{
commandDict = new Dictionary<string, ICommand>() {
{ "SwordSpin", new CommandRef<SwordSpin>() },
{ "BellyRub", new CommandRef<BellyRub>() },
{ "StickyShield", new CommandRef<StickyShield>() },
// Add more commands here
};
}
private class CommandRef<T> where T : ICommand, new()
{
public ICommand GetNew()
{
return new T();
}
}
private static Dictionary<string, ICommand> commandDict;
}
// AbilityActivator.cs
// Attach this to your character objects
public class AbilityActivator : MonoBehaviour
{
List<ICommand> activeAbilities = new List<ICommand>();
void Update()
{
string activatedAbility = GetActivatedAbilityThisFrame();
if (!string.IsNullOrEmpty(acitvatedAbility))
ICommand command = CommandList.Get(activatedAbility).GetRef();
command.Execute(this, this.GetTargets());
activeAbilities.Add(command);
}
foreach (var ability in activeAbilities) {
ability.Update();
}
activeAbilities.RemoveAll(a => !a.IsActive);
}
}
Bu tamamen iyi çalışıyor, ancak daha iyisini yapabilirsiniz (ayrıca, List<T>
zamanlanmış yetenekleri saklamak için en uygun veri yapısı değildir, a LinkedList<T>
veya a isteyebilirsiniz SortedDictionary<float, T>
).
Veriye Dayalı Yaklaşım
Muhtemelen yeteneğinizin etkilerini parametreleştirilebilen mantıksal davranışlara indirgemeniz mümkündür. Birlik gerçekten bunun için inşa edilmişti. Bir programcı olarak, siz veya bir tasarımcının çok çeşitli efektler üretmek için editörde gidip manipüle edebileceği bir sistem tasarlarsınız. Bu, kodun "donanımını" basitleştirecek ve yalnızca bir yeteneğin yürütülmesine odaklanacaktır. Burada temel sınıfları veya arayüzleri ve jenerikleri karıştırmaya gerek yok. Tüm bunlar tamamen veriye dayalı olacaktır (komut örneklerinin başlatılmasını da basitleştirir).
İhtiyacınız olan ilk şey, yeteneklerinizi tanımlayabilen bir ScriptableObject. ScriptableObjects harika. Genel alanlarını Unity'nin müfettişinde ayarlayabilmeniz için MonoBehaviours gibi çalışmak üzere tasarlanmıştır ve bu değişiklikler diske serileştirilir. Ancak, herhangi bir nesneye bağlı değildirler ve bir sahnedeki bir oyun nesnesine iliştirilmeleri veya örneklenmeleri gerekmez. Bunlar Unity'nin tümünü yakalama veri kovalarıdır. Temel türleri, sıralamaları ve işaretli basit sınıfları (kalıtım yok) serileştirebilirler [Serializable]
. Yapılar Unity'de serileştirilemez ve serileştirme, denetçideki nesne alanlarını düzenlemenizi sağlar, bu yüzden unutmayın.
İşte çok yapmaya çalışan bir ScriptableObject. Bunu daha serileştirilmiş sınıflara ve ScriptableObjects'e ayırabilirsiniz, ancak bunun size bunu nasıl yapacağınıza dair bir fikir vermesi gerekir. Normalde bu, C # gibi güzel bir modern nesne yönelimli dilde çirkin görünüyor, çünkü tüm bu numaralarla bazı C89 bokları gibi hissediyor, ancak buradaki gerçek güç, artık desteklemek için yeni kod yazmadan her türlü farklı yetenekleri yaratabiliyor olmanız. onlar. Ve ilk biçiminiz yapmanız gereken şeyi yapmazsa, yapana kadar eklemeye devam edin. Alan adlarını değiştirmediğiniz sürece, tüm eski serileştirilmiş varlık dosyalarınız çalışmaya devam eder.
// CommandAbilityDescription.cs
public class CommandAbilityDecription : ScriptableObject
{
// Identification and information
public string displayName; // Name used for display purposes for the GUI
// We don't need an identifier field, because this will actually be stored
// as a file on disk and thus implicitly have its own identifier string.
// Description of damage to targets
// I put this enum inside the class for answer readability, but it really belongs outside, inside a namespace rather than nested inside a class
public enum DamageType
{
None,
SingleTarget,
SingleTargetOverTime,
Area,
AreaOverTime,
}
public DamageType damageType;
public float damage; // Can represent either insta-hit damage, or damage rate over time (depend)
public float duration; // Used for over-time type damages, or as a delay for insta-hit damage
// Visual FX
public enum EffectPlacement
{
CenteredOnTargets,
CenteredOnFirstTarget,
CenteredOnCharacter,
}
[Serializable]
public class AbilityVisualEffect
{
public EffectPlacement placement;
public VisualEffectBehavior visualEffect;
}
public AbilityVisualEffect[] visualEffects;
}
// VisualEffectBehavior.cs
public abtract class VisualEffectBehavior : MonoBehaviour
{
// When an artist makes a visual effect, they generally make a GameObject Prefab.
// You can extend this base class to support different kinds of visual effects
// such as particle systems, post-processing screen effects, etc.
public virtual void PlayEffect();
}
Hasar bölümünü Serileştirilebilir bir sınıfa daha da soyutlayabilirsiniz, böylece hasar veren veya iyileşen yetenekleri tanımlayabilir ve bir yetenekte birden fazla hasar türüne sahip olabilirsiniz. Birden çok komut dosyası çalıştırılabilir nesne kullanmazsanız ve diskteki farklı karmaşık hasar yapılandırma dosyalarına başvurmazsanız, tek kural devralma olmaz.
Hala AbilityActivator MonoBehaviour'a ihtiyacınız var, ama şimdi biraz daha fazla iş yapıyor.
// AbilityActivator.cs
public class AbilityActivator : MonoBehaviour
{
public void ActivateAbility(string abilityName)
{
var command = (CommandAbilityDescription) Resources.Load(string.Format("Abilities/{0}", abilityName));
ProcessCommand(command);
}
private void ProcessCommand(CommandAbilityDescription command)
{
foreach (var fx in command.visualEffects) {
fx.PlayEffect();
}
switch(command.damageType) {
// yatta yatta yatta
}
// and so forth, whatever your needs require
// You could even make a copy of the CommandAbilityDescription
var myCopy = Object.Instantiate(command);
// So you can keep track of state changes (ie: damage duration)
}
}
EN İYİ kısım
Böylece arayüz ve genel hile ilk yaklaşımda iyi çalışacaktır. Ancak Unity'den gerçekten en iyi şekilde yararlanmak için ScriptableObjects sizi istediğiniz yere götürecektir. Unity, programcılar için çok tutarlı ve mantıklı bir ortam sağlaması açısından harika, aynı zamanda GameMaker, UDK, et. ark.
Geçtiğimiz ay, sanatçımız farklı türlerde güdümlü füzeler için davranış tanımlaması gereken bir powerrip ScriptableObject türü aldı, bunu bir AnimationCurve ve füzeleri yerde gezdiren bir davranışla birleştirdi ve bu çılgın yeni dönen-hokey-puck- ölüm silahı.
Yine de geri dönüp verimli çalıştığından emin olmak için bu davranış için özel destek eklemem gerekiyor. Ancak bu jenerik veri tanımlama arayüzünü yaptığımız için, bu fikri ince havadan çekebildi ve biz gelene kadar programcılar bunu yapmaya çalıştığını bile bilmeden oyuna sokabildi. bu harika şeyde! " Ve açıkça harika olduğu için, daha sağlam destek eklemekten heyecan duyuyorum.