Вопрос по generic методам

Программирование на Юнити.

Вопрос по generic методам

Сообщение Xtir 20 июл 2021, 20:42

Допустим есть у меня класс InventoryItem - ячейка которая хранит какой-то предмет, в котором есть функция SetItem(Item item) - которая помещает предмет в ячейку:
Синтаксис:
Используется csharp
public class InventoryItem2
{
        public Item item;

        public virtual void SetItem(Item item)
        {

        }

        public virtual void SetItem<T>(T item) where T : Item
        {
                this.item = item;
        }
}
 


Так же есть класс InventoryWeapon - ячейка которая в которую можно передать только тип Weapon (наследуется от Item), дабы исключить
попадания в эту ячейку ненужных мне предметов:
Синтаксис:
Используется csharp
public class InventoryWeapon2 : InventoryItem2
{
        //здесь компилятор выдаёт ошибку, поэтому попробовал через generic метод сделать
        public override void SetItem(Weapon weapon)
        {
                base.SetItem(item);
        }

        //здесь студия не видит поля от Weapon, только от Item
        public override void SetItem<Weapon>(Weapon item)
        {
                base.SetItem(item);

                //какие-то дополнительные действия с item
                // ...
        }
}
 


как можно решить эту задачу?
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение Xtir 20 июл 2021, 20:49

Как обычно, как только задашь вопрос, сразу же находится ответ
Синтаксис:
Используется csharp
        public new void SetItem<T>(T item) where T : Weapon
        {
                base.SetItem(item);

                if(text != null)
                {
                        text.text = item.Ammo.Count.ToString();
                }
        }


решил таким образом, не знаю насколько это правильно, но вроде работает :)
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение ~AvA~ 20 июл 2021, 21:36

Мне кажется тут не нужны дженерики. Вот так оно будет норм работать:
Синтаксис:
Используется csharp
public class Test
{
    public Test()
    {
        var inventory = new Inventory();
        inventory.Set(new FireGun());
        inventory.Set(new Weapon());
    }
}

public class Inventory
{
    private List<Item> Items = new ();
    public void Set(Item item) => Items.Add(item);
}

public class Weapon : Item { }
public class FireGun : Weapon { }
public abstract class Item { }


В Item определяшь абстрактное поведение каждого предмета. В наследниках, имплементируешь тем способом, который подходит конкретному оружию. Инвентарю вообще пофиг, что там за айтем у него, он работает с ними на уровни абстракций. Добавить, убрать, показать информацию
Аватара пользователя
~AvA~
UNIверсал
 
Сообщения: 396
Зарегистрирован: 17 фев 2015, 13:09

Re: Вопрос по generic методам

Сообщение Xtir 20 июл 2021, 22:26

Это всё понятно, но есть отдельная ячейка для оружия, головного убора, сапоги, штаны и т.д. Что бы туда случайно не пихалось что попало, а только то что надо. А в общий инвентарь, любой тип.
Как вот здесь например, не моя картинка, просто для примера взял.
Изображение
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение ~AvA~ 21 июл 2021, 08:13

А, кажется понял! :) Тогда я бы делал отдельные дженерик инвентари (а не item)
И соединял их в одном общем инвентаре, который занимался бы управлением остальными инвентарями. В основном инвентаре другие под-инвентари идут отдельными переменными (я бы так делал, не так уж там много тех отделов будет) либо листом. И отдельный метод для добавления каждого вида item, с одинаковым названием (перегрузка).
Типа такого
Синтаксис:
Используется csharp
public class Test
{
    public Test()
    {
        var main = new MainInventory();

        main.Set(new FireGun());
        main.Set(new BFG());
       
        main.Set(new Stuff1());
        main.Set(new Stuff2());
    }
}

public class MainInventory
{
    private Inventory<Weapon> Weapons = new ();
    private Inventory<Stuff> Stuffs = new ();
    //......

    public void Set(Weapon weapon) => Weapons.Set(weapon);
    public void Set(Stuff stuff) => Stuffs.Set(stuff);
}

public class Inventory<T> where T : Item
{
    private List<T> Items = new ();
    public void Set(T item) => Items.Add(item);
}

//Stuff
public class Stuff1: Stuff { }
public class Stuff2: Stuff { }

public abstract class Stuff : Item { }

//Weapons
public class FireGun : Weapon { }
public class BFG : Weapon { }

public abstract class Weapon : Item { }

public abstract class Item { }
Аватара пользователя
~AvA~
UNIверсал
 
Сообщения: 396
Зарегистрирован: 17 фев 2015, 13:09

Re: Вопрос по generic методам

Сообщение Xtir 21 июл 2021, 17:29

Окей, это хорошо когда вручную добавляешь предмет в инвентарь, а как тогда добавить неизвестный предмет?

Ну хотя бы вот так:
Синтаксис:
Используется csharp
if (hit.collider.GetComponent<Item>())
{
    Inventory.Set(hit.collider.GetComponent<Item>());
}
 


неужели придётся проверять, к какому типу относится предмет?
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение Artemka_0 21 июл 2021, 19:29

Ну я бы сделал так:
Синтаксис:
Используется csharp
// Добавление предмета в инвентарь
public bool AddItem(Item item) {
    if (item is Weapon) {
      Weapon weaponItem = item as Weapon; // Так можно получить предмет как оружие
      // some code
      return true; // Возвращаем успех операции
    } else {
      return false; // Возвращаем провал операции
    }
}

// Если
public class Item {
}
public class Weapon: Item {
}
 
Artemka_0
UNец
 
Сообщения: 12
Зарегистрирован: 18 окт 2020, 18:40

Re: Вопрос по generic методам

Сообщение Xtir 22 июл 2021, 01:15

Ну да, походу так и придётся делать...
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение ~AvA~ 22 июл 2021, 14:44

Xtir писал(а):Окей, это хорошо когда вручную добавляешь предмет в инвентарь, а как тогда добавить неизвестный предмет?

Ну хотя бы вот так:
Синтаксис:
Используется csharp
if (hit.collider.GetComponent<Item>())
{
    Inventory.Set(hit.collider.GetComponent<Item>());
}
 


неужели придётся проверять, к какому типу относится предмет?

Не, вручную проверять тип - это прям плохо..
Можно вот так, как вариЯнт :)
Синтаксис:
Используется csharp
public class MainInventory
{
    private Inventory<Weapon> Weapons = new ();
    private Inventory<Stuff> Stuffs = new ();
   
    public void Set(Weapon weapon) => Weapons.Set(weapon);
    public void Set(Stuff stuff) => Stuffs.Set(stuff);
}

public class Inventory<T> where T : Item
{
    public List<T> Items = new ();
    public void Set(T item) => Items.Add(item);
}

public abstract class Item : MonoBehaviour
{
    public abstract void AddTo(MainInventory main);
}
public class Stuff : Item
{
    public override void AddTo(MainInventory main) => main.Set(this);
}
public class Weapon : Item
{
    public override void AddTo(MainInventory main) => main.Set(this);
}

Каждый Item знает как ему добавиться в основной инвентарь.
Проверил, добавляются когда ты Item получаешь как абстрактный класс. Как у тебя в цитируемом примере
Аватара пользователя
~AvA~
UNIверсал
 
Сообщения: 396
Зарегистрирован: 17 фев 2015, 13:09

Re: Вопрос по generic методам

Сообщение Xtir 22 июл 2021, 16:19

Сначала кстати я так и делал, но потом забил на эту идею, потому что всё равно дублировать много кода придётся. Но сейчас всё-таки решил сделать так, думаю это лучше чем много if'ов )))

Массивы из твоего примера решил не делать, потому что под оружие всего три слота
Синтаксис:
Используется csharp
public ItemSlot<Weapon> currentWeaponSlot = new ItemSlot<Weapon>() { id = 0 }; //оружие которое в руках
public ItemSlot<PrimaryWeapon> primaryWeaponSlot = new ItemSlot<PrimaryWeapon>() { id = 1 }; //основное оружие (автомат, винтовка, дробовик и т.д.)
public ItemSlot<SecondaryWeapon> secondaryWeaponSlot = new ItemSlot<SecondaryWeapon> { id = 2 }; //вторичное оружие (пистолет, нож)
 

Может быть пригодится, когда сделаю слоты под другие вещи, такие как гранаты, магазины, аптечки и др.

Но вместо абстрактного метода, сделал виртуальный, так как структура у меня чуть сложнее, иначе была бы (полная Ж) ))
Item(abstract)
/ \
Weapon(abstract)
/ \
Primary(abstract) Secondary(abstract)
/ /
Rifle(abstract) Pistol(abstract)
/ /
M16_A4 M9_Beretta


Всё равно придётся дублировать много одинакового кода, что мне не очень нравится.
Синтаксис:
Используется csharp
public abstract class PrimaryWeapon : Weapon
{
        public override void Put(Inventory inventory)
        {
                if(inventory.AddItem(this))
                {
                        base.Put(inventory);
                }
        }

        public override void Drop()
        {
                owner.Inventory.RemoveItem(this);
                base.Drop();
        }
}
 


Синтаксис:
Используется csharp
public abstract class SecondaryWeapon : Weapon
{
        public override void Put(Inventory inventory)
        {
                if(inventory.AddItem(this))
                {
                        base.Put(inventory);
                }
        }

        public override void Drop()
        {
                owner.Inventory.RemoveItem(this);
                base.Drop();
        }
}


и в инвентаре:
Синтаксис:
Используется csharp
        public bool AddItem(PrimaryWeapon item)
        {
                if (primaryWeaponSlot.item != null) return false;
                primaryWeaponSlot.item = item;
                onItemChanged(primaryWeaponSlot.id);
                return true;
        }

        public bool AddItem(SecondaryWeapon item)
        {
                if (secondaryWeaponSlot.item != null) return false;
                secondaryWeaponSlot.item = item;
                onItemChanged(secondaryWeaponSlot.id);
                return true;
        }

        public void RemoveItem(PrimaryWeapon item)
        {
                primaryWeaponSlot.item = null;
                onItemChanged(primaryWeaponSlot.id);
        }

        public void RemoveItem(SecondaryWeapon item)
        {
                secondaryWeaponSlot.item = null;
                onItemChanged(secondaryWeaponSlot.id);
        }

 
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37

Re: Вопрос по generic методам

Сообщение Xtir 23 июл 2021, 12:46

Короче я решил отказаться от этой затеи и сделать проверку по enum. Спасибо, что пытались помочь :)
Синтаксис:
Используется csharp
        public ItemSlot[] items = new ItemSlot[]
        {
                new ItemSlot(){Type = ItemSlotType.CurrentItem},

                new ItemSlot(){Type = ItemSlotType.PrimaryWeapon },
                new ItemSlot(){Type = ItemSlotType.SecondaryWeapon},

                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine },
                new ItemSlot(){Type = ItemSlotType.Magazine }
        };

        public bool AddItem(Item item)
        {
                if (item == null) return false;

                for (int i = 0; i < items.Length; ++i)
                {
                        if (items[i].Type == item.SlotType && items[i].item == null)
                        {
                                items[i].item = item;
                                item.OnPickup(owner);
                                if(onInventoryUpdated != null) onInventoryUpdated();
                                return true;
                        }
                }

                return false;
        }

 
Xtir
UNIверсал
 
Сообщения: 498
Зарегистрирован: 21 окт 2016, 00:37


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8