Pull to refresh

Comments 46

Ваши шрифты ужасны.

Слишком большой пост у вас получился. Может из-за картинок, но пользуйтесь спойлерами почаще. Плюс предупреждать надо, что тонны трафика жрет ваш пост. У меня он ограничен на работе, например.
Простите, сразу пост было трудно прочитать, слишком объемно. Но, вы уверены, что проще никак нельзя было написать? Думаю, можно было сократить раза в два, если не три. Да и аудитория поста, судя по содержанию для тех, кто совсем не знаком ни с IDE, ни с ООП, ни с полиморфизмом.
Да это для тех кто не знает ничего о полиморфизме или для тех кто думает что он не нужен.
Ну это касается полиморфизма. А вот где его использовать… это возможно, будет интересно не только для новичков
По поводу размера, что есть то есть, хотел по подробнее все описать. Наверно стоит разбивать на части. А что там со шрифтом? Обычный шрифт
Я сначала подумал, что это IDEA в Linux'е без настроек :)
Шрифты просто режут глаза.

Да, лучше на части разбивать даже, а не спойлер использовать. Будет проще читать, да и воспринимать по частям легче.
Понимаю, что из песочницы вы 5 постов не напишете, хотели все описать. Но лучше обрежьте и сделайте цикл.
Шрифт стандартный — Consolas.
А у меня по умолчанию не Consolas
Боже. Это отличный пример, как НЕ надо писать обучающие материалы.

Все в такой каше, что я еле продрался через гору мусорного текста. Нет единой концепции.
Некоторые примеры настолько для нубов, что эти самые нубы не поймут, что же именно им тут хотели сказать, описывая автобусы на страницу текста.
Поэтому под transform(если написать это слово в скрипте) подразумевается трансформ FirstPersonController`а, а не камеры. Transform камеры является под трансформом трансформа FirstPersonController`а

image

Удалите этот текст полностью.
Поймите сами, про что хотите рассказать.
Сделайте это небольшими понятными частями.
)))) Если читать медленно, то все логично
Логика не зависит от скорость прочтения :-)
Читатель статьи, будь осторожен! Использовать объектно-ориентированный подход в Unity неправильно в принципе. Паттерны GameLoop, UpdateMethod, Behaviour предполагают компонентно-ориентированный подход. Даже методы называются GetComponent и SetComponent, а сами классы, унаследованные от MonoBeahavior — компоненты реализующие определённое поведение, а никак не «скрипты».
Можно создавать классы не являющиеся наследниками MonoBeahavior. Правда в них не будут работать Update, Strat и т.д. Поэтому
Использовать объектно-ориентированный подход в Unity неправильно в принципе

сильно резко сказано
private не наследуется

Это неверно в принципе. Объект потомок имеет все, что имеет объект родитель. То, что в потомке метод «невидим» вовсе не означает, что его нет.
+1
Если в классе публичная функция Set(int i) изменяет приватную переменную hp, а класс Child — его наследник, то эта функция работает и в нем
Все мы прекрасно поняли, что имел в виду автор :) Но надо чуточку поправить определение.
В случае, если требуется заставить некоторые классы реализовать определенные методы, логичней пользоваться интерфейсами. Ваш вариант обладает небольшим «логическим» недостатком — фактически все методы вашего класса уже реализованы, только с пустым телом (методы затычки). В случае, когда суперкласс должен содержать только нереализованные методы, обычно выделяется интерфейс. И даже если опустить факт, что методы реализованы, но пустые (что безусловно аукнется позже), использование наследования в данном случае делает код менее гибким, ведь все еще действует ограничение на наследование от одного класса.

Так же не понятно, как быть с пустыми методами, которые не были переопределены в подклассе. Если я вижу у объекта метод Jump, то предполагаю, что он реализован и обладает какой-то логической нагрузкой. И я очень удивлюсь, если вызывая метод ничего не будет происходить.

Несколько «слов» в коде сделают наши скрипты независимыми и взаимозаменяемыми. Вообще надо делать все скрипты не зависимыми друг от друга.

