make changes to material assets in unity3d - c#

I am overriding some Material Assets using an Editor Script, and materials override nicely. I can see new material properties get applied and when I click on individual materials I can see new textures applied etc. However when I hit play, my materials reset to pre-edit state, Same thing happens when I hit CTR+S. My materials all reset back to what they were.
How can I make the changes get saved to database and persist when I hit play?
using UnityEngine;
using UnityEditor;
using Newtonsoft.Json;
using Unify.Utilities;
using System.Collections.Generic;
using System;
using System.IO;
public class ProcessMaterials : MonoBehaviour
{
[MenuItem("Unify/ProcessMaterials")]
static void UnifyProcessMaterials()
{
ImportTextures();
ApplyMaterials();
}
private static void ImportTextures()
{
// check if folder exists and create one if not
if (!AssetDatabase.IsValidFolder("Assets/Resources"))
{
AssetDatabase.CreateFolder("Assets", "Resources");
}
// load settings file
TextAsset ta = Resources.Load("UnifySettings") as TextAsset;
string json = ta.text;
List<List<UnifyObject>> unifyObj = JsonConvert.DeserializeObject<List<List<UnifyObject>>>(json);
List<UnifyObject> allMats = unifyObj[3];
// copy textures over to unity folders
HashSet<string> uniqueTextures = new HashSet<string>();
foreach (UnifyObject obj in allMats)
{
if (obj.DiffuseTexture != null && uniqueTextures.Add(obj.DiffuseTexture))
{
CopyImageAsset(obj.DiffuseTexture);
}
if (obj.BumpTexture != null && uniqueTextures.Add(obj.BumpTexture))
{
CopyImageAsset(obj.BumpTexture);
}
if (obj.TransparencyTexture != null && uniqueTextures.Add(obj.TransparencyTexture))
{
CopyImageAsset(obj.TransparencyTexture);
}
if (obj.EnvironmentTexture != null && uniqueTextures.Add(obj.EnvironmentTexture))
{
CopyImageAsset(obj.EnvironmentTexture);
}
}
}
private static void CopyImageAsset(string sourceFilePath)
{
string fileName = "Resources\\" + Path.GetFileName(sourceFilePath);
string destFile = Path.Combine(Application.dataPath, fileName);
try
{
File.Copy(sourceFilePath, destFile, true);
}
catch (Exception) { }
}
private static void ApplyMaterials()
{
TextAsset ta = Resources.Load("UnifySettings") as TextAsset;
string json = ta.text;
List<List<UnifyObject>> unifyObj = JsonConvert.DeserializeObject<List<List<UnifyObject>>>(json);
GameObject cube;
cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Renderer cubeRenderer = cube.GetComponent<Renderer>();
List<UnifyObject> allMaterials = unifyObj[3];
foreach (UnifyObject obj in allMaterials)
{
// skip layers with no materials assigned/default
if (obj.Guid != Guid.Empty.ToString())
{
// obj replaces all dashes in names with underscores hence material assets will have different names than in Rhino
// if layers had dashes in their names
string objUniqueName = obj.UniqueName.Replace("-", "_");
Material m = (Material)AssetDatabase.LoadAssetAtPath("Assets/Resources/Model/Materials/" + objUniqueName + "Mat.mat", typeof(UnityEngine.Object));
if (m != null)
{
OverrideMaterial(m, obj, cubeRenderer);
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(m));
}
}
}
DestroyImmediate(cube);
}
private static Material OverrideMaterial(Material m, UnifyObject obj, Renderer renderer)
{
renderer.material = m;
// set main color
// set transparency
if (obj.Transparency != "0")
{
Color newColor = Utilities.ConvertToUnityColor(obj.Diffuse, obj.Transparency);
renderer.sharedMaterial.SetFloat("_Mode", 3);
renderer.sharedMaterial.SetColor("_Color", newColor);
}
else
{
Color newColor = Utilities.ConvertToUnityColor(obj.Diffuse);
renderer.sharedMaterial.SetColor("_Color", newColor);
}
// set main texture
if (obj.DiffuseTexture != null)
{
renderer.sharedMaterial.mainTexture = Utilities.Texture2dFromPath(obj.DiffuseTexture);
}
// set bump map
if (obj.BumpTexture != null)
{
Texture2D bumpTexture = Utilities.Texture2dFromPath(obj.BumpTexture);
float strength = Convert.ToSingle("1.0");
Texture2D normalBump = Utilities.CreateNormalMap(bumpTexture, strength);
renderer.sharedMaterial.SetTexture("_BumpMap", normalBump);
// need to get that value from Rhino somehow
renderer.sharedMaterial.SetFloat("_BumpScale", 0.3f);
}
// set metallic
renderer.sharedMaterial.SetFloat("_Metallic", Utilities.ConvertRange(0, 255, 0, 1, Convert.ToSingle(obj.Metallic)));
// set emission color
Color emissionColor = Utilities.ConvertToUnityColor(obj.EmissionColor);
renderer.sharedMaterial.SetColor("_EmissionColor", emissionColor);
return renderer.sharedMaterial;
}
}

