Sprites added to Unity SpriteLibraryAsset programmatically doesn't appear in asset - unity3d

I am creating a Unity Editor script which takes a texture, slices it (this part works) and adds these sprites to a selected SpriteLibraryAsset:
foreach(var currentGroup in selectedDefinitionFile.Groups)
{
for (int i = 1; i <= currentGroup.Item2; i++) {
var rects = dataProvider.GetSpriteRects();
var targetName = String.Format("{0}-{1}", currentGroup.Item1, i);
var sprite = (Sprite)allSprites.Where(x => x.name == targetName).First();
spriteLibraryToPopulate.AddCategoryLabel(sprite, currentGroup.Item1, i.ToString());
}
}
// None of these do anything
spriteLibraryToPopulate.SetDirty();
EditorUtility.SetDirty(spriteLibraryToPopulate);
AssetDatabase.SaveAssets();
AssetDatabase.SaveAssetIfDirty(new UnityEditor.GUID(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(spriteLibraryToPopulate))));
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteLibraryToPopulate));
AssetDatabase.Refresh();
UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
If I run this script multiple times and stop on a breakpoint, I can see in Visual Studio that the sprites are being added as expected to the in-memory object. However, when I examine the Sprite Library asset, both in the editor and in the asset file using Notepad++, none of them appear.
Reimporting via the menu in the editor does nothing as well.
Investigation with a debugger shows that internally, Unity uses the class SpriteLibrarySourceAsset when importing a .spriteLib asset and creates the SpriteLibraryAsset I have access to in my scripts. I haven't been able to find how to go the other way.

