Procedural Asset Management in Unity

Vi har tidigare tittat på att lägga till egna verktyg till Unitys redaktör; Nu, i den här korta handledningen presenterar jag dig för att hantera tillgångarna med script i Unity. Vi hanterar vägar, skapar prefabricerade filer, genererar en textur och sparar den till en bild. Slutligen skapar vi också en materialfil som använder den genererade bilden, och allt detta kommer att göras med kod.


Slutresultatförhandsvisning

Låt oss ta en titt på det slutliga resultatet vi ska arbeta för:


Steg 1: Ställ in projektet

Skapa ett tomt projekt; Vi kommer inte använda någonting snyggt här så vi borde inte bry sig om att importera någonting alls. När det är klart, skapa ett redigeringsskript. Enhet kommer att låta oss använda sina redaktörskurser endast om vi placerar vårt skript i en mapp som heter Redaktör. Eftersom det inte existerar i vårt projekt, behöver vi skapa det.

Låt oss nu skapa ett skript inuti det.


Steg 2: Lägg till en MenuItem

Låt oss rensa upp vårt manus. Förutom den grundläggande funktionaliteten vill vi också kunna använda redaktörsklasserna. Vi måste vara använder UnityEditor och vårt skripts klass borde förlänga Redaktör klass i stället för MonoBehaviour som vanliga spelobjekt gör.

 använder UnityEngine; Använda System.Collections; använder UnityEditor; offentliga klassen Exempel: Redaktör 

I vår första funktion jobbar vi med prefabs, låt oss kalla det a PrefabRoutine.

 public class Exempel: Redaktör void PrefabRoutine () 

För att enkelt utföra den här funktionen från redigeraren, låt oss lägga till den som en MENUITEM.

 public class Exempel: Redaktör [MenuItem ("Exempel / Prefab Routine")] void PrefabRoutine () 

Förutom att låta enheten veta att vi vill att den här funktionen ska kunna köras från Exempel-> Prefab Routine ", vi måste också göra denna funktion statisk.

 public class Exempel: Redaktör [MenuItem ("Exempel / Prefab Routine")] statisk tomgång PrefabRoutine () 

Om du går tillbaka till redigeraren nu (och uppdaterar menyn) kommer du märka att det finns en ny meny som heter exempel där.

Om du markerar Prefab routine inget kommer att hända eftersom vår funktion är tom.


Steg 3: Skapa en mapp

För att forma vårt projekt så som vi vill behöver vi veta hur man skapar mappar så att vi kan flytta saker runt. Att skapa en mapp från manuset är väldigt enkelt, allt vi behöver göra är att låta enighet veta var mappen ska placeras. För att skapa en mapp måste vi använda AssetDatabase klass.

 [MenuItem ("Examples / Prefab Routine")] statisk tomgång PrefabRoutine () AssetDatabase.CreateFolder ("Tillgångar", "Prefab Folder"); 

"Tillgångar" är namnet på den överordnade mappen i katalogen vi vill skapa. I vårt fall är det huvudprojektmappen där alla våra tillgångar importeras / skapas.

Observera att du också kan använda .NET Directory klass. Det här låter dig också radera, flytta eller komma åt katalogfilerna. För att kunna använda den här klassen måste du vara använder system.IO.

Varje gång du väljer Prefab routine från redaktören ska en ny mapp skapas och vara synlig i projektvyn.


Steg 4: Skapa en Prefab

För att skapa en prefab måste vi ringa EditorUtility.CreateEmptyPrefab (). Funktionen tar prefabbanans väg som ett argument.

 [MenuItem ("Examples / Prefab Routine")] statisk tomgång PrefabRoutine () AssetDatabase.CreateFolder ("Tillgångar", "Prefab Folder"); Objekt prefab = EditorUtility.CreateEmptyPrefab ("Tillgångar / Prefab Folder / obj.prefab"); 

Glöm inte förlängningen! När vi har skapat filen måste vi ringa AssetDatabase.Refresh (), så enheten kan se den.

 [MenuItem ("Examples / Prefab Routine")] statisk tomgång PrefabRoutine () AssetDatabase.CreateFolder ("Tillgångar", "Prefab Folder"); Objekt prefab = EditorUtility.CreateEmptyPrefab ("Tillgångar / Prefab Folder / obj.prefab"); AssetDatabase.Refresh (); 

