All streams
Search
Write a publication
Pull to refresh
55
0

Пользователь

Send message
Насколько я знаю, в Unity API ограниченные возможности по пре/после-процессингу билда.
Я разбирался как раз для этого ассета, там ситуация такая: пост-процессинг есть и работает во free версии. Есть два его варианта, один (PostProcessSceneAttribute) срабатывает при запуске сцены в редакторе (почему-то после Awake), и я его использую в своем ассете, чтобы пересобирать xml со списком сцен. А второй вариант — после билда (PostProcessBuildAttribute). И его у меня использовать не получается, так как собранный билд уже проблематично переделывать, особенно, если это apk или ipa. Поэтому пришлось добавить пункт меню, чтобы вручную пересоздавать xml.

В BuildPipeline метод BuildPlayer, к сожалению, pro-only. Кроме того, как я понимаю, это обходной путь, чтобы создавать собственную последовательность сборки и для нее нужно делать отдельную кнопку или пункт меню. А стандартный билд по-прежнему будет собираться, как и раньше. Думал, может у них что-то поприличнее сделано, но вроде нет.

Выполнение скриптов в редакторе тоже не спасет, так как они выполняются иначе, чем в рантайме (например, Update вызывается только при изменении сцены).

А вот зато можно написать скрипт редактора для SpriteRenderer. Например, вот такой скрипт не дает изменить спрайт:
Код
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(SpriteRenderer))]
public class SpriteRendererEditor : Editor
{
	Sprite _oldSprite;

	void OnEnable()
	{
		var sr = (SpriteRenderer)target;
		_oldSprite = sr.sprite;
	}

	public override void OnInspectorGUI ()
	{
		var sr = (SpriteRenderer)target;
		DrawDefaultInspector ();
		if (_oldSprite != null && sr.sprite != _oldSprite)
			sr.sprite = _oldSprite;
	}
}

Таким макаром мы можем отлавливать момент установки пользователем текстуры. А зная, в каком разрешении сцена, можно сразу текстуру поменять. К сожалению, это не покрывает use case'ы, когда пользователь изменяет разрешение сцены или когда он сначала сделал сцену, а потом добавил текстуры для других разрешений. То есть, все равно придется делать кнопку, которую нужно нажать перед билдом.

А еще, кстати, есть префабы, и это вообще отдельный разговор…

Кстати, есть еще решение для свапинга атласа спрайтов с помощью NGUI, нашел я его на lynda.com, но я думаю в интернете это где-то гуляет.
Я так думаю, Вы об этом. Идея вроде бы та же, что и у Вас, меняются атласы в рантайме, но только средствами NGUI.
Просто до того, как писать этот ассет, я работал на проекте (довольно успешная мобильная игра, но не на Unity), где было пять версий сцен для разных разрешений. Именно версий, так как сцены различались кардинально: для телефонов это был портрет, для планшетов — ландшафт, разные элементы UI, + сам игровой процесс отличался, даже количество сцен было разным и т.п. Отсюда и растут уши.

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

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

Мне интересно как реализуется данная фича в 2d Toolkit.
С 2d toolkit я не работал, но, насколько я понимаю, у них сделан собственный компонент спрайта. Атласы текстур хранятся в ресурсах, также как у Вас, но при этом грузится всегда только нужный, как в кокосе. Правда, я могу и ошибаться. Подобная идея мне не очень нравится, так как в Unity уже есть вполне приличный спрайт, которым люди пользуются, в том числе и я.
Спасибо за статью. Я некоторое время назад делал ассет для Unity, который грузит разные сцены в зависимости от разрешения экрана.

Для этого тоже используются суффиксы (у файлов сцены), но вида ".1024x768", плюс еще генерируется xml со списком сцен (так как в рантайме список получить проблематично). Алгоритм выбора у меня посложнее, если интересно, то можно посмотреть на github (много кода).

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

В общем, Вы меня заставили задуматься о добавлении нового функционала к моему проекту :-)
Возможно. Однако, на мой взгляд, лучше было бы что-то человекочитаемое вместо математикочитаемого.
Да, я как раз остановился на том, что узнал, что синтаксис elm основан на Haskell, а учить один язык, чтобы выучить другой язык у меня желания не оказалось.
Я где-то полгода назад смотрел elm, просто для общего развития. Мучил его целую неделю, но так ничего сам на нем написать и не смог (опыт ФП у меня никакой, только лямбды в C-подобных языках). Самый частый вопрос, который у меня возникал в процессе: «что означает вот этот символ?». То есть, до, собственно, программирования, каких-то алгоритмов и т.п. я так и не добрался, так как ответ на этот вопрос в половине случаев найти не удавалось. Так я elm и забросил.
В некоторых испаноязычных странах до сих пор различают 2 профессии: programador и analista programador. Первый — это как раз человек, занимающийся вбиванием исходных кодов, второй — это то, что называем программистом мы: человек, занимающийся решением задачи: выработкой ТЗ на основе полученного задания, разработкой алгоритмов решения задачи, созданием архитектуры программы для решения задачи и, наконец, собственно, кодированием.
А еще, если...
Подобные проблемы решаются с помощью Dependency Injection или Inversion of Control
Inversion of Control (на английском, пример на C#)
… есть простая библиотечная функция...

Если функция библиотечная (то есть а. Вы не можете ее изменять и б. она уже стабильна) то тестировать ее не нужно. Если же функция Ваша, то тестируется логика работы самой функции. А реальный код удаления для теста подменяется при помощи DI или IoC. Для отдельно стоящей функции — это, например, может быть еще один параметр с объектом (или указателем на функцию), ответственным за реальное удаление. Для класса все красивее (смотрите видео).
Я начинала проект «Игра за неделю» для того, чтобы найти мотивацию.

Для того чтобы найти мотивацию можно обратиться к психотерапевту, почитать книжки по мотивации, почитать книжки по психологии. Можно поинтересоваться, чем мотивировались различные успешные люди (ну или неуспешные, если цель — быть неуспешным). Но делать для этого игры? Так и представляется психолог, который еженедельно «вылечивает» по пациенту с целью, например, создать игру для айфона. Потому и фейл в итоге.

А так да, круто, преодоление себя, все такое. Но как-то оно глупо.
Тоже заинтересован, выкладывайте в стор. Только с ценой не наглейте :-)
Написал в личку, хоть и не совсем вопросы. Насчет [&] — Вы не правы.
Если уж создавать подобный макрос, то нужно гарантировать, что аргументы гарантированно используются ровно 1 раз

Благодаря lemelisk удалось решить проблему при помощи лямбд. Теперь макрос подставляет лямбду, и «передает» в нее параметры. Собственно, получилось как раз то, о чем я сам же и писал в статье: «логику макросов перенести в функции, а сами макросы сделать ответственными только за передачу данных в эти функции».

или что лучше — написать шаблонный метод.

У шаблонного метода мне не нравится то, что нужно передавать указатель на метод.
Добавил в статью. Спасибо.
И void версия:
#define safeCallVoid(objectPointer, methodWithArguments)\
    [&](const decltype(objectPointer) pointer)\
    {\
        if(pointer)\
            (pointer->methodWithArguments);\
    }\
    (objectPointer)
Вызов лучше вот так:
((defaultValue), (objectPointer))
Интересная штука. У Страуструпа в статье много упоминаний про замену макросов на лямбды, но вот такого симбиоза там вроде нет.
Пришлось немного переделать, чтобы заработало у меня в XCode:
#define safeCall(defaultValue, objectPointer, methodWithArguments)\
    [&](const decltype(defaultValue)& defaultRetval, const decltype(objectPointer) pointer)\
    {\
        return pointer ? (pointer->methodWithArguments) : defaultRetval;\
    }\
    (defaultValue, objectPointer)

struct Test {
    int test(int value) { return value; }
};

int main(int argc, const char* argv[])
{
    Test* ptr1 = nullptr;
    Test* ptr2 = new Test;
    auto def = 0;
    int param = 20;
    if(0 == safeCall(def, ptr1, test(param)))
        std::cout << safeCall(0, ptr2, test(param)) << std::endl;

    return 0;
}
«extention methods» давно напрашивались, учитывая изначальные идеи STL (внешние функции для работы с объектами, отсутствие виртуальных деструкторов). Но вот что с их помощью будет можно решить эту задачку, я все же сомневаюсь. Но, конечно, будем посмотреть.
Хотя, не так уж и страшно, типы автоматически выводятся… Вы вот такое имели в виду?
template<typename ReturnType, class Class, typename... ParameterTypes>
static inline ReturnType safeCall(const ReturnType& defaultValue, Class *ptr, ReturnType (Class::*Method)(ParameterTypes...), ParameterTypes... arguments)
{
    if(ptr)
        return (ptr->*Method)(std::forward<ParameterTypes>(arguments)...);

    return defaultValue;
}

// вызов
auto x = safeCall(defaultRetval, pointer, &Class::method, param);
А не поделитесь? Я попробовал, у меня получился страшный Франкенштейн.
Я не подразумевал демонстрировать там идеальный макрос умножения. Но, на всякий случай, поправил. Спасибо.

Information

Rating
Does not participate
Location
Россия
Registered
Activity