Procedural Asset Management in Unity

We've looked previously at adding our own tools to Unity's editor; now, in this short tutorial, I'll introduce you to handling the assets by script in Unity. We'll manage paths, create prefab files, generate a texture and save it to an image. Finally we'll also create a material file that uses the generated image, and all this will be done by code.


Final Result Preview

Let's take a look at the final result we will be working towards:

Material with a texture assigned.

Step 1: Set Up the Project

Create an empty project; we won't be using anything fancy here so we shouldn't bother to import anything at all. Once that's done, create an editor script. Unity will let us use its editor classes only if we place our script in a folder named Editor. Since that doesn't exist in our project yet, we need to create it.

Editor folder created in the project view.

Now let's create a script inside it.

Editor script in our folder.

Step 2: Add a MenuItem

Let's clean up our script. Aside from the basic functionality, we also want to be able to use the editor classes. We need to be using UnityEditor and our script's class should extend the Editor class instead of MonoBehaviour like normal game objects do.

In our first function we'll be working with prefabs, let's call it a PrefabRoutine.

To easly execute this function from the editor, let's add it as a MenuItem.

Aside from letting the unity know that we want this function to be executable from the Examples->Prefab Routine", we also need to make this function static.

If you go back to the editor now (and refresh the menu), you'll notice that there's a new menu named Examples there.

Menu item added to the unity editor.

If you select the Prefab Routine nothing will happen since our function is empty.


Step 3: Create a Folder

To shape our project the way we want we need to know how to create folders so we can move stuff around. Creating a folder from the script is very straightforward, all we need to do is to let unity know where the folder should be placed. To create a folder we need to use AssetDatabase class.

"Assets" is the name of the parent folder of the directory we want to create. In our case it's the main project folder where all our assets are imported/created.

Note that you can also use the .NET Directory class. This will also let you delete, move or access the directories' files. To use this class you need to be using System.IO.

Each time you select the Prefab Routine from the editor, a new folder should be created and be visible in the project view.

Folders created in our script.

Step 4: Create a Prefab

To create a prefab we need to call EditorUtility.CreateEmptyPrefab(). The function takes the prefab's path as an argument.

Don't forget about the extension! Also, after we create the file we need to call AssetDatabase.Refresh(), so the unity is able to see it.

If we leave a constant path as an argument, each time we select our routine a new empty prefab will replace the old one. Let's assign each prefab to separate folder to counter that. To do this we need to save the most recently created folder to a string so we can use it as a path argument later. The CreateFolder function returns a GUID, which basically is the file's (or directory's) ID. There's a function that retrieves the path if we submit this ID. It's called GUIDToAssetPath; let's use it to get our folder's path.

Now let's use the path to direct the prefabs we are going to create to the most recently created folder.

You can test whether the created empty prefabs are packed in folders now.

Prefabs in the folders.

Step 5: Set the Prefab

If you create a prefab then you probably don't want to leave it empty because in that case it's pretty much useless. Let's set our prefab if there's any game object selected while our routine is executing. We'll the prefab to the selected object. To get the currently selected object we can use the Selection class which has a reference to it. To set the prefab we need to call ReplacePrefab().

If you run the routine with any game object selected now then you'll notice that the created prefab is automatically set.

Prefab set to the selected object.

That's it, we have created a custom routine for prefab creation, it's not very useful but you should be able to know how to do that now if there will be a need for such a thing in your project.

At the end I also want to mention that AssetDatabase also lets you move assets around, move them to trash or delete them by calling AssetDatabase.MoveAsset(), AssetDatabase.MoveAssetToTrash() and AssetDatabase.DeleteAsset() respectively. The rest of the functionality can be found at the AssetDatabase script reference page.


Step 6: Add Another Menu Item

Let's go to another example, this time we'll create a texture and a material programmatically. Let's call this menu item Material Routine.

Now we have two items to choose from in the Examples menu.


Step 7: Create a Texture

Let's create a Texture2D and set its size to (256, 256) for this example.

Now we shouldn't let all those pixels go to waste, so let's set the texture's pixels according to some kind of thought-up formula. For that we'll need two for loops to go through every pixel. To set the each pixel's color we need to call SetPixel() which takes the position of the pixel on a texture and its color as the arguments.

To assign the color we'll use the Mathf.Sin() function. The Color class can be initialized with three floats, corresponding to the red, green and blue color components, respectively. The max allowed value is 1 and min is 0, so the Sin() function suits our needs perfectly.

It doesn't matter what we submit to the Sin() function, but to get something more interesting we should give a value that changes for each pixel.


Step 8: Create an Image

Now let's create an image from the texture we just created. Since we'll be writing to a file in binary mode, we need to be using System.IO, so let's add it to the top of our script.

To save our texture as a PNG image we first need to call EncodeToPNG() which will return an array of bytes that the PNG file consists of.

Now that we've got our pngData we can write it to a file and create a PNG image in this way.

Since we create the file at a constant path, each time we'll run MaterialRoutine(), the texture will get overwritten.

And since we've got our image, we don't need the generated texture anymore as it won't be referencing an image anyway. Let's destroy it.

Also, we need to let Unity update the project view and file references; to do that we need to call AssetDatabase.Refresh().

Let's test whether the texture gets created when we execute our routine.

The generated texture.

Step 9: Create a Material

We've got an image and now we can create a material that uses it as a texture. Let's create a new Material.

The created material will use a Diffuse shader. To save this material to the file, we can call AssetDatabase.CreateAsset(). This function takes an asset as the first argument, and the path as the second one.

If you run our routine now, you'll see that the material is created.

Material without assigned texture.

As you can see everything is correct, its name is New Material and it uses Diffuse shader, but there's no texture assigned to it.


Step 10: Assign the Texture

First we need to get a reference to the material we just created. We can get that by calling AssetDatabase.LoadAssetAtPath() which loads the asset and returns its reference.

Now let's assign our generated texture as the main texture of the material. We can get the texture reference of the generated texture in the same way we got the material reference.

To see the results, run the Material Routine.

Material with assigned texture.

As you can see, the material has the texture assigned now.

Conclusion

That's the end of the introduction to manage your assets using scripts. If you want to expand your knowladge on the topic you can visit the Unity Editor Classes reference page, particularly the AssetDatabase script reference is worth looking into. If you need to work at a low level, you should also read the docs on System.IO to get more information on its classes and how you can use them. Thanks for your time!

Tags:

Comments

Related Articles