Как стать автором
Обновить

Комментарии 26

Делали аналогичное для хидден-игры, надо было чтобы спрайт по которому кликнули распадался на пикселы и превращался в партикл систему.
А не поломается вся схема если спрайты в атласы сложить, uv координаты ведь придется считать относительно позиции спрайта в атласе?
Второе, просто уточнение, мало ли кто не знает, что isReadable текстура в памяти занимает в два раза больше места.
Третье, осветление записью в текстуру — это наверно не очень вариант, приходится оперировать большими данными в памяти, проще решить это шейдером и/или цветом спрайтов. Но как информация — несомненно полезно.
На счет игры, было бы очень интересно посмотреть, как вы это реализовали)
Про атласы вопрос заставил врасплох, раньше не приходилось с ними работать( к сожалению), но на скорую руку собрал атлас из стран. И да, вы правы, схема ломается. Данный метод работает только, если текстурки отделены друг от друга.
С шейдерами тоже пока углубленно не сталкивался, слишком зелен для этого.
Делал это в игре Dreamwoods 2, на ютубе можно найти трейлеры, там будет этот эффект (правда художник все упростил и заменил разноцветные частицы одним цветом). Делается просто. Готовится партикл система с нужными настройками физики. Затем с определённым шагом читаем пикселя со спрайта и по соответствующим координатам эмитируем партикл цвета этого пикселя, и запускаемых систему. Мне там ещё приходилось заставлять партикли лететь и складываться в картинку в другом месте.
В общем, на счет атласов, провел я не большое исследование, и сразу хочу сказать огромное спасибо, что направили в эту сторону! Уменьшились DrawCalls, да и памяти текстуры стали меньше кушать. Раньше я подозревал, что атласы использовать мудро, но слабо представлял почему.
На счет работы схемы, нужно дописать пару строк, что бы все работало так же прекрасно, а именно:

    private bool IsAlphaPoint(PointerEventData eventData)
    {
        Vector2 localCursor;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(), eventData.position, eventData.pressEventCamera, out localCursor);
        Rect r = RectTransformUtility.PixelAdjustRect(GetComponent<RectTransform>(), GetComponent<Canvas>());
        Vector2 ll = new Vector2(localCursor.x - r.x, localCursor.y - r.y);
        //Найдем пиксель где начинается спрайт нужной страны в атласе - локальный (0,0)
        Vector2 pixelStart=new Vector2(CountryImg.sprite.texture.width*CountryImg.sprite.uv[2].x, 
                                        CountryImg.sprite.texture.height*CountryImg.sprite.uv[2].y);
        int x = (int)(ll.x / r.height * CountryImg.sprite.textureRect.height );
        int y = (int)(ll.y / r.height * CountryImg.sprite.textureRect.height);
        //вычесляем координаты относительно атласа
        pixelStart+=new Vector2(x,y);
        print("itog:" + pixelStart.x + " " + pixelStart.y);
        return IsAlphaPoint((int)pixelStart.x, (int)pixelStart.y);
    }


Правда есть один нюанс касательно чтения пикселей из атласа. Если формат атласа используется Compressed(как я понял все разновидности) то будет возникать такие исключения:

x == 0 && y == 0 && blockWidth == dataWidth && blockHeight == dataHeight
UnityEngine.Texture2D:GetPixel(Int32, Int32)


