# Milestone 7: Game Integration

**Status**: Not Started
**Goal**: Production-ready Unity and Unreal plugins for seamless VR phone integration.

---

## Overview

Game engine plugins enable:
- Rendering the phone in VR scenes
- Touch/raycast interaction
- Virtual hardware provision (camera, mic, speaker)
- Event callbacks to game code

---

## Current State

| Platform | Location | Status |
|----------|----------|--------|
| Unity | `D:\Dev\Mosis\Mosis Unity` | Basic Binder client |
| Unreal | `D:\Dev\Mosis\Mosis Unreal` | WIP |

---

## Unity Plugin

### Package Structure

```
com.mosis.phone/
├── package.json
├── Runtime/
│   ├── MosisPhone.cs           # Main component
│   ├── MosisService.cs         # Binder client
│   ├── MosisInputHandler.cs    # Touch/raycast
│   ├── MosisHardwareProvider.cs # Virtual hardware
│   └── Native/
│       ├── MosisNative.cs      # P/Invoke declarations
│       └── Plugins/
│           └── Android/
│               └── libmosis-client.so
├── Prefabs/
│   ├── MosisPhone.prefab       # Ready-to-use phone
│   └── MosisPhoneVR.prefab     # VR-optimized version
├── Samples~/
│   └── BasicIntegration/
│       └── PhoneDemo.unity
└── Documentation~/
    └── integration-guide.md
```

### Main Component

**File**: `MosisPhone.cs`

```csharp
using UnityEngine;
using UnityEngine.Events;

namespace Mosis {
    public class MosisPhone : MonoBehaviour {
        [Header("Display")]
        [SerializeField] private MeshRenderer phoneScreen;
        [SerializeField] private Vector2Int resolution = new Vector2Int(540, 960);

        [Header("Input")]
        [SerializeField] private bool enableRaycast = true;
        [SerializeField] private LayerMask raycastLayers;

        [Header("Virtual Hardware")]
        [SerializeField] private Camera virtualCamera;
        [SerializeField] private AudioSource virtualMicrophone;
        [SerializeField] private AudioSource virtualSpeaker;

        [Header("Events")]
        public UnityEvent onPhoneReady;
        public UnityEvent<string> onNavigate;
        public UnityEvent<string, string> onMessageReceived;
        public UnityEvent<string> onCallStarted;

        private MosisService service;
        private RenderTexture screenTexture;
        private Material screenMaterial;

        void Awake() {
            // Create render texture for phone screen
            screenTexture = new RenderTexture(resolution.x, resolution.y, 0);
            screenMaterial = new Material(Shader.Find("Unlit/Texture"));
            screenMaterial.mainTexture = screenTexture;
            phoneScreen.material = screenMaterial;
        }

        void Start() {
            service = new MosisService();
            service.OnFrameAvailable += OnFrameAvailable;
            service.OnServiceInitialized += () => onPhoneReady?.Invoke();
            service.Connect();
        }

        void Update() {
            if (enableRaycast) {
                HandleRaycastInput();
            }
        }

        private void HandleRaycastInput() {
            // VR controller raycast
            if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger)) {
                var ray = new Ray(
                    OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch),
                    OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch) * Vector3.forward
                );

                if (Physics.Raycast(ray, out var hit, 10f, raycastLayers)) {
                    if (hit.collider.gameObject == phoneScreen.gameObject) {
                        var uv = hit.textureCoord;
                        var x = uv.x * resolution.x;
                        var y = (1 - uv.y) * resolution.y;
                        service.SendTouchDown(x, y);
                    }
                }
            }
        }

        private void OnFrameAvailable(byte[] pixels) {
            // Update screen texture
            screenTexture.LoadRawTextureData(pixels);
            screenTexture.Apply();
        }

        // Public API
        public void NavigateTo(string screen) {
            service.Navigate(screen);
        }

        public void SendMessage(string to, string text) {
            service.SendMessage(to, text);
        }

        public void MakeCall(string number) {
            service.MakeCall(number);
        }

        void OnDestroy() {
            service?.Disconnect();
            Destroy(screenTexture);
            Destroy(screenMaterial);
        }
    }
}
```

### Hardware Provider

