개요

Unity에서 카메라를 다루기 위해 지난 포스팅에 프로젝트 생성 및 씬 설정, 간단한 스크립트 연결 및 프로젝트 빌드를 다뤄보았다.

이번 포스팅에서는 같은 프로젝트를 리눅스와 안드로이드에서 적절히 다르게 동작시키기 위해 본인이 삽질해본 것들을 언급하려 한다.

안드로이드 빌드 세팅

File > Build Setting에서 Android 로 Switch Platform 하면 처음 1회에는 바로 빌드할 수 있는 것이 아니고 관련 모듈을 설치해줘야한다. 위 이미지와 같이 Install with Unity Hub 버튼을 누르면 Unity Hub에서 관련 라이브러리 및 모듈을 받을 수 있게 연결된다. OpenJDK, Android SDK & NDK Tools 이 체크 되어있고 Continue를 클릭하여 다운로드 한다.

그 후 유니티를 다시 시작해줘야 안드로이드 플랫폼 빌드가 가능하다. 다시 시작해주고 Build Setting에서 Player Settings를 보면 모바일에서 중요하게 셋팅하는 부분이 있다.

  • Resolution and Presentation 항목에서 Orientation 항목을 통해 앱을 세로형태로 사용하는지 가로형태로 사용하는지 결정한다.
  • 또한 Other Settings에서 Minimul API Level 이라던가 Target API Level 등을 설정해준다.

빌드 설정이 완료되면 Build 버튼을 눌러 안드로이드 어플리케이션을 빌드하는데 .apk의 어플리케이션 설치파일 형태로 빌드할 수 있다. 이를 USB 케이블을 통해 공기계를 PC와 연결하여 옮겨서 설치하였다. 실제 구글 Play 스토어에 배포할 앱이라면 추가적으로 Keystore 관련한 설정을 잡아줘야한다.

그래서 이렇게 빌드한 앱을 공기계에 설치하여 실행해보면 전면 혹은 후면 카메라 화면이 보이지 않는다.

안드로이드 관련 권한 설정하기

카메라 화면이 보이지 않는 이유는 카메라 장치를 해당 앱이 사용할 권한이 없어서다. 그래서 유니티에서 빌드하기 전에 AndroidManifest.xml을 통해 사용하려는 권한을 설정해줘야 한다. 기본적으로 AndroidManifest.xml 파일은 에셋에 없는 상황이다.

AndroidManifest 파일 생성 및 작성

AndroidManifest 파일 생성

  • Assets/Plugins/Android 폴더를 생성한다. 유니티 에디터 아래의 Project 창에서 마우스 우클릭을 통해 할 수 있다.
  • Edit > Project Settings > Player 로 이동하여 Android 탭의 Publishing Settings 섹션을 확장.
  • Build 부분에 Custom Main Manifest 체크박스를 활성화하면 기본 AndroidManifest 파일이 Assets/Plugins/Android 폴더 내에 생성된다.

AndroidManifest 파일 수정

  • Assets/Plugins/Android 폴더 내에 있는 AndroidManifest.xml 파일을 연다. vscode 가 연동되어 있다면 해당 에디터로 열린다.
  • 다음과 같이 <manifest> 태그와 <application> 태그 사이에 권한 태그를 작성한다.
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Camera permission -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application>
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>
  • 해당 권한을 줘야하는 부분을 친절한 GPT-4o가 알려줬는데 아마도 WRITE_EXTERNAL_STORAGE 권한과 READ_EXTERNAL_STORAGE 권한은 없어도 되지않을까 싶다.

런타임에서 권한 요청

안드로이드 6.0(API 23) 이상에서는 앱 설치 시가 아닌 런타임에서 권한을 요청해야한다. 그래서 카메라 권한이 없다면 최초 1회 요청할 수 있게하는 스크립트를 작성한다.

카메라 권한 요청 스크립트

  • 프로젝트의 Scripts 폴더에 새로운 CameraPermissionRequest.cs 스크립트를 생성한다.
  • 코드는 다음과 같다. Update()는 기존 처럼 비워두어도 된다. ```c# using UnityEngine; using UnityEngine.Android;

public class CameraPermissionRequest : MonoBehaviour { void Start() { #if PLATFORM_ANDROID if (!Permission.HasUserAuthorizedPermission(Permission.Camera)) { Permission.RequestUserPermission(Permission.Camera); } #endif } }

- 여기에 OS 전처리기문이 있다. `#if PLATFORM_ANDROID`, `#endif` 문은 파이썬이었다면 단순한 주석으로 넘겼겠지만, 유니티(C#)에서는 OS에 따라 동작을 다르게 설정하는 전처리문으로 작동한다.

#### 스크립트 적용
- **Hierarchy** 창에서 `Camera Feed`를 연결해놓은 `Canvas` 오브젝트에서 **Add Component**를 눌러 **Camera Permission Request**를 검색하여 연결해주자. 
- 이러고 안드로이드 빌드를 다시하면 앱 구동 시 카메라 사용 권한을 1회 묻고 카메라 화면이 정상적으로 나오는 것을 볼 수 있다.
- 아마도 모바일 공기계 기준 후면카메라 하나의 화면만 나올 것이다.
  - 그러나 우리는 후면 및 전면 카메라로 자유롭게 사용도 전환하고 싶고, PC에서도 1개보다 많은 카메라가 연결되어있다면 이들간의 전환도 하고 싶다.

## 카메라 전환 기능 구현
기존 우리는 `CameraFeed.cs` 파일을 통해 카메라를 사용하는 스크립트를 작성하고 리눅스 프로그램 혹은 안드로이드 어플리케이션에서 연결된 1개의 카메라 화면을 보여주는 기능을 사용했다. 이제 기능을 업그레이드 하여 기본적으로 카메라 화면은 띄워주고, UI에 배치한 카메라 전환 버튼과 연동하여 여러 카메라를 순차적으로 돌려가며 보여주는 기능을 구현하자.

### UI 버튼 추가
- `Hierarchy` 창에서 `Canvas`를 선택하고, 우클릭하여 `UI` > `Button`을 눌러 `Canvas` 하위에 버튼 UI를 배치한다.
- 해당 `Button`을 클릭하고 `Inspector` 창에서 최상단에 있는 오브젝트 이름을 `SwitchCameraButton`으로 변경한다.
- `Hierarchy` 창에서 `Button`의 자식 오브젝트인 `Text`를 선택하고 `Inspector` 창에서 텍스트를 `Switch Camera`로 변경한다. 

### 카메라 전환 스크립트 작성
- `Assets/Scripts` 폴더에 새로운 스크립트를 생성하고, `CameraSwitcher.cs`로 이름을 변경한다.
- 해당 파일을 열고 다음과 같이 작성.
```c#
using UnityEngine;
using UnityEngine.UI;

public class CameraSwitcher : MonoBehaviour
{
    public RawImage rawImage;
    public AspectRatioFitter aspectRatioFitter;
    public Button switchCameraButton;

    private WebCamTexture webCamTexture;
    private WebCamDevice[] devices;
    private int currentCameraIndex = 0;

    void Start()
    {
        devices = WebCamTexture.devices;
        if (devices.Length > 0)
        {
            StartCamera(currentCameraIndex);
        }

        switchCameraButton.onClick.AddListener(SwitchCamera);
    }

    void StartCamera(int index)
    {
        if (webCamTexture != null && webCamTexture.isPlaying)
        {
            webCamTexture.Stop();
        }

        webCamTexture = new WebCamTexture(devices[index].name);
        rawImage.texture = webCamTexture;
        webCamTexture.Play();
    }

    void SwitchCamera()
    {
        currentCameraIndex = (currentCameraIndex + 1) % devices.Length;
        StartCamera(currentCameraIndex);
    }

    void Update()
    {
        if (webCamTexture != null)
        {
            float aspectRatio = (float)webCamTexture.width / (float)webCamTexture.height;
            aspectRatioFitter.aspectRatio = aspectRatio;
        }
    }
}
  • Start()에서는 StartCamera() 메소드와 연동하여 연결된 카메라 디바이스가 존재한다면 WebCamTexture를 통해 카메라를 구동한다.
  • switchCameraButton에 AddListener로 SwitchCamera()를 연결하여 카메라 전환 기능을 버튼과 연결한다.

스크립트 적용

  • Hierarchy 창에서 Canvas를 클릭하면 Inspector에 기존에 연결한 Camera Feed 가 있다. 이를 우측 점 세개 버튼을 눌러 Remove Component를 통해 제거한다.
  • 이어서 Add Component를 눌러 CameraSwitcher 스크립트를 검색하여 추가한다.
  • 적절한 오브젝트를 스크립트의 변수에 연결해줘야한다. Canvas 하단에 자식으로 있는 RawImage를 드래그 앤 드롭으로 Raw Image 변수에 연결해주고 AspectRatioFitter 또한 연결해준다. 그리고 만들어준 SwtichCameraButton 오브젝트도 해당 변수에 연결한다.
  • 기존 CameraFeed의 기능은 전부 CameraSwicher에 통합되었다.

버튼 위치 조정

이렇게 앱을 빌드해서 실행하면 버튼이 프로그램이나 앱의 정중앙에 있어 여간 불편한게 아니다. 이를 우하단으로 옮겨보자

  • Hierarchy 창에서 SwitchCameraButton 선택
  • Inspector 창에서 RectTransform 컴포넌트의 Anchor Presets 버튼을 클릭한다.

  • Alt 키를 누른상태로 오른쪽 하단을 선택한다. 이러면 피벗과 앵커가 모두 우하단으로 설정된다.

이제는 빌드 시 우측 하단에 카메라 변경 버튼이 존재한채로 카메라를 변경할 수 있다.

카메라 정상 방향으로 보기

리눅스에서 빌드한 웹캠의 경우에는 카메라가 바라보는 방향에 따라 나를 바라보고 있다면 좌우가 반전된 것처럼 보일 수 있다. 문제는 안드로이드 인데 카메라가 가로로 길게 보이는 문제가 있었다. 안드로이드 후면 카메라의 경우에는 반시계방향으로 90도 돌아간 것 처럼 보였다. 이를 리눅스, 안드로이드 운영체제에 맞게 정상적으로 바꿔보자.

본인의 목표는 리눅스에선 카메라가 나를 바라보게 하여 좌우대칭으로 거울모드처럼 보이게 하는 것이고, 안드로이드에서는 후면 및 전면 카메라를 세로로 일반 카메라 동작과 동일하게 이용하는 것이다.

프로젝트 설정 변경

  • File > Build Settings로 이동한다.
  • Platform 목록에서 Android를 선택하고 Player Settings를 클릭한다.
  • Resolution and Presentation 섹션을 눌러 Default OrientationPortrait으로 변경한다.

스크립트 수정

CameraSwitcher 스크립트의 Start() 메소드를 다음과 같이 수정하여 앱 구동 시 우선 리눅스에서의 좌우 반전 및 안드로이드 후면 카메라의 시계방향 회전 90도를 구현하였다.

void Start()
    {
        rawImage.texture = SharedWebCam.webCamTexture;

        switchCameraButton.onClick.AddListener(SwitchCamera);

        #if UNITY_ANDROID 
        rawImage.rectTransform.localEulerAngles = new Vector3(0, 0, -90);
        #else
        rawImage.rectTransform.localScale = new Vector3(-1, 1, 1);
        #endif
    }
  • OS 전처리기를 할용해 안드로이드에서는 후면카메라가 평상시 쓰는 카메라와 동일하게 보이도록 작업하였고 안드로이드가 아닌 다른 OS에서는 좌우를 반전되도록 RectTransform 을 조정하였다.
    • 다른 OS라고 할때 저렇게 작성하면 IOS도 다른 OS에 포함되어 불편해진다. 여기서는 안드로이드와 리눅스의 경우만 다루니 너그럽게 넘어가자. (오히려 리눅스와 그 외(모바일)이라고할걸…)

이어서 안드로이드의 후면 카메라에서 전면 카메라로 변경 시에 상하가 뒤집히는 문제가 있었는데 이를 수정한다. Start() 아래에 있는 SwtichCamera() 메소드를 다음과 같이 수정 한다.

void SwitchCamera()
{
    WebCamDevice[] devices = WebCamTexture.devices;
    if (devices.Length > 0)
    {
        currentCameraIndex = (currentCameraIndex + 1) % devices.Length;
        SharedWebCam.webCamTexture.Stop();
        SharedWebCam.webCamTexture.deviceName = devices[currentCameraIndex].name;
        SharedWebCam.webCamTexture.Play();

        // 전면 카메라일 경우 텍스처를 좌우로 뒤집기 (안드로이드용)
        #if UNITY_ANDROID
        if (devices[currentCameraIndex].isFrontFacing)
        {
            rawImage.rectTransform.localScale = new Vector3(-1, 1, 1);
        }
        else
        {
            rawImage.rectTransform.localScale = new Vector3(1, 1, 1);
        }
        // 카메라 피드를 세로 모드에 맞게 회전 (안드로이드용)
        rawImage.rectTransform.localEulerAngles = new Vector3(0, 0, -90);
        #else
            // 다른 플랫폼 (리눅스 등)에서는 기본 설정 유지
        #endif
    }
}
  • 위 코드는 안드로이드 OS에서 전면카메라의 경우 이미지를 좌우로 Flip 한 뒤 Rotation을 구현한다. 우리가 화면을 보는 입장에서 Z축(depth)를 기준으로 회전을 한 것이다.
  • Portriat에서 이미 반시계로 90도 돌아간 카메라가 기본으로 되어있는 듯 하다.
  • 코드를 안드로이드와 그 외의 OS 기준으로 전처리하긴 했다. 안드로이드는 전면의 경우 좌우 뒤집기 후 -90도 회전, 후면의 경우 그대로 Vector3(1, 1, 1)로 준 뒤 -90도 회전을 구현하니 후면은 카메라의 화각과 동일하게 전면은 거울모드와 같은 기본 카메라 처럼 구현되었다.
  • 뭔가 각도가 90도면 시계 방향, -90도면 반 시계 방향이어야 할 것 같은데 피사체가 본인을 바라보는 역방향 + 거울처럼 좌우대칭 이런 것들이 얽혀있어 결과지향적으로 자동되게끔만 작성하였다. 누군가 보고 작업하시는 분들이 있다면 본인이 세팅하는 카메라 환경 및 피사체 환경에 따라 다른 값을 줘야할 수 있음을 유의하자.

Leave a comment