Следствием чего работать не будет (ну еще бы оно с ошибками работало…).
Это очень печальное ограничение, текстура с форматом ARGB32 например будет занимать 16 Мб…а в памяти и того больше.
Но благо есть ARGB16… в общем поджал атлас до 2 Мб. Смотрится хорошо, все работает, как надо.
А я использовал политическую карту мира. Рисовал обычную, а страну определял по цвету пикселя соотвествующего на политической карте мира. Выбрать можно было любую страну с точностью до пиксела. Довольно удобно и чертовски просто.
Да, это упростило бы задачу, если бы не концепция дизайна игры.
Для вашего случая можно было бы сделать подобно — обычная карта (одно цельное изображение с нормальными цветами), отображаемая на экране, и цветная (также одно цельное изображение), с которой будет происходить внутренняя работа. Проверка страны по координатам — посмотреть смещение относительно левого верхнего угла карты на экране и взять цвет пикселя с таким же смещением в раскрашенной карте. Таким образом узнали, что за страна. Для подсвечивания определённой страны, можно использовать фрагментный шейдер, в который дополнительно передавать текстуру раскрашенной карты и дополнительные параметры — цвет страны, которую нужно подсвечивать и тип подсвечивания. А шейдер проверяет цвет пикселя в раскрашенной карте и в зависимости от этого, либо рисует просто, либо с подкрашиванием, либо не рисует вообще (так можно сделать вариант, что рисоваться будет только одна указанная страна с необходимым подсвечиванием) — то есть, цветная карта используется просто как маска.
ещё один нюанс, совсем маленький. Не стоит использовать сравнение флотов на равенство «color.a ==0f». Рискуете получить непредсказуемые результаты на разных машинах, правильнее обращаться к Mathf.Approx (вроде так). Или сравнивать "< 0.01f" или "< Mathf.Epsilon"
Учту, спасибо!
Вы меня простите, но все чему вы «учите» в этой статье — категорически неверно и нужно бить по рукам каждый раз, когда кто-то делает нечто подобное.

1. Texture read/write очень сильно потребляет расход памяти, делает невозможным использование сжатия текстур, да и вообще — работает очень медленно. Для целей определения клика отлично подходит система полигональных коллайдеров.

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

В результате вы написали тонну лишнего кода (все эти беганья по текстурам и сравнивание пикселей), где можно было обойтись одной строчкой кода «получить объект под мышкой». Да, придется обвести коллайдером спрайт. Но это не так страшно и быстро. Да и если очень хочется, есть автоматические тулзы.
1. Как я думаю, не обязательно это должно быть верным, если есть Texture read/write значит это зачем то нужно, и это нужно использовать. Пожалуйста, разъясните, почему и как это делает невозможным, сжимать текстуры, я сжал атлас стран до 2 Мб, при этом они имеют презентабельный вид, даже лучше чем раньше, до написания статьи. Да и на производительность, я не заметил, что сказывается. Если имеется ввиду что нельзя применить формат Compressed, то да я с вами согласен, нельзя. Но для моего случая это оказалось не критичным.

2.Как я уже говорил в сторону шейдеров, возможности посмотреть не было, и да по примерам, это действительно было бы лучше. Но я привел все это как пример, мало ли кому пригодиться.
Зачем и кому это (read/write) нужно — я написал. Это случай на самый-самый край, который используется только когда никак иначе нельзя. Вы прививаете себе, в главное — всем читающим изначально неверные привычки. Вы увеличили расход памяти в разы (если сравнивать со сжатым атласом, то получится раза в 4-8, в зависимости от формата сжатия), вы понизили производительность на порядок, и все это без какой-либо необходимости делать именно так. Кроме того, вы написали 3 десятка строк там, где можно было обойтись одной. Если вы говорите «я не почувствовал», то это исключительно из-за размера проекта.

Хотите дальше забавать гвозди микроскопом — ок, я не буду переубеждать.

