OOP, наследование - зачем это надо?

Форум для самых маленьких, а так же тех, кому недосуг читать справку самостоятельно.

OOP, наследование - зачем это надо?

Сообщение AABB 02 сен 2018, 10:43

Я раньше как то обходился без наследования классов, но тут ударило в голову, что раз есть такие возможности, то надо их использовать. Целый день разбирался как сделать редактируемый список предметов для игры. И когда наконец что-то получилось, то особой радости не испытал. На что это повлияет? Сэкономит много памяти?
Синтаксис:
Используется csharp
using UnityEngine;

public class TestItem : ScriptableObject {

    public string name;
    public int price;
}
 

Синтаксис:
Используется csharp
public class TestWeapon : TestItem
{
    public int damage, range;
}


Синтаксис:
Используется csharp
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;

public class TestItemsEditor : EditorWindow
{
    TestClasses main;

    void OnGUI()
    {
        if (!main)
            main = FindObjectOfType<TestClasses>();

        if (GUILayout.Button("Add"))
        {
            main.list.Add(CreateInstance<TestItem>());
            AssetDatabase.CreateAsset(main.list[main.list.Count - 1], "Assets/Item" + main.list.Count + ".asset");
            AssetDatabase.SaveAssets();
        }

        for (int i = 0; i < main.list.Count; i++)
        {
            TestItem item = main.list[i];
            GUILayout.BeginHorizontal();
            item.name = EditorGUILayout.TextField(item.name, GUILayout.Width(100));
            item.price = EditorGUILayout.IntField(item.price, GUILayout.Width(100));

            bool itemIsWeapon = item.GetType() == typeof(TestWeapon);
            EditorGUI.BeginChangeCheck();
            itemIsWeapon = EditorGUILayout.Toggle(itemIsWeapon, GUILayout.Width(20));

            if (EditorGUI.EndChangeCheck())
            {
                string pathAndName = AssetDatabase.GetAssetPath(item);
                AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(item));

                if (itemIsWeapon)
                {
                    TestWeapon result = new TestWeapon();
                    main.list[i] = result = Cast(main.list[i], result);
                    AssetDatabase.CreateAsset(result, pathAndName);
                }
                else
                {
                    TestItem result = new TestItem();
                    main.list[i] = result = Cast(main.list[i], result);
                    AssetDatabase.CreateAsset(result, pathAndName);
                }
            }

            if (itemIsWeapon)
            {
                TestWeapon weapon = (TestWeapon)main.list[i];
                weapon.damage = EditorGUILayout.IntField(weapon.damage, GUILayout.Width(100));
                weapon.range = EditorGUILayout.IntField(weapon.range, GUILayout.Width(100));
            }

            EditorGUILayout.Space();

            if (GUILayout.Button("X", GUILayout.Width(20)))
            {
                main.list.RemoveAt(i--);
                AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(item));
            }

            GUILayout.EndHorizontal();
            Undo.RecordObject(main.list[i], "Edit item qualities");
            EditorUtility.SetDirty(main.list[i]);
        }
    }

    T Cast<T, U>(U source, T result)
    {
        System.Type resultType = typeof(T);
        System.Reflection.FieldInfo[] fields = typeof(TestItem).GetFields();

        foreach (var field in fields)
        {
            var propToSet = resultType.GetField(field.Name);
            propToSet.SetValue(result, field.GetValue(source));
        }

        return result;
    }
}

[CustomEditor(typeof(TestClasses))]
public class TestItemsButton : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();
        if (GUILayout.Button("Open editor"))
        {
            TestItemsEditor window = (TestItemsEditor)EditorWindow.GetWindow(typeof(TestItemsEditor), false, "Items Editor");
            window.position = new Rect(100, 100, 1400, 900);
            //TestWeaponsEditor page2 = EditorWindow.GetWindow<TestWeaponsEditor>("Weapons Editor", typeof(TestItemsEditor));
            //window.Focus();
        }
    }
}
#endif

public class TestClasses : MonoBehaviour
{
    public List<TestItem> list = new List<TestItem>();
}
 

Можно было оставить класс предметов одинаковый для всех. Просто если предмет не оружие, то у него damage и range равны нулю. А так чего я добился? Много памяти сэкономил?
По ощущениям стало даже хуже. Потому что каждый раз когда нажимаешь галку "оружие", то редактор на секунду подвисает, чтобы переписать ScriptableObject.
AABB
UNIт
 
Сообщения: 134
Зарегистрирован: 05 фев 2014, 15:52

Re: OOP, наследование - зачем это надо?

