Haibo Haibo

Jusper, ошибка была в том что путь был на русском языке а именно папка с документами

ChaosDevelop ChaosDevelop

Да, к сожалению есть такая тенденция ассоциировать матч3 с мобильными играми, несмотря на то что самый первый Puzzle Quest был выпущен и отлично игрался на ПК.
Арт еще будем развивать конечно же.

Jusper Jusper

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

Jusper Jusper

Haibo,

Ошибку в студию.

Haibo Haibo

А что мне делать я включил данный плагин компилирую игра не компилица выдает ошибку

Octomoon Octomoon

Концепт-арт полицейского для игры Fading Reality

...
Jusper Jusper

Что за проверка? Компиляция? Скрин хотя бы приложите :)

EfReeZe EfReeZe

Он у вас на честном слове трудился?

Dreaman Dreaman

На космической станции появился её голографический макет. Его можно будет использовать как карту локации, а также для решения игровых задач.

...
CHILLNPLAY CHILLNPLAY

Здравствуйте, у нас грустные и радостные новости! Мы начали делать игру с нуля.

Причину этого читайте в нашей статье: store.steampowered.com/news/app/1213020/view/3028081693987661088

Jusper Jusper

TheDarkestRed,

Кайфовые пейзажи получились.

iRediKurou iRediKurou

Diabfall,

К сожалению, ААА не гарантирует качество при огромном бюджете и долгой разработкой игры. CD Project...

iRediKurou iRediKurou

Diabfall, текущие жанры выбраны по мнению многих источников как самые популярные, многочисленные (количество игроков), объему прибыли и других факторов по мнению той же Игровой индустрии. Опрос является отправной точкой...

Diabfall Diabfall

Мало жанров указал. Где кки, где симуляторы?

Странный вопрос. Предпочтительна та игра, в которую интересно играть. Если будет 2 одинаковые по сути игры, но одна ААА по качеству, а другая инди на коленке...

TheDarkestRed TheDarkestRed

Обновили всё окружение и освещение 🎮🏔🌗
https://vk.com/the_darkestred

Dreaman Dreaman

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

Проект ...

...
Octomoon Octomoon

Нарисовали концепт-арт главного героя Fading Reality

Наша группа ВК: https://vk.com/Octomoon 🐙

Логотип проекта 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 не имел сразу доступа ко всем одиночкам и классам внутри него, а брал только те данные, которые ему нужны..

Справка