В вашем случае объект, наследующийся от MyMotor обязательно будет наследовать MonoBehavior, то есть обладать всеми свойствами игрового объекта. Если я захочу сделать полноценного консольного монстра, который не будет находится в списке гейм объектов в игровом поле, а спокойно себе выполнять функции Move, Jump и прочее в консоли — я не смогу сделать это адекватно (без лишних в данном случае свойств и методов), поскольку я уже буду зависеть от реализации класса MonoBehavior.

В общем полиморфизм в данном случае точно лучше было бы сделать с помощью интерфейса, а не суперкласса. Это один из принципов ООП, предложенной знаменитой бандой четверых. Composition over inheritance — «Предпочитай композицию, а не наследование»

Подведем итоги:
1. Крайне не желательно оставлять методы с пустым телом, если предполагается, что подклассы переопределят эти методы. В этом случае нужно использовать абстрактный класс.
2. Если наш абстрактный класс не содержит ничего, кроме абстрактных методов, то логичней выделить эти методы в интерфейс.
Разрешите спросить, как потом хранить в одной переменной экземпляры классов не связанных «родственными» отношениями?
Зачем? Используйте больше переменных с понятными именами. Это лучше чем один универсальный список всех-всех-всех объектов.
Я имел в виду НЕ одновременно хранить
Вот в этом то и соль интерфейсов, в одной переменной можно хранить ссылки на любые объекты, реализующие интерфейс. Вместо класса MyMotor у вас будет интерфейс IMovable допустим. Тогда и переменная MyMotor myLittleMotor превратится в IMovable myLittleMotor.
Плюс ко всему в C# очень здорово сделана модель событий, и события можно определять в интерфейсе.
Просто получается, что у меня в гейм объекте будет полноценный объект MonoBehavior со всеми кишками и костями, хотя в данном случае этого совершенно не требуется — нам нужны только конкретные методы.
Про абстрактные классы вы абсолютно правы, но только в том случае, если у родительского класса совсем нет реализации методов, а это ведь не всегда так. А вот зачем вам делать полноценного монстра не видимого в списке гейм объектов мне совсем не понятно. Класс наследник должен реализовать все методы родителя. В этом весь смысл. Пустые (никак не пере объявленные методы) нужны например если мы заменили Мотор на другой Мотор, который в данной ситуации не должен реализовать прыжок, или мы захотели Мотор от одного моба повесить на другого. И самое главное, дайте мне ссылку на статью про то как пользоваться интерфейсами. Можно ли потом в переменной одного типа хранить экземпляры разных классов?
Класс наследник должен реализовать все методы родителя.

В вашем примере наследник ничего не должен. Компилятор не выдаст ошибок или предупреждений, если вы унаследуете MyMotor и не переопределите методы. В этом случае очень просто запутаться.

Пустые (никак не пере объявленные методы) нужны например если мы заменили Мотор на другой Мотор, который в данной ситуации не должен реализовать прыжок, или мы захотели Мотор от одного моба повесить на другого.

В таком случае тут точно логическая нестыковка — у моба есть метод Jump, но он ничего не делает — это совсем не то, чего я ожидаю от подобной реализации. Непрыгающие мобы, по-хорошему, должны иметь другой тип (интерфейс). Наличие методов это как соглашение — «У тебя есть методы, значит каждым из них ты что-то делаешь». Если вам придется отдать подобный код в чужие руки, то возникнет много нестыковок.

И самое главное, дайте мне ссылку на статью про то как пользоваться интерфейсами. Можно ли потом в переменной одного типа хранить экземпляры разных классов?

Мы создаем переменную типа InterfaceName и можем хранить в ней все объекты, реализующие этот InterfaceName. И при этом не возникнет ситуации, когда методы можно будет оставить пустыми — компилятор не позволит. Смысловая нагрузка останется, логичность выполнения работы программы тоже. Такое использование интерфейсов полностью описывает паттерн Strategy, ссылки на wiki и вики.
Хорошо я понял, я почитал про интерфейсы, они действительно решат проблему пустых методов. Но как из одного скрипта(экземпляра класса) получить ссылку на другой экземпляр другого класса(или теперь уже на интерфейс этого экземпляра) Раньше я делал это так:
MyMotor thisMotor = GetComponent<MyMotor>();