Я лишь буду надеяться, что эта статья никому не пригодится и никто не воспримет ее как руководство к действию :)
Спасибо, за ваши замечания. Я понимаю, что у меня получилось не превосходное решение, однако учитывая мой опыт и сжатые сроки, это оптимальное решение.
Я же все таки, надеюсь, что статья кому-то, да пригодиться, хотя бы в плане ознакомления.
Есть GraphicRaycaster, не надо коллайдеров.
Тем более :)
Мне просто пока не пришлось глубоко сталкиваться с новым uGUI, я старообрядец.
Если не секрет, откуда вы взяли сами картинки (карту)?
Работа арт-отдела компании, в которой я работаю.
Можно вообще оттрассировать контуры стран в 2д/меш-колайдеры, а карту держать одной картинкой — максимальная производительность по рендеру, мало кода, неплохая производительность по физике на unity >=5.0.
Прочитал первый абзац по диагонали, пропустил про выделение цветами. Если нужно выделение территории цветом, то к предыдущему комменту можно добавить следующее — раз уже есть мешколайдеры, то при детекте фокуса достаточно брать этот меш и рендерить его с полупрозрачностью поверх существующей карты с подкраской нужным цветом. Блендинг меша одной страны будет все-равно дешевле карты, работающей 100% в транспаренте.
Какой то список вредных советов, не иначе. Первая картинка особенно пугает. Использовать канвас для такой задачи просто странно. Я бы сделал все так:
1. Страны — это спрайты, рендерятся СпрайтРендерами без всяких канвасов.
2. К СпрайтРендерам добавляется ПолигонКоллайдер2Д и он автоматически принимает форму спрайта, ничего не нужно делать вручную. Если сгенерился не очень аккуратный, можно сразу подредактировать.
3. Вешаем на этот спрайт свой скрипт, он имплементирует iPointerClickHandler.
4. На камеру вешаем PhysicsRaycaster2D, в сцену добавляем объект EventSystem. После этого скрипт карты начинает принимать сообщения о кликах.
5. Если нужно подсветить какую то страну — пишем простенький шейдер и накладываем материал на этот спрайт.

В итоге кода то и не будет в программе. Только обработчик клика. Ну и шейдер осветления, если действительно нужен.
Read/Write — не просто так вынесли в дополнительные настройки — его не стоит использовать просто так.
1. Канвас использовал, как удобный инструмент для соединения стран в одну карту.
2. Редактировать я замучаюсь каждый раз. Мне проще пару строк кода написать.
3-4. Судя по вашей логике, чем вам тогда OnMouseDown не угодил?
5. Согласен, шейдер бы здесь помог, к сожалению я их еще не познал. Надеюсь, что вы посоветуете некоторые источники по их изучению.
Ответил ниже, случайно
Может я просто описал не очень хорошо. Некоторые вещи проще один раз показать:
www.dropbox.com/s/pl7l5i5hsck2yjg/2015-06-22_22-37-36.mp4?dl=0
1. Не вижу сложности при соединение стран — спрайтов. Да и особой разницы канвас или спрайт в этом случае тоже.
2. Так редактировать то ничего и не нужно. Ну если только сгенеренный коллайдер не устроит. Так это ж наоборот плюс. По некоторым маленьким странам очень солжно попасть. И область клика у них явно нужно делать больше, чем сама страна.
3-4. Я просто решил, что вам удобно пользоваться PointerClick. Ну и удобно все таки, тут вам и драг энд дропы и все остальное.
5. Самое верное — скачать стандартные шейдеры и изучать их. Удобно, так как достаточно лишь чуть изменить код и у вас уже готовое решение. В частности стандартный шейдер спрайта советую брать за базовый. Ну и литературу тоже полезно читать, но тут уж без конкретики, что найдете. Разницы нет особой.
1. Просто страны, к сожалению, нарисованы немного коряво, приходится подгонять размеры по реальной карте. Для Image не нужно менять Scale, на сколько я знаю если использовать SpriteRenderer и разный scale, то автоматический батчинг работать не будет, поправьте меня, если я не прав.
3-4. Вы правы, использование PointerClick дает много возможностей, однако, мне в данной задаче нужно только нажатие.
5. Спасибо, буду изучать.
то автоматический батчинг работать не будет

Не уверен насчет SpriteRenderer (не использую штатный гуй / 2д), но батчинг для мешей работает на разном скейле (за исключением отрицательного) на unity >=5.0. Ну и да, спрайты должны лежать в одном атласе — батчинг происходит по материалам.
Батчинг работать будет. Canvas и Image созданы для удобства создания интерфейсов и выйгрыша в скорости в большинстве случаев по сравнению со спрайтами не дадут. Ну и странно немного слышать аргументы о производительности после настолько неоптимального использования ресурсов(read/write текстуры, ReadPixels/SetPixels и пр.) и аргументов о том, что это позволительно, так как производительности хватает.

А расставлять страны вручную, да еще и со скейлом это вообще неблагодарное дело.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации