@dolami-inc/react-native-expo-unity 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,16 +34,16 @@ class ExpoUnityView(context: Context, appContext: AppContext) : ExpoView(context
34
34
  }
35
35
 
36
36
  if (bridge.isReady) {
37
- // Unity already initialized — just reparent the view into this container
38
- bridge.addUnityViewToGroup(this)
39
- Log.i(TAG, "Unity already ready, reparented view")
37
+ // Unity already initialized — reparent into this container
38
+ bridge.reparentInto(this)
40
39
  } else {
41
- // Initialize Unity with a callback that reparents when ready
40
+ // Initialize Unity, then reparent once engine is ready.
41
+ // Use postDelayed to give the engine time to boot before
42
+ // reparenting (avoids window detach timeout).
42
43
  bridge.initialize(activity) {
43
- post {
44
- bridge.addUnityViewToGroup(this)
45
- Log.i(TAG, "Unity ready, view reparented into container")
46
- }
44
+ postDelayed({
45
+ bridge.reparentInto(this)
46
+ }, 3000)
47
47
  }
48
48
  }
49
49
  }
@@ -61,13 +61,12 @@ class ExpoUnityView(context: Context, appContext: AppContext) : ExpoView(context
61
61
 
62
62
  if (bridge.isInitialized) {
63
63
  if (autoUnloadOnUnmount) {
64
+ bridge.detachView()
64
65
  bridge.unload()
65
- Log.i(TAG, "Auto-unloaded (view detached)")
66
+ Log.i(TAG, "Detached and unloaded (view detached)")
66
67
  } else {
67
- // Park Unity in the background instead of unloading
68
- bridge.parkUnityViewInBackground()
69
68
  bridge.setPaused(true)
70
- Log.i(TAG, "Parked in background (autoUnloadOnUnmount=false)")
69
+ Log.i(TAG, "Paused (autoUnloadOnUnmount=false)")
71
70
  }
72
71
  }
73
72
 
@@ -18,10 +18,9 @@ import com.unity3d.player.UnityPlayerForActivityOrService
18
18
  * Singleton managing the UnityPlayer lifecycle.
19
19
  * Android equivalent of ios/UnityBridge.mm.
20
20
  *
21
- * Uses a "background parking" pattern: Unity is always attached to the
22
- * Activity's content view (at 1x1px, Z=-1) so it stays alive. When a
23
- * React Native view wants to show Unity, we reparent the FrameLayout
24
- * into that view. When it unmounts, we park it back in the background.
21
+ * Unity 6's engine only boots when the view is in the Activity's content
22
+ * view hierarchy. We park the view at MATCH_PARENT behind everything (Z=-1)
23
+ * to let the engine start, then reparent into the React Native container.
25
24
  */
26
25
  class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCallProxy.MessageListener {
27
26
 
@@ -55,13 +54,13 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
55
54
  val isInitialized: Boolean
56
55
  get() = unityPlayer != null
57
56
 
58
- /**
59
- * Returns the UnityPlayer's FrameLayout for embedding.
60
- * UnityPlayerForActivityOrService creates its own rendering surface internally.
61
- */
62
57
  val unityPlayerView: FrameLayout?
63
58
  get() = unityPlayer?.frameLayout
64
59
 
60
+ /**
61
+ * Creates the Unity player, parks it in the Activity's content view
62
+ * (behind everything) to let the engine start, then fires [onReady].
63
+ */
65
64
  fun initialize(activity: Activity, onReady: (() -> Unit)? = null) {
66
65
  if (isInitialized) {
67
66
  onReady?.invoke()
@@ -70,10 +69,8 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
70
69
 
71
70
  val runInit = Runnable {
72
71
  try {
73
- // Set RGBA_8888 format for proper rendering
74
72
  activity.window.setFormat(PixelFormat.RGBA_8888)
75
73
 
76
- // Save fullscreen state before Unity potentially changes it
77
74
  val flags = activity.window.attributes.flags
78
75
  val wasFullScreen = (flags and WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
79
76
 
@@ -83,18 +80,23 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
83
80
  NativeCallProxy.registerListener(this)
84
81
  Log.i(TAG, "Unity player created")
85
82
 
86
- // Give Unity time to initialize its rendering pipeline
87
- Thread.sleep(1000)
88
-
89
- // Park the Unity view in the background (1x1px, behind everything)
90
- addUnityViewToBackground(activity)
91
-
92
- // Kick-start rendering
83
+ // Park in Activity's content view at full size but behind
84
+ // everything. Unity's engine only starts when the view is
85
+ // in the Activity's window hierarchy.
86
+ val frame = player.frameLayout
87
+ frame.z = -1f
88
+ activity.addContentView(frame, ViewGroup.LayoutParams(
89
+ ViewGroup.LayoutParams.MATCH_PARENT,
90
+ ViewGroup.LayoutParams.MATCH_PARENT
91
+ ))
92
+ Log.i(TAG, "Unity view parked in Activity (background)")
93
+
94
+ // Start the rendering pipeline
93
95
  player.windowFocusChanged(true)
94
- player.frameLayout?.requestFocus()
96
+ frame.requestFocus()
95
97
  player.resume()
96
98
 
97
- // Restore fullscreen state if Unity changed it
99
+ // Restore fullscreen state
98
100
  if (!wasFullScreen) {
99
101
  activity.window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
100
102
  activity.window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
@@ -117,67 +119,46 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
117
119
  }
118
120
 
119
121
  /**
120
- * Parks the Unity view in the Activity's content view at 1x1 pixels
121
- * behind all other views (Z=-1). This keeps Unity alive but invisible.
122
+ * Moves the Unity view from the Activity background into the given
123
+ * container. Called when the React Native component is ready to show Unity.
122
124
  */
123
- private fun addUnityViewToBackground(activity: Activity) {
124
- val frame = unityPlayerView ?: return
125
+ fun reparentInto(container: ViewGroup) {
126
+ val frame = unityPlayerView ?: run {
127
+ Log.w(TAG, "Unity player view not available")
128
+ return
129
+ }
125
130
 
126
- // Remove from current parent if any
131
+ // Remove from Activity's content view
127
132
  (frame.parent as? ViewGroup)?.let { parent ->
128
133
  parent.endViewTransition(frame)
129
134
  parent.removeView(frame)
130
135
  }
131
136
 
132
- frame.z = -1f
133
-
134
- val layoutParams = ViewGroup.LayoutParams(1, 1)
135
- activity.addContentView(frame, layoutParams)
136
- Log.i(TAG, "Unity view parked in background")
137
- }
138
-
139
- /**
140
- * Moves the Unity view from wherever it currently is into the
141
- * specified ViewGroup with MATCH_PARENT layout. Called when the
142
- * React Native component mounts.
143
- */
144
- fun addUnityViewToGroup(group: ViewGroup) {
145
- val frame = unityPlayerView ?: return
146
-
147
- // Remove from current parent
148
- (frame.parent as? ViewGroup)?.removeView(frame)
149
-
150
- val layoutParams = ViewGroup.LayoutParams(
151
- ViewGroup.LayoutParams.MATCH_PARENT,
152
- ViewGroup.LayoutParams.MATCH_PARENT
153
- )
154
- group.addView(frame, 0, layoutParams)
137
+ // Reset Z and add to the React Native container
138
+ frame.z = 0f
139
+ container.addView(frame, 0, FrameLayout.LayoutParams(
140
+ FrameLayout.LayoutParams.MATCH_PARENT,
141
+ FrameLayout.LayoutParams.MATCH_PARENT
142
+ ))
155
143
 
144
+ // Re-kick rendering after reparenting
156
145
  unityPlayer?.windowFocusChanged(true)
157
146
  frame.requestFocus()
158
147
  unityPlayer?.resume()
159
148
 
160
- Log.i(TAG, "Unity view moved to visible container")
149
+ Log.i(TAG, "Unity view reparented into container")
161
150
  }
162
151
 
163
152
  /**
164
- * Parks the Unity view back to the background. Called when the
165
- * React Native component unmounts.
153
+ * Detaches the Unity view from its current parent.
166
154
  */
167
- fun parkUnityViewInBackground() {
155
+ fun detachView() {
168
156
  val frame = unityPlayerView ?: return
169
- val activity = frame.context as? Activity ?: return
170
-
171
157
  (frame.parent as? ViewGroup)?.let { parent ->
172
158
  parent.endViewTransition(frame)
173
159
  parent.removeView(frame)
174
160
  }
175
-
176
- frame.z = -1f
177
-
178
- val layoutParams = ViewGroup.LayoutParams(1, 1)
179
- activity.addContentView(frame, layoutParams)
180
- Log.i(TAG, "Unity view parked back to background")
161
+ Log.i(TAG, "Unity view detached")
181
162
  }
182
163
 
183
164
  fun sendMessage(gameObject: String, methodName: String, message: String) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dolami-inc/react-native-expo-unity",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Unity as a Library (UaaL) bridge for React Native / Expo",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",