А в «вики» имеется такой пример для C#
.....
/// <summary>
    /// Класс приложения.
    /// В данном примере выступает как клиент контекста.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// Точка входа в программу.
        /// </summary>
        public static void Main()
        {
            // Создаём контекст и инициализируем его первой стратегией.
            Context context = new Context(new ConcreteStrategy1());
            // Выполняем операцию контекста, которая использует первую стратегию.
            context.ExecuteOperation();
            // Заменяем в контексте первую стратегию второй.
            context.SetStrategy(new ConcreteStrategy2());
            // Выполняем операцию контекста, которая теперь использует вторую стратегию.
            context.ExecuteOperation();
        }
    }
.......


На мой взгляд класс Program не так уж и не зависим. Он точно знает когда какую стратегию надо использовать. И если появиться новая стратегия придется менять код в void Main(). В то время как используя
MyMotor thisMotor = GetComponent<MyMotor>();

мы добьемся того что MyBehaviour сам будет выбирать первый попавшийся наследник MyMotor`а (мы наперед знаем что он один). И работать с ним.
Как я понимаю, подобной независимости в примере из вики можно добиться, сделав чтобы в классе Program происходил перебор всех имеющихся скриптов(экземпляров классов) и из них выбирался поддерживающий нужный нам интерфейс?(мы опять же знаем что он всего один на данном игровом объекте) Или я чего-то не так понял?
Пример с вики простейший, Program это просто главный класс, а Main — точка входа в программу. Главное в примере — показать возможность устанавливать любую Стратегию. Если вызвать метод SetStrategy извне, Program не будет знать, какую именно стратегию ему подсунули — он будет работать с ней как с объектом, реализующим интерфейс IStrategy. В Main просто эксплицитно устанавливается начальная стратегия.

Он точно знает когда какую стратегию надо использовать.

Как раз таки нет. Если скормить нашему Program любую Стратегию через SetStrategy, он не будет знать какой класс скрывается за этим интерфейсом.

В то время как используя

MyMotor thisMotor = GetComponent<MyMotor>();

С интерфейсом точно так же, через GetComponent

Как я понимаю, подобной независимости в примере из вики можно добиться, сделав чтобы в классе Program происходил перебор всех имеющихся скриптов(экземпляров классов) и из них выбирался поддерживающий нужный нам интерфейс?(мы опять же знаем что он всего один на данном игровом объекте) Или я чего-то не так понял?

Вы опять путаете «каноническое» программирование и программирование в юнити.
Признаю так действительно будет лучше. Не зря статью написал. Столько нового узнал)
Я пытаюсь получить интерфейс так:
CreateBlockIntefrace createBlocInterfase =    GetComponent<CreateBlockIntefrace  > ();

И что-то не компилится так?
Вот так работает)

blockType.Stone = (CreateBlockIntefrace )GetComponent<Stone> ( );

где blockType.Stone объявлена как CreateBlockIntefrace. Класс Stone реализует интерфейс CreateBlockIntefrace.
Спасибо за ответ, буду разбираться
ПОЛИМОРФИЗМ — не знаю, как дать ему определение. Может вы сами потом его дадите. Мы лучше рассмотрим все на примере.

У полиморфизма есть совершенно точное, лаконично определение. Один интерфейс, множество реализаций.
Из него следует, что нужно использовать интерфейсы, которых к сожалению нет в ваших примерах.
У полиморфизма есть совершенно точное, лаконично определение
Я в статье четко сказал, что все определения можно спокойно найти в интернете. И переписывать их я не стал
по-моему, вы сильно загнобили парня. Он хотел в принципе как лучше. Не из каждого сразу получается писатель. Ужать можно было, не спорю, всё это сравнение никому не нужно. Объяснение ООП от вас тоже не нужно было. Взялись за Unity, тогда просто напишите как использовать ООП в Unity, что в принципе было бы рациональнее. Итого данный текст уменьшился бы в 10-ки раз.
Вы пытались объяснить как для полных noobs, но тут я не думаю, что многие не знающие программирование или хотя бы основы стали влезать в Unity. Поэтому и стоит некоторые вещи обрезать…

Насчёт Интерфейсов, то на сколько мне известно, то использование Интерфейсов необдумано, тоже не имеет смысла. По производительности будет рационально сделать правильную иерархию классов с минимальным количеством интерфейсов. А полиморфизм можно и по другому трактовать тогда.
Вы, кажется, веткой промахнулись.
дочитал до скрина класса MobHp, в котором есть замечательная строчка:

public float originalHp=100; // Начальное значение здоровья (не меняется в ходе игры)

Это открытый член класса, поменять значение которого можно в любом месте кода:

mobHpObject.originalHp = -300;

И все. Здоровье стало отрицательным! >)

Автор, неизменяемые члены надо объявлять константами (может быть даже закрытыми) или ограничивать доступ к члену через свойства (или методы). Почитайте про инкапсуляцию (это еще один из принципов ООП).
Это Unity3D — все public свойства видны в редакторе и их можно задавать, соответственно если есть моб с таким скриптом, то ему сразу прописывается его количество hp, так что в рамках Unity3D это частое явление.
Вы не так поняли. Разумеется можно поменять значение этой переменной. Она характеризует стартовое значение здоровья. Его можно поменять если мы хотим получить более мощьного моба например. А глобально она объявлена чтобы её можно было менять в самой Unity, и не лазить в код. Ну а вот это
Это открытый член класса, поменять значение которого можно в любом месте кода:
mobHpObject.originalHp = -300;

извините, полный моразм. Можно еще сделать так
mobHpObject.originalHp = "Охо хо я хочу делать игры";

и тогда вообще не будет работать представьте себе! Какой ужас
Разумеется можно поменять значение этой переменной. Она характеризует стартовое значение здоровья. Его можно поменять если мы хотим получить более мощьного моба например. А глобально она объявлена чтобы её можно было менять в самой Unity, и не лазить в код.

Авторы юнити последние маразматики, если не добавили возможность изменить переменную в редакторе, а в коде оставить неизменным.
Поэкспериментируйте, может быть, использование модификаторов вроде static для переменной все-таки проймут редактор?
Во — первых такая возможность есть. Во — вторых есть смысл в коде задавать стартовое значение переменной. И самое главное
Авторы юнити последние маразматики

Зачем вы так комментируете статью посвященную работе в Unity если никогда не работали в Unity?
А тут-то вы попали пальцем в небо :)
С юнити я знаком еще с января, был зарегистрирован на нескольких форумах по юнити, где иногда задавал вопросы, где-то помогал, то, другое.
Естественно, что с того времени, когда я в конце марта бросил юнити, я многое подзабыл.

Автору: все круто, не забивать!

А еще я уважаю разработчиков юнити и верю в дружбомагию
Авторы Unity3D сделали возможность написания собственных скриптов для редактора — с помощью них можно реализовать редактирование в редакторе private переменных, но для этого надо писать скрипт редактора, а многим лень, да и многочисленные статьи по юнити изобилуют вот такими public переменными.

(Собственно, в общем случае нельзя в редакторе менять ничего кроме public потому, что к остальному банально нет доступа у кода редактора)
извините, полный моразм. Можно еще сделать так
mobHpObject.originalHp = «Охо хо я хочу делать игры»;

Так вы сделать не сможете, ибо член originalHp типа int.
Уважаемый человек, скажите, зачем мобу отрицательное здоровье? С какими мыслями программист должен сидеть чтобы написать
mobHpObject.originalHp = -300;
Кстати даже в этом случае ничего фатального не будет, но тем не менее это полный маразм.
Это даунсайд юнити, а не программистов. Как уже сказали, для юнити вполне приемлимо определять важные переменные как public, чтобы иметь доступ к ним напрямую из инспектора. Более того, это позволяет менять значение переменных прямо при выполнении Гейм превью, что в другом случае было бы невозможно. К сожалению, private переменные в инспекторе не отображаются и та же история с геттерами и сеттерами. То есть их (private, protected, {get; set;} нельзя поменять не залезая в код.
В общем для юнити паблик переменные это нормальная практика. (Конечно если предполагается, что значения менять будем прямо из инспектора)
Sign up to leave a comment.

Articles