Om vi ​​lämnar en konstant väg som argument, kommer varje gång vi väljer vår rutin att ersätta den gamla, en ny tom prefabrik. Låt oss tilldela varje prefab att separera mappen för att motverka det. För att göra detta måste vi spara den senast skapade mappen till en sträng så att vi kan använda den som ett sökargument senare. De Skapa mapp funktion returnerar a GUID, vilket i princip är filens (eller katalogens) ID. Det finns en funktion som hämtar sökvägen om vi lämnar in detta ID. Det heter GUIDToAssetPath; låt oss använda det för att få vår mapps väg.

 [MenuItem ("Exemplar / Prefab Routine")] statisk tomgång PrefabRoutine () strängväg = AssetDatabase.GUIDToAssetPath (AssetDatabase.CreateFolder ("Tillgångar", "Prefabrikerad mapp")); Objekt prefab = EditorUtility.CreateEmptyPrefab ("Tillgångar / Prefab Folder / obj.prefab"); AssetDatabase.Refresh (); 

Låt oss nu använda väg att styra prefabs som vi ska skapa till den senast skapade mappen.

 [MenuItem ("Exemplar / Prefab Routine")] statisk tomgång PrefabRoutine () strängväg = AssetDatabase.GUIDToAssetPath (AssetDatabase.CreateFolder ("Tillgångar", "Prefabrikerad mapp")); Objekt prefab = EditorUtility.CreateEmptyPrefab (sökväg + "/obj.prefab"); AssetDatabase.Refresh (); 

Du kan testa om de skapade tomma prefabsna är förpackade i mappar nu.


Steg 5: Ställ in Prefab

Om du skapar en prefab så vill du förmodligen inte lämna den tom eftersom det i så fall är ganska oanvändbart. Låt oss ställa in vår prefab om det finns något spelobjekt valt medan vår rutin körs. Vi prefabrikerar till det valda objektet. För att få det aktuella valda objektet kan vi använda Urval klass som hänvisar till den. För att ställa in prefabeten måste vi ringa ReplacePrefab ().

 [MenuItem ("Exemplar / Prefab Routine")] statisk tomgång PrefabRoutine () strängväg = AssetDatabase.GUIDToAssetPath (AssetDatabase.CreateFolder ("Tillgångar", "Prefabrikerad mapp")); Objekt prefab = EditorUtility.CreateEmptyPrefab (sökväg + "/obj.prefab"); AssetDatabase.Refresh (); om (Selection.activeObject) EditorUtility.ReplacePrefab (Selection.activeGameObject, prefab); 

Om du kör rutinen med något spelobjekt valt nu så märker du att den skapade prefab är automatiskt inställd.

Det är det vi har skapat en anpassad rutin för prefabricering, det är inte särskilt användbart men du borde kunna veta hur man gör det nu om det finns behov av en sådan sak i ditt projekt.

I slutet vill jag också nämna det AssetDatabase Du kan också flytta tillgångar runt, flytta dem till papperskorgen eller ta bort dem genom att ringa AssetDatabase.MoveAsset (), AssetDatabase.MoveAssetToTrash () och AssetDatabase.DeleteAsset () respektive. Resten av funktionaliteten finns på AssetDatabase skript referens sida.


Steg 6: Lägg till ett annat menyobjekt

Låt oss gå till ett annat exempel, den här gången skapar vi en textur och ett material programmatiskt. Låt oss ringa det här menyalternativet Material Rutin.

 [MenuItem ("Exemplar / Prefab Routine")] statisk tomgång PrefabRoutine () strängväg = AssetDatabase.GUIDToAssetPath (AssetDatabase.CreateFolder ("Tillgångar", "Prefabrikerad mapp")); Objekt prefab = EditorUtility.CreateEmptyPrefab (sökväg + "/obj.prefab"); AssetDatabase.Refresh (); om (Selection.activeObject) EditorUtility.ReplacePrefab (Selection.activeGameObject, prefab);  [MenuItem ("Examples / Material Routine")] statisk tomrum MaterialRoutine () 

Nu har vi två saker att välja mellan i exempel meny.


Steg 7: Skapa en textur

Låt oss skapa en Texture2D och sätta sin storlek till (256, 256) för detta exempel.

 [MenuItem ("Exempel / Material Rutin")] Statisk tomrum MaterialRoutine () Texture2D tex = Ny Texture2D (256, 256); 

Nu borde vi inte låta alla pixlarna gå i spill, så låt oss sätta texturens pixlar enligt någon form av uppskattad formel. För det behöver vi två för loopar för att gå igenom alla pixlar. För att ställa in varje pixels färg måste vi ringa SetPixel () som tar pixelns position på en textur och dess färg som argumenten.

 [MenuItem ("Exempel / Material Rutin")] Statisk tomrum MaterialRoutine () Texture2D tex = Ny Texture2D (256, 256); för (int y = 0; y < 256; ++y)  for (int x = 0; x < 256; ++x) tex.SetPixel(x, y, new Color());  

För att tilldela färgen använder vi Mathf.Sin () fungera. De Färg klassen kan initieras med tre flottor, motsvarande respektive röda, gröna och blå färgkomponenter. Det maximala tillåtna värdet är 1 och min är 0, så Synd() funktionen passar våra behov perfekt.

 för (int y = 0; y < 256; ++y)  for (int x = 0; x < 256; ++x) tex.SetPixel(Mathf.Sin(x*y), Mathf.Sin(x*y), Mathf.Sin(x*y))); 

Det spelar ingen roll vad vi lägger fram för Synd() funktion, men för att få något mer intressant borde vi ge ett värde som ändras för varje pixel.


Steg 8: Skapa en bild

Låt oss nu skapa en bild från den textur som vi just skapat. Eftersom vi ska skriva till en fil i binärt läge måste vi vara använder system.IO, så låt oss lägga till det till toppen av vårt manus.

 använder UnityEngine; Använda System.Collections; använder UnityEditor; använder system.IO; offentlig klass Exempel: Redaktör

För att spara vår textur som en PNG bild som vi först behöver ringa EncodeToPNG () som kommer att returnera en rad byte som PNG filen består av.

 för (int y = 0; y < 256; ++y)  for (int x = 0; x < 256; ++x) tex.SetPixel(x, y, new Color(Mathf.Sin(x*y), Mathf.Sin(x*y), Mathf.Sin(x*y)));  byte[] pngData = tex.EncodeToPNG();

Nu när vi har vårt pngData vi kan skriva den till en fil och skapa en PNG bild på detta sätt.

 byte [] pngData = tex.EncodeToPNG (); om (pngData! = null) File.WriteAllBytes ("Assets / texture.png", pngData);

Eftersom vi skapar filen på en konstant väg, varje gång vi kör MaterialRoutine (), Texturen kommer att bli överskriven.

Och eftersom vi har vår bild behöver vi inte den genererade strukturen längre eftersom det inte kommer att referera till en bild ändå. Låt oss förstöra det.

 byte [] pngData = tex.EncodeToPNG (); om (pngData! = null) File.WriteAllBytes ("Assets / texture.png", pngData); DestroyImmediate (tex);

Vi måste också låta Unity uppdatera projektvyn och filreferenser. att göra det vi behöver ringa AssetDatabase.Refresh ().

 byte [] pngData = tex.EncodeToPNG (); om (pngData! = null) File.WriteAllBytes ("Assets / texture.png", pngData); DestroyImmediate (tex); AssetDatabase.Refresh ();

Låt oss testa om texturen skapas när vi utför vår rutin.


Steg 9: Skapa ett material

Vi har en bild och nu kan vi skapa ett material som använder det som en textur. Låt oss skapa en nytt material.

 AssetDatabase.Refresh (); nytt material (Shader.Find ("Diffuse"));

Det skapade materialet kommer att använda en Diffus shader. För att spara detta material till filen kan vi ringa AssetDatabase.CreateAsset (). Denna funktion tar en tillgång som det första argumentet, och sökvägen som den andra.

 AssetDatabase.Refresh (); AssetDatabase.CreateAsset (nytt material (Shader.Find ("Diffuse")), "Tillgångar / New Material.mat");

Om du kör vår rutin nu ser du att materialet är skapat.

Som du kan se är allt korrekt, heter det Nytt material och den använder Diffus shader, men det finns ingen textur tilldelad.


Steg 10: Tilldela texturen

Först måste vi hänvisa till det material vi just skapat. Vi kan få det genom att ringa AssetDatabase.LoadAssetAtPath () som laddar tillgången och returnerar sin referens.

 AssetDatabase.CreateAsset (nytt material (Shader.Find ("Diffuse")), "Tillgångar / New Material.mat"); Materialmaterial = (Material) (AssetDatabase.LoadAssetAtPath ("Tillgångar / Ny Material.mat", typ av (Material)));

Låt oss nu tilldela vår genererade textur som materialets huvudsakliga textur. Vi kan få texturen referens av den genererade strukturen på samma sätt som vi fick materialreferensen.

 Materialmaterial = (Material) (AssetDatabase.LoadAssetAtPath ("Tillgångar / Ny Material.mat", typ av (Material))); material.mainTexture = (Texture2D) (AssetDatabase.LoadAssetAtPath ("Assets / texture.png", typof (Texture2D)));

För att se resultaten, kör Material Rutin.

Som du kan se har materialet nu tilldelats textur.

Slutsats

Det är slutet på introduktionen för att hantera dina tillgångar med hjälp av skript. Om du vill expandera din knowladge om ämnet kan du besöka Unity Editor Classes referenssida, särskilt AssetDatabase script referensen är värt att titta på. Om du behöver arbeta på en låg nivå borde du också läsa docs på System.IO för att få mer information om sina klasser och hur du kan använda dem. Tack för din tid!