Skip to main content

il2cpp_bridge_rs/structs/components/core/
unity_object.rs

1//! Base wrapper for managed `UnityEngine.Object` values.
2use crate::api::cache;
3use crate::structs::components::Transform;
4use crate::structs::core::{Class, Il2cppObject, Object};
5use crate::structs::math::{Quaternion, Vector3};
6use crate::structs::Il2cppString;
7use std::ffi::c_void;
8use std::ops::Deref;
9
10/// Wrapper for a managed `UnityEngine.Object`.
11///
12/// This sits between the generic [`Object`] wrapper and more specific Unity
13/// types such as `GameObject`, `Component`, and `Transform`.
14#[repr(C)]
15#[derive(Debug, Clone, Copy)]
16pub struct UnityObject {
17    /// Base Object structure
18    pub object: Object,
19    /// Cached pointer to the object
20    pub m_cached_ptr: *mut c_void,
21}
22
23impl UnityObject {
24    /// Creates a `UnityObject` from a raw managed pointer.
25    pub fn from_ptr(ptr: *mut c_void) -> Self {
26        let object = unsafe { Object::from_ptr(ptr) };
27        let m_cached_ptr = unsafe {
28            let offset = std::mem::size_of::<Il2cppObject>() as isize;
29            *(ptr.offset(offset) as *mut *mut c_void)
30        };
31        Self {
32            object,
33            m_cached_ptr,
34        }
35    }
36
37    /// Returns the raw managed pointer.
38    pub fn as_ptr(&self) -> *mut c_void {
39        self.object.as_ptr()
40    }
41
42    /// Returns the Unity object name.
43    pub fn get_name(&self) -> Result<String, String> {
44        unsafe {
45            let obj = Object::from_ptr(self.as_ptr());
46            let ptr = obj
47                .method("get_name")
48                .ok_or("Method 'get_name' not found")?
49                .call::<*mut Il2cppString>(&[])?;
50
51            if ptr.is_null() {
52                return Err("Name is null".to_string());
53            }
54
55            (*ptr)
56                .to_string()
57                .ok_or_else(|| "Failed to convert name to String".to_string())
58        }
59    }
60
61    /// Returns the managed `ToString()` representation.
62    pub fn to_string(&self) -> Result<String, String> {
63        unsafe {
64            let obj = Object::from_ptr(self.as_ptr());
65            let ptr = obj
66                .method("ToString")
67                .ok_or("Method 'ToString' not found")?
68                .call::<*mut Il2cppString>(&[])?;
69
70            if ptr.is_null() {
71                return Err("String is null".to_string());
72            }
73
74            (*ptr)
75                .to_string()
76                .ok_or_else(|| "Failed to convert String to String".to_string())
77        }
78    }
79    /// Returns the cached `UnityEngine.Object` class definition.
80    pub fn get_class() -> Option<Class> {
81        cache::coremodule().class("Object")
82    }
83
84    /// Clones the given Unity object using `Object.Instantiate`.
85    pub fn instantiate(original: &UnityObject) -> Result<UnityObject, String> {
86        unsafe {
87            let object_class = Self::get_class()
88                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
89            let method = object_class
90                .method(("Instantiate", 1))
91                .ok_or("Method 'Instantiate' not found")?;
92
93            let ptr = method.call::<*mut c_void>(&[original.as_ptr()])?;
94
95            if ptr.is_null() {
96                return Err("Failed to instantiate object".to_string());
97            }
98
99            Ok(UnityObject::from_ptr(ptr))
100        }
101    }
102
103    /// Clones the object at the given position and rotation.
104    pub fn instantiate_at(
105        original: &UnityObject,
106        position: Vector3,
107        rotation: Quaternion,
108    ) -> Result<UnityObject, String> {
109        unsafe {
110            let object_class = Self::get_class()
111                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
112            let method = object_class
113                .method(("Instantiate", 3))
114                .ok_or("Method 'Instantiate' not found")?;
115
116            let ptr = method.call::<*mut c_void>(&[
117                original.as_ptr(),
118                &position as *const _ as *mut c_void,
119                &rotation as *const _ as *mut c_void,
120            ])?;
121
122            if ptr.is_null() {
123                return Err("Failed to instantiate object".to_string());
124            }
125
126            Ok(UnityObject::from_ptr(ptr))
127        }
128    }
129
130    /// Clones the object and parents it under `parent`.
131    pub fn instantiate_with_parent(
132        original: &UnityObject,
133        parent: &Transform,
134    ) -> Result<UnityObject, String> {
135        unsafe {
136            let object_class = Self::get_class()
137                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
138            let method = object_class
139                .method(("Instantiate", 2))
140                .ok_or("Method 'Instantiate' not found")?;
141
142            let ptr = method.call::<*mut c_void>(&[original.as_ptr(), parent.as_ptr()])?;
143
144            if ptr.is_null() {
145                return Err("Failed to instantiate object".to_string());
146            }
147
148            Ok(UnityObject::from_ptr(ptr))
149        }
150    }
151
152    /// Schedules this object for destruction after `time_delay` seconds.
153    pub fn destroy(&self, time_delay: f32) -> Result<(), String> {
154        unsafe {
155            let object_class = Self::get_class()
156                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
157            let method = object_class
158                .method("Destroy")
159                .ok_or("Method 'Destroy' not found")?;
160
161            method.call::<()>(&[self.as_ptr(), &time_delay as *const f32 as *mut c_void])?;
162            Ok(())
163        }
164    }
165
166    /// Destroys the object immediately.
167    pub fn destroy_immediate(
168        obj: &UnityObject,
169        allow_destroying_assets: bool,
170    ) -> Result<(), String> {
171        unsafe {
172            let object_class = Self::get_class()
173                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
174            let method = object_class
175                .method("DestroyImmediate")
176                .ok_or("Method 'DestroyImmediate' not found")?;
177
178            method.call::<()>(&[
179                obj.as_ptr(),
180                &allow_destroying_assets as *const bool as *mut c_void,
181            ])?;
182            Ok(())
183        }
184    }
185
186    /// Marks the object to survive scene loads.
187    pub fn dont_destroy_on_load(obj: &UnityObject) -> Result<(), String> {
188        unsafe {
189            let object_class = Self::get_class()
190                .ok_or_else(|| "Could not find UnityEngine.Object class".to_string())?;
191            let method = object_class
192                .method("DontDestroyOnLoad")
193                .ok_or("Method 'DontDestroyOnLoad' not found")?;
194
195            method.call::<()>(&[obj.as_ptr()])?;
196            Ok(())
197        }
198    }
199}
200
201impl Deref for UnityObject {
202    type Target = Object;
203
204    fn deref(&self) -> &Self::Target {
205        &self.object
206    }
207}