Unity에서 Android JAR플러그인 사용하기

U

Unity를 사용해 게임을 만들다 보면 Android 알림(로컬 푸시)을 사용하거나 Unity Plugin으로 지원하지 않는 SNS 기능을 사용해야 하는 경우 네이티브로 기능을 구현해야 하는 상황이 종종 발생하게 됩니다. C#에서 Java 메소드를 호출하고 Java에서 C# 메소드를 호출하는 방법을 알아보도록 하겠습니다.

JAR 플러그인?

JAR(Java Archive) 파일은 Java 코드로만 이루어져 있어 리소스를 포함할 수 없으며 C# 스크립트에서 Java로 작성된 메소드를 호출하는 데 사용됩니다.

JAR 플러그인 만들기

이 글에서는 아래 환경을 기준으로 작업되었습니다.

  • Unity3D 2018.3.8f1
  • Android Studio 3.4.1
  • Android Gradle Plugin Version 3.4.1
  • Gradle Version 5.1.1

먼저 Android Studio를 이용해 JAR 플러그인을 생성하는 방법을 알아보겠습니다.

프로젝트를 생성을 선택하고 Empty Activity를 선택합니다.
프로젝트 이름과 위치를 지정하고 생성을 마무리합니다.

Unity 프로젝트와 상호작용을 하기 위해서는 Unity Activity를 상속받아야 하는데 그러기 위해서는 Unity 라이브러리를 프로젝트에 포함시켜야 합니다. Mac OS와 Windows 환경에서의 라이브러리 위치가 상이하므로 이곳에서 classes.jar 위치를 확인하시고 다음으로 넘어가시면 됩니다. 제가 사용하는 Mac 기준으로 /Applications/Unity/Hub/Editor/2018.3.8f1/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar 위치에 존재하고 있습니다.

위에서 확인한 classes.jar 파일을 복사하여 {project path}/app/libs 폴더에 복사해 주시면 UnityActivity를 확장할 수 있습니다.

기본 설정으로 프로젝트 하위 libs 폴더가 의존성 참조 대상이므로 해당 폴더 아래에 라이브러리를 위치하면 추가 설정 없이 사용이 가능합니다.

기존 프로젝트는 플러그인이 앱 모듈로 설정되어 있으므로 라이브러리로 변경을 해주어야 합니다. Android Studio의 좌측 프로젝트 창에서 build.gradle (Module: app) 파일을 열고 라이브러리 모듈을 명시해 줍니다. gradle 파일이 수정되면 우측 상단에 Sync Now 링크가 출력되는데 해당 링크를 클릭하여 Sync가 정상적으로 완료되는지 확인합니다.

apply plugin: 'com.android.library'

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 28
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
gradle 파일이 수정되면 꼭 우측 상단의 Sync Now를 클릭하여 동기화 시켜 주세요.

이제 Unity 라이브러리를 사용할 수 있게 되었습니다. UnityPlayerActivity를 상속받고 C#과 상호작용을 하는 코드를 만들어보겠습니다.
좌측 프로젝트 창에서 app/java/{package name}/MainActivity 파일을 열고 UnityPlayerActivity를 상속받습니다.

package com.santacodes.plugintest;

import android.os.Bundle;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    public void TestMethod() {
        UnityPlayer.UnitySendMessage("AndroidManager", "PrintMessage", "Test Method");
    }

    public int TestMethod(int value) {
        return value;
    }

    public void TestMethod(String value) {
        UnityPlayer.UnitySendMessage("AndroidManager", "PrintMessage", value);
    }
}

기본으로 생성되는 onCreate 이외에 3개의 메소드를 추가하였는데 그 안에 UnityPlayer.UnitySendMessage라는 메소드를 사용하고 있습니다. 이 메소드는 Java에서 Unity GameObject에 존재하는 C# 스크립트를 호출하는 기능을 갖고 있습니다.

UnityPlayer.UnitySendMessage(String var0, String var1, String var2)
var0 : 호출 대상이 되는 현재 Scene의 GameObject 이름
var1 : 첫 번째 파라미터의 이름의 GameObject에 존재하는 메소드 이름
var2 : 두 번쨰 파라미터의 메소드에 전달할 파라미터

이제 Android Studio에서의 마지막 작업인 jar 파일을 생성해보겠습니다. 위에서 라이브러리 모듈로 변경하였던 gradle 파일을 다시 열고 아래 코드를 추가하고 Sync Now를 클릭합니다.

// 기존 플러그인을 삭제.
task deleteObjectJar(type: Delete){
    delete 'release/AndroidPlugin.jar'
}

// from 경로에 위치한 class결과물을 release 폴더에 AndroidPlugin.jar이름으로 위치.
task exportJar(type: Copy){
    from('build/intermediates/packaged-classes/release/')
    into('release/')
    include('classes.jar')
    rename('classes.jar', 'AndroidPlugin.jar')
}

// exportJar 실행시 deleteObjectJar가 실행되도록 의존성 연결.
exportJar.dependsOn(deleteObjectJar, build)

2개의 gradle 이벤트를 등록하고 exportJar가 실행되면 기존 파일이 제거되도록 하였습니다.
여기서 exportJar에 위치한 from 경로가 gradle 버전에 따라 다를 수 있습니다.
build/intermediates/packaged-classes/release/ 경로로 진행했을 때 jar 파일이 생성되지 않는다면 build/intermediates/bundles/release/ 경로로 변경하시면 됩니다.