I finally was able to achieve what I intended using reflection to call some internal Unity APIs.
// Perform your modification to the sprite library before this
// The logic to transform the SpriteLibraryAsset to the internal representation works on the selected objects.
Selection.objects = new UnityEngine.Object[]
{
spriteLibraryToPopulate
};
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(spriteLibraryToPopulate)) as SpriteLibrarySourceAssetImporter;
var saveMethod = typeof(SpriteLibrarySourceAssetImporter).GetMethod("ConvertToSourceAsset", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
saveMethod.Invoke(null, null);
var rootPath = Path.Join(AssetDatabase.GetAssetPath(spriteLibraryToPopulate), "..");
// The save logic creates a differently named instance of the asset, so we copy the data from our newly created one into the original
File.Copy(Path.Join(rootPath, string.Format("{0} 1.spriteLib", spriteLibraryToPopulate.name)),
Path.Join(rootPath, string.Format("{0}.spriteLib", spriteLibraryToPopulate.name)), true);
// Get rid of the extra asset and its associated meta file.
File.Delete(Path.Join(rootPath, string.Format("{0} 1.spriteLib", spriteLibraryToPopulate.name)));
File.Delete(Path.Join(rootPath, string.Format("{0} 1.spriteLib.meta", spriteLibraryToPopulate.name)));
At the end, the library should be selected in the Unity Editor. If you try and click away, it will be marked dirty and Unity will prompt you to save or revert; it appears to me that both have the same result.

Related

Automatic texture importer not assigning alpha in Unity

I have written a custom texture importer, and put it in my Editor folder inside the Assets folder, which is supposed to apply my custom settings automatically when a new texture is imported to the project.
This is my code:
'''
public class TexturePostProcessor : AssetPostprocessor
{
void OnPostprocessTexture(Texture2D texture)
{
TextureImporter importer = assetImporter as TextureImporter;
importer.textureType = TextureImporterType.Default;
TextureImporterPlatformSettings settings = importer.GetPlatformTextureSettings("WebGL");
settings.overridden = true;
settings.name = "WebGL";
settings.maxTextureSize = 1024;
settings.format = TextureImporterFormat.DXT5Crunched;
settings.compressionQuality = 50;
settings.allowsAlphaSplitting = false;
importer.SetPlatformTextureSettings(settings);
}
}
'''
The issue i have is, when the texture is imported, it doesn't get the alpha channel, but if I manually right click and re-import the texture, then the alpha is applied. You can see that in below image:
Re-importing is not an option though because our textures will be coming in from external sources automatically, so it needs to have the correct alpha and format the first time.
Can someone point me in the right direction please?

Unity how to Create Texts via script?

I'm trying to create Texts via script. I want to create texts with the name of objects of a certain tag.
"For example, if I have two objects named Cube and Sphere and both have a tag of "TargetObj" then their names should be displayed as texts on the screen(in the case Cube, Sphere)"
I want to achieve this regardless of the number of objects. so a loop is needed.
Here is what I've tried so far.
[SerializeField] GameObject LevelCanvas;
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
foreach (var obj in targetObjects)
{
Text mytext = LevelCanvas.AddComponent<Text>();
mytext.text = "Find " + obj.name;
Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");
mytext.font = ArialFont;
mytext.material = ArialFont.material;
}
it only shows one object name although I have 2 more objects that were supposed to be shown as well.
You need to create a text object on a new gameobject.
static Text CreateText(Transform parent)
{
var go = new GameObject();
go.transform.parent = parent;
var text = go.AddComponent<Text>();
return text;
}
var mytext = CreateText(LevelCanvas.transform);
myext.text = ...
Many components in unity can only be added once per game object. That means you need a separate game object for every text you want to show.
A typical way to approach this is to create a "prefab" of a game object which has a text element already. This way you can set a default font and other settings.
Then in code you use the Instantiate method on that prefab and via GetComponent() you can access the text component instance to set the desired string to display.

Alternative to AssetDatabase.LoadAssetAtPath for any path?

Currently I am using the following piece of code to load Textures from image files.
Texture my_pic = (Texture) AssetDatabase.LoadAssetAtPath(path, typeof(Texture));
Unfortunately this method doesn't work if the target path isn't in the Asset/ folder. I was wondering how I would load an image, given some absolute path of the form
/Users/Alan/SomeFolder/SomePic.png
(note that I am currently writing a custom Unity editor plugin by extending EditorWindow if that matters)
AssetDatabase is an Editor-only class.
Furthermore, it can only read assets in the /Assets directory (you know, the ones that are known to the asset database).
If you want to read any file on the file system, you need to use the System.IO classes.
Here is some code that will open a unity file dialog, and load the selected texture into a material of the attached object.
string path = EditorUtility.OpenFilePanel("Load an image", "", "png");
if (string.IsNullOrEmpty(path)) {
return;
}
// Load the images bytes from file (this is a synchronous call!)
byte[] bytes = null;
try {
bytes = System.IO.File.ReadAllBytes(path);
} catch (System.Exception e) {
Debug.LogError(e.Message);
return;
}
// Load the bytes into a Unity Texture2D
Texture2D _tex = new Texture2D(2,2);
_tex.LoadImage(bytes);
// Apply this texture to the object
Renderer r = (target as Component).gameObject.GetComponent<Renderer>();
if (r != null) {
r.material.SetTexture("_MainTex", _tex);
}
The last part, just for demonstration, will work only on a script derived from Editor because it uses target to find the attached renderer. It's up to you to decide what to do with the texture in your EditorWindow script.
Also, remember to explicitly call DestroyImmediate on your texture when you no longer need it, as you may end up with a memory leak in your editor code.
You can use UnityWebRequestTexture
var www = UnityWebRequestTexture.GetTexture("file:///Users/Alan/SomeFolder/SomePic.png");
www.SendWebRequest();
while(!www.isDone)
continue;
var texture = DownloadHandlerTexture.GetContent(www);

How to get the path and file size of some Unity built-in assets?

Background
I am developing a Unity editor plugin that enables users to send a selected image file to a REST API endpoint in the cloud for processing (e.g. adding transforms and optimizations). The plugin also shows a comparison of the selected image's details before and after processing (e.g. width/height/size before vs after).
The user selects the desired image through the following piece of code:
selected_texture = (Texture2D) EditorGUI.ObjectField(drawing_rect, selected_texture, typeof(Texture2D), false);
Once its selected, I can then get the respective file size by doing this:
file_size = new FileInfo(AssetDatabase.GetAssetPath(selected_texture)).Length;
Problem
This works for most textures selected, but I encounter an error when I choose a built-in Unity texture. Any guidance would be greatly appreciated.
FileNotFoundException: Could not find file 'Resources/unity_builtin_extra'
There are two built-in asset-librarys in Unity:
BuiltIn-Library in "Resources/unity_builtin_extra": contains UGUI sprite、Default-Material、Shader and so on.
BuiltIn-Library in "Library/unity default resources": contains built-in 3D mesh and OnGUI assets.
If you are using AssetDatabase.GetAssetPath, you will always get one or another path above.
To solve the problem, you need do something like below code:
public const string BuiltinResources = "Resources/unity_builtin_extra";
public const string BuiltinExtraResources = "Library/unity default resources";
public static bool IsBuiltInAsset(string assetPath)
{
return assetPath.Equals(BuiltinResources) || assetPath.Equals(BuiltinExtraResources);
}
public static long GetTextureFileLength(Texture texture)
{
string texturePath = AssetDatabase.GetAssetPath(texture);
if (IsBuiltInAsset(texturePath))
{
/*
* You can get all built-in assets by this way.
*
var allAssets = AssetDatabase.LoadAllAssetsAtPath(BuiltinResources);
var allExtraAssets = AssetDatabase.LoadAllAssetsAtPath(BuiltinExtraResources);
*/
// not supportted
// return -1;
// using MemorySize
return Profiler.GetRuntimeMemorySizeLong(texture);
}
else
{
return new FileInfo(texturePath).Length;
}
}

ArgumentException: The prefab you want to instantiate is null

I'm getting an error and I don't know why. From the error I can see that there is something wrong with line 12, I think.
The script is larger but is not needed to solve the problem.
But if you really need it, you can ask for it.
Here is the code:
if(RandomInt==2) {
var randomNumberB = Random.Range(3,5);
for(var b = 0; b < randomNumberB; b++) {
var xCoB = childVector3.x + Random.Range(0,10);
var zCoB = childVector3.z + Random.Range(0,10);
var randomRotationB = Quaternion.Euler(0,Random.Range(0,360),0);
var chancheB = Random.Range(0,2);
if(chancheB == 0) {
var bushC = Instantiate(bushes[Random.Range(0,bushes.length)], Vector3(xCoB, childVector3.y, zCoB), randomRotationB);
bushC.transform.name = "bush";
} else {
Instantiate(cactus, Vector3(xCoB, childVector3.y, zCoB), randomRotationB).transform.name = "cactus";
}
}
}
Make cactus a prefab.
Then make a public variable:
public GameObject cactus;
Then drag and drop the prefab into the script variable via de Editor. Then you can instantiate.
if(cactus != null)
{
GameObject g = Instantiate(cactus, new Vector3(xCoB, childVector3.y, zCoB), randomRotation) as GameObject;
g.name = "cactus";
}
it is easy if you have prefabs sitting in the root Resources folder. It is different if you want them to be in a subfolder of Resources. You will need to create a "Prefabs" folder within.
Since you are using Javascript
Given a prefab named "Your Prefab.prefab" within Assets/Resources:
var NewGameObject : GameObject = Resources.Load("Your Prefab"),Vector3.zero,Quaternion.identity);
This will allow you to have a reference to that prefab, loaded into memory.
If you wish to contain a prefab or more within a folder in Assets/Resources you must do the following:
2a. Create a folder named "Prefabs" within Assets/Resources.
2b. Create your folder there (e.g. "Resources/Prefabs/Your Prefab Folder");
2c. Given a prefab named "Your Prefab.prefab":
var NewGameObject : GameObject = Resources.Load("Prefabs/Your SubFolder/Your Prefab"),Vector3.zero,Quaternion.identity);
Notes:
Don't forget to remove the ".prefab" extension.
The Unity manual states that you can load assets from Resources without supplying a path with folder names. Certainly in regards to prefabs (and javascript?) that does not seem to be true (Unity 4.6).