Saving components changed at runtime - weird behaviour of some fields / properties

Hello,

No no no, don’t post you comment “This has been requested so often…” yet, I’m not here to complain about it.

This is my first post, and I’m new to unity and c#, I do have previous experience with c++ and some other languages. When I started using unity I immediately was irritated that I couldn’t save my runtime changes.
The “PlaySave” component I’m building allows you to save any changes made to a GameObject at runtime. It’s going well so far. The PlaySave component restores almost all fields and properties properly, it even restores components added at runtime.
Some fields and properties don’t work well though. Here are the ones I found

Mesh Filter: The mesh always get’s set to null
(It also gets called cube instance (Mesh filter) instead of just cube (Mesh filter))

Mesh renderer: Everything in the “Materials” tab.
(A MonoBehaviour with a Material field does work)

Particle Emitters: The one shot,Interpolate triangles and systematic toggle
(really weird, other toggles are fine).

Camera: The normalized view port rect gets weird W and H values (W ~2, H ~0,04)

I don’t want to release my entire script here, since it’s going on the asset store. Here is a brief explanation of how it works though:

A control object overrides the GUI function for the PlaySaveComponent. This master controller has a static list of CompObj. CompObj is a simple class to store a component and it’s current values. When runtime is exited the runtime components are deleted, so the controller loops through the CompObjs to assign them the editor variant of the component they are attached to. The CompObj finally loops through all the fields and properties of it’s component it can acces to restore them.

I know it’s probarbly a bit vague, but I hope there’s a unity guru spotting a pattern in the variables or something, I don’t know, I’m really stuck. Thanks in advance! :slight_smile:

best regards,
Arthur Brusse

p.s. C# is great, I can;t even imagine how hard this would have been in c++.

Mesh Filter: the mesh you’re saving is probably destroyed once the level unloads. You should use .sharedMesh and not .mesh if the mesh your saving is a asset and not generated by script (in which case you couldnt save at all)

Mesh Renderer: probably the same issue use the .sharedMaterials instead of .materials. Same case goes with materials generated at runtime.

Particle Emitters: Those things are not exposed script side. I do not think even reflection will pick up on them. I beleive the custom editor in unity for those components use native code.

I loved my first time going to c# from c++ too. Stuff gets done so much quicker… but i wish c# had real macros, typedefs, and of course templates ( ie things about c++ people hate, but make life so much easier haha )

to go in more about the shared* version of those properties. The non shared version like mesh or .materials actually makes a clone of whatever was set, then sets the clone in and returns it to you to avoid editing of the actual asset. Its annoying, but generally always use anything prefixed with shared when you see it and its in the unityengine namespace.

Thank you for your speedy and extenive response :slight_smile: Implenting sharedMaterial and sharedMesh shouldn’t be to hard. To bad the ParticleEmitters are “broken”.

Wait, there are no macros in c#? Ok, that is annoying. But I don’t see the need for templates. Can’t you use a c# object instead of class T? C# has some very usefull functions to find out the type of an object. I’m not very experienced in c# though so you’re probarbly right.

There are macros in C# but they are more like switches, they need to be at the top of the file and are only recognized within the file itself ( unless they are specified in command line ). You cannot do inline function or even inline definition of value. This is about it:
#define USE_THAT

#if USE_THAT
#elif USE_OTHER
#endinf

#if !USE_THAT
#endif

And C# has generics. It handles some things you would use templates for on C++ but have a lot more restrictions. It works ok but is much less powerful than C++ templates.

object type in C# is well, odd. It does everything it should do, but the parts that get annoying come in with value types ( or structs ). Whenever you cast a struct to object it takes heap memory to box the data as if it were a class reference, but it doesnt hold a reference to the original data ( not that it should ), so once you’ve got a struct ‘cast’ to a object, you have to cast to the struct type, to change a value inside the struct and then box it again to object. But classes work fine, though theres still performance loss dealing with object directly.

The best thing about C# is probably interfaces, but those too have similar issues. If you have a struct implement a interface for example, a function which takes the interface type as a argument and past a instance of the struct to it, it boxes it similarly to how object would, meaning if you have anything non-readonly in the interface type you should not use struct unless your planning to pass by reference with generics like
void Kill( ref T structValHealth ) where T : struct, ILifeform { structValHealth.health = 0; }
but that leaves you in a situation where you then restrict ILifeform to needing to be a struct interface. so you need to then implement
void Kill( T classValHealth ) where T : class, ILifeform { classValHealth.health = 0; }
but then your at a point where you have to specialize calls between struct and class ebcause the ref overload.

I’m kind of going on, but theres just really annoying things in C# that are a product of trying to make things simpler but add in complexity in the long run. With the above situation the real resolution is to forget about trying to be good on memory with structs, only use interfaces on structs if the struct is for data reading only, and avoid using generics that have restrictions on either class or struct with the inclusion of interface.

Wheh, that was harder than I thought, After ignoring the material and materials property it still didn’t work. After a while I started digging into why the materials were listed as material (instance). Apparently they are instantiated for some reason. After I while I found out that not only writing to material made a copy of the object, but even reading made the renderer instantiate itself. So after adjusting my code to not even read material(s) it worked :slight_smile: Same thing goes for many variables actually, this was a real improvement to the script!

That’s weird, why wouldn’t they implement some proper generics for values? Haven’t had the need for generics so far but this isn’t encouraging…

About the macros: That really sucks. I hadn’t noticed it before but now when I was fixing the problem above I really missed it. Now I need some wanky function to easily disclude a variable from being read. A #define would have been better. Microsoft says it’s because it;s not readable, but I don;t think that’s valid argument. A variable name a isn’t really descriptive either, so should short var names give compiller errors? I think it’s up to developers to make readable code, not to MS.

C# is still pretty cool though.

Anyway, thanks for the help again!

best regards,
Arthur Brussee

ps: Your signature is bothering me. Isn’t it pronounced C sharp?
pps: Or am I totally missing your sarcasm?