Сообщение Anonymyx 02 сен 2018, 12:49

А теперь представьте что у вас много разных видов оружия, пистолеты, ракетные установки, снайперские винтовки... все они имеют разное поведение в том или ином плане.
Метод стрельбы у каждого из них будет существенно отличаться, будут свои реализации методов прицеливания, перезарядки. Как вы все это сделаете через 1 класс? Если делать для каждого оружия свой класс, без наследования, будет очень много дублирующего кода.
Вы делаете 1 общий класс, с общими переменными, типа цена, вес, иконка, название и т.д. А также объявляете виртуальные методы стрельбы, перезарядки, прицеливания, и .т.д с базовыми, общими алгоритмами.
Дальше в конкретных классах реализовываете (дополняете) логику этих методов, путем переопределения (override). Это один из видов полиморфизма. У каждого вида оружия свой класс, без лишних переменных, без лишнего кода и легко читаемый. Также вы можете хранить все виды оружия, независимо от того какое оно, в одной коллекции.
Аватара пользователя
Anonymyx
Адепт
 
Сообщения: 1973
Зарегистрирован: 05 апр 2015, 15:55

Re: OOP, наследование - зачем это надо?

Сообщение getAlex 03 сен 2018, 11:11

Ты не правильно используешь наследование. Оно не для того чтобы хранить 2 переменные в классе.

Синтаксис:
Используется csharp
public class TestItem : ScriptableObject
{
    public string name;
    public int price;

    virtual void ShowItem()
    {
          ...//допустим, тут то, как отображается предмет в магазине, вращается и тому подобное
    }
   
    virtual void BuyItem()
    {
         ...// а тут, допустим, механика покупки предмета
    }
}

public class TestWeapon : TestItem
{
    public int damage, range;
   
    public flost attack_speed, reload_time;
   
    public Bullet bullet_prefab;
   
    public Transform particle_shoot;
    ...
    virtual void Shoot()
    {
        ...//тут механика выстрела: создаётся пуля(класс Bullet) через промежуток скорострельность, играет какой-то звук, вылетают какие-то партиклы со ствола в стороны(у обьекта пули свои партиклы).
    }

    virtual void Reload()
    {
        ...//перезарядка: анимация
    }

    ...
//тут мы переопределяем метод ShowItem() для магазина, потому что хотим чтоб оружие в магазине не просто вертелось в слотах, как остальные предметы в магазине,  а ещё допустим сверкало или стреляло во время этого процесса
    override void ShowItem()
    {
        base.ShowItem();//тут работает код "оригинальной" части метода ShowItem()

        //а дальше мы добавляем что-то новое для этого метода
          ...
        PlayWeaponShopAnimation();
    }
}

public class Laser: TestWeapon
{
    override void Reload() { }    //У лазера нет перезарядки. Мы переопределяем метод и оставляем его пустым

}
 
getAlex
Адепт
 
Сообщения: 1775
Зарегистрирован: 10 авг 2013, 18:30

Re: OOP, наследование - зачем это надо?

Сообщение AABB 03 сен 2018, 12:31

Оно не для того чтобы хранить 2 переменные в классе.
Это для теста, на самом деле их побольше.

У меня в игре не было таких заморочек, чтобы делать разную анимацию в магазине.

А если делать методы в подклассах, то надо будет кучу параметров в них передавать. Как-то не очень красиво. Не знаю, в чём выгода...
AABB
UNIт
 
Сообщения: 134
Зарегистрирован: 05 фев 2014, 15:52

Re: OOP, наследование - зачем это надо?

Сообщение 1max1 03 сен 2018, 12:52

Тебе уже сказали зачем нужно наследование не морочь себе голову, делай так как удобней.
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: OOP, наследование - зачем это надо?

Сообщение ~AvA~ 07 сен 2018, 13:11

что раз есть такие возможности, то надо их использовать.

Вот здесь ошибка, всё остальное уже следствие.. )
Аватара пользователя
~AvA~
UNIверсал
 
Сообщения: 396
Зарегистрирован: 17 фев 2015, 13:09

Re: OOP, наследование - зачем это надо?

Сообщение AABB 10 сен 2018, 19:34

item.GetType() == typeof(TestWeapon)

Могли бы хоть подсказать, что проще писать "item is TestWeapon"!

Но вообще спасибо тем, кто дал себе труд расписать как это должно использоваться. Я теперь частично так и буду делать.
AABB
UNIт
 
Сообщения: 134
Зарегистрирован: 05 фев 2014, 15:52


Вернуться в Почемучка

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

Сейчас этот форум просматривают: Google [Bot] и гости: 23