Pull to refresh

Tiling в 2D-играх на Unity, масштабирование материала

Reading time4 min
Views17K
При разработке 2D-игр на Unity часто возникает необходимость делать множество элементов различного размера из одного и того же материала. Самый простой пример – тайлы земли, травы, каменей и прочие элементы во всякого рода платформерах. Как правило, одинаковые тайлы по умолчанию используют один и тот же материал (в противном случае кол-во заранее созаднных материалов было бы чересчур большим). Часто делать каркас уровня из элементарных тайлов может быть неудобно из-за слишком большого кол-ва объектов на уровне, поэтому вместо элементарных тайлов используются большие тайлы – те же тайлы, только увеличенного масштаба. Допустим, нам надо поверх земли «посадить» два участка травы:

image



На этом рисунке поверх земли находятся 9 отдельных тайлов травы, размером 1х1. При помощи Ctrl+C, Ctrl+V восемь тайлов справа были успешно расставлены, но если на уровне должно быть порядка пары сотен тайлов травы, а самих уровней – несколько десятков, то расстановка одиночных тайлов будет отнимать слишком много времени. Оптимальным решением для такого рода задач является масштабирование одиночного тайла. Т.е. в данном случае нужно будет скопировать кусок травы и растянуть полученный объект по оси Х в восемь раз (например, в инспекторе редактора), получится такая штука:

image

Едва ли полученный результат можно назвать приемлемым. Для нормального масштабирования, помимо самого объекта, нужно изменить масштаб материала данного объекта (опять же в редакторе, во вкладке Inspector), а именно – параметр Tiling:

image

Получим следующий результат:

image

Как видите, блок из восьми тайлов выглядит как и задумывалось, а вот блок из одного тайла слева – сжался в восемь раз. Всё дело в том, что эти два объекта используют один и тот же материал, поэтому изменения материала одного объекта влекут за собой изменения материала другого объекта.
Данную проблему можно решить следующим путем: у каждого объекта есть компонента transform.sharedMaterial – это общий материал, а так же компонента transfrom.material – при изменении данной компоненты, создается копия используемого объектом материала, которую данный объект и будет в дальнейшем использовать, и которую можно изменять, при этом не затронув другие объекты. Создадим простенький скрипт:

TexTilingScript.js

scaleMaterial();
function scaleMaterial() {

	//создадим новый материал и изменим его масштаб
	 renderer.material.mainTextureScale.x = transform.localScale.x;
	 renderer.material.mainTextureScale.y = transform.localScale.y;
	 
}


Данный скрипт создает и масштабирует новый материал для объекта во время инициализации объекта. Добавим данный скрипт обоим объектам. Если нажать Play, то получим следующий результат:

image

Казалось бы, жизнь прекрасна, но есть одно «но»: такой результат получается только во время «игры», во время редактирования по-прежнему всё печально:

image

Вполне ожидаемый результат, т.к. скрипт выполняется только во время запуска приложения. Для того, что бы материал правильно масштабировался и во время редактирования, надо добавить обработчик данного скрипта в редактор. Для этого создадим еще один скрипт в папке Assets/Editor:

TexTilingEditorScript.js

@CustomEditor(TexTilingScript)
class TexTilingEditorScript extends Editor {

    function OnSceneGUI  () {
	
		//получим ссылку на компоненту TexTilingScript для каждого объекта на сцене, который данную компоненту имеет
		var script = target as TexTilingScript;
		
		//создаём временный материал и изменяем масштаб материала объекта
		script.scaleMaterial();
	 
	 }
}


Данный скрипт будет вызывать функцию scaleMaterial для объекта, который имеет компоненту TexTilingScript, каждый раз, когда такой объект выделяется на экране (например, выбирается мышью), и сохраняет полученные изменения:

image

Всё хорошо, кроме одного – в Log-е появляется ошибка:
Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.
Дело в том, что изменяя материал объекта во время редактирования – мы создаем новый временный материал, который после запуска приложения будет удалён. Я искренне не понимаю, почему разработчики Unity оформили данный case в виде ошибки, а не в виде предупреждения, т.к. такой расклад вполне приемлем и не приводит к каким-то конфликтам. Но если кого напрягает красный цвет – можно последовать их же совету – для масштабирования можно использовать не renderer.material, а renderer.sharedMaterial – в таком случае во время редактирования выделенного объекта его пропорции будут правильными, но другие объекты с тем же материалом будут искажены, тем не менее такой вариант вполне можно считать примелимым и удобным. Добавим новую функцию scaleSharedMaterial() в TexTilingScript:

TexTilingScript.js

scaleMaterial();

function scaleMaterial() {

	//создадим новый материал и изменим его масштаб
	 renderer.material.mainTextureScale.x = transform.localScale.x;
	 renderer.material.mainTextureScale.y = transform.localScale.y;
	 
}

function scaleSharedMaterial() {
	
	//редактируем только общий материал, не создавая при этом нового
	 renderer.sharedMaterial.mainTextureScale.x = transform.localScale.x;
	 renderer.sharedMaterial.mainTextureScale.y = transform.localScale.y;

}


Теперь в скрипте TexTilingEditorScript изменим вызываемую ф-ю:


TexTilingEditorScript.js

@CustomEditor(TexTilingScript)
class TexTilingEditorScript extends Editor {

    function OnSceneGUI  () {
	
		//получим ссылку на компоненту TexTilingScript для каждого объекта на сцене, который данную компоненту имеет
		var script = target as TexTilingScript;	
		
		//изменяем масштаб общего материала объекта
		script.scaleSharedMaterial();
	 
	 }
}


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

Все скрипты написаны на JavaScript. Unity 3.3, Pro.
Tags:
Hubs:
+25
Comments9

Articles