Tiling in Unity 2D games, material scaling

When developing 2D games on Unity, it is often necessary to make many elements of different sizes from the same material. The simplest example is the tiles of the earth, grass, stones and other elements in all kinds of platformers. As a rule, identical tiles use the same material by default (otherwise, the number of previously created materials would be too large). Often, making a level frame from elementary tiles can be inconvenient due to too many objects on the level, so instead of elementary tiles, large tiles are used - the same tiles, only on an enlarged scale. Suppose we need to "plant" two grass sections on top of the ground:

image



In this illustration, on top of the ground are 9 separate grass tiles, 1x1 in size. Using Ctrl + C, Ctrl + V, the eight tiles on the right were successfully placed, but if the level should be on the order of a couple of hundreds of grass tiles, and the levels themselves should be several tens, then arranging single tiles will take too much time. The best solution for this kind of task is to scale a single tile. Those. in this case, you will need to copy a piece of grass and stretch the resulting object along the X axis eight times (for example, in the editor’s inspector), you get the following thing: You can

image

hardly call the result acceptable. For normal scaling, in addition to the object itself, you need to change the scale of the material of this object (again in the editor, in the Inspector tab), namely the Tiling parameter:

image

We get the following result:

image

As you can see, a block of eight tiles looks as intended, but a block of one tile on the left is compressed eight times. The thing is that these two objects use the same material, so changes in the material of one object entail changes in the material of the other object.
This problem can be solved in the following way: each object has a transform.sharedMaterial component - this is a common material, as well as a transfrom.material component - when this component is changed, a copy of the material used by the object is created, which this object will continue to use, and which can be changed without affecting other objects. Let's create a simple script:

TexTilingScript.js

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


This script creates and scales new material for the object during the initialization of the object. Add this script to both objects. If we press Play, we get the following result:

image

It would seem that life is beautiful, but there is one “but”: this result is obtained only during the “game”, while editing, everything is still sad:

image

It is the expected result, because the script is executed only during application launch. In order for the material to scale correctly during editing, you need to add the handler of this script to the editor. To do this, create another script in the Assets / Editor folder:

TexTilingEditorScript.js

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


This script will call the scaleMaterial function for an object that has a TexTilingScript component, each time such an object is selected on the screen (for example, is selected with the mouse) and saves the changes:

image

Everything is fine, except for one - an error appears in the 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.
The fact is that changing the material of the object during editing - we create new temporary material, which will be deleted after the application starts. I sincerely do not understand why Unity developers designed this case as an error, and not as a warning, because this alignment is quite acceptable and does not lead to any conflicts. But if someone is annoyed by the red color - you can follow their own advice - to scale, you can use not renderer.material, but renderer.sharedMaterial - in this case, while editing the selected object, its proportions will be correct, but other objects with the same material will be distorted, Nevertheless, this option can be considered as acceptable and convenient. Add a new scaleSharedMaterial () function to 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;
}


Now in the TexTilingEditorScript script, change the called f-th:


TexTilingEditorScript.js

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


Now the “error” does not appear, but in order for the material of the object to scale correctly during editing, you must first select it. Also a very acceptable option.
Well, another option for solving this problem is to create your own material for each object in advance. But not everyone will like to have hundreds of pre-created materials in the project source, when you can do just one, so using scripts is one of the best solutions.

All scripts are written in JavaScript. Unity 3.3, Pro.

Also popular now: