TL; DR
Ця відповідь трохи божевільна. Але це тому, що я бачу, що ви говорите про реалізацію своїх здібностей як "Команди", що передбачає шаблони дизайну C ++ / Java / .NET, що передбачає важкий підхід. Цей підхід справедливий, але є кращий спосіб. Можливо, ви вже робите інший шлях. Якщо так, то добре. Сподіваємось, інші вважають його корисним, якщо це так.
Подивіться на підхід, керований даними, нижче, щоб скоротити погоню. Отримайте користувальницьку програму CustomAssetUtility Jacob Pennock тут і прочитайте його публікацію про це .
Робота з єдністю
Як і інші згадували, проїзд списку 100-300 предметів - не така вже й велика справа, як ви могли подумати. Тож якщо це інтуїтивний підхід для вас, тоді просто зробіть це. Оптимізуйте ефективність роботи мозку. Але Словник, як @Norguard продемонстрував у своїй відповіді , є простим способом усунення цієї проблеми , не потребуючим мозкових сил, оскільки ви отримуєте вставлення та пошук постійного часу. Вам, мабуть, варто скористатися.
Що стосується того, щоб ця робота була добре в Unity, моя кишка говорить мені, що один MonoBehaviour на здатність - це небезпечний шлях, який слід знижувати. Якщо хтось із ваших здібностей підтримує стан протягом часу, коли вони виконуються, вам потрібно буде керувати цим способом, щоб забезпечити спосіб скидання цього стану. Розслідування усувають цю проблему, але ви все ще керуєте посиланням на IEnumerator у кожному оновлювальному кадрі цього сценарію, і ви повинні абсолютно впевнитись, що у вас є безпечний спосіб скидання здібностей, щоб уникнути неповного та зациклічного стану контуру здібності тихо починають викручувати стабільність вашої гри, коли вони проходять непомітно. "Звичайно, я це зроблю!" ви кажете: "Я" хороший програміст "!". Але насправді, ви знаєте, всі ми об’єктивно жахливі програмісти і навіть найвидатніші дослідники AI та автори-компілятори весь час накручують справи.
З усіх способів, яким ви могли б реалізувати інстанціювання та пошук команд в Unity, я можу подумати про два: один прекрасний і не дасть вам аневризми, а другий дозволяє отримати необмежену магічну творчість . Типу.
Кодоцентричний підхід
По-перше, це підхід в основному в коді. Що я рекомендую, щоб ви зробили кожну команду простим класом, який або успадковує клас абстрагування BaseCommand, або реалізує інтерфейс ICommand (я припускаю заради стислості, що ці Команди коли-небудь будуть лише здібностями символів, це не важко включити інші види використання). Ця система передбачає, що кожна команда є ICommand, має відкритий конструктор, який не приймає жодних параметрів, і потребує оновлення кожного кадру, поки він активний.
Речі простіше, якщо ви використовуєте абстрактний базовий клас, але моя версія використовує інтерфейси.
Важливо, щоб ваші MonoBehaviours містили одну конкретну поведінку або систему тісно пов'язаних з ними поведінок. Добре мати багато MonoBehaviours, які ефективно просто проксі для звичайних класів C #, але якщо ви виявите, що ви робите занадто, ви можете оновлювати дзвінки на всілякі різні об’єкти до того моменту, як це починає виглядати як гра XNA, тоді ви ' знову в серйозних проблемах і потрібно змінити свою архітектуру.
// 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);
}
}
Це працює зовсім чудово, але ви можете зробити краще (також, List<T>
це не оптимальна структура даних для зберігання приурочених здібностей, можливо, ви хочете отримати a LinkedList<T>
або a SortedDictionary<float, T>
).
Підхід, керований даними
Можливо, ви можете зменшити ефекти своєї здатності до логічної поведінки, яку можна параметризувати. Саме для цього було створено Єдність. Ви, як програміст, проектуєте систему, яка потім або ви, або дизайнер зможете запускати та маніпулювати в редакторі для отримання найрізноманітніших ефектів. Це значно спростить "фальсифікацію" коду і зосередиться виключно на виконанні вміння. Тут не потрібно жонглювати базовими класами чи інтерфейсами та загальними характеристиками. Все це буде виключно керованим даними (що також спрощує ініціалізацію екземплярів команди).
Перше, що вам потрібно - це ScriptableObject, який може описати ваші здібності. ScriptableObjects є приголомшливими. Вони розроблені так, як MonoBehaviours, оскільки ви можете встановити їхні публічні поля в інспектора Unity, і ці зміни будуть серіалізовані на диск. Однак вони не прикріплені до будь-якого об’єкта і не повинні бути приєднані до ігрового об’єкта в сцені або інстанційним. Вони є сукупністю даних Unity. Вони можуть серіалізувати основні типи, перерахунки та прості класи (без успадкування) [Serializable]
. Структури не можна серіалізувати в Unity, а серіалізація - це те, що дозволяє редагувати об’єкти поля в інспекторі, тому пам’ятайте про це.
Ось ScriptableObject, який намагається зробити багато. Ви можете розділити це на більш серіалізовані класи та ScriptableObjects, але це, як передбачається, просто дасть вам уявлення про те, як це робити. Зазвичай це виглядає некрасиво в чудовій сучасній об'єктно-орієнтованій мові на зразок C #, оскільки це справді відчуває, що якесь лайно C89 з усіма цими перерахунками, але справжня сила тут полягає в тому, що тепер ви можете створювати всілякі різні здібності, не писаючи ніколи нового коду для підтримки їх. І якщо ваш перший формат не робить те, що вам потрібно для цього, просто продовжуйте додавати його до тих пір, поки він не стане. Поки ви не зміните назви полів, всі ваші старі серіалізовані файли активів все ще працюватимуть.
// 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();
}
Ви можете додатково абстрагувати розділ «Пошкодження» класом «Серіалізація», щоб ви могли визначити здібності, які завдають шкоди, або заживають, і мають декілька типів пошкоджень в одній здатності. Єдине правило - це не успадкування, якщо ви не використовуєте декілька об'єктів, що проглядаються, та не посилаєтесь на різні складні файли конфігурації пошкоджень на диску.
Вам все ще потрібен AbilityActivator MonoBehaviour, але тепер він робить трохи більше роботи.
// 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)
}
}
НАЙКЛАДНІша частина
Тож інтерфейс та загальна хитрість у першому підході спрацюють чудово. Але щоб дійсно максимально використати Unity, ScriptableObjects отримає вас там, де ви хочете бути. Unity чудовий тим, що він забезпечує дуже послідовне та логічне середовище для програмістів, але також має всі приємності для введення даних для дизайнерів та художників, які ви отримуєте від GameMaker, UDK та ін. ін.
Минулого місяця наш художник взяв потужний тип ScriptableObject, який повинен був визначати поведінку для різних видів навісних ракет, поєднав його з AnimationCurve та поведінкою, яка змусила ракети ширяти по землі, і зробила цей божевільний новий спінінг-хокей-шайбу- зброї смерті.
Мені все одно потрібно повернутися назад і додати конкретну підтримку такої поведінки, щоб переконатися, що вона працює ефективно. Але оскільки ми створили цей загальний інтерфейс опису даних, він зміг витягнути цю ідею з повітря і втілити її в гру без нас, програмістів, навіть не знаючи, що намагається це зробити, поки він не підійшов і сказав: "Ей, хлопці, подивіться на цій крутій річ! " І тому, що це було явно приголомшливо, я дуже радий досягти більш надійної підтримки для цього.