[Unity, C#, Разработка игр] Как мы переосмыслили работу со сценами в Unity
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Unity, как движок, имеет ряд недостатков, но которые благодаря возможностям для кастомизации и инструментам для кодогенерации, можно решить. Сейчас я вам расскажу о том, как мы написали плагин для Unity на основе пост-процессинга проектов и кодогенератора CodeDom.ПроблемаВ Unity загрузка сцен происходит через строковой идентификатор. Он не стабильный, а это означает, что он легко изменяем без явных последствий. Например, при переименовании сцены всё полетит, а выяснится это только в самом конце на этапе выполнения. Проблема обнаруживается быстро на часто используемых сценах, но может быть трудно обнаруживаемая, если речь идёт о небольших additive сценах или сценах, которые используются редко. РешениеПри добавлении сцены в проект, генерируется одноимённый класс с методом Load. Если мы добавим сцену Menu, то в проекте сгенерируется класс Menu и в дальнейшем мы можем запустить сцену следующим образом:
Menu.Load();
Да, статический метод - это не лучшая компоновка. Но мне показалось это лаконичным и удобным дизайном. Генерация происходит автоматически, исходный код такого класса:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace IJunior.TypedScenes
{
public class Menu : TypedScene
{
private const string GUID = "a3ac3ba38209c7744b9e05301cbfa453";
public static void Load()
{
LoadScene(GUID);
}
}
}
По-хорошему, класс должен быть статическим, так как не предполагается создание экземпляра из него. Это ошибка, которую мы исправим. Как видно уже из этого фрагмента, кода мы цепляемся не к имени, а к GUID сцены, что является более надежным. Сам базовый класс выглядит вот так:
namespace IJunior.TypedScenes
{
public abstract class TypedScene
{
protected static void LoadScene(string guid)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
SceneManager.LoadScene(path);
}
protected static void LoadScene<T>(string guid, T argument)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
UnityAction<Scene, Scene> handler = null;
handler = (from, to) =>
{
if (to.name == Path.GetFileNameWithoutExtension(path))
{
SceneManager.activeSceneChanged -= handler;
HandleSceneLoaders(argument);
}
};
SceneManager.activeSceneChanged += handler;
SceneManager.LoadScene(path);
}
private static void HandleSceneLoaders<T>(T loadingModel)
{
foreach (var rootObjects in SceneManager.GetActiveScene().GetRootGameObjects())
{
foreach (var handler in rootObjects.GetComponentsInChildren<ISceneLoadHandler<T>>())
{
handler.OnSceneLoaded(loadingModel);
}
}
}
}
}
В этой реализации видна ещё одна фишка - передача параметров сценам. Параллельно с решением первой проблемы - избавить код от строковых идентификаторов сцены (в том числе и от констант, которые нужно обновлять вручную), мы добавили еще и параметризацию сцен. Теперь сцена может задекларировать список параметров, а вызывающий код должен передать для них аргументы. Например, сцена Game хочет получить перечисление, содержащее всех игроков и при инициализации добавить для них аватаров. В таком случае мы можем сами создать такой компонент.
using IJunior.TypedScenes;
using System.Collections.Generic;
using UnityEngine;
public class GameLoadHandler : MonoBehaviour, ISceneLoadHandler<IEnumerable<Player>>
{
public void OnSceneLoaded(IEnumerable<Player> players)
{
foreach (var player in players)
{
//make avatars
}
}
}
После размещения его на сцене и последующим её сохранением, генератор добавит в класс сцены метода запуска сцены с обязательной передачей перечисления игроков.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace IJunior.TypedScenes
{
public class Game : TypedScene
{
private const string GUID = "976661b7057d74e41abb6eb799024ada";
public static void Load(System.Collections.Generic.IEnumerable<Player> argument)
{
LoadScene(GUID, argument);
}
}
}
В данный момент реализована возможность перегрузки обработчиков. Т.е. если на сцене будет N обработчиков с разными параметрами, под них создастся N методов запуска. Также не запрещается наличие нескольких компонентов-обработчиков с одинаковыми параметрами. Это не фишка, а скорее недоработка, так как такой функционал быстрее создаст путаницу, нежели будет полезен.А почему не сделать через N? Первую версию плагина я осветил на своём YouTube канале в этом видео.Извините, данный ресурс не поддреживается. :( Там задали ряд вопросов и предложили альтернативные решения. Есть интересные подходы, которые имеют право на жизнь, а есть откровенно идиотские, которые я хочу разобрать. Чем плох статический класс с полями, через которые передаются данные для сцены?Нередко встречаю и такое. Речь идёт о классе по типу этого:
public class GameArguments
{
public IEnumerable<Player> Players { get; set; }
}
А уже внутри сцены, вероятно, будет группа компонентов, которая достаёт данные из свойств. Основная проблема в том, что нет формализации: при загрузки сцены не совсем ясно, что нам нужно предоставить для корректной работы. Получателей в целом определить будет проще, так как мы можем воспользоваться поиском по ссылке. Ну и опять же сцену придётся запускать по ID или имени. Чем плох PlayerPerfsПредлагали и такой экзотический вариант. Можно было бы начать с того, что PlayerPrefs вообще не предназначен для передачи значений внутри одного инстанса. Этим можно было бы и закончить, но продолжим критику тем, что вам также придётся работать с неформальными строковыми идентификаторами параметров. Параллель с ASPNetМне хотелось получить что-то схожее с строго типизированными View из ASPNet Core. Мы считаем плохим тоном использовать ViewData и стараемся определять ViewModel. В Unity хочется что-то такого же толка с теми же преимуществами.Отличие Unity в первую очередь в том, что сцена - это обычно более громоздкое предприятие, нежели View в ASPNet. Это решается разбивкой одной сцены на несколько подсцен с режимом загрузки Additive (наш плагин, к слову, его поддерживает), что позволяет скомпоновать сцену из сцен поменьше со своими более атомарными моделями. Но такой подход не очень распространён, к сожалению, и на это, я думаю, есть свои причины. Где скачать Плагин мы сделали в паре с Владиславом Койдо в рамках Proof-of-concept. Он ещё не стабилен и не обкатан как следует, но с ним уже можно поиграться. Репозиторий на GitHub - https://github.com/HolyMonkey/unity-typed-scenesЕсли вам интересно, я попрошу Владислава в следующей статье рассказать, как он работал с Code Dom в Unity и как работать с пост-процессингом на примере того, что мы сегодня обсуждали.
===========
Источник:
habr.com
===========
Похожие новости:
- [Godot, Разработка игр] Механики для реализации платформера на Godot engine. 3 часть
- [Информационная безопасность] Android Guards. История создания, развития и первый meetup
- [C++, CGI (графика), Программирование, Работа с 3D-графикой, Разработка игр] Vulkan-tutorial. Урок 1.1 — Вступление (перевод)
- [Высокая производительность, D] Независимый HttpBench для D, или врут ли тесты TechEmpower? (перевод)
- [.NET, Visual Studio, Unity] Sheduler удобный расспорядок вызова функций, моя система кондиций
- [Unity, Разработка игр] Database using ScriptableObjects with save/load system (перевод)
- [CGI (графика), Unity, WebGL, Работа с 3D-графикой] Оптимизация 3D-графики под WebGL (опыт PLANT-SIM)
- [.NET, ASP, Microsoft SQL Server, C#, Облачные сервисы] Как выбрать инструмент для бизнес-анализа
- [Игры и игровые приставки, Разработка игр] Бесплатная онлайн-конференция «Хочу в геймдев»
- [Разработка игр, Управление персоналом, Карьера в IT-индустрии, Удалённая работа] Один день в офисе будущего
Теги для поиска: #_unity, #_c#, #_razrabotka_igr (Разработка игр), #_unity, #_unity3d, #_csharp, #_gamedev, #_unity, #_c#, #_razrabotka_igr (
Разработка игр
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 26-Ноя 14:00
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Unity, как движок, имеет ряд недостатков, но которые благодаря возможностям для кастомизации и инструментам для кодогенерации, можно решить. Сейчас я вам расскажу о том, как мы написали плагин для Unity на основе пост-процессинга проектов и кодогенератора CodeDom.ПроблемаВ Unity загрузка сцен происходит через строковой идентификатор. Он не стабильный, а это означает, что он легко изменяем без явных последствий. Например, при переименовании сцены всё полетит, а выяснится это только в самом конце на этапе выполнения. Проблема обнаруживается быстро на часто используемых сценах, но может быть трудно обнаруживаемая, если речь идёт о небольших additive сценах или сценах, которые используются редко. РешениеПри добавлении сцены в проект, генерируется одноимённый класс с методом Load. Если мы добавим сцену Menu, то в проекте сгенерируется класс Menu и в дальнейшем мы можем запустить сцену следующим образом: Menu.Load();
//------------------------------------------------------------------------------
// <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace IJunior.TypedScenes { public class Menu : TypedScene { private const string GUID = "a3ac3ba38209c7744b9e05301cbfa453"; public static void Load() { LoadScene(GUID); } } } namespace IJunior.TypedScenes
{ public abstract class TypedScene { protected static void LoadScene(string guid) { var path = AssetDatabase.GUIDToAssetPath(guid); SceneManager.LoadScene(path); } protected static void LoadScene<T>(string guid, T argument) { var path = AssetDatabase.GUIDToAssetPath(guid); UnityAction<Scene, Scene> handler = null; handler = (from, to) => { if (to.name == Path.GetFileNameWithoutExtension(path)) { SceneManager.activeSceneChanged -= handler; HandleSceneLoaders(argument); } }; SceneManager.activeSceneChanged += handler; SceneManager.LoadScene(path); } private static void HandleSceneLoaders<T>(T loadingModel) { foreach (var rootObjects in SceneManager.GetActiveScene().GetRootGameObjects()) { foreach (var handler in rootObjects.GetComponentsInChildren<ISceneLoadHandler<T>>()) { handler.OnSceneLoaded(loadingModel); } } } } } using IJunior.TypedScenes;
using System.Collections.Generic; using UnityEngine; public class GameLoadHandler : MonoBehaviour, ISceneLoadHandler<IEnumerable<Player>> { public void OnSceneLoaded(IEnumerable<Player> players) { foreach (var player in players) { //make avatars } } } //------------------------------------------------------------------------------
// <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace IJunior.TypedScenes { public class Game : TypedScene { private const string GUID = "976661b7057d74e41abb6eb799024ada"; public static void Load(System.Collections.Generic.IEnumerable<Player> argument) { LoadScene(GUID, argument); } } } public class GameArguments
{ public IEnumerable<Player> Players { get; set; } } =========== Источник: habr.com =========== Похожие новости:
Разработка игр ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 26-Ноя 14:00
Часовой пояс: UTC + 5