Я сейчас работаю над игрой, и у меня возникли проблемы с моим дизайном.А именно, мои системы предметов и способностей.
Я начал с использования наследования, чтобы добавить функциональность к моим предметам, скажем, с базовым классом Item
и наследованием от него обоих ConsumableItem
и Equipment
.Но потом я начал хотеть предметы, где были и расходные материалы, и снаряжение (скажем, набор стрелок).Поскольку я использую Unity 3D и C #, я не могу наследовать от нескольких классов, плюс я читаю, что это может привести к проблемам в дальнейшем.
Я слышал об интерфейсах, которые могут быть решением моей проблемы, но все равно потребуютя жестко закодировал каждую комбинацию атрибутов вручную.
Я начал читать о шаблонах проектирования и подумал, можно ли использовать шаблон декоратора для объединения атрибутов и методов для создания пользовательских объектов в редакторе.
Я надеюсь на возможность создать базовый класс ItemBlueprint
, который будет принимать массив функций в качестве значений enum
.Затем я передал бы этот объект ItemBlueprint
другому классу, у которого был бы метод с именем Build
, и создал бы элемент, добавив все соответствующие атрибуты и методы к объекту Item
.Затем выходной продукт может быть добавлен в сцену игрового объекта в качестве компонента или сохранен непосредственно в ресурсы в виде scriptableObject.После этого я смогу редактировать открытые поля вручную.
До сих пор я мог создавать элемент, используя приведенный ниже код.Однако в инспекторе единства отображается только поле атрибутов, соответствующих последнему применяемому декоратору.Поэтому я не могу изменить description
, когда его расходные материалы установлены в качестве последней записи в массиве характеристик чертежа, но не атрибута equipmentSlot
.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class ItemBuilder : MonoBehaviour {
public ItemBluePrint bp;
private List<Decorator> items;
public void OnValidate()
{
items = new List<Decorator>();
}
[ContextMenu("Build Item")]
public void Build ()
{
// Create Item and Decorators
DiscreteItem newItem = ScriptableObject.CreateInstance<DiscreteItem>();
for (int i = 0; i < bp.properties.Length; i++) {
if (i == (int) ItemProperties.Consumable) {
items.Add(ScriptableObject.CreateInstance<ConsumableItem>());
}
if (i == (int) ItemProperties.Equipable) {
items.Add(ScriptableObject.CreateInstance<EquipableItem>());
}
}
// Link decorators
Item previousItem = (Item) newItem;
for (int i = 0; i < items.Count; i++) {
items [i].SetItem (previousItem);
previousItem = (Item) items [i];
}
AssetDatabase.CreateAsset (previousItem, "Assets/MynewItem.asset");
Debug.Log ("Item created !");
}
abstract class Item : ScriptableObject
{
public abstract void Operation();
}
class DiscreteItem : Item
{
public override void Operation()
{
Debug.Log("Use item.");
}
}
abstract class Decorator : Item
{
protected Item item;
public void SetItem(Item item)
{
this.item = item;
}
public override void Operation()
{
if (item != null)
{
item.Operation();
}
}
}
class ConsumableItem : Decorator
{
public string description;
public override void Operation()
{
base.Operation();
Debug.Log("I consume the item.");
}
}
class EquipableItem : Decorator
{
public int equipementSlot;
public override void Operation()
{
base.Operation();
AddedBehavior();
Debug.Log("I equip the item.");
}
void AddedBehavior()
{
Debug.Log("I added the item to my equipment.");
}
}
}
И класс чертежа:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum ItemProperties
{
Consumable = 0,
Equipable = 1,
}
[CreateAssetMenu(fileName= "Item", menuName="Item")]
public class ItemBluePrint : ScriptableObject {
public ItemProperties[] properties;
}