GetAssetPreview always returns null for UnityEngine.UI.Image - unity3d

I have an array of prefabs i want to show a preview of in my custom editor. This works for gameobjects with a mesh renderer, for example the basic quad. However when i try to use AssetPreview.GetAssetPreview(tilePrefab.gameObject); on gameobject with a UnityEngine.UI.Image and a canvas renderer it always returns null.
Below is the part of the code that draws the previews.
public class MapEditor : Editor
{
public override void OnInspectorGUI()
{
for (int prefabIndex = 0; prefabIndex < TileSet.TilePrefabs.Count; prefabIndex++)
DrawIconTexture(prefabIndex, columnCount);
}
private void DrawIconTexture(int prefabIndex, int columnCount)
{
TileBehaviour tilePrefab = TileSet.TilePrefabs[prefabIndex];
Texture iconTexture = AssetPreview.GetAssetPreview(tilePrefab.gameObject);
Rect iconRect = GetIconRect(prefabIndex, columnCount);
if (iconTexture != null)
GUI.DrawTexture(iconRect, iconTexture);
else if (AssetPreview.IsLoadingAssetPreview(tilePrefab.gameObject.GetInstanceID()))
Repaint();
}
}
I know GetAssetPreview loads assets async, that is solved by the repaint. I have also tried
while(iconTexture == null)
iconTexture = AssetPreview.GetAssetPreview(tilePrefab.gameObject);
But that never finishes.
I also tried to use the texture of the Image
if (iconTexture == null)
iconTexture = tilePrefab.GetComponent<Image>().sprite.texture;
But that does not work because the sprite is in an atlas and all of the atlas is shown.

Edit: misread the question. I actually tried to use IsLoadingAssetPreview and IsLoadingAssetPreviews for few hours, without success. I ended up using a sad little trick
if (_previews.All(pair => pair.Value != null)) return;
_previews = GeneratePreviews();
I put that in the Update() loop of my EditorWindow. It's pretty hacky, I'm going to ask Unity if the AssetPreview methods are actually working.
Old answer, irrelevant:
You are not using the while loop and GetAssetPreview correctly.
GetAssetPreview will launch an asynchronous loading of the preview. To know if the preview is fully loaded you need to call AssetPreview.IsLoadingAssetPreview.
A pretty simple and brutal way of doing it (it will block execution) is :
var preview = AssetPreview.GetAssetPreview(item);
while (AssetPreview.IsLoadingAssetPreview(item.GetInstanceID())) {
// Loading
}
As usual, careful with while loops. Note that there is also a AssetPreview.IsLoadingAssetPreviews method with no parameters.

you should just use "using UnityEditor"

Related

UNITY How do I change image position?

Below is a snippet of code thats running every update but when I log the local position of the image it still says 0,0,0 when it should be 10,10,10. What am I doing wrong??? Ultimately I am trying to understand how to programmatically move an image around on screen
public partial class MainCanvasSystem : SystemBase
{
protected override void OnUpdate()
{
if (MainGameObjectCanvas.Instance != null && SystemAPI.HasSingleton<MainEntityCanvas>())
{
Entity mainEntityCanvasEntity = SystemAPI.GetSingletonEntity<MainEntityCanvas>();
LocalToWorld targetLocalToWorld = SystemAPI.GetComponent<LocalToWorld>(mainEntityCanvasEntity);
Canvas canvas = MainGameObjectCanvas.Instance;
Image image = canvas.GetComponentInChildren<Image>();
var rect = image.GetComponent<RectTransform>();
rect.localScale.Set(10,10,10);
Debug.Log(rect.localPosition.x);
}
}
}
I think there is general misunderstanding here.
rect.localScale.Set(10,10,10);
does .. nothing!
Transform.localScale is a property and returns a COPY of a Vector3 struct.
You are calling Vector3.Set on it which replaces the values within that Vector3 copy, yes, but then you never actually apply it anywhere.
=> you need to actually set the property!
You rather would do e.g.
rect.locaScale = Vector3.one * 10;
or
rect.localScale = new Vector3(10,10,10);
However, this said, changing a localScale won't change the position at all. The RectTransform.anchoredPosition is probably rather the one to go with.

