Моя мета - створити модульну / максимально універсальну систему предметів, яка могла б обробляти такі речі, як:
- Елементи, що можна оновити (+6 Катани)
- Модифікатори стату (+15 спритність)
- Модифікатори предмета (% X шанс завдати шкоди Y, шанс заморозити)
- Акумуляторні товари (Чарівний персонал з 30 звичаями)
- Встановити елементи (обладнайте 4 частини X набору для активації функції Y)
- Рідкість (загальна, унікальна, легендарна)
- Disenchavable (врізається в деякі майстерні матеріали)
- Craftable (можна виготовити з певними матеріалами)
- Витратний (5 хв.% Атакова потужність, оздоровлення +15 к.с.)
* Мені вдалося вирішити функції, які сміливі в наступних налаштуваннях.
Тепер я спробував додати багато варіантів, щоб ми відобразили те, що я маю на увазі. Я не планую додавати всі ці необхідні функції, але я хотів би мати можливість їх реалізувати так, як вважаю за потрібне. Вони також повинні бути сумісні з системою інвентаризації та серіалізацією даних.
Я планую взагалі не використовувати спадщину, а навпаки, підхід, орієнтований на суб’єкти / дані. Спочатку я думав про систему, яка має:
- BaseStat: загальний клас, який містить статистику в ході (може бути використаний і для елементів, і для символів)
- Item: клас, який містить дані, такі як список, ім'я, тип предмета та речі, пов’язані з ui, actionName, описом тощо.
- IWeapon: інтерфейс для зброї. Кожна зброя матиме свій клас із впровадженим IWeapon. Це матиме Attack та посилання на статистику персонажів. Коли зброя оснащена, її дані (item item 'stat) будуть введені в символьні stat (що б у BaseStat не було, воно буде додано до класу символів як бонус Stat). Наприклад, ми хочемо створити меч (думаючи створити класи предметів з json), тому меч додасть 5 атак до статистики символів. Отже, у нас є BaseStat as ("Attack", 5) (ми можемо використовувати і перерахунок). Ця статистика буде додана до статусу "Атака" персонажа як BonusStat (що був би іншим класом) після його оснащення. Отже, клас з ім'ям Sword реалізує IWeapon , коли він буде створений 'Клас предметів створено. Таким чином, ми можемо вводити статистику символів у цей меч, і при атаці він може витягувати загальну статистику атаки зі статусу символів та завдавати шкоди методом Attack.
- BonusStat: це спосіб додавання статистики як бонусів, не торкаючись BaseStat.
- IConsumable: та ж логіка, що і у IWeapon. Додавати пряму статистику досить просто (+15 к.с.), але я не впевнений у додаванні тимчасової зброї з цією установкою (% x для атаки протягом 5 хв).
- IUpgradeable: це може бути реалізовано за допомогою цієї установки. Я думаю, що UpgradeLevel є базовим статом , який збільшується при оновленні зброї. Після оновлення ми можемо перерахувати BaseStat зброї відповідно до рівня оновлення.
До цього моменту я бачу, що система є досить хорошою. Щодо інших функцій, я думаю, що нам потрібно щось інше, тому що, наприклад, я не можу реалізувати функцію Craftable в цьому, оскільки мій BaseStat не міг би впоратися з цією функцією, і саме тут я застряг. Я можу додати всі інгредієнти як Стати, але це не мало б сенсу.
Щоб полегшити вам внесок у це, ось кілька питань, з якими ви можете допомогти:
- Чи варто продовжувати цю налаштування для впровадження інших функцій? Чи можливо це було б без спадщини?
- Чи є якийсь спосіб, який ви можете придумати, реалізувати всі ці функції без успадкування?
- Щодо модифікаторів предметів, як можна цього досягти? Тому що це дуже родова природа.
- Що можна зробити, щоб полегшити процес побудови такої архітектури, будь-які рекомендації?
- Чи є джерела, які я можу викопати, пов'язані з цією проблемою?
- Я дійсно намагаюся уникати спадкування, але ви вважаєте, що це було б вирішено / досягнуто спадщиною з легкістю, зберігаючи це досить ретельно?
Не соромтесь відповісти лише на одне запитання, оскільки я тримав питання дуже широкі, щоб я міг отримати знання з різних аспектів / людей.
EDIT
Після відповіді @ jjimenezg93 я створив в C # дуже базову систему для тестування, вона працює! Перевірте, чи можете ви додати що-небудь до нього:
public interface IItem
{
List<IAttribute> Components { get; set; }
void ReceiveMessage<T>(T message);
}
public interface IAttribute
{
IItem source { get; set; }
void ReceiveMessage<T>(T message);
}
Поки IItem та IAttribute є базовими інтерфейсами. Не було потреби (що я можу придумати) мати базовий інтерфейс / атрибут для повідомлення, тому ми безпосередньо створимо клас тестового повідомлення. Тепер для тестових занять:
public class TestItem : IItem
{
private List<IAttribute> _components = new List<IAttribute>();
public List<IAttribute> Components
{
get
{
return _components;
}
set
{
_components = value;
}
}
public void ReceiveMessage<T>(T message)
{
foreach (IAttribute attribute in Components)
{
attribute.ReceiveMessage(message);
}
}
}
public class TestAttribute : IAttribute
{
string _infoRequiredFromMessage;
public TestAttribute(IItem source)
{
_source = source;
}
private IItem _source;
public IItem source
{
get
{
return _source;
}
set
{
_source = value;
}
}
public void ReceiveMessage<T>(T message)
{
TestMessage convertedMessage = message as TestMessage;
if (convertedMessage != null)
{
convertedMessage.Execute();
_infoRequiredFromMessage = convertedMessage._particularInformationThatNeedsToBePassed;
Debug.Log("Message passed : " + _infoRequiredFromMessage);
}
}
}
public class TestMessage
{
private string _messageString;
private int _messageInt;
public string _particularInformationThatNeedsToBePassed;
public TestMessage(string messageString, int messageInt, string particularInformationThatNeedsToBePassed)
{
_messageString = messageString;
_messageInt = messageInt;
_particularInformationThatNeedsToBePassed = particularInformationThatNeedsToBePassed;
}
//messages should not have methods, so this is here for fun and testing.
public void Execute()
{
Debug.Log("Desired Execution Method: \nThis is test message : " + _messageString + "\nThis is test int : " + _messageInt);
}
}
Це необхідні установки. Тепер ми можемо скористатися системою (Далі - для Єдності).
public class TestManager : MonoBehaviour
{
// Use this for initialization
void Start()
{
TestItem testItem = new TestItem();
TestAttribute testAttribute = new TestAttribute(testItem);
testItem.Components.Add(testAttribute);
TestMessage testMessage = new TestMessage("my test message", 1, "VERYIMPORTANTINFO");
testItem.ReceiveMessage(testMessage);
}
}
Приєднайте цей скрипт TestManager до компонента в сцені, і ви зможете побачити в налагодженні, що повідомлення успішно передається.
Для того, щоб пояснити все: Кожен елемент гри буде реалізовувати інтерфейс IItem та кожен атрибут (ім'я не повинно вас бентежити, це означає, що функція / система елемента. Як і оновлений, або неможливий) буде реалізовувати IAttribute. Тоді у нас є метод обробки повідомлення (для чого нам потрібне повідомлення, буде пояснено в подальшому прикладі). Тож у контексті ви можете приєднати атрибути до елемента та дозволити іншим зробити за вас. Що дуже гнучко, тому що ви можете легко додавати / видаляти атрибути. Таким чином, псевдо-приклад був би незмінним. У нас буде клас під назвою Disenchantable (IAttribute), і він у методі Disenchant запитає:
- Перелічі інгредієнтів (коли елемент не заперечується, який предмет слід надати гравцеві) Примітка: IItem слід розширити, щоб мати ItemType, ItemID і т.д.
- int resultModifier (якщо ви впроваджуєте якусь функцію посилення розчарування, ви можете передати тут int, щоб збільшити інгредієнти, отримані при знеціненні)
- int failChance (якщо процес зневаги має шанс відмови)
тощо.
Ці відомості будуть надані класом під назвою DisenchantManager, він отримає елемент і сформує це повідомлення відповідно до пункту (інгредієнти елемента при відключенні) та прогресії гравця (resultModifier and failChance). Для того, щоб передати це повідомлення, ми створимо клас DisenchantMessage, який буде виконувати функції цього повідомлення. Тож DisenchantManager заповнить DisenchantMessage і надішле його на предмет. Елемент отримає повідомлення та передасть його всім доданим атрибутам. Оскільки метод ReceiveMessage класу Disenchantable буде шукати DisenchantMessage, тільки атрибут Disenchantable отримає це повідомлення і діятиме на нього. Сподіваюся, це очищає речі настільки ж, як і для мене :).