설정이 완료되었으면 Android Studio 우측 Gradle 창에서 :app/Tasks/other에 위치한 exportJar 이벤트를 더블클릭하여 Jar 파일을 생성합니다.

Gradle 창이 보이지 않는 경우 Android Studio의 우측 Gradle을 클릭하시면 창이 열립니다.
exportJar가 완료되면 {project path}/app 하위에 release 폴더 및 jar 파일이 생성됩니다.

C# 스크립트에서 Java 메소드 호출하기

C#에서 Java 코드를 호출하기 위해서는 Unity 프로젝트의 특수 폴더에 미리 생성해 두었던 AndroidPlugin.jar를 위치시켜야 합니다. {project path}/Assets/Plugins/Android 경로에 해당 파일을 이동시킵니다. (폴더가 존재하지 않으면 생성해주세요.)
Unity 특수 폴더에 대한 설명은 Unity 매뉴얼에서 확인 가능합니다.

Assets/Plugins/Android 경로에 파일을 위치시킵니다.

플러그인 파일을 위치시킨 후 UnityPlayerActivity를 상속받은 객체에 접근하기 위해서는 AndroidManifest.xml에 해당 정보를 입력해주어야 합니다. UnityPlayerActivity를 상속받은 MainActivity를 명시해주고 해당 파일을 플러그인과 동일한 위치에 저장합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.santacodes.plugintest" android:versionCode="1" android:versionName="1.0">
  <application android:label="@string/app_name" android:icon="@drawable/app_icon">
    <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
      <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>

마지막으로 C#과 Java가 상호작용을 할 수 있도록 스크립트를 작성합니다.

using UnityEngine;

public class AndroidManager : MonoBehaviour
{
	private AndroidJavaObject m_JavaObject;

    void Start()
    {
		// 현재 Activity 정보를 얻어온다.
		var jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
		m_JavaObject = jc.GetStatic<AndroidJavaObject>("currentActivity");
    }

	void OnGUI()
	{
		if (GUI.Button(new Rect(0.0f, 0.0f, 720.0f, 200.0f), "Test Method 1"))
		{
			// 현재 Activity에 존재하는 TestMethod를 호출한다.
			m_JavaObject.Call("TestMethod");
		}
		
		if (GUI.Button(new Rect(0.0f, 300.0f, 720.0f, 200.0f), "Test Method 2"))
		{
			// 현재 Activity에 존재하는 TestMethod를 호출하고 int 타입의 값을 반환 받는다.
			var result = m_JavaObject.Call<int>("TestMethod", 777);
			Debug.Log("Result = " + result);
		}
		
		if (GUI.Button(new Rect(0.0f, 600.0f, 720.0f, 200.0f), "Test Method 3"))
		{
			// 현재 Activity에 존재하는 TestMethod에 파라미터를 함께 전달한다.
			m_JavaObject.Call("TestMethod", "Hello Native Plugin");
		}
	}

	private void PrintMessage(string msg)
	{
		Debug.Log("AndroidManager::PrintMessage - " + msg);
	}
}

스크립트 작성이 완료되면 Unity Scene에 GameObject를 생성해 주어야 합니다. GameObject의 이름은 Java 플러그인 생성 시 UnityPlayer.UnitySendMessage 함수의 첫 번째 파라미터의 이름과 동일해야 Java 플러그인에서 호출하는 C# 메소드가 정상적으로 호출이 됩니다.

UnityPlayer.UnitySendMessage 첫 번째 인자로 입력된 AndroidManager와 동일하게 GameObject의 이름을 적용하였습니다.

프로젝트를 빌드 하여 Android 단말기에서 실행을 하면 버튼이 3개가 출력되고 버튼을 하나씩 눌러 로그를 확인해보면 C#과 Java 코드가 정상적으로 상호작용하는 것을 확인할 수 있습니다.

3 comments

  • dependencies {
    implementation fileTree(dir: ‘libs’, include: [‘*.jar’], excludes: [‘classes.jar’])
    compileOnly files(‘libs/classes.jar’)
    implementation ‘androidx.appcompat:appcompat:1.0.2’
    testImplementation ‘junit:junit:4.12’
    androidTestImplementation ‘androidx.test.ext:junit:1.1.0’
    androidTestImplementation ‘androidx.test.espresso:espresso-core:3.1.1’
    }
    Gladle 파일에 위 처럼 제외한 다음 compileOnly 옵션으로 하면 “classes.jar” 파일을 지워주지 않아도 됩니다.

  • 오후 3:28:01: Executing task ‘exportJar’…

    Executing tasks: [exportJar] in project *****

    > Task :app:exportJar NO-SOURCE

    BUILD SUCCESSFUL in 99ms
    오후 3:28:01: Task execution finished ‘exportJar’.

    성공했다고 뜨는데 대상폴더에 없는거는 어떤것이 문제인가요…

    build.gradle 내부에
    from(‘build/intermediates/packaged-classes/release/’)
    // from(‘build/intermediates/bundles/release/’)
    두가지 모두 테스트해보았습니다..

  • 그래들 버전에 따라 경로가 다릅니다
    6.0.1 버전 기준
    from(‘build/intermediates/aar_main_jar/release/’)
    으로 변경하니 됩니다

By berabue

최신 글

보관함

최신 댓글

메타