After you overwrite the materials, call the following functions
UnityEditor.EditorUtility.SetDirty(AssetName);
UnityEditor.AssetDatabase.SaveAssets();
UnityEditor.AssetDatabase.Refresh();
If the method above did not work, another method that might work is to create a simple cube, assign the loaded material to the cube then modify the Renderer.sharedMaterial of the cube instead of Renderer.material. Usually, modifying sharedMaterial changes the original material permanently but I don't know if applies to loaded materials from AssetDatabase. This should be done inside your OverrideMaterial function. The GameObject.CreatePrimitive(PrimitiveType.Cube); function should only be called once.
GameObject cube;
cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Renderer cubeRenderer = cube.GetComponent<Renderer>();
//Change the cube material to the material that is loaded from the disk
cubeRenderer.material = m;
//Now modify the shared array of the cube
cubeRenderer.sharedMaterial.SetFloat("_Mode", 3);
cubeRenderer.sharedMaterial.SetColor("_Color", newColor);
//cubeRenderer.sharedMaterial.
//cubeRenderer.sharedMaterial.

Related

Why are areas targets get mixed up and overlap in unity when running the project?

I'm working on "Indoor Navigation" project using AR with Vuforia, I have scanned the map using "Vuforia Area Target Creator" Application. I have imported the area targets to unity and I have created NavMesh. I'm following this: https://library.vuforia.com/develop-area-targets/multiple-area-targets. I'm also created NavMeshAgent and destination but when I run the project, area targets overlap and NavMeshAgent can't pass through, What is the problem ??
MultiArea code (as above link explained):
using System.Collections.Generic;
using UnityEngine;
using Vuforia;
public class MultiArea : MonoBehaviour
{
#region PUBLIC_MEMBER_VARIABLES
public bool hideAugmentationsWhenNotTracked = true;
#endregion PUBLIC_MEMBER_VARIABLES
#region PRIVATE_MEMBER_VARS
/// <summary>
/// Trackable poses relative to the MultiArea root
/// </summary>
private readonly Dictionary<string, Matrix4x4> mPoses = new Dictionary<string, Matrix4x4>();
private bool m_Tracked = false;
#endregion PRIVATE_MEMBER_VARS
#region UNITY_MONOBEHAVIOUR_METHODS
// Start is called before the first frame update
void Start()
{
var areaTargets = GetComponentsInChildren<AreaTargetBehaviour>(includeInactive: true);
foreach (var at in areaTargets)
{
// Remember the relative pose of each AT to the group root node
var matrix = GetFromToMatrix(at.transform, transform);
mPoses[at.TargetName] = matrix;
Debug.Log("Original pose: " + at.TargetName + "\n" + matrix.ToString(""));
// Detach augmentation and re-parent it under the group root node
for (int i = at.transform.childCount - 1; i >= 0; i--)
{
var child = at.transform.GetChild(i);
child.SetParent(transform, worldPositionStays: true);
}
if (hideAugmentationsWhenNotTracked)
{
ShowAugmentations(false);
}
}
}
// Update is called once per frame
void Update()
{
if (!VuforiaApplication.Instance.IsRunning)
{
return;
}
// Find current "best tracked" Area Target
var atb = GetBestTrackedAreaTarget();
if (!atb)
{
if (m_Tracked)
{
m_Tracked = false;
if (hideAugmentationsWhenNotTracked)
{
ShowAugmentations(false);
}
}
return;
}
if (!m_Tracked)
{
m_Tracked = true;
ShowAugmentations(true);
}
if (GetGroupPoseFromAreaTarget(atb, out Matrix4x4 groupPose))
{
// set new group pose
transform.position = groupPose.GetColumn(3);
transform.rotation = Quaternion.LookRotation(groupPose.GetColumn(2), groupPose.GetColumn(1));
}
}
#endregion UNITY_MONOBEHAVIOUR_METHODS
#region PRIVATE_METHODS
private void ShowAugmentations(bool show)
{
var renderers = GetComponentsInChildren<Renderer>();
foreach (var rnd in renderers)
{
rnd.enabled = show;
}
}
private AreaTargetBehaviour GetBestTrackedAreaTarget()
{
var trackedAreaTargets = GetTrackedAreaTargets(includeLimited: true);
if (trackedAreaTargets.Count == 0)
{
return null;
}
// look for extended/tracked targets
foreach (var at in trackedAreaTargets)
{
if (at.TargetStatus.Status == Status.TRACKED ||
at.TargetStatus.Status == Status.EXTENDED_TRACKED)
{
return at;
}
}
// if no target in EXT/TRACKED was found,
// then fallback to any other target
// i.e. including LIMITED ones;
// just report the first in the list
return trackedAreaTargets[0];
}
private List<AreaTargetBehaviour> GetTrackedAreaTargets(bool includeLimited = false)
{
var trackedTargets = new List<AreaTargetBehaviour>();
var activeAreaTargets = FindObjectsOfType<AreaTargetBehaviour>();
foreach (var target in activeAreaTargets)
{
if (target.enabled &&
(target.TargetStatus.Status == Status.TRACKED ||
target.TargetStatus.Status == Status.EXTENDED_TRACKED ||
(includeLimited && target.TargetStatus.Status == Status.LIMITED)))
{
trackedTargets.Add(target);
}
}
return trackedTargets;
}
private bool GetGroupPoseFromAreaTarget(AreaTargetBehaviour atb, out Matrix4x4 groupPose)
{
groupPose = Matrix4x4.identity;
if (mPoses.TryGetValue(atb.TargetName, out Matrix4x4 areaTargetToGroup))
{
// Matrix of group root node w.r.t. AT
var groupToAreaTarget = areaTargetToGroup.inverse;
// Current atb matrix
var areaTargetToWorld = atb.transform.localToWorldMatrix;
groupPose = areaTargetToWorld * groupToAreaTarget;
return true;
}
return false;
}
private static Matrix4x4 GetFromToMatrix(Transform from, Transform to)
{
var m1 = from ? from.localToWorldMatrix : Matrix4x4.identity;
var m2 = to ? to.worldToLocalMatrix : Matrix4x4.identity;
return m2 * m1;
}
#endregion PRIVATE_METHODS
}
you probably set the area target as static am I right?
the area target should move always so they can't be static even if you use a navmesh.
try to change it to "Navigation static" and then bake again using the "Navigation" menu.
Link for reference:
https://library.vuforia.com/develop-area-targets/using-unitys-navmesh-navigation-area-targets

