402 lines
14 KiB
Markdown
402 lines
14 KiB
Markdown
# Game Engine Integrations
|
|
|
|
MosisService provides a virtual phone that game engines can display and interact with.
|
|
|
|
## Integration Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ MosisService │
|
|
│ (OpenGL ES rendering → AHardwareBuffer) │
|
|
└───────────────────────────┬─────────────────────────────────────┘
|
|
│ Binder IPC + Shared Memory
|
|
┌─────────────────┴─────────────────┐
|
|
▼ ▼
|
|
┌─────────────────────┐ ┌─────────────────────┐
|
|
│ MosisUnreal │ │ MosisVR │
|
|
│ (UE5.5 Plugin) │ │ (Unity Package) │
|
|
│ Vulkan Import │ │ Vulkan/OpenGL │
|
|
└─────────────────────┘ └─────────────────────┘
|
|
```
|
|
|
|
## MosisUnreal (Unreal Engine 5.5)
|
|
|
|
**Location**: `D:\Dev\Mosis\MosisUnreal\Plugins\MosisSDK\`
|
|
|
|
### Build Script (Recommended)
|
|
|
|
Use the build script for one-command builds and deployment:
|
|
|
|
```batch
|
|
cd D:\Dev\Mosis\MosisUnreal
|
|
|
|
:: Build, install, and launch (default)
|
|
build-unreal.bat
|
|
|
|
:: Build APK only
|
|
build-unreal.bat build
|
|
|
|
:: Install to device only (requires prior build)
|
|
build-unreal.bat install
|
|
|
|
:: Launch app (starts MosisService first, then MosisUnreal)
|
|
build-unreal.bat launch
|
|
|
|
:: Clean build artifacts
|
|
build-unreal.bat clean
|
|
```
|
|
|
|
The script auto-detects connected Android devices, handles APK and OBB installation, and properly sequences app launches (MosisService must start before MosisUnreal).
|
|
|
|
### Manual Build Commands
|
|
|
|
```batch
|
|
:: Windows Editor Build
|
|
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\Build.bat" ^
|
|
MosisUnrealEditor Win64 Development ^
|
|
-Project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject"
|
|
|
|
:: Android APK Build
|
|
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" ^
|
|
BuildCookRun ^
|
|
-project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" ^
|
|
-platform=Android -clientconfig=Development ^
|
|
-build -cook -stage -pak -package -noP4
|
|
|
|
:: Clean Build (delete these folders first)
|
|
rmdir /s /q "Intermediate\Build"
|
|
rmdir /s /q "Binaries"
|
|
```
|
|
|
|
### Output Files
|
|
|
|
| Build | Output |
|
|
|-------|--------|
|
|
| Windows Editor | `Plugins/MosisSDK/Binaries/Win64/UnrealEditor-MosisSDK.dll` |
|
|
| Android APK | `Binaries/Android/MosisUnreal-arm64.apk` |
|
|
| Android OBB | `Binaries/Android/main.1.com.omixlab.MosisUnreal.obb` |
|
|
|
|
### Requirements
|
|
|
|
- Android SDK Platform 36 (for AIDL binder headers)
|
|
- Android Build Tools 36.1.0 (for AIDL compiler)
|
|
- `ANDROID_HOME` environment variable set
|
|
|
|
### Quest Deployment
|
|
|
|
**IMPORTANT**: Git Bash on Windows converts Unix paths to Windows paths. Use `MSYS_NO_PATHCONV=1` prefix for all adb push commands.
|
|
|
|
```bash
|
|
# Set Quest device ID (check with: adb devices -l)
|
|
QUEST=2G0YC5ZF7W01T0
|
|
|
|
# Install APK
|
|
adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk
|
|
|
|
# Create OBB temp directory
|
|
adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal"
|
|
|
|
# Push OBB to temp location (MUST use MSYS_NO_PATHCONV=1)
|
|
MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/
|
|
|
|
# Move OBB to final location
|
|
adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal"
|
|
adb -s $QUEST shell "mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/"
|
|
|
|
# Start MosisService first, then MosisUnreal
|
|
adb -s $QUEST shell am start -n com.omixlab.mosis/.MainActivity
|
|
sleep 3
|
|
adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
|
|
|
|
# Monitor logs
|
|
adb -s $QUEST logcat -s MosisSDK MosisOS MosisTest
|
|
```
|
|
|
|
**Full rebuild and deploy sequence**:
|
|
```bash
|
|
# 1. Build Android APK+OBB
|
|
"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun \
|
|
-project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" \
|
|
-platform=Android -clientconfig=Development \
|
|
-build -cook -stage -pak -package -noP4
|
|
|
|
# 2. Stop app, push APK and OBB, restart
|
|
QUEST=2G0YC5ZF7W01T0
|
|
adb -s $QUEST shell am force-stop com.omixlab.MosisUnreal
|
|
adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk
|
|
adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal"
|
|
MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/
|
|
adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal && mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/"
|
|
adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
|
|
```
|
|
|
|
**Common issues**:
|
|
- App stuck on loading screen: OBB not pushed or wrong version
|
|
- `adb push` path errors: Missing `MSYS_NO_PATHCONV=1` prefix
|
|
- Service not connecting: Start `com.omixlab.mosis` before the game
|
|
|
|
### VR Pointer Interaction
|
|
|
|
The MosisSDK includes `UMosisPointerComponent` for VR ray-based touch interaction.
|
|
|
|
**Key Files**:
|
|
- `Public/MosisPointerComponent.h` - Component header
|
|
- `Private/MosisPointerComponent.cpp` - Implementation
|
|
|
|
**Features**:
|
|
- Raycast from component transform (attach as child of motion controller)
|
|
- Automatic detection of `AMosisPhoneActor` hits
|
|
- Touch state machine: Down/Move/Up events
|
|
- Optional Enhanced Input binding via `TriggerAction` property
|
|
- Manual control via `SetTriggerPressed(bool)`
|
|
- Debug ray visualization
|
|
|
|
**Blueprint Setup**:
|
|
1. Add `MosisPointerComponent` as child of `MotionControllerComponent`
|
|
2. Set `TriggerAction` to your trigger input action (optional)
|
|
3. Touch events are sent automatically when pointing at phone and triggering
|
|
|
|
**C++ Setup**:
|
|
```cpp
|
|
// In VR Pawn header
|
|
UPROPERTY(VisibleAnywhere)
|
|
UMosisPointerComponent* RightPointer;
|
|
|
|
// In constructor
|
|
RightPointer = CreateDefaultSubobject<UMosisPointerComponent>(TEXT("RightPointer"));
|
|
RightPointer->SetupAttachment(RightMotionController);
|
|
|
|
// In BeginPlay (if using Enhanced Input)
|
|
RightPointer->TriggerAction = TriggerInputAction;
|
|
```
|
|
|
|
**Manual Control (without Enhanced Input)**:
|
|
```cpp
|
|
// In your input handling code
|
|
void AMyVRPawn::OnTriggerPressed()
|
|
{
|
|
RightPointer->SetTriggerPressed(true);
|
|
}
|
|
|
|
void AMyVRPawn::OnTriggerReleased()
|
|
{
|
|
RightPointer->SetTriggerPressed(false);
|
|
}
|
|
```
|
|
|
|
**Component Properties**:
|
|
|
|
| Property | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `RayLength` | float | 500.0 | Maximum ray length in cm |
|
|
| `bShowDebugRay` | bool | false | Draw debug visualization |
|
|
| `DebugRayColor` | FLinearColor | Red | Ray color when not hitting |
|
|
| `DebugRayHitColor` | FLinearColor | Green | Ray color when hitting phone |
|
|
| `TraceChannel` | ECollisionChannel | Visibility | Collision channel for raycast |
|
|
| `TriggerAction` | UInputAction* | nullptr | Enhanced Input action for trigger |
|
|
|
|
**Blueprint Functions**:
|
|
|
|
| Function | Returns | Description |
|
|
|----------|---------|-------------|
|
|
| `IsPointingAtPhone()` | bool | True if ray hits a phone actor |
|
|
| `GetTargetPhone()` | AMosisPhoneActor* | Currently targeted phone (or nullptr) |
|
|
| `GetHitLocation()` | FVector | World location of ray hit |
|
|
| `GetRayOrigin()` | FVector | Ray start position |
|
|
| `GetRayDirection()` | FVector | Ray direction vector |
|
|
| `IsTriggerPressed()` | bool | Current trigger state |
|
|
| `SetTriggerPressed(bool)` | void | Manual trigger control |
|
|
|
|
**Touch State Machine**:
|
|
```
|
|
Idle ──[raycast hit]──► Hover ──[trigger]──► Pressed
|
|
▲ │ │
|
|
│ │ raycast miss │ trigger release
|
|
│ ▼ ▼
|
|
└──────────────────── Idle ◄────────────────────
|
|
|
|
Events:
|
|
- Idle → Pressed: SendTouch(Down)
|
|
- Pressed + moving: SendTouch(Move)
|
|
- Pressed → Idle: SendTouch(Up)
|
|
```
|
|
|
|
## MosisVR (Unity 6000.3.2f1)
|
|
|
|
**Location**: `D:\Dev\Mosis\MosisVR\Packages\com.omixlab.mosis_sdk\`
|
|
|
|
### Direct APK Build (Recommended)
|
|
|
|
```batch
|
|
"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^
|
|
-batchmode -quit -nographics ^
|
|
-projectPath "D:\Dev\Mosis\MosisVR" ^
|
|
-executeMethod BuildScript.BuildAndroidDirectCI ^
|
|
-outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR.apk"
|
|
```
|
|
|
|
### Export + Gradle Build
|
|
|
|
For more control, export a Gradle project then build separately:
|
|
|
|
```batch
|
|
:: Step 1: Export from Unity
|
|
"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^
|
|
-batchmode -quit -nographics ^
|
|
-projectPath "D:\Dev\Mosis\MosisVR" ^
|
|
-executeMethod BuildScript.BuildAndroidCI ^
|
|
-export true ^
|
|
-outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR"
|
|
|
|
:: Step 2: Build with Gradle
|
|
cd D:\Dev\Mosis\Builds\Unity\Android\MosisVR
|
|
gradle assembleRelease
|
|
:: APK at: launcher\build\outputs\apk\release\launcher-release.apk
|
|
```
|
|
|
|
### Unity Editor Manual Build
|
|
|
|
1. File > Build Settings > Android
|
|
2. Player Settings: IL2CPP, ARM64, Vulkan + OpenGLES3
|
|
3. For direct APK: Uncheck "Export Project", click Build
|
|
4. For export: Check "Export Project", click Export
|
|
|
|
### Native Plugin Build (Manual)
|
|
|
|
The native plugin builds automatically via CMake during Unity's build. To rebuild manually:
|
|
|
|
```batch
|
|
cd Packages/com.omixlab.mosis_sdk/Plugins/Android/cpp
|
|
cmake -B build -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^
|
|
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29
|
|
cmake --build build
|
|
```
|
|
|
|
## Device Testing (Both Engines)
|
|
|
|
```bash
|
|
# Install MosisService first
|
|
adb install -r MosisService-debug.apk
|
|
|
|
# Install game client
|
|
adb install -r MosisUnreal-arm64.apk # or MosisVR.apk
|
|
|
|
# Launch service
|
|
adb shell am start -n com.omixlab.mosis/.MainActivity
|
|
|
|
# Launch client
|
|
adb shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity
|
|
# or for Unity:
|
|
adb shell am start -n com.omixlab.mosisvr/com.unity3d.player.UnityPlayerActivity
|
|
|
|
# Monitor all Mosis logs
|
|
adb logcat -s MosisSDK MosisTest RMLUI Vulkan
|
|
```
|
|
|
|
## Vulkan HardwareBuffer Import
|
|
|
|
Both game engines use Vulkan to import AHardwareBuffer from MosisService.
|
|
|
|
### UE5 Implementation
|
|
|
|
MosisUnreal uses UE5's built-in Vulkan RHI API for hardware buffer import:
|
|
|
|
```cpp
|
|
#include "IVulkanDynamicRHI.h"
|
|
|
|
// Import AHardwareBuffer as Vulkan texture (zero-copy)
|
|
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
|
|
FTextureRHIRef ImportedTexture = VulkanRHI->RHICreateTexture2DFromAndroidHardwareBuffer(Buffer);
|
|
|
|
// GPU-to-GPU copy to destination texture each frame
|
|
ENQUEUE_RENDER_COMMAND(CopyMosisTexture)(
|
|
[SrcTexture, DstTexture, Width, Height](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
RHICmdList.Transition(FRHITransitionInfo(SrcTexture, ERHIAccess::Unknown, ERHIAccess::CopySrc));
|
|
RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::Unknown, ERHIAccess::CopyDest));
|
|
|
|
FRHICopyTextureInfo CopyInfo;
|
|
CopyInfo.Size = FIntVector(Width, Height, 1);
|
|
RHICmdList.CopyTexture(SrcTexture, DstTexture, CopyInfo);
|
|
|
|
RHICmdList.Transition(FRHITransitionInfo(DstTexture, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
|
|
}
|
|
);
|
|
```
|
|
|
|
**Key files:**
|
|
- `MosisPhoneTexture.cpp` - Vulkan import and GPU copy
|
|
- `MosisPhoneComponent.cpp` - Frame update coordination
|
|
|
|
### Required Vulkan Extensions
|
|
|
|
UE5's VulkanRHI enables these automatically on Android:
|
|
|
|
```
|
|
VK_ANDROID_external_memory_android_hardware_buffer
|
|
VK_KHR_external_memory
|
|
VK_KHR_dedicated_allocation
|
|
```
|
|
|
|
### Raw Vulkan Import Pattern (Reference)
|
|
|
|
For custom implementations outside UE5:
|
|
|
|
```cpp
|
|
// 1. Query buffer properties
|
|
VkAndroidHardwareBufferPropertiesANDROID props = {
|
|
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID
|
|
};
|
|
VkAndroidHardwareBufferFormatPropertiesANDROID formatProps = {
|
|
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID
|
|
};
|
|
props.pNext = &formatProps;
|
|
vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &props);
|
|
|
|
// 2. Create image with external memory
|
|
VkExternalMemoryImageCreateInfo extInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
|
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID
|
|
};
|
|
|
|
VkImageCreateInfo imageInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
.pNext = &extInfo,
|
|
.imageType = VK_IMAGE_TYPE_2D,
|
|
.format = formatProps.format,
|
|
.extent = {desc.width, desc.height, 1},
|
|
.mipLevels = 1,
|
|
.arrayLayers = 1,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
|
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
|
};
|
|
vkCreateImage(device, &imageInfo, nullptr, &image);
|
|
|
|
// 3. Import memory from HardwareBuffer
|
|
VkImportAndroidHardwareBufferInfoANDROID importInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
|
.buffer = buffer
|
|
};
|
|
|
|
VkMemoryDedicatedAllocateInfo dedicatedInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
|
.pNext = &importInfo,
|
|
.image = image
|
|
};
|
|
|
|
VkMemoryAllocateInfo allocInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.pNext = &dedicatedInfo,
|
|
.allocationSize = props.allocationSize,
|
|
.memoryTypeIndex = FindMemoryType(props.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
|
};
|
|
vkAllocateMemory(device, &allocInfo, nullptr, &memory);
|
|
vkBindImageMemory(device, image, memory, 0);
|
|
```
|
|
|
|
### Double Buffering
|
|
|
|
The imported image is copied to a local texture each frame via GPU copy to prevent data races with MosisService rendering. This is a fast GPU-to-GPU blit operation.
|