Jusper Jusper

Normanof, картинки исправлены, файлы для скачивания снова доступны.

Jusper Jusper

Normanof,

Проверяем, Спасибо.

Normanof Normanof

Пожалуйста, проверьте картинки. Они не открываются.

Jusper Jusper

Оформите, пожалуйста, гайд на сайте.
Редирект на внешний ресурс в таком виде противоречит правилам

alexprey alexprey

platformengine, ого, весьма интересная механика, подписался, буду ждать еще новостей об игре)

Rummy_Games Rummy_Games

Доброго субботнего вечера! Сегодня мы ходим поделиться наработками нашего 3D-моделлера в рамках #saturdayscreenshot.

Не забудьте перейти по ссылке на страницу в Steam и добавить игру в wishlist!

platformengine platformengine

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

alexprey alexprey

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

TheDarkestRed TheDarkestRed

Выкопали картошку, постелили газон к лету 🎮 👩‍🌾 🌱🌿🍀
https://vk.com/the_darkestred

IDALGAME IDALGAME

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

...
Jusper Jusper

Сражение с самураем на новой гладиторской арене. Вооруженный длинной нагинатой самурай требует более "близкого" подхода

Jusper Jusper

EfReeZe,

Да, это как раз то, что зацепило взгляд. Танчик ездит через препятствия и это объясняется сеттингом.

alexprey alexprey

Подписался!
И сразу к вопросам, мультиплеер планируется?)

alexprey alexprey

IDALGAME,

так и есть, мечты детства в реальности!!!

EfReeZe EfReeZe

На андроид бы. А что, как на счёт покататься на каких-то других танках и видах техники, как на счёт интересных орудий и всего такого? Как на счёт деревьев? Танк к слову сквозь дерево не проедет, не повалит его...

IDALGAME IDALGAME

Jusper, О спасибо!

Jusper Jusper

Подклеил видео и скриншоты.
Гиперлинки соц. сети подкрепил под кнопочками.

Логотип проекта Unity

Паттерны. "Singleton", "Decorator". Unity

Паттерны. "Singleton", "Decorator". Unity

Здравствуйте уважаемые читатели данной статьи. Сегодня я решил поговорить о ОО-проектировании в сфере геймдева, используя движок Unity. Сразу скажу, что данная статься является объективным видением использования паттернов и в особенности, их реализация. Кто-то может говорить о том, что в моих далее приведенных примерах лучше использовать тот или иной паттерн и возможно вы будете правы, но моя задача как минимум поверхностно пройтись по этой достаточно сложной теме. Я рассчитываю сделать небольшой цикл статей про разные паттерны и их примерное использование, чтобы архитектура вашего проекта стала гибкой и расширяемой.
Также хочу отметить, что данный цикл будет требовать определенных знаний в области ОО-программирования и базового ознакомления с понятием "паттерн".
Вроде всё оговорил, можем начинать!

Паттерн "Одиночка"

Данный паттерн предназначен для реализации единственного, уникального объекта во всей программе. При разработке игры вы можете столкнутся даже не один раз, когда вам нужно будет реализовать какой-либо модуль для игры (например систему сохранения игры или систему достижений). За продолжительное время в геймдеве я столкнулся с несколькими подходами к реализации уникальных объектов, но сегодня я хочу рассказать о том, на котором остановился на данный момент.
Былое: Ранее, я думал, что все подобные модули, которые я приводил в пример, должны быть уникальными объектами и делал каждый такой модуль на базе паттерна "Одиночка", но вскоре понял, что есть и более элегантный способ.
Собственно, я пришёл к выводу, что лучше всего сделать один единственный класс-одиночку, а внутри него хранить экзмепляры классов, которые могут быть задействованы из любой точки программы. Иначе говоря, у меня есть один класс на базе "Одиночка", который даёт доступ только для чтения всех ранее проинициализированных экземпляров классов.
Вот собственно его реализация:

using UnityEngine;

namespace Game.Main
{
    public class GameStorage : MonoBehaviour
    {
        public static GameStorage Instance { get; private set; }
        public FileManager FileManager { get; private set; }
        public AchievementsManager AchievementsManager { get; private set; }

        private void Awake()
        {
            CheckInstance();
            InitManagers();      
        }

        private void CheckInstance()
        {
            if (Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(this);
            }
            else
            {
                Destroy(gameObject);
            }
        }

        private void InitManagers()
        {
            FileManager = gameObject?.GetComponent<FileManager>();
            AchievementsManager = gameObject?.GetComponent<AchivementsManager>();
        }
    }
}

Как вы можете видеть, класс-одиночка не сильно привязан к тем экземплярам, которые инициализируются за счёт оператора '??', а также инициализирует он их на том же игровом объекте, на котором находится сам. Это значит, что если вам не обязательно добавлять все модули сразу. С другой стороны, если вы забудете добавить нужный модуль (модуль в моей интерпретации это управляющий класс), то движок вам напомнить не сможет.

Примечание:
Помните, что это пример на Unity и лишь одна из интерпретаций паттерна. Дело в том, что данный вариант не будет работать корректно с несколькими потоками. Возможным решением может стать ключевое слово для статического экзмепляра одиночки "volatile". С учётом новой Job System в Unity, для меня неизвестно, как именно будет работать одиночка, стоит проверить.

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

Более подробнее ознакомится с паттерном можно будет здесь

Паттерн "Декоратор"