How can I successfully store and load data from a game character?

I was wrote a script that combines whale characters to create a new character. However, it is difficult to save and load data from a list of names, levels and locations of the newly created characters.
Whale data does not seem to be stored and loaded when the game is played.
I'd really thanks it if you could tell me where's wrong.
using system;
using System.Collections;
using System.Collections.General;
using system.IO;
using LitJson;
using UnityEngine;
using Random = UnityEngine.Random;
[System.Serializable]
Public class WhaleData
{
Public in dataLevel{get; set;// whale level
public Vector3 dataMousePosition{get; set;//the whale location
public string dataName{get; set;} //whale name
}
Public class Whale: MonoBehaviour
{
public int level;
Private pool isDrag;
Public GameObject NextPrepab;
Private Vector3 mousePosition;
Public WhaleData whaleData;
Private List[WhaleData] WhaleDatas = new List[WhaleData]();
private pool IsSave; //bool value to check for stored data
void start()
{
IsSave = PlayerPrefs.HasKey ("0_name"); //saved_level Check if a key value named //saved_level exists
//initialize all data values if no save data exists
If (!IsSave)
{
Debug.Log ("No data stored).");
PlayerPrefs.DeleteAll();
}
//recall value if save data exists
else
{
Debug.Log ("Stored Data").");
LoadData();
}
}
Private void Update ()
{
SaveData();
LoadData();
}
private void onMouseDown()
{
isDrag = true;
}
private void OnMouseDrag()
{
if (isDrag)
{
mousePosition.x = Input.mousePosition.x;
mousePosition.y = input.mousePositionY;
mousePosition.z = 10;
//change the mouse coordinates to the screen to world and set the position of this object
gameObject.transform.position = Camera.main.ScreenToWorldPoint(mousePosition);
// store the location of the whale in the whalData.dataMousePosition= gameObject.transform.position; //
}
}
private void onMouseUp()
{
//OverlapSphere : Return from the specified location to the collider array in contact within range
var colliders = Physics.OverlapSphere (transform.position, 0.1f);
foreach (var col in colliders) // arrangement of surrounding contacts
{
If (name!= col. gameObject.name) //if the name is different, go
{
If (level==col.gameObject.GetComponent[Whale>().level) //If the level of contact with the level stored in me is the same,
{
whatData.dataLevel = col.gameObject.GetComponent[Whale>().level; // store the level of the whale in the whalData class
var newWhale = Instantiate(NextPrepab, transform.position, quaternion.identity);
newWhale.name = Random.Range (0f, 200f).ToString(); //name is random.
dateData.dataName = name;// store the name of the whale in the whalData class
SaveData();
print(col.gameObject.name);
print(gameObject.name);
whatDatas.Add(whaleData);
Destroy (col.gameObject);
Destroy (gameObject);
// delete if there is any whale information with the same name as Whale's name that will disappear from the dateDatas list (use a simple calculation expression of the unknown method)
if (whaleDatas.Find(whaleData=>whaleData.dataName=colgameObject.GetComponent[Whale>(.name).dataName==colgameObject.GetComponent[Whale>(.name)
{
whaleDatasRemove (col.gameObject.GetComponent[Whale>(whaleData));
Destroy (col.gameObject);
Destroy (gameObject);
}
else
{
break;
}
break;
}
}
}
isDrag = false;
}
Public static void setVector3 (string key, Vector3 value)
{
PlayerPrefs.SetFloat (key + "X", value.x);
PlayerPrefs.SetFloat (key + "Y", value.y);
PlayerPrefs.SetFloat (key + "Z", value.z);
}
Public static Vector3 GetVector3 (string key)
{
Vector3 v3 = Vector3.zero;
v3.x = PlayerPrefs.GetFloat (key + "X");
v3.y = PlayerPrefs.GetFloat (key + "Y");
v3.z = PlayerPrefs.GetFloat (key + "Z");
return v3;
}
private void saveData()
{
for (int i=0; i <whaleDatas.Count; i++)
{
PlayerPrefs.SetString(i+"_name",whaleDatas[i‐dataName));
PlayerPrefs.SetInt(i+"_level",whaleDatas[i‐dataLevel));
Vector3 value = whatDatas[i].dataMousePosition;
string key = i + "_position";
SetVector3 (key,value);
PlayerPrefs.Save();
Debug.Log ("Saved");
}
}
private void LoadData()
{
for(int i=0; i <whaleDatas.Count; i++)
{
whaleDatas[i].dataName = PlayerPrefs.GetString(i+"_name");
whaleDatas[i].dataLevel = PlayerPrefs.GetInt(i+"_level");
string key = i + "_position";
whaleDatas[i].dataMousePosition = GetVector3(key);
}
}
}
Your code is actually broken in term of design.
First PlayerPref doesnt support boolean save by default.
IsSave = PlayerPrefs.HasKey //this will always return false.
thats why everytime you start the game all your saved data is deleted.
You can try this work around to save and load a boolean type How to save bool to PlayerPrefs Unity
Now, How on earth you decided to save and load data at update function?! , this is terrible and has huge performance cost if your data is huge. you just cant do it this way.
Instead, try to make events that store the data and load it instead, like once player enter a trigger box , the data is saved, and ofcourse you dont need to reload it if its saved , bec why you would have to load the data after its being saved?!!
Once again, dont read and write at Update Function like this all the time, this is bad design .

How can I sort the objects in the hierarchy also after filtered them in the search bar?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public class CustomHierarchy : MonoBehaviour
{
private static Vector2 offset = new Vector2(0, 2);
public static Color gameObjectFontColor = Color.black;
public static Color prefabOrgFontColor = Color.black;
public static Color prefabModFontColor = Color.white;
public static Color inActiveColor = new Color(0.01f, 0.4f, 0.25f);
public static Color meshRendererColor = Color.yellow;
static CustomHierarchy()
{
EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI;
}
private static void HandleHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
{
Color fontColor = gameObjectFontColor;
Color backgroundColor = new Color(.76f, .76f, .76f);
FontStyle styleFont = FontStyle.Normal;
var obj = EditorUtility.InstanceIDToObject(instanceID);
GameObject gameObj = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
if (Selection.instanceIDs.Contains(instanceID))
{
backgroundColor = new Color(0.24f, 0.48f, 0.90f);
}
if (obj != null)
{
var prefabType = PrefabUtility.GetPrefabType(obj);
if (gameObj.activeInHierarchy == false)
{
backgroundColor = inActiveColor;
}
if (prefabType == PrefabType.PrefabInstance)
{
styleFont = FontStyle.Bold;
PropertyModification[] prefabMods = PrefabUtility.GetPropertyModifications(obj);
foreach (PropertyModification prefabMod in prefabMods)
{
if (prefabMod.propertyPath.ToString() != "m_Name" && prefabMod.propertyPath.ToString() != "m_LocalPosition.x" && prefabMod.propertyPath.ToString() != "m_LocalPosition.y" && prefabMod.propertyPath.ToString() != "m_LocalPosition.z" && prefabMod.propertyPath.ToString() != "m_LocalRotation.x" && prefabMod.propertyPath.ToString() != "m_LocalRotation.y" && prefabMod.propertyPath.ToString() != "m_LocalRotation.z" && prefabMod.propertyPath.ToString() != "m_LocalRotation.w" && prefabMod.propertyPath.ToString() != "m_RootOrder" && prefabMod.propertyPath.ToString() != "m_IsActive")
{
if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
{
gameObj.transform.SetSiblingIndex(0);
fontColor = meshRendererColor;
}
else
{
fontColor = prefabModFontColor;
}
break;
}
}
if (fontColor != prefabModFontColor)
{
if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
{
gameObj.transform.SetSiblingIndex(0);
fontColor = meshRendererColor;
}
else
{
fontColor = prefabOrgFontColor;
}
}
}
else
{
if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
{
gameObj.transform.SetSiblingIndex(0);
fontColor = meshRendererColor;
}
}
Rect offsetRect = new Rect(selectionRect.position + offset, selectionRect.size);
EditorGUI.DrawRect(selectionRect, backgroundColor);
EditorGUI.LabelField(offsetRect, obj.name, new GUIStyle()
{
normal = new GUIStyleState() { textColor = fontColor },
fontStyle = styleFont
}
);
}
}
public static bool HasAllComponents(GameObject gameObject, params System.Type[] types)
{
for (int i = 0; i < types.Length; i++)
{
if (gameObject.GetComponent(types[i]) == null)
return false;
}
return true;
}
}
I did in some places:
gameObj.transform.SetSiblingIndex(0);
It does working in the main root of the hierarchy there is one object in yellow and he is moving to the top.
But when I filter objects by name in the search bar for example typing: Corridor
There are many objects and none of them is moving to the top.
I want that all the objects in yellow in all the places I added:
gameObj.transform.SetSiblingIndex(0);
Will move to the top.
So if for example there are 30 objects in yellow that are HasAllComponents move all of them to the top even when filtering.
The search bar is a view filter; it has no effect on the state of your object hierarchies. SetSiblingIndex doesn't know anything about search view so it can't reorder things in there. It can only reorder objects that share the same parent.
If you truly have a good reason to modify your object hierarchy and move GameObjects around based on the result of a search, you could parent all the currently selected objects to the same container object near the top of your Scene hierarchy so they all show up first in searches and can then be re-ordered relative to one another.
gameObj.transform.SetParent(someContainerGameObject.transform);
Or you could write your own custom editor window that can search and display in whatever order you want without affecting the Scene hierarchy at all.
Edit: More information about asset searching:
AssetDatabase can be used in editor code to search the entire project library instead of the Scene. Searching is extremely fast even with thousands of assets, so something like this is reasonable to do:
AssetDatabase.FindAssets("t:GameObject");
Which will give you guid strings you can turn into object references using AssetDatabase.GUIDToAssetPath() and AssetDatabase.LoadAssetAtPath(). Once you have the GameObject references, you can check each one with your HasAllComponents method.
If you want to make changes to them, you either a) update your script to do things like AddComponent on them automatically if needed, or b) wrap all the code in a new Editor Window that lists those objects for easy selection in the editor, or c) you can create a new selection automatically through code.
Explaining the details of each option is beyond the scope of this question, but this should get you started.

How can I return from a method also List of transforms or gameobjects without making instances fro new gameobjects?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine.SceneManagement;
public static class TransformSaver
{
[System.Serializable]
public class TransformInfo
{
public string sceneName;
public string name;
public Transform parent;
public Vector3 pos;
public Quaternion rot;
public Vector3 scale;
}
//Save Transform
public static void SaveTransform(Transform[] tranformToSave)
{
TransformInfo[] trnfrm = new TransformInfo[tranformToSave.Length];
for (int i = 0; i < trnfrm.Length; i++)
{
trnfrm[i] = new TransformInfo();
trnfrm[i].sceneName = tranformToSave[i].gameObject.scene.name;
trnfrm[i].name = tranformToSave[i].name;
trnfrm[i].parent = tranformToSave[i].parent;
trnfrm[i].pos = tranformToSave[i].localPosition;
trnfrm[i].rot = tranformToSave[i].localRotation;
trnfrm[i].scale = tranformToSave[i].localScale;
}
string jsonTransform = JsonHelper.ToJson(trnfrm, true);
File.WriteAllText(#"d:\json\json.txt", jsonTransform);
}
//Load Transform
public static Transform[] LoadTransform()
{
string jsonTransform = File.ReadAllText(#"d:\json\json.txt");
if (jsonTransform == null)
{
return null;
}
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(savedTransforms[i].sceneName));
gameObjects[i] = new GameObject();
loadedTransforms[i] = gameObjects[i].transform;
loadedTransforms[i].name = savedTransforms[i].name;
loadedTransforms[i].parent = savedTransforms[i].parent;
loadedTransforms[i].localPosition = savedTransforms[i].pos;
loadedTransforms[i].localRotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
}
The problem is that the method LoadTransform will return array of Transforms but also will create new GameObjects in the Editor in the Hierarchy.
What I want to do is to add something like a boolean flag or maybe other way so I can select if toe return the array of transforms with or without creating new gameobjects so I tried something like that:
//Load Transform
public static Transform[] LoadTransform(bool returnInfo)
{
string jsonTransform = File.ReadAllText(#"d:\json\json.txt");
if (jsonTransform == null)
{
return null;
}
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(savedTransforms[i].sceneName));
if (returnInfo == true)
{
gameObjects[i] = new GameObject();
loadedTransforms[i] = gameObjects[i].transform;
}
loadedTransforms[i].name = savedTransforms[i].name;
loadedTransforms[i].parent = savedTransforms[i].parent;
loadedTransforms[i].localPosition = savedTransforms[i].pos;
loadedTransforms[i].localRotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
I added a bool flag returnInfo but now loadedTransforms is null.
But the main goal is to decide if to return array of transforms with or without making instances for new gameobjects.
The problem is when I'm using Editorwindow type script and inside OnGUI to save and load the objects:
List<GameObject> selections = Selection.objects.OfType<GameObject>().ToList();
if (selections.Count > 0)
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
if (GUILayout.Button("Save selected objects data"))
{
if (selections.Count > 0)
{
tempTransformSelection = selections;
for (var i = selections.Count - 1; i >= 0; --i)
{
var selected = selections[i];
transformSelection.Add(selected.transform);
}
TransformSaver.SaveTransform(transformSelection.ToArray());
transformSelection = new List<Transform>();
}
}
var file = #"d:\json\json.txt";
FileInfo fi = new FileInfo(file);
// By default asume you don't want to show the button
// it will than only be enabled if the later conditions match
bool showButton = false;
// you can check this already here not in the loop
// if no file -> nothing to do
if (!File.Exists(#"d:\json\json.txt") || fi.Length <= 0) return;
// This is only reached if the file exists and is not empty
// check for null transforms
for (int i = 0; i < tempTransformSelection.Count(); i++)
{
// if not null do nothing
if (tempTransformSelection[i] != null)
continue;
// otherwise enable the button and leave the loop
showButton = true;
break;
}
// if not true then button won't be shown
if (showButton == true)
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
if (GUILayout.Button("Instantiate back deleted selected bojects"))
{
TransformSaver.LoadTransform(true);
showButton = false;
}
}
private void OnInspectorUpdate()
{
Repaint();
}
The main goal is to select objects in the hierachy in the editor. Then to click on "Save selected objects data" to save the selected object to a json file.
Then I want to make that only if one of the saved objects to the file is null in the hierarchy fro example if I deleted one or more of the objects in the saved file ! only then enable true the button "Instantiate back deleted selected bojects"
And then when clicking on the button "Instantiate back deleted selected bojects" set the button back to enabled false !
But the important thing is to check for null against the saved list of objects in the json file ! But once again I'm stuck with the tempTransformSelection and with how to enable false/true the button.

Unity - Creating a UI list at runtime programmatically

I have a scene where I should be able to select a 3D model from a list of items, which will then be loaded into the next scene. I want to generate the list of items programmatically, based on what is already stored in the local database. But I have problems getting the list of items to be displayed in the scene ...
So I have a class for the items:
[System.Serializable] public class Item {
public string ObjectGuid;
public string Name;
public string Description;
public Sprite Icon;
public Button.ButtonClickedEvent SelectObject;
}
A script for my scene, that contains a list of items and a method that I call to load the objects from the database:
public class ObjectSelectionScript : MonoBehaviour {
public List<Item> itemList;
public Transform contentPanel;
void LoadObjects() {
StreamReader str = new StreamReader (Application.persistentDataPath + "/" + DataManager.LOCATION_XML_PATH);
string result = str.ReadToEnd ();
str.Close();
var jo = SimpleJSON.JSON.Parse (result);
if (jo ["Objects"] != null) {
for (int i = 0; i < jo["Objects"].Count; i++) {
if (!string.IsNullOrEmpty(jo["Objects"][i]["FileGuid"])) {
Item newObject = new Item();
newObject.ObjectGuid = jo["Objects"][i]["ObjectGuid"];
newObject.Name = jo["Objects"][i]["Name"];
newObject.Description = jo["Objects"][i]["Description"];
//newObject.SelectObject = new Button.ButtonClickedEvent();
itemList.Add(newObject);
}
}
}
}
A ModelObject class:
public class ModelObject : MonoBehaviour {
public Button button;
public Text Name;
public Text Description;
public Image Icon;
}
And a prefab ModelObjectPrefab that contains Name, Description, a Thumbnail, added to my assests folder.
The PopulateList() method is my problem. I can't get to instatiate the prefab and then create ModelObjects that I can throw in the contentPanel in the interface. This is my code:
void PopulateList()
{
try {
foreach (var item in itemList)
{
GameObject newModel = Instantiate(ModelObject) as GameObject;
ModelObject myModelObject = newModel.GetComponent<ModelObject>();
myModelObject.Name.text = item.Name;
myModelObject.Description.text = item.IDescription;
myModelObject.icon.sprite = item.Icon;
myModelObject.button.onClick = item.selectObject;
newModel.transform.SetParent(contentPanel);
}
} catch (System.Exception ex) {
}
}
I currently get null pointer on instantiate. I have tried Resources.Load, and variations of the Instatiate method, but I did not find a solution.
Can anyone give me a hint what is the problem? How should PopulateList() look like to get the items to the interface?
Note that the Instantiate() method should not be given a type (eg. ModelObject) as an argument. Rather, you should be passing it an object instance (in this case, your prefab GameObject "ModelObjectPrefab"). The documentation specifically indicates that it requires:
An existing object that you want to make a copy of
Otherwise, you're probably going to get unexpected behaviour like this. In your code, we need to pass a reference to "ModelObjectPrefab" as an argument of the Instantiate() call. I would suggest modifying your class containing PopulateList() to look like:
// New variable to hold prefab object
public GameObject modelObjectPrefab;
// ...
void PopulateList()
{
try {
foreach (var item in itemList)
{
// Reference prefab variable instead of class type
GameObject newModel = Instantiate(modelObjectPrefab) as GameObject;
ModelObject myModelObject = newModel.GetComponent<ModelObject>();
myModelObject.Name.text = item.Name;
myModelObject.Description.text = item.IDescription;
myModelObject.icon.sprite = item.Icon;
myModelObject.button.onClick = item.selectObject;
newModel.transform.SetParent(contentPanel);
}
} catch (System.Exception ex) {
}
}
Then, in the Editor window, drag your prefab "ModelObjectPrefab" from the assets panel onto the new Model Object Prefab field in the script properties, to form the connection between the script and the prefab.
Hope this helps! Let me know if you have any questions.
Note for readers. "GUI" is no longer available in Unity. Simply use the new UI system. Example http://www.folio3.com/blog/creating-dynamic-scrollable-lists-with-new-unity-canvas-ui/
The only thing that did work was to create the whole GUI programmatically.
I created a GUI.matrix and added everything inside it. It did not look very good, so I spent a lot of time trying to make it look better and to be responsive on mobile devices, but in the end this was the only solution that worked.
Just as a quick reference for who ever might have the same issue, the solution has this structure:
public void OnGUI ()
{
var savedMatrix = GUI.matrix;
scrollPosition = GUI.BeginScrollView (new Rect (10f, 10f, Screen.width - 10f, Screen.height - 10f), scrollPosition, new Rect (10f, 10f, Screen.width - 10f, (_files.Count + 2) * 200f));
if (Input.touchCount == 1) {
Vector2 touchDelta2 = Input.GetTouch (0).deltaPosition;
scrollPosition.y += touchDelta2.y;
}
var selected = GUILayout.SelectionGrid (gridInt, _files.ToArray (), 1, MyStyle);
if (selected >= 0) {
if (selected == 0) {
if (selectedItem != null )
if (selectedItem.Parent != null)
selectedItem = selectedItem.Parent;
else
selectedItem = null;
} else {
SelectFolder (selected);
}
RefreshViewModel ();
}
GUI.EndScrollView ();
GUI.matrix = savedMatrix;
}

Categories