@dolami-inc/react-native-expo-unity 0.4.5 → 0.5.1
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.
|
@@ -2,8 +2,6 @@ package expo.modules.unity
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.util.Log
|
|
5
|
-
import android.view.ViewGroup
|
|
6
|
-
import android.widget.FrameLayout
|
|
7
5
|
import expo.modules.kotlin.AppContext
|
|
8
6
|
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
9
7
|
import expo.modules.kotlin.views.ExpoView
|
|
@@ -29,47 +27,28 @@ class ExpoUnityView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
29
27
|
|
|
30
28
|
val bridge = UnityBridge.getInstance()
|
|
31
29
|
|
|
32
|
-
if (!bridge.isInitialized) {
|
|
33
|
-
bridge.initialize(activity)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
30
|
bridge.onMessage = { message ->
|
|
37
31
|
post {
|
|
38
32
|
onUnityMessage(mapOf("message" to message))
|
|
39
33
|
}
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
if (bridge.isInitialized) {
|
|
37
|
+
// Unity already created — attach the view to this container
|
|
38
|
+
bridge.attachToContainer(this)
|
|
39
|
+
} else {
|
|
40
|
+
// Create Unity player, then attach the view once ready
|
|
41
|
+
bridge.initialize(activity) {
|
|
42
|
+
bridge.attachToContainer(this)
|
|
43
|
+
}
|
|
49
44
|
}
|
|
50
|
-
|
|
51
|
-
// Remove from previous parent if needed
|
|
52
|
-
(playerView.parent as? ViewGroup)?.removeView(playerView)
|
|
53
|
-
|
|
54
|
-
addView(
|
|
55
|
-
playerView,
|
|
56
|
-
FrameLayout.LayoutParams(
|
|
57
|
-
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
58
|
-
FrameLayout.LayoutParams.MATCH_PARENT
|
|
59
|
-
)
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
Log.i(TAG, "Unity view mounted")
|
|
63
45
|
}
|
|
64
46
|
|
|
65
|
-
override fun
|
|
66
|
-
super.
|
|
67
|
-
// Start rendering after the view is in the window hierarchy,
|
|
68
|
-
// so Unity's surface is properly connected to the display.
|
|
47
|
+
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
|
|
48
|
+
super.onWindowFocusChanged(hasWindowFocus)
|
|
69
49
|
val bridge = UnityBridge.getInstance()
|
|
70
|
-
if (bridge.isInitialized)
|
|
71
|
-
|
|
72
|
-
}
|
|
50
|
+
if (!bridge.isInitialized) return
|
|
51
|
+
bridge.unityPlayer?.windowFocusChanged(hasWindowFocus)
|
|
73
52
|
}
|
|
74
53
|
|
|
75
54
|
override fun onDetachedFromWindow() {
|
|
@@ -78,11 +57,12 @@ class ExpoUnityView(context: Context, appContext: AppContext) : ExpoView(context
|
|
|
78
57
|
|
|
79
58
|
if (bridge.isInitialized) {
|
|
80
59
|
if (autoUnloadOnUnmount) {
|
|
60
|
+
bridge.detachFromContainer()
|
|
81
61
|
bridge.unload()
|
|
82
|
-
Log.i(TAG, "
|
|
62
|
+
Log.i(TAG, "Detached and unloaded (view detached)")
|
|
83
63
|
} else {
|
|
84
64
|
bridge.setPaused(true)
|
|
85
|
-
Log.i(TAG, "
|
|
65
|
+
Log.i(TAG, "Paused (autoUnloadOnUnmount=false)")
|
|
86
66
|
}
|
|
87
67
|
}
|
|
88
68
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
package expo.modules.unity
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
+
import android.graphics.PixelFormat
|
|
4
5
|
import android.os.Handler
|
|
5
6
|
import android.os.Looper
|
|
6
7
|
import android.util.Log
|
|
7
8
|
import android.view.View
|
|
9
|
+
import android.view.ViewGroup
|
|
10
|
+
import android.view.WindowManager
|
|
11
|
+
import android.widget.FrameLayout
|
|
8
12
|
import com.expounity.bridge.NativeCallProxy
|
|
9
13
|
import com.unity3d.player.IUnityPlayerLifecycleEvents
|
|
10
14
|
import com.unity3d.player.UnityPlayer
|
|
@@ -14,8 +18,9 @@ import com.unity3d.player.UnityPlayerForActivityOrService
|
|
|
14
18
|
* Singleton managing the UnityPlayer lifecycle.
|
|
15
19
|
* Android equivalent of ios/UnityBridge.mm.
|
|
16
20
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
21
|
+
* Creates the Unity player and lets the ExpoUnityView add the
|
|
22
|
+
* FrameLayout directly — no background parking, since Unity 6's
|
|
23
|
+
* window management times out when reparenting from a background view.
|
|
19
24
|
*/
|
|
20
25
|
class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCallProxy.MessageListener {
|
|
21
26
|
|
|
@@ -50,20 +55,41 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
|
|
|
50
55
|
* Returns the UnityPlayer's FrameLayout for embedding.
|
|
51
56
|
* UnityPlayerForActivityOrService creates its own rendering surface internally.
|
|
52
57
|
*/
|
|
53
|
-
val unityPlayerView:
|
|
58
|
+
val unityPlayerView: FrameLayout?
|
|
54
59
|
get() = unityPlayer?.frameLayout
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Creates the UnityPlayer. The caller is responsible for adding
|
|
63
|
+
* [unityPlayerView] to the view hierarchy immediately after [onReady] fires.
|
|
64
|
+
*/
|
|
65
|
+
fun initialize(activity: Activity, onReady: (() -> Unit)? = null) {
|
|
66
|
+
if (isInitialized) {
|
|
67
|
+
onReady?.invoke()
|
|
68
|
+
return
|
|
69
|
+
}
|
|
58
70
|
|
|
59
71
|
val runInit = Runnable {
|
|
60
72
|
try {
|
|
73
|
+
// Set RGBA_8888 format for proper rendering
|
|
74
|
+
activity.window.setFormat(PixelFormat.RGBA_8888)
|
|
75
|
+
|
|
76
|
+
// Save fullscreen state before Unity potentially changes it
|
|
77
|
+
val flags = activity.window.attributes.flags
|
|
78
|
+
val wasFullScreen = (flags and WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
|
|
79
|
+
|
|
61
80
|
val player = UnityPlayerForActivityOrService(activity, this)
|
|
62
81
|
unityPlayer = player
|
|
63
82
|
|
|
64
83
|
NativeCallProxy.registerListener(this)
|
|
84
|
+
Log.i(TAG, "Unity player created")
|
|
85
|
+
|
|
86
|
+
// Restore fullscreen state if Unity changed it
|
|
87
|
+
if (!wasFullScreen) {
|
|
88
|
+
activity.window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
|
|
89
|
+
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
|
90
|
+
}
|
|
65
91
|
|
|
66
|
-
|
|
92
|
+
onReady?.invoke()
|
|
67
93
|
} catch (e: Exception) {
|
|
68
94
|
Log.e(TAG, "Failed to initialize Unity", e)
|
|
69
95
|
}
|
|
@@ -77,14 +103,42 @@ class UnityBridge private constructor() : IUnityPlayerLifecycleEvents, NativeCal
|
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
106
|
+
* Adds the Unity FrameLayout to the given container and starts rendering.
|
|
107
|
+
* Must be called after [initialize] completes.
|
|
108
|
+
*/
|
|
109
|
+
fun attachToContainer(container: ViewGroup) {
|
|
110
|
+
val frame = unityPlayerView ?: run {
|
|
111
|
+
Log.w(TAG, "Unity player view not available")
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Remove from current parent if any
|
|
116
|
+
(frame.parent as? ViewGroup)?.removeView(frame)
|
|
117
|
+
|
|
118
|
+
val layoutParams = FrameLayout.LayoutParams(
|
|
119
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
120
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
121
|
+
)
|
|
122
|
+
container.addView(frame, 0, layoutParams)
|
|
123
|
+
Log.i(TAG, "Unity view attached to container")
|
|
124
|
+
|
|
125
|
+
// Kick-start rendering after the view is in the hierarchy.
|
|
126
|
+
// Use post to let the layout pass complete first.
|
|
127
|
+
frame.post {
|
|
128
|
+
unityPlayer?.windowFocusChanged(true)
|
|
129
|
+
frame.requestFocus()
|
|
130
|
+
unityPlayer?.resume()
|
|
131
|
+
Log.i(TAG, "Rendering started")
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Detaches the Unity view from its current parent without destroying it.
|
|
82
137
|
*/
|
|
83
|
-
fun
|
|
84
|
-
val
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Log.i(TAG, "Rendering started")
|
|
138
|
+
fun detachFromContainer() {
|
|
139
|
+
val frame = unityPlayerView ?: return
|
|
140
|
+
(frame.parent as? ViewGroup)?.removeView(frame)
|
|
141
|
+
Log.i(TAG, "Unity view detached from container")
|
|
88
142
|
}
|
|
89
143
|
|
|
90
144
|
fun sendMessage(gameObject: String, methodName: String, message: String) {
|