Мой прогресс и достижения в разработке (Часть 2)

Главная Форумы Разработка игр Мой прогресс и достижения в разработке (Часть 2)

Просмотр 15 веток ответов
  • Автор
    Сообщения
    • #5684
      InFallen
      Участник

      Продолжение моего отчета об изучении языка программирования C#. (Первая часть)
      Пройдено не так много. Как я сам понял на ООП нужно обратить достаточное внимание и в связи с этим у меня возникли вопросы, но об этом позже. В начале отчет:
      1. Пройдена вторая парадигма ООП – “Наследование”. Две модели: наследование и включение/делегации.
      2. Изучено ключевое слово sealed, которое предотвращает наследование.
      3. Изучена такая функция Visual Studio, как Диаграмма классов – очень сильно помогает разбираться при изучении кода и особенно разбираться в наследовании.
      4. Ключевое слово base – которое используется для доступа к членам базового класса из производного, используется в конструкторах и методах производного класса.
      5. Ключевое слово protected – которое позволяет выдать доступ для членов только в базовом и производном от него классе.
      6. Вложенные типы, когда один тип, включает в себя другой тип класса и может создавать члены этого типа.
      7. Третья парадигма ООП – “Полиморфизм”.
      8. Ключевое слово virtual. Объявленные с помощью него методы могут быть переопределены в производных классах.
      9. Ключевое слово override. С помощью него в производных классах переопределяются методы, которые в базовом классе объявлены с помощью ключевого слово virtual.
      10. Абстрактные классы. Классы объявленые с помощью ключевого слова abstract. Экземпляры таких классов нельзя создавать. Все методы данного класса должны быть также абстрактными и переопределны в производных класса с помощью слова override.
      11. Сокрытие членов. Это осуществляется с помощью ключевого слова new, в производном классе, содержащем идентичный метод, идентичный унаследованному.
      12. Приведение базовых и производных классов.
      13. Ключевое слово as. С помощью которого можно определить совместимость объектов, проверив возвращаемое значение на предмет null.
      14. Ключевое слово is. С помощью данного слова можно проверить совместимость типов двух элементов.
      15. Изучен класс System.object. Данный класс является родительским для всех пользовательских классов.
      16. Начато изучение обработки исключений и класса System.Exception. Данный класс также является производным от System.Object. Все исключения порождены данным классом.
      17. Ключевое слово throw – с помощью которого генерируется исключение.
      18. Перехват исключений с помощью блока try/catch. Блок try – это раздел операторов, которые могут генерировать исключение. Блок catch – обрабатывает исключение, которое генерируется в блоке try. После блока catch приложение может продолжать функционировать.

      Вроде ничего не упустил об изученном материале, очень много, все равно еще в некоторых понятиях путаюсь, но стараюсь разбираться в коде, пошагово изучать код, как и откуда, то или иное действие(метод, конструктор, свойство) вызываются, но нужно больше практики, а вот сам её придумать не могу, поэтому наверно и топчусь на одном месте.

      Ну и на последок.
      ВОПРОСЫ:
      1. Инкапсуляция – это сокрытие членов класса ключевыми словами: protected, private, и обращение к ним только через методы, свойства, констукторы, правильно ли я понимаю данную парадигму?
      2. Наследование – это создание класса на основе другого, с дополнительными свойствами, методами, конструкторами, , но производный класс с помощью ключевого слова base может также обращаться к членам базового класса, правильно ли я понимаю?
      3. Полиморфизм – это переопределение виртуальных и абстрактых членов базового класса в производном классе?
      4. Абстрактые классы, которые содержат асбтрактные методы и работу с ними составляют четвертую парадигму ООП – “Абстракцию”? Просто о данной парадигме нет описания ни в официальной документации от Майкрософт, ни в книге.
      5. И последний вопрос, сколько всего парадигм ООП? Так как разные источники, пишут разную информацию.

    • #5687
      Сергей Кормишин
      Хранитель

      Привет, здорово получается у тебя!

      1. Инкапсуляция – это сокрытие реализации. Это всё, что об этом нужно знать. Реализация это и методы, и поля, и свойства. Суть в том, что разработчик класса (программист который его пишет) предоставляет “открытый интерфейс” (не термин Interface из C#) себе и другим программистам. Что это такое? Это все открытые члены этого класса. Хороший интерфейс соответствует правилам: легко и понятно использовать правильно, тяжело использовать неправильно.

      2. Да, все правильно

      3. Полиморфизм – просто еще один инструмент, который позволяет сделать использование кода – проще. В C# два вида полиморфизма – статический и динамический, в зависимости от того когда разрешается какой именно метод нужно вызвать.
      статический:

      // Допустим, что это код из какой-то математической библиотеки
      void countNumber(int a);
      void countNumber(decimal a);
      

      Программисты, которые используют эту библиотеку не парятся об аргументах функции countNumber – вызвали с int – вызвалась одна версия, вызвали с decimal – другая. Какая будет вызвана – решает компилятор, поэтому такой полиморфизм называется статический.
      динамический:
      Это как раз то, что ты сказал – виртуальные функции. Какая версия будет вызвана будет известно только во время выполнения программы, поэтому такой полиморфизм называется динамический.

      4. Не слышал ни разу, чтобы кто-то вводил понятие “абстракция” как понятие ООП. Абстрактный класс можно воспринимать как заготовку, в которой уже что-то реализовано, но использовать “заготовку” нельзя, нужно доделать, реализовать абстрактные методы, например.

      5. Я встречал три: инкапсуляция, полиморфизм, наследование. Не парься об этом – терминология для теоретиков, крышу от этого сносить не должно. Всегда должны работать правила здравого смысла: код легко использовать, тяжело сломать, легко тестировать, легко расширять и модифицировать.

      С практикой всё усвоится – больше практики – лучше. Если есть идеи программы какой-то – уже можно сразу браться за нее, будут появляться вопросы: как сделать то или иное – гугление, особенно по-английски, поиск на stackoverflow, msdn и др источниках – тоже очень важный скилл.

      p.s. оказалось что движок форума не поддерживает разбиение на страницы, поэтому я выделил 2ю часть

    • #5689
      InFallen
      Участник

      Благодарю, в данный момент практикую задачи на http://www.codeabbey.com, вроде получается, правда приходиться очень много гуглить, т.к. некоторые вещи в книге не рассмотрены, и msdn стал моим большим другом.

      Идеи с программой есть, но мне кажется реализовывать поведение персонажа рпг игры рановато, но опишу свои мысли хотя бы:
      1. Создается абстрактный класс Hero, в котором описываются основные параметры персонажа, такие как сила, ловкость, выносливость, магия, опыт, уровень зависящий от опыта. Каждый со своей логикой, что зависит от того или иного параметра.
      2. Создаются производные классы, например Swordsman, Archer, Sorcerer, в них уже описываются как параметры будут изменять от уровня к уровню, куда сколько прибавляться, какие бонусы получать например от достижения определенного уровня.

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

    • #5690
      Сергей Кормишин
      Хранитель

      Можно сделать таким образом, да. В больших играх часто какие-то характеристики выносят из кода в компонентную систему, вроде такого: есть класс Character, а в нем внутри List() а Level, Health, Ability, Strength от него наследуются. Для чего это нужно – чтобы дать дизайнерам возможность настраивать компоненты без участия программиста. Все в угоду быстрых итераций при разработке.

      Но для начала – твой подход – очень хорош. Все эти нагромождения – они появляются из-за различных проблем и нужд. И пока с ними не столкнешься – они не нужны. Не помню автора фразы: Любая проблема решается введением дополнительного уровня косвенности, вот из этого сложность и происходит.

    • #5691
      InFallen
      Участник

      Ну до больших игр, да и вообще до игр пока далеко. Это пока задумки, о том как и что я могу сделать с помощью полученных мной знаний. Попробую написать данную реализацию, а там вдруг, во время работы интересное в голову что придет, творчество – оно такое.

    • #5744
      InFallen
      Участник

      Начал практиковаться в программировании, и решил разработать классы персонажей для рпг, классические, Воин, Лучник, Маг, все наследуются от общего класса Герои, в котором определены все общие свойства, у меня появилась проблема при определении уровня, который зависит от количества опыта, формула вроде правильная, но как её реализовать что-то не доходит, получается рекурсивный вызов, еще не определенных данных, и как от него избавиться не представляю…

      Ссылка на проект:
      https://github.com/InFallen/Hero.git

    • #5755
      Сергей Кормишин
      Хранитель

      Привет, вообще да – рекурсия. Даже две – мне кажется.

      Одна тут:

      byte Level(int experience)
              {
                  if(HeroesLevel <= 99)
                  {
                      return (byte)(experience / 1000 + 100 * HeroesLevel);
                  }
                  else
                  {
                      return HeroesLevel;
                  }
              }

      и одна в методе:

      byte Level(int experience)
              {
                  if(HeroesLevel <= 99)
                  {
                      return (byte)(experience / 1000 + 100 * HeroesLevel);
                  }
                  else
                  {
                      return HeroesLevel;
                  }
              }

      Вообще хороший стиль: методы называют “глаголами”, подразумевая действие, а свойства – существительными. В свойствах по-красоте – не должно быть рассчетов.

      Твою задачу я бы решил так: есть метод RecalculateLevel – в нем устанавливается значение поля – оно доступно извне только через Get (Set’a нет, такие свойства называются read-only). Этот метод RecalculateLevel – ты вызваешь в каком нибудь AddExperience и конструкторе.

      По твоему коду – очень не понятно зачем делать какой-то Cap – ограничение уровня – if(HeroesLevel < = 99). Тебе нельзя здесь использовать свойство. Если ты хочешь какой-то Cap сделать – то по идее ты должен делать что-то вроде:

      byte GetRecalculatedLevel(int exp)
      {
        byte level = (byte)(experience / 1000 + 100 * HeroesLevel);
        if(level <= 99)
        {
          return level;
        }
        else
        {
          return 99;
        }
      }

      если Cap (ограничение максимального уровня) то, что нужно то можно сделать красивее так:

      
      const int MAX_HERO_LEVEL;
      byte GetRecalculatedLevel(int exp)
      {
        return Clamp(1, MAX_HERO_LEVEL, (byte)(experience / 1000 + 100 * HeroesLevel));
      }

      Clamp – функция ограничения, у Unity есть в библиотеке Mathf, в .NET посмотри сам где она или похожая.
      И еще – нет особого смысла использовать byte – можно смело использовать uint, int

    • #5791
      InFallen
      Участник

      Привет, спасибо за примеры, попроовал, но получается не так как хотелось бы.
      Ведь если идти таким путем, тогда это не свойство вычисление которого скрыто, это возврат значения метода GetRecalculatedLevel, в которое мы передаем значение Experience, и из созданного объекта придется обращаться не к свойству объекта Heroes.Level, а к методу Heroes.GetRecalculatedLevel(Heroes.Experience)
      А хотелось бы обращаться к свойствам для получения информации, а они уже вычислялись бы методами, но получается что свойство HeroesLevel зависит от свойства Experience. Мы не задаем как таковой HeroesLevel в начале, по умолчанию он равен при типе byte или int он равен нулю, поэтому формула отрабатывает некорректно.
      Буду думать дальше, что-то помойму для месяца изучения языка слишком сильно замахнулся, либо сказывается отсутствие практики. Еще раз благодарю!

    • #5792
      Сергей Кормишин
      Хранитель

      Привет, так ведь я же говорю – read only свойство. Метод приватный. Вызывается когда изменяется Experience для пересчета и сохраняет значение в поле, которое доступно только на чтение: для этого можно сделать или field, или private set; у этого поля.

    • #5897
      InFallen
      Участник

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

      Ссылка на проект:
      https://github.com/InFallen/MyHero.git

      Думал реализовать защиту персонажа через интерфейсы, но что-то никак в голову не приходит как задавать тогда защиту, тут реализованно обычным свойством класса.

    • #5898
      Сергей Кормишин
      Хранитель

      Привет, а что эта защита делает?
      Работает как “абсолютная” броня? Она не разрушаема сама по себе? Сейчас работает – как неразрушаемый щит, на фиксированный показатель урона. И такой вариант в каких-то простых играх может работать.

      • #5899
        InFallen
        Участник

        Да в данный момент она работает как неразрушимый щит, на фиксированный показатель урона.
        В данный момент пытаюсь реализовать через отдельный класс Armor, также у класса Hero должно быть поле с таким классом, и вот оно уже должно показывать защиту.
        Только вот затык такой, лучше несколько вариантов брони реализовывать как отдельнные классы, наследованные от Armor, или как вариант через List<Armor> в классе Hero?

      • #5900
        InFallen
        Участник

        В общем что-то получилось у меня с классом Armor, у которого кроме Defence, есть еще Durability.

        На всякий случай ссылка:
        https://github.com/InFallen/MyHero.git

    • #5901
      Сергей Кормишин
      Хранитель

      Все как раз зависит от того какие возможности должны быть в твоей игре. Часто делают какой-нибудь static класс BattleCalculator, в нем метод ApplyDamage например – и этот метод уже перебирает: айтемы юнита (защитные например), бафы, дебафы, делает какой-нибудь life-stealing и тп вещи.
      Вариантов решить задачу очень много – главное чтобы “удобно использовать правильно, тяжело использовать неправильно”.

      • #5902
        InFallen
        Участник

        Да тут до игры, еще далеко, я просто пытаюсь понять как работает та или иная возможность языка, переводя её на свои примеры. Потому что прочитав 10 глав книги, я понял, что материал я понимаю, примерно понимаю как он работает, а как его реализовать я не знаю, и вот из-за этого руки опускаются больше всего… А начиная хотя бы делать что-то своё, опираясь на примеры из учебника, вроде как что-то получается, а в какой-то момент затык, я понимаю что тут надо реализовать через эту возможность, но оно не получается и упираюсь в стену… Сказывается отсутствие опыта, ведь изучаю язык программирования всего-то 1,5 месяц(но это больше похоже на отмазку какую-то)

    • #5924
      Сергей Кормишин
      Хранитель

      Да это все придет с практикой) Главное делать и не бояться делать неправильно, на первых порах – на учебных проектиках,- вообще можно писать коряво, главное – Писать.

    • #6006
      InFallen
      Участник

      Всем привет. Ух… долго ничего не писал тут, зато практиковался много, попытался реализовать консольную змейку. Вариантов в интернете много, но захотелось реализовать самому, попытка завела меня в тупик в нескольких пунктах:
      1. Не могу реализовать автоматическое движение змейки.
      2. При реализации головы змейки как отдельного символа, управление работает нормально, при реализации головы змейки, как части массива, управление не работает…

      Ссылка на проект:
      https://github.com/InFallen/Console-Snake.git

      • #6008
        InFallen
        Участник

        Разобрался с перемещением по клавишам, но перемещается весь массив, а не как змея…

    • #6013
      Сергей Кормишин
      Хранитель

      Привет, взять код и подебажить чтобы подсказать – что не так, я к сожалению не могу – сейчас совсем нет времени. Теоретически в дебаге – главное – вычлени место, где есть четкое: должен быть такой output, а он другой. И разворачивай с самого низа покомпонентно – что привело к такому результату.

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

      • #6042
        InFallen
        Участник

        В принципе я знаю свою проблему, просто пока думаю как её реализовать, и вроде как разорабрался с передвижением за время, через System.Timers.Timer, только вот теперь к нему не могу управление привязать. Как говориться: “Терпение и труд – все перетрут”. Посижу подумаю еще и все получиться, зато опыт получу.

    • #6728
      InFallen
      Участник

      Ух… Давно я тут не был, я все еще жив здоров, изучаю язык программирования C# и разработку игр на движке Unity. Долго отсутствовал по той причине, что не знал о чем писать, и думал стоит ли вообще все продолжать. Понял что стоит, когда сам написал пару небольших игрушек со своими маленькими знаниями. Посмотреть как выглядят можно на видео на моем канале youtube
      Пока все, буду находить время чтобы продолжать что-то писать, а без этого теряешься, где, когда и что ты делал =)

Просмотр 15 веток ответов
  • Для ответа в этой теме необходимо авторизоваться.
Понравилась статья? Поделиться с друзьями: