This shows you the differences between two versions of the page.
— |
objectlayout [2021/05/07 12:13] (current) 101.115.158.4 created |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Object Layout ====== | ||
+ | **WARNING: This is an // | ||
+ | |||
+ | All classes that inherit from UnityEngine.Object are actually wrappers for a native object on the < | ||
+ | <code c#> | ||
+ | class Object | ||
+ | { | ||
+ | // Cached pointer to the native object | ||
+ | IntPtr m_CachedPtr; | ||
+ | | ||
+ | // Cached instance ID from native object | ||
+ | int m_InstanceID; | ||
+ | | ||
+ | // True purpose unknown | ||
+ | string m_UnityRuntimeErrorString; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The cached pointer can be accessed through reflection in two ways: | ||
+ | * Accessing the m_CachedPtr field | ||
+ | * Calling the ' | ||
+ | |||
+ | Once you have retrieved the pointer, you can access the native object by defining the appropriate structs and using unsafe code. | ||
+ | |||
+ | ===== The C++ Class Hierarchy ===== | ||
+ | The < | ||
+ | |||
+ | You can define this from C# using the following set of types (for the sake of brevity, these are simplified definitions): | ||
+ | |||
+ | <code c#> | ||
+ | public struct NonCopyable | ||
+ | { | ||
+ | public IntPtr MethodTable; | ||
+ | } | ||
+ | |||
+ | public struct AllocationRootWithSalt | ||
+ | { | ||
+ | public uint m_Salt; | ||
+ | public uint m_RootReferenceIndex; | ||
+ | } | ||
+ | |||
+ | public struct MemLabelId | ||
+ | { | ||
+ | #if UNITY_EDITOR || DEVELOPMENT_BUILD | ||
+ | public AllocationRootWithSalt m_RootReferenceWithSalt; | ||
+ | #endif | ||
+ | |||
+ | public int identifier; | ||
+ | } | ||
+ | |||
+ | public struct ScriptingGCHandle | ||
+ | { | ||
+ | public IntPtr m_Handle; | ||
+ | public int m_Weakness; | ||
+ | public IntPtr m_Object; | ||
+ | } | ||
+ | |||
+ | // Note that this struct layout differs between Unity versions. | ||
+ | // This is correct for Unity 2020, but hasn't been tested with | ||
+ | // other engine versions. | ||
+ | // | ||
+ | // The type has been renamed to NativeObject in order to avoid | ||
+ | // conflicts with UnityEngine.Object. | ||
+ | // | ||
+ | public struct NativeObject | ||
+ | { | ||
+ | public NonCopyable Base; | ||
+ | public int m_InstanceID; | ||
+ | | ||
+ | // This is represented on the C++ side as several bit-fields. | ||
+ | public int m_BitFields; | ||
+ | |||
+ | public IntPtr m_EventIndex; | ||
+ | |||
+ | #if UNITY_EDITOR || DEVELOPMENT_BUILD | ||
+ | public MemLabelId m_FullMemoryLabel; | ||
+ | #endif | ||
+ | |||
+ | public ScriptingGCHandle m_MonoReference; | ||
+ | |||
+ | #if UNITY_EDITOR | ||
+ | public uint m_DirtyIndex; | ||
+ | public ulong m_FileIDHint; | ||
+ | public bool m_IsPreviewSceneObject; | ||
+ | #endif | ||
+ | |||
+ | #if UNITY_EDITOR || DEVELOPMENT_BUILD | ||
+ | public int m_ObjectProfilerListIndex; | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | public struct PPtr< | ||
+ | where T : unmanaged | ||
+ | { | ||
+ | public int m_InstanceID; | ||
+ | } | ||
+ | |||
+ | // Renamed to NativePrefab to avoid name clashes | ||
+ | public struct NativePrefab | ||
+ | { | ||
+ | public NativeObject Base; | ||
+ | // ... | ||
+ | } | ||
+ | |||
+ | // Renamed to NativePrefabInstance to avoid name clashes | ||
+ | public struct NativePrefabInstance | ||
+ | { | ||
+ | public NativeObject Base; | ||
+ | // ... | ||
+ | } | ||
+ | |||
+ | public struct EditorExtension | ||
+ | { | ||
+ | public NativeObject Base; | ||
+ | |||
+ | #if UNITY_EDITOR | ||
+ | public PPtr< | ||
+ | public PPtr< | ||
+ | public PPtr< | ||
+ | public PPtr< | ||
+ | public bool m_IsClonedFromPrefabObject; | ||
+ | #endif | ||
+ | } | ||
+ | |||
+ | public struct ConstantString | ||
+ | { | ||
+ | public unsafe byte* m_Buffer; | ||
+ | } | ||
+ | |||
+ | public struct NamedObject | ||
+ | { | ||
+ | public EditorExtension Base; | ||
+ | public ConstantString m_Name; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | All of the above information was obtained from inspecting the .pdb files that are included with Unity installations. Some versions of Unity contain more information in these files than others. | ||
+ | |||
+ | ===== Advanced Use Cases ===== | ||
+ | Once you have access to native objects, you can perform many different actions that are usually only possible in the editor via SerializedObject and SerializedProperty. For example, changing the m_LineSpacing value on a Font object: | ||
+ | |||
+ | <code c#> | ||
+ | // Since these structures are accessed through pointers, | ||
+ | // we don't need to define the entire structure in order | ||
+ | // to use it. Fonts have several more fields in them that | ||
+ | // are not particularly useful to access in this way. | ||
+ | public struct NativeFont | ||
+ | { | ||
+ | public NamedObject Base; | ||
+ | public float m_LineSpacing; | ||
+ | public int m_FontSize; | ||
+ | } | ||
+ | |||
+ | // Make a new Font and access its native object. | ||
+ | Font f = new Font(); | ||
+ | var fp = (NativeFont*)f.GetCachedPtr(); | ||
+ | |||
+ | // Now we can simply set m_LineSpacing on it | ||
+ | fp-> | ||
+ | |||
+ | // This should print 12 | ||
+ | Debug.Log(f.lineHeight); | ||
+ | </ |