- Home /
Script variable reverted to prefab when hit play.
Hello!
I use a c# mono behaviour that is also executed in edit mode. I have a public bool variable that i set to true via script. in edit mode i can see this value is set as aspected to true. when i hit play, this value switches to false, because i have a prefab with a false value. how can i prevent unity from reverting this value when i hit play? when i set this bool manually, it gets bold and is not affected, when i hit play.
Best, Andre
Sorry for the double answer! Here the script attatched - i hope it's not to much to read ;) #if (UNITY_EDITOR_WIN||UNITY_EDITOR||UNITY_EDITOR_64) using UnityEditor; using UnityEngine; using System; using System.Collections;
/// <summary>
/// Billboard baker pro class. Needs to be attatched to a GameObject, the billboard object.
/// Handles all parameters to setup a working multitexture billboard.
/// </summary>
/// Also executed in edit mode to interact with materials and shader.
[ExecuteInEditMode]
// The GameObject requires a MeshRenderer component
[RequireComponent (typeof (MeshRenderer))]
// The GameObject requires a MeshFilter component
[RequireComponent (typeof (MeshFilter))]
public class Billboard : MonoBehaviour {
/// <summary>
/// String defining the billboard class. Billboards of the same billboard class
/// share the same billbord textures. Keep in mind that two target objects containing the same render objects can't share the same billboard class if they have different rotations or scalings.
/// Used to associate prefabs and scene GameObjects for baking process.
/// </summary>
[Tooltip("String defining the billboard class. Billboards of the same billboard class share the same billbord textures. Keep in mind that two target objects containing the same render objects can't share the same billboard class if they have different rotations or scalings.")]
public string _billboardClass="";
/// <summary>
/// The GameObject to create a billboard from.
/// </summary>
[Tooltip("GameObject to bake a billboard from.")]
public GameObject _targetGameObject;
/// <summary>
/// The size of one frame in pixel.
/// </summary>
[Tooltip("The size (height and width) of one frame in pixel.")]
public int _size;
/// <summary>
/// The number of tiles that represent the target GameObject from different views.
/// </summary>
[Tooltip("The number of tiles that represent the target object from different views.")]
public int _count;
/// <summary>
/// The zoom factor the orthographic size of the camera is divided by.
/// </summary>
[Tooltip("The zoom factor the orthographic size of the camera is divided by.")]
public float _zoomFactor=1.0f;
/// <summary>
/// You can disable baking this billboard. If you have multiple billboards of the same class
/// it is sensful to only bake one billboard. If you want to keep your bake, disable all bake
/// values of all billboards of the same class.
/// </summary>
[Tooltip("You can disable baking this billboard class. If you have multiple billboards of the same class it is sensful to only bake one billboard. If you want to keep your bake, disable all bake values of all billboards of the same class.")]
public bool _bake=true;
public bool _billboardInitialized=false;
/// <summary>
/// GameObject with information component
/// </summary>
private GameObject _billboardInformationGameObject=null;
/// <summary>
/// At the very begnning of loading scene
/// </summary>
void Awake () {
// If it is in edit mode
if (!EditorApplication.isPlayingOrWillChangePlaymode) {
// Check if billboard class is an empty string
if(_billboardClass=="") {
// Billboard class string hasn't been set jet,
// so set string to target game objects name.
_billboardClass = _targetGameObject.name;
}
// Load Object from Ressources
UnityEngine.Object _tmpObject = Resources.Load (GetSceneName(EditorApplication.currentScene) + "/" + "info" + _billboardClass);
// Does the thing we want to load exist?
if(_tmpObject==null)
// Abort if this ressource doesn't exist
return;
// Instantiate GameObject
_billboardInformationGameObject = (GameObject)Instantiate (_tmpObject);
// Is the billboard class new baked?
if (_billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._newBake == true) {
// This billboard class is not baked new anymore
_billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._newBake = false;
// Store back as prefab
PrefabUtility.CreatePrefab ("Assets/Billboard Baker Pro/Resources/" + GetSceneName(EditorApplication.currentScene) + "/" + "info" + _billboardClass + ".prefab", _billboardInformationGameObject);
// Do this only once every billboard class:
// Declare texture variable
Texture2D _tex;
// Load Texture, if not exists, return
if ((_tex = Resources.Load (GetSceneName(EditorApplication.currentScene) + "/" + _billboardClass, typeof(Texture2D)) as Texture2D) == null) {
// Show warning for debugging.
Debug.LogWarning("Load billboard texture " + _billboardClass + " failed.");
return;
}
// As we are now in edit mode, change texture import settings
SetTextureImporterFormat (_tex, true);
// Declare material variable
Material _materialToModify;
// Load Material from assets. if no stored material exists, return.
if ((_materialToModify = Resources.Load (GetSceneName(EditorApplication.currentScene) + "/" +_billboardClass, typeof(Material)) as Material) == null)
return;
// Also make changes to the material. They are stored directly, as we are in edit mode. Set materials main texture
_materialToModify.mainTexture = _tex;
// Set shader frame count
_materialToModify.SetFloat ("_Count",(float)_count);
}
// Has the billboard class been initialized after last bake?
if (this._billboardInitialized==false) {
// Set billboard class to initialized, so it will initialize only once
this._billboardInitialized = true;
EditorUtility.SetDirty (this);
// Declare material variable
Material _tmpMaterial;
// Load Material from assets, if not exists, return
if ((_tmpMaterial = Resources.Load (GetSceneName (EditorApplication.currentScene) + "/" + _billboardClass, typeof(Material)) as Material) == null)
return;
// Select Renderer
Renderer _billboardRenderer = this.gameObject.GetComponent<Renderer> ();
// Set Renderer material
_billboardRenderer.material = _tmpMaterial;
// Resize Billboard transform
this.transform.localScale = new Vector3 (_billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._orthographicSize * 2.0f, _billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._orthographicSize * 2.0f, _billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._orthographicSize * 2.0f);
// Set Billboard world position to be target world position
this.transform.position = _targetGameObject.transform.position;
// Retrieve local position
Vector3 _tmpLocalPosition = this.transform.localPosition;
// Add y offset
_tmpLocalPosition.y += _billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._offset;
// Adjust Billboard local position
this.transform.localPosition = _tmpLocalPosition;
// Retrieve MeshFilter
MeshFilter _tmpMeshFilter = this.gameObject.GetComponent<MeshFilter> ();
// If mesh filter is attatched and shared mesh is not empty
if ((_tmpMeshFilter != null) && (_tmpMeshFilter.sharedMesh != null)) {
// Create a new independant mesh instance with the same propertys as the shared mesh
Mesh _meshCopy = Mesh.Instantiate (_tmpMeshFilter.sharedMesh) as Mesh;
// Create uv2 2 dimensional vectors
Vector2[] _uv2s = new Vector2[_meshCopy.vertices.Length];
// Loop through all vertices
for (int i = 0; i < _uv2s.Length; i++) {
// Pass object (billboard) position needed for draw call batching support.
// Here all mesh objects are combined and the billboard positions get lost.
// So we save this information in the unneeded uv's
_uv2s [i] = new Vector2 (this.transform.position.x, this.transform.position.y);
}
// Store uv's in the second slot
_meshCopy.uv2 = _uv2s;
// Create uv3 2 dimensional vectors
Vector2[] _uv3s = new Vector2[_meshCopy.vertices.Length];
// Loop through all vertices
for (int i = 0; i < _uv3s.Length; i++) {
// Pass object (billboard) position needed for draw call batching support.
// Also pass object scale. As the billboard should be scaled uniformly (x==y==z)
// one value is enough.
_uv3s [i] = new Vector2 (this.transform.position.z, this.transform.localScale.x);
}
// Store uv's in the third slot
_meshCopy.uv3 = _uv3s;
// Replace the shared mesh with the new instance
_tmpMeshFilter.mesh = _meshCopy;
}
}
// Destroy information GameObject
_billboardInformationGameObject.GetComponent<BillboardBakerInformationExchange> ()._destroySelf =true;
}
}
/// <summary>
/// OnValidate is called when user makes input
/// </summary>
void OnValidate () {
// Check if billboard class is an empty string
if ((_billboardClass == "")&&(_targetGameObject!=null))
// Set billboard class to target GameObject's name
_billboardClass = _targetGameObject.name;
// Check count variable
if (_count < 1)
// set to 1 if value is below
_count = 1;
}
/// <summary>
/// Sets the isReadable flag of the Texture
/// </summary>
/// <param name="texture">Texture.</param>
/// <param name="isAlphaTransparency">If set to <c>true</c> alpha is transparency.</param>
public static void SetTextureImporterFormat( Texture2D texture, bool isAlphaTransparency)
{
// Nothing to do if no texture passed
if ( null == texture ) return;
// Retrieve textures file path
string assetPath = AssetDatabase.GetAssetPath( texture );
// Open path with texture importer class
var tImporter = AssetImporter.GetAtPath( assetPath ) as TextureImporter;
// Did we succeed wopen the file?
if ( tImporter != null )
{
// Change texture type to advanced
tImporter.textureType = TextureImporterType.Advanced;
// Set alpha to be the transparency value
tImporter.alphaIsTransparency = isAlphaTransparency;
// Keep the original border pixels in all mip map levels
tImporter.borderMipmap = true;
// Reimport the texture with the chosen settings
AssetDatabase.ImportAsset( assetPath );
// Refresh the database
AssetDatabase.Refresh();
}
}
/// <summary>
/// Creates the billboard game object.
/// </summary>
/// <param name="menuCommand">Menu command.</param>
[MenuItem("GameObject/3D Object/Billboard", false, 10)]
static void CreateBillboardGameObject(MenuCommand menuCommand) {
// Create a custom game object with quad
GameObject _gameObject = GameObject.CreatePrimitive (PrimitiveType.Quad);
// Set Name to default
_gameObject.name = "Billboard";
// Remove Mesh Collider as you should use the mesh collider of the original mesh
DestroyImmediate(_gameObject.GetComponent<MeshCollider>());
// Add billboard component to the game object
_gameObject.AddComponent<Billboard>();
// Enable component
_gameObject.GetComponent<Billboard> ().enabled = true;
// Ensure it gets reparented if this was a context click (otherwise does nothing)
GameObjectUtility.SetParentAndAlign(_gameObject, menuCommand.context as GameObject);
// If parent exists,
if (_gameObject.transform.parent.gameObject != null)
// set parent to be the target game object
_gameObject.GetComponent<Billboard>()._targetGameObject = _gameObject.transform.parent.gameObject;
// Initialize count to 16
_gameObject.GetComponent<Billboard> ()._count = 16;
// Initialize size to 64 px
_gameObject.GetComponent<Billboard> ()._size = 64;
// Register the creation in the undo system
Undo.RegisterCreatedObjectUndo(_gameObject, "Create " + _gameObject.name);
// Select the created game object
Selection.activeObject = _gameObject;
}
/// <summary>
/// Gets the name of the scene.
/// </summary>
/// <returns>The scene path.</returns>
public static string GetSceneName(string _scenePath)
{
// Return the scene name only
return System.IO.Path.GetFileNameWithoutExtension(_scenePath);
}
}
Yes, you are absolutely right, when i save, close and reopen the scene these values are the prefab values again. I tryed this:
EditorUtility.SetDirty (this);
in my monobehaviour function right after the variable change, but nothing changes. Is there something else i have to put there ins$$anonymous$$d of this? (this is pointing at the mono behaviour instance)
thank you for the quick response!
best, Andre
Edit:
at vivien: so you wrote that the setdirty() function needs a gameobject or component to be passed. as the 'this' statement refers to the monobehaviour instance just edited, EditorUtility.SetDirty (this); should work, but doesn't actually.
at brunocoimbra: so you mean i change the bool variable somewhere else when i hit play. i do not think so, because when i use the script on a gameobject that has no prefab linked, it works as aspected. I use $$anonymous$$onoDevelop as editor.
You can reproduce this case quite easy. Add this script to a GameObject and make a prefab of it. When your prefabs _test value is false and you hit play, _test is false. In edit mode it will be true.
[ExecuteInEdit$$anonymous$$ode]
public class Test : $$anonymous$$onoBehaviour() {
public bool _test=false;
void Awake() {
_test=true;
}
}
Oh, i tryed it myself and this time it works. Huh. Strange ;)
at brunocoimbra:
The first bool _bake is not in use jet, as it's a future feature. It's this bool causing issues: _billboardInitialized
I tried to set up my gameobject from scratch and it still doesn't work although having the same structure as the working test script above.
Unbelievable! This was the solution! You have to add the undo line, else it won't work in this case: Undo.RecordObject(this,"Set Value");
So really thank you all for your effort and saving my day ;)
Convert your answer to a comment, never post something as answer that is not meant to be the answer to the question.
Back to the question, can you post the script? I believe that maybe you are initializing it somewhere in your code. If you use Visual Studio, you can right click on the variable name and click in "Find all references", it will make the search easier and more accurate.
There are just 2 bools:
public bool _bake = true;
public bool _billboardInitialized = false;
The first one is never referenced in that script, while the other is setted to true on Awake in line 111 (the line number will look diferent in your project, as you did not formated your entire code).
Which one is causing you trouble?
Answer by VivienS · Jan 28, 2016 at 01:53 PM
Try http://docs.unity3d.com/ScriptReference/EditorUtility.SetDirty.html when you execute the script that changes the public variable.
I guess the value is not correctly flagged as an override to the prefab and probably also wont be stored if you save / re-open the scene?
Edit:
You have to Execute the SetDirty() on the Component that you are modifying. Plus, since Unity 5.3 you apparently need to record an Undo action too. (Had to google that myself)
I stripped your code down a bit (for visibility's sake ;)) and tested it. This combination of calls works: the variable becomes an override (you can recognize it by the fat font in the editor).
[ExecuteInEditMode]
public class Billboard : MonoBehaviour
{
public bool _billboardInitialized = false;
void Awake ()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
if (this._billboardInitialized == false)
{
Undo.RecordObject (this, "Set Value");
this._billboardInitialized = true;
EditorUtility.SetDirty (this);
}
}
}
}