- Home /
Handle modified variables being temporarily reset at runtime
(I asked this same question in the forum before finding this section. I apologise if that breaks any rules and will remove the offending duplicate if necessary)
edit: here's the link to the forum thread http://forum.unity3d.com/threads/174300-Variables-modified-by-handles-are-being-temporarily-reset-at-runtime
Hello, long-time lurker, first time poster. I've tried my best to get as far as I could with existing documentation and support that I've found online, but now I am officially stuck! I'm sure I'm missing something fundamental here, so lets see how badly I'm failing.
What I'm trying to do:
I'm creating an editor extension that will include the ability to quickly add and modify box and sphere colliders to aid in camera tracking.
I've made prefabs of these objects with custom inspectors to modify their size and shape, among other settings. I've also written some custom camera facing gizmos to display their size and shape to the user while they are testing their game. The objects also have handles that will allow the user to modify the collider's size and shape from within the scene view, with the intention being that they can quickly make modifications without having to look over to the inspector.
The actual problem:
My problem is that, while these handles will successfully modify the variables I want, updating the gizmos to show the updated information, the moment I press play, the gizmos reset to the last known setting made by the inspector, completely ignoring the handle modifications.
The variables aren't forgotten though, and when I select the object again in editor mode, the gizmos instantly revert to their correct size and shape.
This is only happening when the variables are modified by the handles. when they are modified by the custom inspector, the whole thing works as intended.
Here is the code I'm using. It's based on this star tutorial.
The data storage, retrieval and gizmo drawing script
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class TestData : MonoBehaviour {
public Vector2 innerRect = new Vector2(7, 4); //Area data
public Vector2 outerRect = new Vector2(10, 5);
public float Radius = 0.5f; //radius data
//Set Zone Data
public void UpdateTestData(){
ClampValues();
var boxInner = GetComponent<BoxCollider>();
boxInner.size = new Vector3(innerRect.x, innerRect.y, innerRect.x);
var boxOuter = transform.Find("Outer Box").GetComponent<BoxCollider>();
boxOuter.size = new Vector3(outerRect.x, outerRect.y, outerRect.x);
var sphere = transform.Find("Outer Sphere").GetComponent<SphereCollider>();
sphere.radius = Radius;
}
//Get Test Data
public void GetTestData(){
var boxInner = GetComponent<BoxCollider>();
innerRect = new Vector2((boxInner.size.x > boxInner.size.z) ? boxInner.size.x : boxInner.size.z, boxInner.size.y);
var boxOuter = transform.Find("Outer Box").GetComponent<BoxCollider>();
outerRect = new Vector2((boxOuter.size.x > boxOuter.size.z) ? boxOuter.size.x : boxOuter.size.z, boxOuter.size.y);
var sphere = transform.Find("Outer Sphere").GetComponent<SphereCollider>();
Radius = sphere.radius;
ClampValues();
}
//Clamp Values to reasonable limits
void ClampValues() {
//Inner Bounds
innerRect.x = (innerRect.x < 0.1f) ? 0.1f : innerRect.x;
innerRect.y = (innerRect.y < 0.1f) ? 0.1f : innerRect.y;
//Outer Bounds
outerRect.x = (innerRect.x >= outerRect.x) ? innerRect.x : outerRect.x;
outerRect.y = (innerRect.y >= outerRect.y) ? innerRect.y : outerRect.y;
Radius = (Radius < 0) ? 0: Radius;
}
void onEnable(){
UpdateTestData();
}
void Reset () {
UpdateTestData();
}
void OnDrawGizmos(){
//Drawing a custom rectangle to represent the zone bounds
Vector3 currentCamRot = Camera.main.transform.forward;
Quaternion lookRot = Quaternion.LookRotation(currentCamRot); //Get the camera-facing angle
//Draw inner bounds
Gizmos.color = Color.red;
//Get the 4 corners of the area
Vector3 PointA = new Vector3((-innerRect.x/2), innerRect.y/2, 0f),
PointB = new Vector3(innerRect.x/2, innerRect.y/2, 0f),
PointC = new Vector3(innerRect.x/2, (-innerRect.y/2), 0f),
PointD = new Vector3((-innerRect.x/2), (-innerRect.y/2), 0f);
//Draw the 4 edges of the area
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointA), transform.TransformPoint(lookRot * PointB));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointB), transform.TransformPoint(lookRot * PointC));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointC), transform.TransformPoint(lookRot * PointD));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointD), transform.TransformPoint(lookRot * PointA));
//Draw outer bounds
Gizmos.color = Color.yellow;
//Get the 4 corners of the area
PointA = new Vector3((-outerRect.x/2), outerRect.y/2, 0f);
PointB = new Vector3(outerRect.x/2, outerRect.y/2, 0f);
PointC = new Vector3(outerRect.x/2, (-outerRect.y/2), 0f);
PointD = new Vector3((-outerRect.x/2), (-outerRect.y/2), 0f);
//Draw the 4 edges of the area
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointA), transform.TransformPoint(lookRot * PointB));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointB), transform.TransformPoint(lookRot * PointC));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointC), transform.TransformPoint(lookRot * PointD));
Gizmos.DrawLine(transform.TransformPoint(lookRot * PointD), transform.TransformPoint(lookRot * PointA));
//Draw Circle
Gizmos.color = Color.green;
int sides = 32;
Vector3 RadiusVector = new Vector3(0f, Radius, 0f);
for(int i=0;i<sides;i++){
Quaternion rotationA = Quaternion.Euler(0f, 0f, (360f/sides)*i),
rotationB = Quaternion.Euler(0f, 0f, (360f/sides)*(i+1));//get points for the side we're on
PointA = rotationA * RadiusVector;
PointB = rotationB * RadiusVector;//scale points by the size of the ring
PointA = transform.TransformPoint(lookRot * PointA);
PointB = transform.TransformPoint(lookRot * PointB);//rotate the ring towards the camera
Gizmos.DrawLine(PointA, PointB);
}
}
}
The editor script
using UnityEngine;
using UnityEditor;
using System.Collections;
[CanEditMultipleObjects, CustomEditor( typeof(TestData))]
public class ACTestEditor : Editor {
private static Vector3 pointSnap = Vector3.one * 0.1f;
private SerializedObject test;
private SerializedProperty
innerRect,
outerRect,
Radius;
void OnEnable(){
test = new SerializedObject(targets);
innerRect = test.FindProperty("innerRect");
outerRect = test.FindProperty("outerRect");
Radius = test.FindProperty("Radius");
}
public override void OnInspectorGUI ()
{
foreach(TestData t in targets){
t.GetTestData();
}
test.Update();
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PropertyField(innerRect, true);
EditorGUILayout.PropertyField(outerRect, true);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PropertyField(Radius);
}
EditorGUILayout.EndHorizontal();
if(test.ApplyModifiedProperties() || (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")){
foreach(TestData t in targets){
t.UpdateTestData();
}
}
}
void OnSceneGUI(){
//Custom Handles
TestData test = (TestData)target;
test.GetTestData();
Transform tTransform = test.transform;
Undo.SetSnapshotTarget(test, "Adjust Test Data");
//Camera angle
Vector3 CamRot = Camera.main.transform.forward;
Quaternion lookRot = Quaternion.LookRotation(CamRot);
//inner box
Handles.color = Color.red;
Vector3 innerRectWidth = new Vector3(test.innerRect.x/2, 0, 0),
innerRectHeight = new Vector3(0, test.innerRect.y/2, 0);
for(int i=0; i<4; i++){
Vector3 handlePoint;
switch(i){
case 0:
handlePoint = innerRectHeight - innerRectWidth/2;
break;
case 1:
handlePoint = innerRectWidth + innerRectHeight/2;
break;
case 2:
handlePoint = -innerRectHeight + innerRectWidth/2;
break;
case 3:
handlePoint = -innerRectWidth - innerRectHeight/2;
break;
default:
handlePoint = Vector3.zero;
break;
}
handlePoint = tTransform.TransformPoint(lookRot * handlePoint);
Vector3 newPoint = Handles.FreeMoveHandle(handlePoint, Quaternion.identity, HandleUtility.GetHandleSize(handlePoint)/5, pointSnap, Handles.SphereCap);
if(handlePoint != newPoint){
newPoint = newPoint - tTransform.position;
newPoint = Quaternion.Inverse(lookRot) * newPoint;
switch(i){
case 0:
test.innerRect.y = newPoint.y*2;
break;
case 1:
test.innerRect.x = newPoint.x*2;
break;
case 2:
test.innerRect.y = -newPoint.y*2;
break;
case 3:
test.innerRect.x = -newPoint.x*2;
break;
default:
break;
}
test.UpdateTestData();
}
}
//outer box
Handles.color = Color.yellow;
Vector3 outerRectWidth = new Vector3(test.outerRect.x/2, 0, 0),
outerRectHeight = new Vector3(0, test.outerRect.y/2, 0);
for(int i=0; i<4; i++){
Vector3 handlePoint;
switch(i){
case 0:
handlePoint = outerRectHeight + outerRectWidth/2;
break;
case 1:
handlePoint = outerRectWidth - outerRectHeight/2;
break;
case 2:
handlePoint = -outerRectHeight - outerRectWidth/2;
break;
case 3:
handlePoint = -outerRectWidth + outerRectHeight/2;
break;
default:
handlePoint = Vector3.zero;
break;
}
handlePoint = tTransform.TransformPoint(lookRot * handlePoint);
Vector3 newPoint = Handles.FreeMoveHandle(handlePoint, Quaternion.identity, HandleUtility.GetHandleSize(handlePoint)/5, pointSnap, Handles.SphereCap);
if(handlePoint != newPoint){
newPoint = newPoint - tTransform.position;
newPoint = Quaternion.Inverse(lookRot) * newPoint;
switch(i){
case 0:
test.outerRect = new Vector2(test.outerRect.x, newPoint.y*2);
break;
case 1:
test.outerRect = new Vector2(newPoint.x*2, test.outerRect.y);
break;
case 2:
test.outerRect = new Vector2(test.outerRect.x, -newPoint.y*2);
break;
case 3:
test.outerRect = new Vector2(-newPoint.x*2, test.outerRect.y);
break;
default:
break;
}
test.UpdateTestData();
}
}
//radius
Handles.color = Color.green;
Vector3 RadiusVector = new Vector3(0f, test.Radius, 0f);
for(int i = 0; i < 4; i++){
Quaternion rotation = Quaternion.Euler(0f, 0f, 90f*i);
Vector3 oldPoint = rotation * RadiusVector;
oldPoint = tTransform.TransformPoint(lookRot * oldPoint);
Vector3 newPoint = Handles.FreeMoveHandle(oldPoint, Quaternion.identity, HandleUtility.GetHandleSize(oldPoint)/5, pointSnap, Handles.SphereCap);
if(oldPoint != newPoint){
test.Radius = Vector3.Magnitude(newPoint - tTransform.position);
test.UpdateTestData();
}
}
}
}
An example:
I've created an isolated package of the problem in hopes that it might make my issue clearer to others. https://dl.dropbox.com/u/1672880/test%20bug%20package.unitypackage
Simply add the package to an empty project, drag the test object into the scene and modify its shape using the handles.
(If you see wireframes of the collider components over the gizmos, just collapse the components in the inspector)
Make sure the object is deselected and then press play. You'll see the gizmos reset to their original shape and size.
If you go back to edit mode and reselect the object, you should see the gizmos update to their proper settings.
Additionally, if you now edit the shapes from the custom inspector, those settings will be remembered at runtime.
My current suspicions are that it is something to do with serialization, which I admit I know very little about, so any help or push in the right direction would be greatly appreciated, thankyou.
Answer by whydoidoit · Mar 16, 2013 at 08:34 PM
A long question, but a short answer!
You need to put an EditorUtility.SetDirty(test); after each test.UpdateTestData() to tell the system that you've modified something and it needs saving.
If you cross post on the forum please include a link to the other post in each direction.
That did it! Thank you very much! I'll make sure to update the forum thread with the answer and a link to here.