[Unity VR과 Android BLE] #8 안드로이드 Plugin

in kr-dev •  11 days ago

Unity VR앱과 안드로이드 앱을 integration하는 앱을 만들어 봅니다. 또 안드로이드의 BLE 장치 제어를 Unity에서 하는 내용을 다룹니다.


출처: https://medium.com/@narendrapal39/android-add-support-library-for-unity-205b45ca243d

이전글 - [Unity VR과 Android BLE] #7 버튼 UI 감추기


Unity와 Android 통합 (integration)

지끔까지는 VR관련 기본 내용입니다. 관련하여 자료도 많고요. 큰 어려움 없이 진행할 수 있는 부분입니다. 지금부터 진행할 부분에서 고생 좀 했습니다. 사실 이 글을 쓰기 시작한 이유는 대문 그림에 나온대로 안드로이드의 기능을 Unity로 불러 들이는 것을 구현하기가 간단치 않았기 때문입니다.

사실 구현할 것은 별로 없는데, 자료가 좀 부족합니다. 방법들의 여러 피스를 맞추는 기분이랄까요~

이번에는 안드로이드에서 구현한 것을 Unity에서 사용하는 방법을 알아보겠습니다. 궁극적으로는 안드로이드 앱에서 블루투스 장치를 제어하는 것을 Unity 앱에 통합하여 구현하는 것을 해볼 것입니다.

여기서 구현하지는 않지만, Unity 기능을 안드로이드로 불러들이는 것도 가능합니다. 즉 통합의 주체를 Unity로 할 것이냐 안드로이드로 할 것이냐 하는 것이죠. 이 방법에 대한 참고자료만 몇 가지 링크를 공유하는 정도로만 하겠습니다. 이 방법도 시도해 보긴 했지만 성공했따고 말하긴 어렵습니다. Unity 버전이 예전 걸로 구현된거라 최신 Unity랑 안맞는 부분이 있는 거 같기도 합니다.
https://medium.com/@davidbeloosesky/embedded-unity-within-android-app-7061f4f473a
http://techraveller.com/how-to-integrate-unity-program-to-android-studio/


Android에서 함수를 구현해서 Unity에서 호출하여 실행시키는 방법을 알아보겠습니다. 즉 통합 주체는 Unity가 되고 Android는 Library가 되는 방식입니다.

Unity에서는 매우 간편한 방법을 제공하고 있습니다. 매우 좋죠!

통합을 위해 참고한 동영상입니다. 매우 자세히 하나하나 설명해줘서 고맙게 도움이 많이 됐습니다.

단지, 동영상에서는 단순한 함수 라이브러리만 import 하는 방식이어서 블루투스 장치를 사용하기 위한 방법들은 없어서 많이 고생했습니다.

그럼 먼저 안드로이드 부분을 구현해 보겠습니다. 블루투스 관련 부분을 당장 구현하기 보다는 간단히 테스트 해볼 수 있는 정도로 라이브러리를 만들어 보려고 합니다.

구현을 위해 Android Studio 설치가 필요합니다. 설치과정은 생략하겠습니다.
https://developer.android.com/studio/

간략한 구현 과정은 다음과 같습니다.

  1. 빈 안드로이드 프로젝트 생성
  2. 안드로이드 라이브러리 모듈 추가
  3. 함수 코딩
  4. 라이브러리 빌드
  5. 빌드 파일을 Unity에 추가
  6. Unity에서 안드로이드 라이브러리 Import

정말 간단히 구현됩니다.

빈 안드로이드 프로젝트 만들기

Android Studio를 실행하여 New Project를 만듭니다.

설정: Application 이름은 적당히 합니다. 우린 App을 만드는 것이 아니라 Library를 만들 것이기 때문에 App 이름은 중요하지 않습니다.
Company domain을 입력합니다.

설정: Target Android Device는 Phone으로 하고 API Level은 24로 합니다.

설정: App을 만드는 것이 아니라 Library를 만들 것이기 때문에 Add No Activity를 선턱하고 Finish합니다.

안드로이드 Library 추가

아래 그림과 같이 빈 안드로이드 프로젝트가 생성된 후, New Module을 추가합니다.

설정: File -> New -> New Module

그런 다음 Android Library를 선택합니다.

Library 이름은 BLELibrary로 했습니다. 적당한 이름으로 하면 됩니다.

그러면 Android Studio을 프로젝트 창에 아래 그림과 같이 두 개의 폴더가 보입니다 (Gradle 제외.)

앞으로 작업은 새로 추가한 blelibrary에서만 할 것입니다. 특히 빌드할 때 주의할 것은 아래 그림과 같이 blelibrary를 빌드해야 한다는 것입니다. App을 빌드해서는 아무것도 안될 것입니다.

소스 코드 코딩

안드로이드에서 구현하고자 하는 기능을 코딩합니다. 한가지 주의할 것은 Android Application이 아니라 Library이기 때문에 앱 만드는 것과는 조금 다릅니다. 기능 제약이 좀 된다고 보시면 됩니다. 특히 StartActivity와 같은 함수 사용이 안됩니다. 되는 방법을 있을수도 있지만 저는 못찾았습니다. AppCompat 클래스를 상속하여 어찌 어찌 하는 거 같은데 계속 종속 라이브러리 오류로 빌드 실패했습니다.

그래서 Activity 관련된 기능은 Unity에서 제공하는 Activity 인스턴스를 사용해야 합니다. 그래도 여전히 StartAcitivyForResult와 같은 함수는 Library에서 실행이 불가합니다.

그럼 간단히 구조를 잡아 보겠습니다. 먼저 새로운 클래스 파일을 추가합니다. 여기서도 주의할 것이 반드시 blelibrary 폴더를 선택한 상태에서 작업을 해야 합니다. App 폴더를 선택하고 작업하면 App에 파일이 추가되는 것이기 때문에 제대로 동작하지 않습니다. 꼭 Library 폴더를 선택한 후 작업을 해주세요.

설정: club.etain.blelibrary를 선택한 후 New -> Java Class

클래스 이름은 적당히 BLEControl로 합니다.

그런 다음 아래와 같이 코딩합니다.

package club.etain.blelibrary;

import android.app.Activity;
import android.util.Log;

public class BLEControl {
    // tag for log message
    public static String TAG= "BLEControl";
    // initialized message
    public static String INIT_MSG= "initialized";
    // current activity
    protected Activity current_activity_;

    // set current activity by unity
    public void setActivity(Activity _activity) {
        current_activity_= _activity;
    }

    // initialize
    public String init() {
        Log.d( TAG, "Call by Unity");
        Toast.makeText( current_activity_, "Call by Unity", Toast.LENGTH_SHORT).show();
        return INIT_MSG;
    }
}

내용이 별로 없습니다. Unity에서 setActivity 함수를 호출하여 현재 activity 인스턴스를 넘겨주는 부분과 Unity에서 호출하여 값을 리턴할 init() 함수 부분이 구현되었습니다. 기본적으로 이 두 가지 함수의 동작만 되면 Unity에서 안드로이드 라이브러리 사용하는 것이 문제가 없게 됩니다.

BLELibrary 모듈 빌드

반드시 BLELibrary 폴더를 Project 창에서 선택한 후 Build -> Make Module blelibrary 합니다.
그러면 다음과 같은 폴더에 classes.jar 파일이 생깁니다. 이것이 BLELibrary를 빌드하여 생긴 파일입니다.

빌드 파일 Unity Assets에 추가

이 파일을 Unity 프로젝트의 Assets -> Plugins -> Android 폴더 밑에 복사합니다. Unity 프로젝트 뷰로 드래그 앤 드랍해도 됩니다.

위와 같이 Project 뷰에서 classes.jar 파일을 클릭하면 Inspector 뷰에 Android plugin 이라고 체크되어 있습니다.

라이브러리 호출 코딩

그런 다음 Hierarchy 뷰에 이 파일을 이용하는 Empty Object를 만들고, 이름을 PluginScript라고 바꿉니다.
다음으로 PluginScript에 추가할 AndroidWrapper.cs 스크립트를 Assets -> Scripts 폴더 밑에 만듭니다. 그런 다음
AndroidWrapper.cs 스크립트를 PluginScript에 추가시킵니다. 추가하는 방법은 스크립트 파일을 Hierarchy 뷰의 PluginScript로 드래그 앤 드랍하면 됩니다. 그러면 아래와 같이 됩니다.

Inspector 뷰를 보면 AndroidWrapper.cs가 추가되어 있는 것이 보입니다. 이 스크립트 파일을 열고 다음과 같이 코딩합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AndroidWrapper : MonoBehaviour
{
    // android object
    private AndroidJavaObject AndroidObject = null;
    // text 
    public Text Message;

    private AndroidJavaObject GetJavaObject()
    {
        if (AndroidObject == null)
        {
            AndroidObject = new AndroidJavaObject("club.etain.blelibrary.BLEControl");
        }
        return AndroidObject;
    }

    // Use this for initialization
    void Start()
    {
        // Retrieve current Android Activity from the Unity Player
        AndroidJavaClass jclass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = jclass.GetStatic<AndroidJavaObject>("currentActivity");

        // Pass reference to the current Activity into the native plugin,
        // using the 'setActivity' method that we defined in the ImageTargetLogger Java class
        GetJavaObject().Call("setActivity", activity);

        Message.text = GetJavaObject().Call<string>("init");
    }

    // Update is called once per frame
    void Update()
    {

    }
}

Android Activity 관련해서 잘 안되던 부분인데, 다음 사이트에서 정보를 얻어서 해결했습니다.
https://library.vuforia.com/articles/Solution/How-To-Use-Android-Plugins-in-Unity-Apps

가장 먼저 Android에서 작성한 클래스의 인스턴스를 얻어 오는 부분입니다.

    // android object
    private AndroidJavaObject AndroidObject = null;
   생략
    AndroidObject = new AndroidJavaObject("club.etain.blelibrary.BLEControl");

위와 같이 AndroidJavaObject라는 변수 타입을 사용하고, 안드로이드에서 만든 정확한 패키지명을과 클래스 이름으로 클래스 인스턴스를 얻어옵니다. 패키지명과 클래스이름을 정확히 입력해야 합니다.

중요한 부분이 바로 이 부분입니다. 안드로이드의 현재 activity 인스턴스를 얻는 부분입니다. 우리가 만든 BLELibrary는 라이브러리이지 Application이 아닙니다. Application에 대한 인스턴스는 Unity가 관리하는 것입니다. 따라서 이것을 얻어와서 안드로이드 Library에 전달시켜주면, 안드로이드 라이브러리에서도 Activity 관련 기능을 수행할 수 있게 되는 것입니다.

     // Retrieve current Android Activity from the Unity Player
        AndroidJavaClass jclass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = jclass.GetStatic<AndroidJavaObject>("currentActivity");

이를 위해 위 코드에서 먼저 Unity의 CurrentActivity 인스턴스를 얻어 옵니다. 그런 다음 아래와 같이 안드로이드 라이브러리에 인스턴스를 전달하고, 라이브러리에 있는 'init`함수를 호출해서 결과 메시지를 받습니다.

       GetJavaObject().Call("setActivity", activity);
       Message.text = GetJavaObject().Call<string>("init");

위 코드가 바로 안드로이드 라이브러리 함수 호출 예시입니다. 앞에서 안드로이드 라이브러리 인스턴스를 얻었기 때문에 이것을 이용해서 함수를 호출하면 됩니다. 이때, Call함수가 사용되고, 호출할 함수에 리턴값이 있다면 Call<String>("init')의 경우와 같이 리턴 데이터의 타입을 넣어줍니다.

Message 변수는 Text 타입으로 화면에 표시할 메시지입니다. 이전에 Text라는 UI를 만들어 뒀었는데, 이 변수와 UI를 연결시켜줍니다.

설정: Hierarchy 뷰의 Menu 밑의 Text를 PluginScript의 Message 필드에 드래그 앤 드랍


결과 확인

이제 다 됐습니다. 그럼 이제 실행해 보겠습니다. 그런데 한가지 문제가 있습니다! VR 앱으로 만들었기 때문에 확인할 방법이 없습니다. VR 화면으로 직접 확인하는 수 밖에 없습니다. Unity에서 Build and Run을 실행하여 안드로이드 폰에 앱을 설치하고 Gear VR로 보니 Status에 "intialized"라고 제대로 뜹니다. 그리고 Toast 메시지는 VR에서는 동작하지 않습니다만, VR 앱이 아닌경우는 제대로 동작합니다.

VR앱을 개발할 때, 이와 같이 안드로이드 라이브러리 통합하는 작업은 나중으로 하면 디버그에 시간이 많이 소요됩니다. 따라서 안드로이드 라이브러리 관련 구현을 VR이 아닌 환경에서 구현하여 테스트하고, VR 내용을 구현하는 것이 바람직합니다. 저는 Unity와 VR 내용을 먼저 다루다 보니 이런 순서로 갔지만 여러분은 개발 시간을 단축할 수 있는 방법으로 가세요. 사실 저도 먼저 라이브러리 동작을 확인 후에 VR 내용을 추가했었습니다. 지금은 강좌를 만들다 보니 이런 순서가 되어버렸네요.

이전글에서 소개해드린 Unity Remote 앱도 VR 앱의 경우는 동작을 안합니다. 안드로이드와 vR앱을 통합하는 것은 개발 시간이 많이 소요되는 작업이네요.


다음에는 블루투스 권한 설정하는 부분과 장치 연결하는 부분을 구현해보겠습니다.

오늘의 실습: 먼저 안드로이드 라이브러리를 구현하여 테스트 한 후 VR 앱으로 만들어 보세요.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

안드로이드 라이브러리를 먼저 테스트 한 후에 VR 앱으로 만들어야겠어요. VR과 블루투스 연동 개발이 쉽지 않네요.

·

네 그래야 하겠더라구요. 아니면 별도의 프로젝트에서 안드로이드 기능을 테스트 할 수 있게끔 하던지요~