Моя цель - создать модульную / как можно более общую систему элементов, которая могла бы обрабатывать такие вещи, как:
- Обновляемые предметы (+6 Катана)
- Модификаторы статов (+15 к ловкости)
- Модификаторы предметов (% X шанс нанести Y урон, шанс заморозить)
- Перезаряжаемые предметы (Магический посох с 30 использованиями)
- Набор предметов (4 предмета Х установлены для активации функции Y)
- Редкость (обычная, уникальная, легендарная)
- Disenchantable (разбивается на некоторые материалы для крафта)
- Можно обрабатывать (можно с определенными материалами)
- Расходуется (5 мин.% Силы атаки X, лечение +15 л. С.)
* Я смог решить функции, которые выделены жирным шрифтом в следующей настройке.
Теперь я попытался добавить много вариантов, чтобы отразить то, что я имею в виду. Я не планирую добавлять все эти функции по необходимости, но я хотел бы иметь возможность реализовать их по своему усмотрению. Они также должны быть совместимы с системой инвентаризации и сериализации данных.
Я планирую вообще не использовать наследование, а скорее подход, основанный на компонентах / данных. Сначала я подумал о системе, которая имеет:
- BaseStat: универсальный класс, который хранит статистику на ходу (также может использоваться для статистики предметов и персонажей)
- Item: класс, который содержит данные, такие как список, имя, тип элемента и вещи, связанные с пользовательским интерфейсом, actionName, описанием и т. Д.
- IWeapon: интерфейс для оружия. Каждое оружие будет иметь свой собственный класс с реализованным IWeapon. Это будет иметь Атаку и ссылку на статистику персонажей. Когда оружие экипировано, его данные (характеристика класса предмета) будут введены в стат персонажа (независимо от того, какое у него базовое состояние, оно будет добавлено к классу персонажа в качестве бонуса к стату). Например, мы хотим изготовить меч (думая о производить классы предметов с помощью json), поэтому меч добавит 5 атак к характеристикам персонажа. Таким образом, у нас есть BaseStat as («Attack», 5) (мы также можем использовать enum). Эта характеристика будет добавлена к характеристике «Атака» персонажа в виде BonusStat (это будет другой класс) после оснащения. Таким образом, класс с именем Sword реализует IWeapon будет создан, когда онКласс предметов создан. Таким образом, мы можем ввести характеристики персонажа в этот меч, а при атаке он может получить общую статистику атаки из характеристики персонажа и нанести урон в методе атаки.
- BonusStat: способ добавления статистики в качестве бонусов, не касаясь BaseStat.
- IConsumable: та же логика, что и в IWeapon. Добавить прямую статистику довольно легко (+15 л. С.), Но я не уверен насчет добавления временного оружия с этой настройкой (% x к атаке в течение 5 минут).
- IUpgradeable: это может быть реализовано с этой настройкой. Я считаю UpgradeLevel базовым статом , который увеличивается при улучшении оружия. При обновлении мы можем пересчитать BaseStat оружия в соответствии с его уровнем улучшения.
До этого момента я вижу, что система довольно хорошая. Но для других функций, я думаю, нам нужно что-то еще, потому что, например, я не могу внедрить в него функцию Craftable, так как мой BaseStat не сможет обработать эту функцию, и именно здесь я застрял. Я могу добавить все ингредиенты как стат, но это не имеет смысла.
Чтобы вам было легче внести свой вклад, вот несколько вопросов, с которыми вы можете помочь:
- Должен ли я продолжить эту настройку для реализации других функций? Будет ли это возможно без наследства?
- Можно ли придумать, как реализовать все эти функции без наследования?
- О модификаторах предметов, как можно этого достичь? Потому что это очень общий характер.
- Что можно сделать, чтобы облегчить процесс построения архитектуры такого типа, какие-либо рекомендации?
- Есть ли источники, которые я могу найти, связанные с этой проблемой?
- Я действительно стараюсь избегать наследования, но думаете ли вы, что это будет легко решено / достигнуто с помощью наследования при сохранении его достаточной ремонтопригодности?
Не стесняйтесь отвечать только на один вопрос, поскольку я держал вопросы очень широко, чтобы я мог получать знания из разных аспектов / людей.
РЕДАКТИРОВАТЬ
Следуя ответу @ 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);
}
}
Это необходимые настройки. Теперь мы можем использовать систему (ниже для Unity).
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, а каждый Атрибут (имя не должно вас смущать, это означает, что функция / система элемента. Подобно Upgradeable или disenchantable) будет реализовывать IAttribute. Затем у нас есть метод для обработки сообщения (почему нам нужно сообщение, будет объяснено в следующем примере). Таким образом, в контексте вы можете прикрепить атрибуты к элементу, а остальное сделает за вас. Что очень гибко, потому что вы можете легко добавлять / удалять атрибуты. Таким образом, псевдо-пример был бы Disenchantable. У нас будет класс с именем Disenchantable (IAttribute) и он в методе Disenchant будет запрашивать:
- Перечислите ингредиенты (когда предмет распыляется, какой предмет должен быть отдан игроку) примечание: IItem должен быть расширен, чтобы иметь ItemType, ItemID и т. Д.
- int resultModifier (если вы реализуете какую-то функцию усиления распыления, вы можете передать здесь int, чтобы увеличить количество ингредиентов, получаемых при разочаровании)
- int failChance (если у процесса disenchant есть шанс сбоя)
и т.п.
Эта информация будет предоставлена классом DisenchantManager, он получит предмет и сформирует это сообщение в соответствии с предметом (ингредиенты предмета при распускании) и прогрессом игрока (resultModifier и failChance). Чтобы передать это сообщение, мы создадим класс DisenchantMessage, который будет действовать как тело для этого сообщения. Таким образом, DisenchantManager заполнит сообщение DisenchantMessage и отправит его элементу. Предмет получит сообщение и передаст его всем прикрепленным атрибутам. Поскольку метод ReceiveMessage класса Disenchantable будет искать DisenchantMessage, только атрибут Disenchantable получит это сообщение и будет воздействовать на него. Надеюсь, это прояснит вещи так же, как и для меня :).
источник
Ответы:
Я думаю, что вы можете достичь того, что вы хотите с точки зрения масштабируемости и удобства обслуживания, используя Entity-Component System с базовым наследованием и систему обмена сообщениями. Конечно, имейте в виду, что эта система является самой модульной / настраиваемой / масштабируемой, которую я могу себе представить, но, вероятно, она будет работать хуже, чем ваше текущее решение.
Я объясню дальше:
Прежде всего, вы создаете интерфейс
IItem
и интерфейсIComponent
. Любой элемент, который вы хотите сохранить, должен наследоватьсяIItem
, а любой компонент, на который вы хотите повлиять, должен наследоватьIComponent
.IItem
будет иметь массив компонентов и метод для обработкиIMessage
. Этот метод обработки просто отправляет любое полученное сообщение всем сохраненным компонентам. Затем компоненты, которые заинтересованы в данном сообщении, будут действовать соответствующим образом, а остальные будут его игнорировать.Например, одно сообщение имеет тип повреждения, и оно информирует как атакующего, так и атакованного, так что вы знаете, сколько вы ударили, и, возможно, начисляете свою ярость на основе этого урона. Или ИИ противника может принять решение бежать, если он ударяет вас и наносит менее 2HP урона. Это глупые примеры, но используя систему, подобную той, о которой я упоминаю, вам не нужно ничего делать, кроме создания сообщения и соответствующих обработок, чтобы добавить большую часть этого вида механики.
У меня есть реализация для ECS с обменом сообщениями здесь , но это используется для сущностей вместо элементов, и это использует C ++. Во всяком случае, я думаю, это может помочь, если вы посмотрите
component.h
,entity.h
иmessages.h
. Есть много вещей, которые нужно улучшить, но это помогло мне в этой простой работе в университете.Надеюсь, поможет.
источник