**File**: `MosisHardwareProvider.cs`

```csharp
namespace Mosis {
    public class MosisHardwareProvider : MonoBehaviour {
        [SerializeField] private MosisPhone phone;
        [SerializeField] private Camera gameCamera;
        [SerializeField] private AudioListener audioListener;

        private RenderTexture cameraTexture;

        void Start() {
            // Setup virtual camera
            cameraTexture = new RenderTexture(640, 480, 0);
            gameCamera.targetTexture = cameraTexture;

            // Register with phone service
            phone.Service.SetCameraProvider(() => {
                var pixels = new byte[cameraTexture.width * cameraTexture.height * 4];
                // Read pixels from RenderTexture
                return pixels;
            });

            phone.Service.SetMicrophoneProvider(() => {
                // Get audio from AudioListener
                return audioSamples;
            });
        }

        public void PlayAudio(float[] samples) {
            // Play on virtual speaker
            var audioSource = GetComponent<AudioSource>();
            audioSource.clip = AudioClip.Create("phone", samples.Length, 1, 44100, false);
            audioSource.clip.SetData(samples, 0);
            audioSource.Play();
        }
    }
}
```

### VR Interaction

**File**: `MosisInputHandler.cs`

```csharp
namespace Mosis {
    public class MosisInputHandler : MonoBehaviour {
        [SerializeField] private MosisPhone phone;
        [SerializeField] private Transform pointerOrigin;
        [SerializeField] private LineRenderer laserPointer;

        private bool isTouching;
        private Vector2 lastTouchPos;

        void Update() {
            // Update laser pointer
            var ray = new Ray(pointerOrigin.position, pointerOrigin.forward);

            if (Physics.Raycast(ray, out var hit, 10f)) {
                laserPointer.SetPosition(1, hit.point);

                if (hit.collider.GetComponent<MosisPhone>() != null) {
                    var uv = hit.textureCoord;
                    var touchPos = new Vector2(
                        uv.x * phone.Resolution.x,
                        (1 - uv.y) * phone.Resolution.y
                    );

                    HandleTouch(touchPos);
                }
            }
        }

        private void HandleTouch(Vector2 pos) {
            bool triggerDown = OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger);

            if (triggerDown && !isTouching) {
                phone.Service.SendTouchDown(pos.x, pos.y);
                isTouching = true;
            }
            else if (triggerDown && isTouching) {
                if (Vector2.Distance(pos, lastTouchPos) > 2f) {
                    phone.Service.SendTouchMove(pos.x, pos.y);
                }
            }
            else if (!triggerDown && isTouching) {
                phone.Service.SendTouchUp(pos.x, pos.y);
                isTouching = false;
            }

            lastTouchPos = pos;
        }
    }
}
```

---

## Unreal Plugin

### Module Structure

```
MosisPhone/
├── MosisPhone.uplugin
├── Source/
│   ├── MosisPhone/
│   │   ├── MosisPhone.Build.cs
│   │   ├── Public/
│   │   │   ├── MosisPhoneActor.h
│   │   │   ├── MosisService.h
│   │   │   └── MosisHardwareProvider.h
│   │   └── Private/
│   │       ├── MosisPhoneActor.cpp
│   │       ├── MosisService.cpp
│   │       └── MosisHardwareProvider.cpp
│   └── ThirdParty/
│       └── MosisClient/
│           └── libmosis-client.so
├── Content/
│   ├── BP_MosisPhone.uasset
│   └── M_PhoneScreen.uasset
└── Documentation/
    └── integration-guide.md
```

### Main Actor

**File**: `MosisPhoneActor.h`

```cpp
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MosisPhoneActor.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPhoneReady);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnNavigate, FString, Screen);

UCLASS()
class MOSISPHONE_API AMosisPhoneActor : public AActor {
    GENERATED_BODY()

public:
    AMosisPhoneActor();

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Display")
    FIntPoint Resolution = FIntPoint(540, 960);

    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnPhoneReady OnPhoneReady;

    UPROPERTY(BlueprintAssignable, Category = "Events")
    FOnNavigate OnNavigate;

    UFUNCTION(BlueprintCallable, Category = "Mosis")
    void NavigateTo(const FString& Screen);

    UFUNCTION(BlueprintCallable, Category = "Mosis")
    void SendMessage(const FString& To, const FString& Text);

    UFUNCTION(BlueprintCallable, Category = "Mosis")
    void MakeCall(const FString& Number);

protected:
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;
    virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

private:
    UPROPERTY()
    UTextureRenderTarget2D* ScreenTexture;

    UPROPERTY()
    UMaterialInstanceDynamic* ScreenMaterial;

    TSharedPtr<FMosisService> Service;

    void HandleRaycastInput();
    void OnFrameAvailable(const TArray<uint8>& Pixels);
};
```

### Blueprint Integration

```cpp
// Blueprint callable functions for easy integration

UFUNCTION(BlueprintCallable, Category = "Mosis", meta = (WorldContext = "WorldContextObject"))
static AMosisPhoneActor* SpawnMosisPhone(
    UObject* WorldContextObject,
    FTransform SpawnTransform,
    FIntPoint Resolution = FIntPoint(540, 960)
);
```

---

## Hardware Button Simulation

### Physical Buttons

| Button | Function |
|--------|----------|
| Power | Lock/wake screen |
| Volume Up | Increase volume |
| Volume Down | Decrease volume |
| Home (soft) | Go to home screen |
| Back (soft) | Navigation back |

### Unity Implementation

```csharp
public class MosisPhoneButtons : MonoBehaviour {
    [SerializeField] private MosisPhone phone;
    [SerializeField] private Collider powerButton;
    [SerializeField] private Collider volumeUp;
    [SerializeField] private Collider volumeDown;

    void Update() {
        // Check for button presses via physics overlap
        if (IsButtonPressed(powerButton)) {
            phone.Service.SendButton("power");
        }
        // ...
    }
}
```

### Unreal Implementation

```cpp
// In Blueprint or C++
UFUNCTION(BlueprintCallable)
void PressButton(EMosisButton Button) {
    switch (Button) {
        case EMosisButton::Power:
            Service->SendButton("power");
            break;
        case EMosisButton::VolumeUp:
            Service->SendButton("volume_up");
            break;
        case EMosisButton::VolumeDown:
            Service->SendButton("volume_down");
            break;
    }
}
```

---

## Implementation Plan

### Phase 1: Unity Core
- [ ] Package structure
- [ ] MosisPhone component
- [ ] Basic touch input
- [ ] Frame rendering

### Phase 2: Unity VR
- [ ] VR raycast interaction
- [ ] Laser pointer visualization
- [ ] Controller haptics
- [ ] Two-handed support

### Phase 3: Unity Hardware
- [ ] Camera provider (RenderTexture)
- [ ] Microphone provider
- [ ] Speaker output
- [ ] Vibration

### Phase 4: Unreal Core
- [ ] Plugin structure
- [ ] MosisPhoneActor
- [ ] Blueprint integration
- [ ] Touch input

### Phase 5: Unreal VR
- [ ] Motion controller input
- [ ] Widget interaction
- [ ] Hardware providers

### Phase 6: Documentation
- [ ] Quick start guide
- [ ] API reference
- [ ] Sample scenes
- [ ] Troubleshooting

---

## Testing

### Unity Test Scene

1. Phone mounted in VR scene
2. Touch interaction works
3. Virtual camera shows game view
4. Audio plays through virtual speaker

### Unreal Test Level

1. Blueprint phone actor
2. Motion controller interaction
3. Event callbacks work
4. Performance acceptable

---

## Performance Targets

| Metric | Target |
|--------|--------|
| Frame latency | < 16ms |
| Touch latency | < 50ms |
| Memory usage | < 100MB |
| Draw calls | < 5 per phone |

---

## Acceptance Criteria

### Unity
- [ ] One-click prefab placement
- [ ] VR raycast touch works
- [ ] Events fire correctly
- [ ] Camera feed from game
- [ ] Audio bidirectional

### Unreal
- [ ] Blueprint spawnable
- [ ] Motion controller support
- [ ] Blueprint events
- [ ] Hardware providers

### Documentation
- [ ] Integration guide
- [ ] API docs
- [ ] Example projects
- [ ] Video tutorial
