프로그래밍/Android

안드로이드 Unity 플러그인 만들기

Lou Park 2022. 4. 16. 17:17

회사에서 GoogleAnalaytics나 AppsFlyer처럼 게임 중 발생하는 이벤트들을 캐치해서 보내는 SDK를 개발할 일이 생겼는데, Native를 왠만하면 이용하지 않는 편으로 구현하려했으나 최종 코드 가독성이라던가 파일 관리, 추상화 등의 이유로 Native를 사용하기로 했다. 오늘은 그때를 돌아보며 귀찮지만...처음 연결했던 방법을 적어보려한다.

 

1. classes.jar 추출하기

이번이 나의 처음 SDK 개발이었으며, Unity조차도 잘 몰랐다. 처음엔 Unity와 Android가 어떻게 소통한다는거지? 부터 이해가 필요했다. 둘 사이의 소통을 위해, Unity 라이브러리가 존재한다. 일반적으로는 아래 경로들에 위치하며, 이름은 classes.jar다.

이 파일을 복사하여 어딘가에 저장해두자.

[Windows]
C:\Program Files\Unity\Hub\Editor\<내 유니티 버전>\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes

[Mac]
Applications/Unity/Hub/Editor/<내 유니티 버전>/PlaybackEngines/AndroidPlayer/Variations/il2cpp/Release/classes.jar

 

2. 안드로이드 프로젝트, 모듈 생성하기

Android Studio에서 Phone and Tablet 템플릿을 선택하여, 프로젝트를 하나 생성한다.

프로젝트 생성이 완료되었다면, File > New > New Module로 새로운 모듈을 생성해야한다.

 

unityplugin 또는 plugin이라고 이름을 짓고, 최소 SDK 버전을 확인 후 모듈을 생성해주면 된다. 사진에서는 API 19로 되어있으나, 범용성을 위해 그 아래로 설정하는 것도 좋다. Language는 Java로 변경한다.

 

좌측의 Project 탐색기에서 Android > Project로 바꿔주면 사진과 같이 만들어진 모듈 아래 libs 폴더가 보이는데, 여기에 아까 복사했던 classes.jar 파일을 붙여넣어준다. 그리고 unityplugin 폴더 속에 있는 build.gradle을 열어주어 classes.jar 의존성을 추가한다.

dependencies {
    // Unity에서 classes.jar 가지고 있기때문에 Compile only로 선언
    compileOnly files('libs/classes.jar')
    ...
}

여기서 주목할 점은 주석에도 적혀있듯이 complileOnly 키워드인데, 이를 implementation이나 api로 할경우에는 유니티에서 Duplicate class exception이나므로 컴파일할때만 의존성이 추가되는 것으로 설정해주어야한다.

 

3. 브릿지 만들기

간단하게 2가지 메소드를 준비해볼 것인데, Unity에서 Android로, Android에서 Unity로 연결을 시험해보기 좋다.

 

1. showToast(String message): 토스트 메세지를 띄운다.

2. getRandomNumber(): 랜덤한 숫자를 만들어서 반환한다.

 

Unity에서 호출할 WrapperClass를 만들어야하는데, 저는 우리 회사가 흥하라는 의미에서 PlayioUnityWrapper라고 이름을 지어보았다. Unity에서 호출하기때문에 반드시 static으로 선언해주어야한다. 

package com.playio.plugin;
import android.widget.Toast;
import com.unity3d.player.UnityPlayer;
import java.util.Random;

public class PlayioUnityWrapper {
    public static final String UNITY_OBJECT_NAME = "Playio";
    
    public static void getRandomNumber() {
        int number = new Random().nextInt(100);
        UnityPlayer.UnitySendMessage(UNITY_OBJECT_NAME, "OnReceiveRandomNumber", String.valueOf(number));
    }

    public static void showToast(String message) {
        UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(
                        UnityPlayer.currentActivity.getApplicationContext(),
                        message, Toast.LENGTH_LONG
                ).show();
            }
        });
    }
}

