2D panel communication
Updated: Nov 7, 2024
Spatial SDK uses Android for rendering panels. If you want to update your panel from another panel, you will have to use native Android view models, depending on your UI framework of choice. For example, if you are using Jetpack Compose, you can manage two separate panels as Composable
functions that react to state changes.
For example:
class PanelViewModel : ViewModel() {
private val _panelData = MutableLiveData<String>("0")
val panelData: LiveData<String> = _panelData
fun incrementData() {
val currentValue = _panelData.value?.toInt() ?: 0
_panelData.value = (currentValue + 1).toString()
}
}
@Composable
fun PanelOne(viewModel: PanelViewModel) {
val panelData by viewModel.panelData.observeAsState("0")
Column {
Text(text = "Panel One: $panelData")
Button(onClick = onIncrement) {
Text("Increment")
}
}
}
@Composable
fun PanelTwo(viewModel: PanelViewModel) {
val panelData by viewModel.panelData.observeAsState("0")
Text(text = "Panel Two: $panelData")
}
2D-to-3D data driven communication
This
ViewModel
driven approach works in more cases than just 2D-to-2D communication. For example a Spatial SDK
System can be driven by a shared
ViewModel
.
The following system spawns a sphere whenever a button is clicked inside a Compose panel:
class AddSpheresSystem(panelViewModel: PanelViewModel) : SystemBase() {
var numSpheres = panelViewModel.numberOfSpheres.value
override fun execute() {
if (numSpheres < panelViewModel.numberOfSpheres.value) {
val numSpheresToAdd = panelViewModel.numberOfSpheres.value - numSpheres
numSpheres = panelViewModel.numberOfSpheres.value
for (x in 0..(numSpheresToAdd - 1)) {
Entity.fromSphere(
.3f,
Transform(Pose(Vector3(x = x - 1.5f, y = 1f, z = 1f))),
FlatColorMaterials.TAN,
Grabbable(),
FloatingDependentOrb(),
)
.registerEventListener<ButtonReleaseEventArgs>(ButtonReleaseEventArgs.EVENT_NAME) {
_,
eventArgs ->
if (eventArgs.button == ControllerButton.RightTrigger) {
panelViewModel.incrementData()
}
}
}
}
}
}
3D-to-2D data driven communication
If you look closely at the code example above, you’ll see that a click listener is added to the spheres that spawn.
.registerEventListener<ButtonReleaseEventArgs>(ButtonReleaseEventArgs.EVENT_NAME) {
_, eventArgs ->
if (eventArgs.button == ControllerButton.RightTrigger) {
panelViewModel.incrementData()
}
}
This click listener modifies the viewModel
, which can be picked up in a panel rendering the ViewModel
.
@Composable
fun PanelTwo(viewModel: PanelViewModel) {
val panelData by viewModel.panelData.observeAsState("0")
Text(text = "Panel Two: $panelData")
}
2D-to-3D event driven communication
If you prioritize code association over data encapsulation in your 2D app, consider adopting an event-driven model. This model allows you to invoke functions in your VRActivity
directly from your 2D activity. The SpatialActivityManager
provides four main functions to facilitate this integration.
inline fun <reified T : AppSystemActivity> getVrActivity(): T
fun getAppSystemActivity(): AppSystemActivity
inline fun <reified T : AppSystemActivity> executeOnVrActivity(
crossinline runnable: (activity: T) -> Unit
)
fun executeOnAppSystemActivity(runnable: (activity: AppSystemActivity) -> Unit)
You can use this to call into Spatial SDK from anywhere inside of your 2D application. For example:
SpatialActivityManager.executeOnAppSystemActivity { _ ->
for (x in 0..2) {
Entity.fromSphere(
.3f,
Transform(Pose(Vector3(x = x - 1.5f, y = 1f, z = 1f))),
FlatColorMaterials.TAN,
Grabbable(),
FloatingDependentOrb(),
).registerEventListener<ButtonReleaseEventArgs>(ButtonReleaseEventArgs.EVENT_NAME) {
_, eventArgs ->
if (eventArgs.button == ControllerButton.RightTrigger) {
PanelViewModel.clickCount.value++
}
}
}
}