TL; DR
Jawaban ini sedikit gila. Tapi itu karena saya melihat Anda sedang berbicara tentang menerapkan kemampuan Anda sebagai "Perintah," yang menyiratkan pola desain C ++ / Java / .NET, yang menyiratkan pendekatan kode-berat. Approah itu valid, tetapi ada cara yang lebih baik. Mungkin Anda sudah melakukan sebaliknya. Jika demikian, oh baiklah. Semoga orang lain merasakan manfaatnya jika itu masalahnya.
Lihatlah Pendekatan Data-Driven di bawah ini untuk memotong ke pengejaran. Dapatkan CustomAssetUility Jacob Pennock di sini dan baca postingnya tentang hal itu .
Bekerja Dengan Persatuan
Seperti yang telah disebutkan orang lain, melintasi daftar 100-300 item bukanlah masalah besar seperti yang Anda pikirkan. Jadi jika itu pendekatan intuitif untuk Anda, maka lakukan saja. Optimalkan untuk efisiensi otak. Tetapi Kamus, seperti yang diperlihatkan @Norguard dalam jawabannya , adalah cara mudah tanpa tenaga otak untuk menghilangkan masalah itu karena Anda mendapatkan penyisipan dan pengambilan waktu yang konstan. Anda mungkin harus menggunakannya.
Dalam hal membuat ini bekerja dengan baik dalam Unity, usus saya mengatakan kepada saya bahwa satu MonoBehaviour per kemampuan adalah jalur berbahaya untuk turun. Jika salah satu dari kemampuan Anda mempertahankan status seiring berjalannya waktu, Anda harus mengelola bahwa menyediakan cara untuk mengatur ulang keadaan itu. Coroutines mengatasi masalah ini, tetapi Anda masih mengelola referensi IEnumerator pada setiap kerangka pembaruan skrip itu, dan harus benar-benar memastikan Anda memiliki cara yang pasti untuk mengatur ulang kemampuan agar jangan sampai tidak lengkap dan macet dalam keadaan tidak terkunci kemampuan diam-diam mulai mengacaukan stabilitas game Anda ketika mereka luput dari perhatian. "Tentu saja aku akan melakukannya!" Anda berkata, "Saya seorang 'Programmer Baik'!". Tapi sungguh, Anda tahu, kita semua adalah programmer yang secara objektif mengerikan dan bahkan para peneliti dan penulis kompiler AI terbesar selalu mengacaukan semuanya.
Dari semua cara Anda dapat mengimplementasikan instantiation dan pengambilan perintah di Unity, saya dapat memikirkan dua: satu baik-baik saja dan tidak akan memberi Anda aneurisma, dan yang lainnya memungkinkan untuk KREATIVITAS MAGIS YANG TIDAK DITINGGALKAN . Semacam.
Pendekatan Kode-Sentris
Pertama adalah pendekatan sebagian besar dalam kode. Apa yang saya sarankan adalah bahwa Anda membuat setiap perintah kelas sederhana yang mewarisi dari BaseCommand mengurangi kelas atau mengimplementasikan antarmuka ICommand (saya berasumsi untuk singkatnya bahwa Perintah ini hanya akan menjadi kemampuan karakter, tidak sulit untuk menggabungkan penggunaan lainnya). Sistem ini mengasumsikan bahwa setiap perintah adalah perintah IC, memiliki konstruktor publik yang tidak memiliki parameter, dan mengharuskan memperbarui setiap frame saat aktif.
Segalanya lebih sederhana jika Anda menggunakan kelas dasar abstrak, tetapi versi saya menggunakan antarmuka.
Penting bahwa MonoBehaviour Anda merangkum satu perilaku tertentu, atau sistem perilaku yang berkaitan erat. Tidak apa-apa untuk memiliki banyak MonoBehaviour yang secara efektif hanya proxy ke kelas C # biasa, tetapi jika Anda menemukan diri Anda melakukannya juga dapat memperbarui panggilan ke semua jenis objek yang berbeda ke titik di mana ia mulai terlihat seperti permainan XNA, maka Anda ' kembali dalam masalah serius dan perlu mengubah arsitektur Anda.
// 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);
}
}
Ini berfungsi dengan sangat baik, tetapi Anda bisa melakukan yang lebih baik (juga, List<T>
bukan struktur data yang optimal untuk menyimpan kemampuan waktunya, Anda mungkin ingin a LinkedList<T>
atau a SortedDictionary<float, T>
).
Pendekatan Berbasis Data
Mungkin saja Anda bisa mengurangi efek kemampuan Anda menjadi perilaku logis yang dapat dijadikan parameter. Inilah tujuan Unity dibangun. Anda, sebagai seorang programmer, merancang sebuah sistem yang kemudian Anda atau perancang dapat gunakan dan manipulasi dalam editor untuk menghasilkan berbagai efek. Ini akan sangat menyederhanakan "kecurangan" kode, dan fokus secara eksklusif pada pelaksanaan suatu kemampuan. Tidak perlu menyulap kelas dasar atau antarmuka dan generik di sini. Itu semua akan murni berbasis data (yang juga menyederhanakan inisialisasi instance perintah).
Hal pertama yang Anda butuhkan adalah ScriptableObject yang dapat menggambarkan kemampuan Anda. ScriptableObjects luar biasa. Mereka dirancang untuk bekerja seperti MonoBehaviour di mana Anda dapat mengatur bidang publik mereka di inspektur Unity, dan perubahan itu akan diserialisasi ke disk. Namun, mereka tidak melekat pada objek apa pun dan tidak harus melekat pada objek game dalam adegan atau instantiated. Mereka adalah semua-menangkap ember data Unity. Mereka dapat membuat serial jenis dasar, enum, dan kelas sederhana (tanpa warisan) yang ditandai [Serializable]
. Structs tidak dapat diserialisasi dalam Unity, dan serialisasi adalah apa yang memungkinkan Anda untuk mengedit bidang objek di inspektur, jadi ingatlah itu.
Berikut adalah ScriptableObject yang mencoba melakukan banyak hal. Anda dapat memecah ini menjadi lebih banyak kelas serial dan ScriptableObjects, tetapi ini seharusnya hanya memberi Anda gambaran tentang bagaimana cara melakukannya. Biasanya ini terlihat jelek dalam bahasa berorientasi objek modern bagus seperti C #, karena itu benar-benar terasa seperti omong kosong C89 dengan semua enum itu, tetapi kekuatan sebenarnya di sini adalah bahwa sekarang Anda dapat membuat segala macam kemampuan yang berbeda tanpa pernah menulis kode baru untuk mendukung mereka. Dan jika format pertama Anda tidak melakukan apa yang perlu Anda lakukan, terus tambahkan sampai itu. Selama Anda tidak mengubah nama bidang, semua file aset serial lama Anda masih akan berfungsi.
// 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();
}
Anda bisa mengabstraksi bagian Damage lebih lanjut menjadi kelas Serializable sehingga Anda bisa mendefinisikan kemampuan yang menangani kerusakan, atau menyembuhkan, dan memiliki beberapa tipe kerusakan dalam satu kemampuan. Satu-satunya aturan adalah tidak ada warisan kecuali jika Anda menggunakan beberapa objek skrip dan referensi file konfigurasi kerusakan kompleks pada disk.
Anda masih memerlukan AbilityActivator MonoBehaviour, tetapi sekarang dia melakukan sedikit pekerjaan lagi.
// 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)
}
}
Bagian TERTINGGI
Jadi antarmuka dan tipuan generik pada pendekatan pertama akan berfungsi dengan baik. Tetapi untuk benar-benar mendapatkan yang terbaik dari Unity, ScriptableObjects akan membawa Anda ke tempat yang Anda inginkan. Unity sangat bagus karena menyediakan lingkungan yang sangat konsisten dan logis untuk programmer, tetapi juga memiliki semua masukan data yang bagus untuk desainer dan artis yang Anda dapatkan dari GameMaker, UDK, et. Al.
Bulan lalu, artis kami mengambil jenis ScriptableObject powerup yang seharusnya mendefinisikan perilaku untuk berbagai jenis rudal homing, dikombinasikan dengan AnimationCurve dan perilaku yang membuat rudal melayang-layang di tanah, dan membuat pemintalan-hoki-keping baru yang gila ini senjata kematian.
Saya masih perlu kembali dan menambahkan dukungan spesifik untuk perilaku ini untuk memastikan itu berjalan secara efisien. Tetapi karena kami membuat antarmuka deskripsi data umum ini, ia dapat mengeluarkan ide ini dari udara kosong dan memasukkannya ke dalam permainan tanpa kami pemrogram, bahkan mengetahui bahwa ia sedang mencoba melakukannya sampai ia datang dan berkata, "Hai teman-teman, lihat pada hal keren ini! " Dan karena itu jelas luar biasa, saya bersemangat untuk pergi menambahkan dukungan yang lebih kuat untuk itu.