Лично я вижу сложность в том, чтобы придумать серьезную практическую задачу для данного паттерна. Его суть в том, что он создаёт некоторые дополнения для одного объекта. Это если говорить двумя словами, но на деле всё может показаться чуть сложнее.
Предположим, что у нас есть класс "Weapon". Мы хотим, чтобы наше оружие могло иметь разные модификации (глушитель, прицел, например), это и будут называться "дополнения" для одного объекта "Weapon". Сам процесс добавления таких дополнений реализован посредством "оборачивания" объекта "Weapon".
В нашем случае, рассмотрим простой пример с пистолетом и глушителем и выведем конечное описание оружия.
Создадим для начала абстрактный класс Weapon:

using UnityEngine;

namespace Patterns.Decorator
{

    public abstract class Weapon : MonoBehaviour
    {
        [SerializeField]
        private string nameWeapon;       

        public abstract string GetDescription();
        public string GetWeaponName() => nameWeapon;
    }
}

Далее, чтобы добавлять разные модные штучки на нашу мощную пушку, сделаем класс-обертку, которая будет являться классом-наследником для класса "Weapon" и назовём этот класс "WeaponWrapper". Вот как он выглядит:

using UnityEngine;

namespace Patterns.Decorator
{

    public abstract class WeaponWrapper : Weapon
    {
        protected Weapon weapon;

        public void SetWeapon(Weapon weapon) => this.weapon = weapon;

        public override string GetDescription() => weapon.GetDescription();

    }
}

Но не будем спешить и сделаем класс, который будет нашей пушкой (конкретной реализацией). Пусть это будет пистолет. Собственно его реализация:

using UnityEngine;

namespace Patterns.Decorator
{

    public class WeaponPistol : Weapon
    {
        private WeaponWrapper muffler = new Muffler();

        public override string GetDescription()
        {
            return GetWeaponName();
        }

        private void Start()
        {
            Debug.Log(GetDescription());
            muffler.SetWeapon(this);
            Debug.Log(muffler.GetDescription());
        }
    }

}

Что такое "Muffler"? Сейчас покажу, но скажу пока кое-что на счёт данного класса. В данном примере я хочу выводить описание нашего оружия. Как видите, здесь я вывожу описание сначала пистолета (просто название оружия), а потом описание объекта "Muffler". Перейдём теперь к классу "Muffler".

Сделаем класс, который будет одним из модификаций для нашей пушки, пусть это будет глушитель. Класс будет называться "Muffler" и выглядеть так:

using UnityEngine;

namespace Patterns.Decorator
{

    public class Muffler : WeaponWrapper
    {

        public override string GetDescription()
        {
            return weapon.GetWeaponName() + " с глушителем";
        }
    }
}

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

Паттерны. "Singleton", "Decorator". Unity — Unity — DevTribe: инди-игры, разработка, сообщество (GameDev, patterns, Unity)

Как видите, в данном случае, не оружие хранит модификации, а модификации хранят оружие. В этом специфика данного паттерна. Возможно, с точки зрения логики, было бы правильнее хранить модификации оружия внутри класса оружия, однако, как я говорил ранее, придумать пример на базе такого паттерна в сфере разработки игр для меня оказалось достаточно интересной задачей. Может быть ваш пример будет гораздо лучше? Если так, то предлагайте, будет интересно посмотреть!
В конце хочется отметить, что данный паттерн хорошо используете в комбинации с паттерном "абстрактная фабрика", но сегодня мы рассмотрели чистый паттерн "декоратор" суть которого дополнять объект новыми свойствами без изменения классов самих оружий.

Более подробно ознакомится с паттерном можно здесь

Спасибо за внимание!

Смотрите также:


Комментарии



Весьма интересный подход к реализации синглтона в Unity. Надо взять на заметку, а то я сейчас просто через FindObject пытаюсь найти инстанс к таким важным вещам. Единственное, как быть если все такие компоненты разбросаны в разных частях сцены? Например, InventoryController, UnitSelectionController и т.д.?

alexprey, В таком случае, вы можете самостоятельно инициализировать экземпляры этих классов через инспектор. Например:
[SerializedField]
private InventoryController inventoryController;
А далее просто сделать общедоступный Get-метод для доступа к этому объекту, как вариант :)
Однако минус такого подхода в том, что при переходе между сценами, будут проблемы. В таком случае, нужно заранее продумать все детали.

RedHelium, да действительно, все чуть проще чем я себе накрутил...

alexprey, В таком случае, вы можете самостоятельно инициализировать экземпляры этих классов через инспектор. Например:
[SerializedField]
private InventoryController inventoryController;
А далее просто сделать общедоступный Get-метод для доступа к этому объекту, как вариант :)
Однако минус такого подхода в том, что при переходе между сценами, будут проблемы. В таком случае, нужно заранее продумать все детали.
alexprey, Немного порассуждав над вопросом, я для себя понял следующее. Если у вас много таких классов, к которым можно обратится из любого класса, то вероятно, лучший способ - подумать над архитектурой т.к. рационально ли хранить, скажем на 10-20 игровых объектах разные важные глобальные данные? И второе. Если объектов таких несколько, в таком случае, может будет резонно сделать несколько классов-одиночек, причём тогда бы я разграничил всё по пространствам имён, чтобы класс N не имел сразу доступа ко всем одиночкам и классам внутри него, а брал только те данные, которые ему нужны..

Справка