classes.jar가 제대로 import되었다면 UnityPlayer를 사용할 수 있게된다.

UnityPlayer.currentActivity는 게임에서 현재 Activity를 반환해주므로 context가 필요한 작업에 이용할 수 있다.

UnityPlayer.UnitySendMessage는 3개의 String 인자를 받는데, 각 인자는 다음과 같다.

1: 유니티 GameObject이름,
2: 해당 GameObject에서 호출할 함수 이름,
3: 전달할 값 내용

 

따라서 Unity에서 PlayioUnityWrapper.getRandomNumber()를 호출하면, Unity의 Playio라는 GameObject의 OnReceiveRandomNumber()라는 함수에 랜덤 값을 넘겨주게 되는 것이다.

 

4. aar 만들기

필요한 기능이 구현되었다면 aar 파일로 만들어서 unity에 써봐야하는데, 다음 명령어를 통해 빌드할 수 있다.

Android Studio에서 터미널을 열고 아래 명령어를 입력해주자.

gradlew assembleRelease

만들어진 aar 파일은 다음 경로에 위치해 있을 것이다.

<프로젝트 경로>/<플러그인모듈명>/build/outputs/aar/<플러그인모듈명>-release.aar

복사해서 Unity 프로젝트의 Assets/Plugins아래에 붙여넣는다.

 

5. 유니티 Script 작성

아까 안드로이드 플러그인을 만들때 Unity Game Object이름을  Playio라고 했으므로, Playio.cs를 만들어 스크립트를 작성해볼 것이다.싱글톤으로 구현했고, 초기화시에 "Playio"라는 게임 오브젝트를 만들어 붙여준다. 

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

public class Playio : MonoBehaviour
{
    static Playio instance;
    
    void Awake() {
        if (instance == null) {
            instance = this;
        } else if (instance != this) {
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);
    }

    public static Playio Instance {
        get {
            if (instance == null) {
                GameObject obj = new GameObject("Playio");
                instance = obj.AddComponent<Playio>();
            }
            return instance;
        }
    }
}

 

아까만든 WrapperClass를 호출하기 위해 AndroidJavaClass를 선언해준다. com.playio.plugin은 패키지명인데, 이를 모르겠다면 안드로이드 스튜디오에서 작성한 PlayioUnityWrapper 클래스 가장 상단에 보면 package ; 구문이있을 것이다. 그것을 확인해보면 된다.

public class Playio : MonoBehaviour 
{
	private static AndroidJavaClass playioAndroid = new AndroidJavaClass("com.playio.plugin.PlayioUnityWrapper");
    ...
}

Playio.cs에 아래 세 개의 함수를 추가해주었다. 

public void ShowToast(string message) {
#if !UNITY_EDITOR && UNITY_ANDROID
    playioAndroid.CallStatic("showToast", message);
#endif
}
public void GetRandomNumber() {
#if !UNITY_EDITOR && UNITY_ANDROID
    playioAndroid.CallStatic("getRandomNumber");
#endif
}

public void OnReceiveRandomNumber(string number) {
    Debug.Log("Got random number:" + number);
}

6. Scene에 Script 작성 후 연결

버튼 두 개를 만들어서 아래 ClickListener를 등록시켜두고 테스트를 해볼 것이다. Android로 Switch Platform하여 빌드해보자!

public class TestScript : MonoBehaviour
{
    public void OnClickShowToast() {
        Playio.Instance.ShowToast("Hi my name is lou.");
    }

    public void OnClickGetRandomNumber() {
        Playio.Instance.GetRandomNumber();
    }
}

 

 

7. 완성!

Show Toast 버튼을 누르면, 예상대로 토스트 메세지가 뜨는 것을 확인할 수 있다.

랜덤 숫자는 Debug.Log로 남겨서 UI 상으로 확인하기 어려운데, adb logcat -s "Unity"로 확인하면 간단하다.