UNITY An object reference is required to access non-static member `Outline.OutlineMode'

I'm trying to create an outline when you are near it, but i'm getting all the time the same error.
void Update () {
if (Input.GetKeyDown(KeyCode.E)){
var outline = gameObject.AddComponent<Outline>();
outline.OutlineMode = Outline.Mode.OutlineAll;
outline.OutlineColor = Color.yellow;
outline.OutlineWidth = 5f;
}
}
void OnTriggerStay(Collider other) {
if (Outline.OutlineMode == Outline.Mode.OutlineAll) {
Debug.Log("test");
}
}
If i press E it works, and if i change it to ontriggerstay works too, but im trying that it only applies one time, because im getting some errors if its on. I have to say that im using an asset, called quick outline
Srry for my very bad english and explanation and thank you
add the outline to your object in Awake() then set it to disabled.
then enable it in OnTriggerEnter() and disable it in OnTriggerExit()
this will keep you from making multiple copies, and it will only be active when you are in range of your trigger

Unity3D - GameControl always null?

I don't know what is wrong with my destroy portion of the below code. I'm trying to have only 1 GameControl persist throughout the scenes. It seems that every time I switch back to this scene, my GameControl is read as null and a new GameControl is generated and I end up having more than 1 GameControl persisting.
For reference: I tried to port the code from 18:24 to JavaScript from this link
Please advise.
#pragma strict
var control : GameControl;
function Awake () {
Debug.Log("GameControl runs");
if (control == null)
{
DontDestroyOnLoad(gameObject);
control = this;
Debug.Log(control);
} else if (control != this)
{
Destroy(gameObject);
Debug.Log("Destroy?");
}
}
function Update ()
{
}
The problem is that you're not using a static variable like this:
static var control : GameControl;
In simple terms being static means this variable will hold the same value across all your scripts thus the check if(control == null) will always remain true because your GameObject will create a new variable when you load a new scene. This new object is still not static and is therefore completely different. I hope this helps. If you are still confused I'd advise reading some single pattern articles because there are many ways to achieve it other than this.

Scripting GUI buttons to trigger an event of changing materials

Apologies if there's a similar question, however, I have probably seen it and it has not fixed my problem.
I am trying to write a JS script for unity in order to achieve an event to be triggered once clicked.
I have searched online on UnityAnswers website and others, the closest I can get is based on these questions
http://answers.unity3d.com/questions/368303/changing-shaders-of-a-gameobject-via-script-1.html
and this one
http://answers.unity3d.com/questions/319875/change-objects-material-using-gui-buttons-via-scri.html
and also looked at this one
http://docs.unity3d.com/ScriptReference/Material-shader.html
So, my code is this so far
var button2_tex : Texture;
var button3_tex : Texture;
var button4_tex : Texture;
var seat_mat1 : Material;
var seat_mat2 : Material;
var veneer1 : Texture;
var veneer2 : Texture;
var rend : Renderer;
var _mouseDown = false;
function Start() {
seat_mat1 = Resources.Load( "seat_mat1" );
seat_mat2 = Resources.Load( "seat_mat2" );
}
function Update(){
if(Input.GetMouseButtonDown(0)){
_mouseDown = true;
}
}
function OnGUI() {
GUI.Box (Rect (10,10,100,200), "Menu");
if (_mouseDown){
if (GUI.Button (Rect (20,40,40,20), button1_tex)){
if(seat_mat1){
rend.material = seat_mat2;
Debug.Log("This button was clicked!");
}
else{
rend.material = seat_mat1;
}
}
}
Please note some variables I haven't used yet, as am still testing bunch of other codes to get it working..
the code snippet I am trying to fix starts with "function OnGUI()" but I maybe wrong and could use some fresh insight.
this is a screenshot of the resulting script. The button on the left side is supposedly to change the colour of the material from seat_mat1 to seat_mat2 by the event of mouse clicking on the button.
I have attached the previous script to the 3D object in unity and had made a folder names "Resources" for the materials to be visible and referenced through the script.
My problem is that upon clicking the GUI button, nothing happens, and it maybe something very simple and I am just missing it .. apologies for being inexperienced in JS much or unity.
Thanks in advance.
EDIT:
So after playing a bit more with the code. I added a Debug.Log() after this line
GetComponent.<Renderer>().material = seat_mat2;
Debug.Log("This button was clicked!");
and seems to be this error that I am getting every time the button is pressed
"MissingComponentException: There is no 'Renderer' attached to the "scene_export3" game object, but a script is trying to access it.
You probably need to add a Renderer to the game object "scene_export3". Or your script needs to check if the component is attached before using it.
materialChanger.OnGUI () (at Assets/materialChanger.js:44)"
So with simple understanding, it seems that the renderer is not attached somehow?
Your code is working fine But still i think once you have loaded The materials in start function you do not need to load them every time. And one thing more is to make sure you have applied the script to the gameobject you want to change the material of. If you just want to change the color not want to change complete material use color property of material and your OnGUI function should look like this.
function OnGUI() {
GUI.Box (Rect (10,10,100,200), "Menu");
if (_mouseDown){
if (GUI.Button (Rect (20,40,40,20), button1_tex)){
if(GetComponent.<Renderer>().material == seat_mat1)
GetComponent.<Renderer>().material.color = seat_mat2.color;
else
GetComponent.<Renderer>().material.color = seat_mat1.color;
}
}
}
But if you do not use color poperty it will work fine just make sure you have applied script to game boject you want to change material i your case may be to your seat1.
Thanks Nain for your guidance, with your help I was able to come up with a partial solution, where I know I can fix from there.
The solution is as follows.
The code:
function OnGUI() {
GUI.Box (Rect (10,10,100,200), "Menu");
if (_mouseDown){
if (GUI.Button (Rect (20,40,40,20), button1_tex)){
var rendArray : Renderer[];
rendArray = GetComponentsInChildren.<Renderer>(true);
for(var rend : Renderer in rendArray){
if(rend.sharedMaterial == seat_mat1){
rend.sharedMaterial = seat_mat2;
Debug.Log("This button was clicked!");
}
else{
rend.sharedMaterial = seat_mat1;
}
}
}
}
}
After saving the script in MonoDevelop, open Unity where you create an empty game object and add all the objects with specific material e.g. seat_mat1 under it as children.
Then click on the empty object, rename it if you like, and add a "Mesh Renderer" component from "Add Component > Mesh > Mesh Renderer".
After that drag the script to the empty object and in the inspector you can find an option which you can choose a renderer called "rend". Choose the empty object as the Mesh Renderer.
Upon testing the outcome, I have managed to change materials of some objects together to seat_mat2, but unrelated materials changed themselves to seat_mat1 and once the button is clicked again, they alternate between seat_mat1 and seat_mat2
and even though it is not perfectly done, I am answering this question as anything else is fixable (I hope).
Thank you again Nain for your help :)

Unity - When does the frameCount goes negative

I'm learning unity scripting. But I don't understand the following :
static private var lastRecalculation = -1;
static function RecalculateValue () {
if (lastRecalculation == Time.frameCount)
return;
ProcessData.AndDoSomeCalculations();
}
I don't get the third line or the condition in the IF statement. I know its a bit amateur, but pls help.
Thank You.
This is from the Time.frameCount documentation. This example shows how to create a function that executes only once per frame, regardless of how many objects you attach your script to. I suspect though that this example is incomplete because it never updates lastRecalculation (or it was assumed you would do so in AndDoSomeCalculations() ).
The reason setting lastRecalculation = -1 initially is so this function runs during the first frame.
Working version:
static var lastRecalculation = -1;
static function RecalculateValue () {
if (lastRecalculation == Time.frameCount) {
return;
}
lastRecalculation = Time.frameCount;
Debug.Log (Time.frameCount);
//ProcessData.AndDoSomeCalculations();
}
function Update () {
RecalculateValue();
}
Attach this script to 2 different GameObjects and run it. You will only see unique frame values 1,2,3,4,5.... even though 2 GameObjects are each calling RecalculateValue()
Now comment out the return and run it again. Now the ProcessData portion of that code runs for both objects every frame and you'll see something like 1,1,2,2,3,3,4,4......