IT_Games/Unity3D

Unity에서 Android의 기능을 확장하는 두 가지 방법은?

JJun ™ 2014. 6. 29. 16:37


 출처: http://www.atmarkit.co.jp/ait/articles/1107/04/news109.html 

        http://www.atmarkit.co.jp/ait/articles/1107/04/news109_2.html

        http://www.atmarkit.co.jp/ait/articles/1107/04/news109_3.html


 

 

 

Unity는 Android의 기능을 자유롭게 확장 할 수있다!

 마지막 " iOS 앱의 Android 이식도 간단 Unity의 기초 지식」에서는, 「Unity」의 개요와 사용법, Unity의 기능을 확장하는 플러그인 원격 디버깅 도구 등을 소개했습니다.

 개발 비용뿐만 아니라 이식 비용을 최소화 해주는 Unity이지만 자주 묻는 멀티 OS 지원 솔루션은 반대로 개별 OS 별 개별 구현 수 없거나 어렵다는 점이 있기도합니다.

 특히 네이티브 코드와의 연계 기본 디바이스와의 연계 등이 제일 것입니다. 그러나 Unity에서는이 문제를 해결하는 방법을 제공합니다.

 이번에는 Unity가 제공하는 네이티브 코드와의 연계 수단으로 다음의 2 가지 방법을 예제 코드를 섞어 해설합니다.

  1. JNI에서 Unity 측에서 Java 클래스를 호출
  2. Unity를 실행하는 Activity을 사용자 정의

JNI에서 Unity 측에서 Java 클래스를 호출 준비

 우선, 평범하기는하지만 Java 클래스에서 출력 한 문자열 "Hello World"를 Unity 측에서 취득하는 샘플에서 설명합니다. 어떻게 Unity에서 Java 측의 클래스  메소드 를 호출하는지 확인하자.

【1】 Unity 프로젝트 "HelloWorldJNI"새

 [File] → [New Project]에서 "HelloWorldJNI"프로젝트를 만듭니다. 패키지는 특히 가져올 수 없습니다.

【2】 Android 용 빌드 설정

 [File] → [Build Settings ...]에서 [Build Settings를 열고 Platform을 "Android"에 다음 Switch Platform]을 누릅니다. [Player Settings ...를 눌러 Inspector 창에서 [Bundle Identifier]에 패키지 이름을 지정합니다. 여기에서는 「com.example.unity.hello "고합니다.

【3】 호출되는 Java 클래스 "HelloWorldJava"를 창조

 Java 클래스의 개발 환경은 무엇을 이용해도 상관 없습니다. 여기에서는 Eclipse를 사용하여 프로젝트와 클래스를 만들었습니다.

 예를 들어, 패키지 이름을 "com.example.java.helloworld"며 "HelloWorldJava"클래스를 만들고 생성자 하나와 정적 메서드를 하나 인스턴스 메서드를 하나 만들었습니다.

package com.example.java.helloworld ;
public  class  HelloWorldJava  {
    public  HelloWorldJava () { 
    }
    static  public  String getStaticHello () { 
        return  "Hello World, Static!" ; 
    }
    public  String getHello () { 
        return  "Hello World!" ; 
    } 
}
  • public HelloWorldJava () : 생성자
  • static public String getStaticHello () : 정적 함수로 "Hello World Static!"문자열을 반환하는 함수
  • public String getHello () : 인스턴스 함수로 "Hello World!"문자열을 반환하는 함수

【4】 클래스의 빌드와 JAR 파일 설치

 만든 클래스를 컴파일, JAR 파일에하고 Unity 프로젝트 디렉터리에 설치합니다. 여기에서는 Eclipse에서 JAR Export 기능을 이용하여 "HelloWorldJava.jar"로 만들고 있습니다.

그림 1 JAR Export 대화

 대상에는 규칙이 "[Unity 프로젝트 디렉토리] / Assets / Plugins / Android /"아래에 설치해야합니다.

그림 2 【Unity 프로젝트 디렉토리] / Assets / Plugins / Android 다음에 설치

 

C #에서 JNI 액세스를 간소화하는 도우미 클래스

 Unity에서 Java 클래스를 읽는 방법에는 여러 가지가 있지만 가장 알기 쉽고 간단한 방법은 Unity 스크립트상에서 C #  JavaScript 등을 이용한 JNI (Java Native Interface) 액세스를 간소화하는 도우미 클래스 "AndroidJNIHelper" 를 이용하는 방법입니다.

 JNI와 나중에 사용 Android NDK 에 대한 자세한 내용은 다음 기사를 참조해야합니다.

Android 임베디드 개발의 새로운가능성 (2)
: 네이티브 코드와의 연계를 통한 Android 향상 (1 / 3) - @ IT MONOist
 viakwout

 

도우미 클래스를 사용하는 코드를 받기

 이 도우미 클래스를 사용하는 코드는 Unity 사이트에서 다운로드 할 수 있습니다.Unity Plugin에 대한 페이지 의 "Java Plugin Sample"항목에서 "AndroidJNIHelper"을 이용한 샘플 애플리케이션이 제공되며이 때 아래에서 사용하는 각 파일을 볼 수 있습니다.

. 
네이버 ─ JavaPluginSample 
├ ─ Assets 
 
 네이버 ─ Plugins 
  JavaVM . CS ← 이용하는 
 JNI . CS ← 이용하는 
 
 네이버 ─ 안드로이드 
 
 ├ ─ bin
  
 ├ ─ JNI
   안드로이드 . mk ← 이용하는 
 JNI . cpp ← 이용하는 
 
 ├ ─ libs
  
 네이버 ─ src
 
네이버 ─ Library

Android NDK를 사용하여 도우미 클래스 용의 네이티브 라이브러리를 빌드

 먼저 헬퍼 클래스 용의 네이티브 라이브러리 "libjni.so"을 컴파일하기 위해 jni.cpp 파일과 NDK 빌드 용 파일 "Android.mk"을 Java 프로젝트의 "jni"디렉토리에 설치합니다.

그림 3 Eclipse의 Package Explorer]

 그리고 Android NDK를 사용하여 빌드합니다.

$ pwd
 / Users / daisuke . sato / workspace_from0308 / itmedia / HelloWorldJava
$ ls
bin jni src
$ ndk - build - B  ( 안드로이드 NDK 제공 명령을 실행) 
Compile + arm : jni 
 / Users / daisuke . sato / workspace_from0308 / itmedia / HelloWorldJava / libs / armeabi

 빌드 할 때 "/ libs / armeabi /"아래에 libjni.so 파일이 있습니다.

$ ls
bin jni libs obj src
$ ls - l / libs / armeabi / 
total 88 
- rwxr - xr - x 1 daisuke . sato staff 41776  3  21  17 : 56 libjni . so

 생성 된 libjni.so을 JAR 파일과 같은 위치에 설치합니다. 
그러면 Unity 프로젝트의 디렉토리 구조는 다음과 같습니다.

그림 4 생성된 "libjni.so"파일을 설치

 다음 페이지 에서는 Java 클래스 호출 용 C # 스크립트를 만들고 실제로 움직여 봅니다. 또한 Unity가 Android에서 어떻게 작동하고 있는지를 설명합니다.

 

 

 

 


 

 

 

Java 클래스 호출 용 C # 스크립트

 다음은 Unity 쪽 작업입니다.

Unity 측 파일의 준비

 먼저, Project 창에서 방금 추가 한 HelloWorldJava.jar과 libjni.so가 "/ Plugins / Android"폴더에 표시되어 있는지 확인하십시오 (그림 5).

 그런 다음 Java 클래스를 호출하는 C # 용 헬퍼 클래스 파일 "JavaVM.cs」 「JNI.cs"를 "Plugins"폴더에 등록합니다 (그림 6).

 

그림 5 Unity에서 Project 창

 

그림 6 C # 용 헬퍼 클래스 파일을 "Plugins"폴더에


 

 이상에서 Java 클래스를 호출하기위한 준비는 완료입니다. 후 클래스를 호출하여 함수를 실행하는 스크립트를 작성합니다.

C # 스크립트를 작성하고 실행

 C # 스크립트 파일로 "CallJavaCode.cs"를 루트 폴더에 생성하고 편집기에서 엽니 다.

CallJavaCode.cs
using UnityEngine;
using System. Collections;
using System. Runtime.InteropServices;
using System;
 
public class CallJavaCode : MonoBehaviour {
    private IntPtr HelloWorldJavaClass;
    private int ptr_getStaticHello;
    private int ptr_getHello;
    private string announceString;
 
    void Start () {
        / / attach our thread to the java vm; obviously the main thread is already attached but this is good practice / / ...... [1]
        JavaVM.AttachCurrentThread ();
 
        / / create reference of HelloWorldJava class / / ...... [2]
        IntPtr cls_HelloWorldJavaClass = JNI.FindClass ( "com / example / java / helloworld / HelloWorldJava");
        int mid_HelloWorldJavaClass = JNI.GetMethodID (cls_HelloWorldJavaClass "" "() V");
        IntPtr obj_HelloWorldJavaClass = JNI.NewObject (cls_HelloWorldJavaClass, mid_HelloWorldJavaClass);
 
        HelloWorldJavaClass = JNI.NewGlobalRef (obj_HelloWorldJavaClass);
 
        / / create references of HelloWorldJava class methods / / ...... [3]
        ptr_getStaticHello = JNI.GetStaticMethodID (cls_HelloWorldJavaClass "getStaticHello", "() Ljava / lang / String;");
        ptr_getHello = JNI.GetMethodID (cls_HelloWorldJavaClass "getHello", "() Ljava / lang / String;");
        announceString = "Sample Started";
    }
 
    / / getStaticHello () / / ...... [4]
    private String getStaticHello () {
        IntPtr str_GetStaticHello = JNI.CallStaticObjectMethod (HelloWorldJavaClass, ptr_getStaticHello);
        Debug.Log ( "str_GetStaticHello ="+ str_GetStaticHello);
        IntPtr stringPtr = JNI.GetStringUTFChars (str_GetStaticHello 0);
        Debug.Log ( "stringPtr ="+ stringPtr);
        String staticHello = Marshal.PtrToStringAnsi (stringPtr);
        JNI.ReleaseStringUTFChars (str_GetStaticHello, stringPtr);
        Debug.Log ( "return value is ="+ staticHello);
        return staticHello;
    }
 
    / / getHello () / / ...... [5]
    private string getHello () {
        IntPtr str_GetHello = JNI.CallObjectMethod (HelloWorldJavaClass, ptr_getHello);
        Debug.Log ( "str_GetHello ="+ str_GetHello);
        IntPtr stringPtr = JNI.GetStringUTFChars (str_GetHello 0);
        Debug.Log ( "stringPtr ="+ stringPtr);
        String hello = Marshal.PtrToStringAnsi (stringPtr);
        JNI.ReleaseStringUTFChars (str_GetHello, stringPtr);
        Debug.Log ( "return value is ="+ hello);
        return hello;
    }
 
    void onGUI () {
        resetButton ();
 
        if (makeButton ( "getStaticHello")) {
            string message = "Message From HelloWorldJava (getStaticHello ()) :"+ getStaticHello (); / / ...... [6]
            announceString = message;
            _log (message);
        }
        if (makeButton ( "getHello")) {
            string message = "Message From HelloWorldJava (getHello ()) :"+ getHello (); / / ...... [7]
            announceString = message;
            _log (message);
        }
        showMessage (announceString);
    }
 
    int buttonX = 0;
    int buttonY = 0;
 
    void resetButton () {
        buttonX = 0;
        buttonY = 0;
    }
 
    void showMessage (string message) {
        int labelW = 400;
        int labelH = 50;
        GUI.Label (new Rect (10,80, labelW, labelH), message);
    }
 
    bool makeButton (string label) {
        int buttonW = 150;
        int buttonH = 50;
        int buttonsInRow = 2;
        bool b = GUI.Button (new Rect (10 ~ buttonX * (buttonW +5) 160 + buttonY * (buttonH +5), buttonW, buttonH), label);
        buttonX + +;
 
        if (buttonX == buttonsInRow) {
            buttonX = 0;
            buttonY + +;
        }
        return b;
    }
    void _log (string logstring) {
        Debug.Log (logstring);
    }
}

 [1]에서는 JavaVM  스레드 를 연결하고 JNI 액세스 할 수있는 상태에 있습니다.

 [2]에서는 HelloWorldJava 클래스를 찾아 새 개체로 생성합니다. JNI.FindClass ()에 이번 사용하는 Java 클래스 "com.example.java.helloworld.HelloWorldJava"을 지정하여 액세스되는 클래스를 지정합니다.

 [3]은 [2]에서 얻은 클래스 개체에서 참조하려는 함수 ( "getStaticHello ()」 「getHello ()")에 대한 포인터 를 가져옵니다. 정적 메서드 에 대해서는 "GetStaticMethodID ()"함수 인스턴스 메서드 에 대해서는 "GetMethodID ()"함수에서 얻을 수 있습니다.

 [4]는 "getStaticHello ()"함수를 호출하면 검색된 문자열 데이터를 Unity로 취급 String 형식으로 변환하는 내부 함수입니다. [5]에서는 [4]와 같은 처리를 "getHello () '함수에 대해 실시하고 있습니다.

 [6]는 버튼을 클릭했을 때 "getStaticHello ()"에서 얻은 문자열을 출력합니다.

그림 7 "getStaticHello ()"에서 얻은 문자열을 출력

 

 [7]는 버튼을 클릭했을 때 "getHello ()"에서 얻은 문자열을 출력합니다.

그림 8 "getHello ()"에서 얻은 문자열을 출력

 이상에서 Java로 작성된 클래스에 Unity에서 액세스 할 수있었습니다.

 또한 네이티브 부분을 변경시켜 보겠습니다하려면 Unity를 실행하는 Activity 자체의 커스터마이즈도 가능합니다.

 먼저 그 방법을 설명하기 전에 Unity가 Android에서 어떻게 작동하고 있는지를 간략하게 설명합니다.

Unity는 Android에서 어떻게 작동하고 있는지

 Unity에서 빌드 된 응용 프로그램은 개발자가 개발하는 Unity 프로그램 자체와 해당 프로그램을 실행시키는 'UnityPlayer "라는 실행 프로그램으로 구성되어 있습니다.UnityPlayer 자체가 이전 간단히 소개 한 바와 같이 복수의 OS를 향해 볼 수 있으며, 결과 Unity 응용 프로그램이 여러 OS에서 작동합니다

 Unity가 Android 용 앱을 빌드 할 때 아래와 같이 프로그램이 구성됩니다.

 

그림 9 Android 용 Unity 앱 구성

 APK에 "UnityPlayerActivity"라는 Activity를 상속 한 클래스가 하나 포함되어 그 안에서 UnityPlayer 클래스가로드되어 그 위에 개발자가 만드는 Unity 콘텐츠가 재생됩니다.

 즉, Android Framework에서 보면 일반 Android 애플리케이션처럼 "UnityPlayerActivity"라는 Activity가 실행되고 Android Framework 사이의 라이프 사이클 관리 를하고 있습니다.

UnityPlayerActivity 클래스를 정의

 Unity는이 UnityPlayerActivity 클래스의 소스 코드를 공개하고 개발자가 Activity에 사용자를 추가 할 수 있습니다. UnityPlayerActivity 소스 코드 자체는 아래 경로에서 확인할 수 있습니다.

/ Applications / Unity / Unity . app / Contents / PlaybackEngines / AndroidPlayer / src / com / unity3d / player / UnityPlayerActivity . java

사용자 정의 할 수있는 저런 일, 이런 일

 이 UnityPlayerActivity 클래스를 상속 한 클래스를 구현하거나 해당 클래스를 구현하고 교체 빌드하여 예를 들어, 다음과 같이 행동을 다양한 사용자 정의 할 수 있습니다.

  • Activity 자체에 이벤트 처리기를 추가
  • Activity의 구성을 변경
  • 다른 Activity에서 Intent 를받을
  • 다른 Activity 호출하고 종료시 콜백 받기
  • "Activity를 추가하고 싶다"등의 수요가 있고 Activity 클래스 나 AndroidManifest.xml을 수정

Unity에서 Android의 Activity를 확장하려면

 Unity에서 기본적으로 추가로드되는 UnityPlayerActivity의 교환이나 AndroidManifest.xml 변경을 할 수 방법을 제공합니다.

 다음 페이지 에서는 Eclipse 와 Unity를 이용한 경우의 절차에 대해 설명합니다. Unity 사이트에서는 「Unity - Integrating Unity with Eclipse "에서 소개되고 있습니다.

 단계는 대략적으로 아래와 같습니다 (아래 목록은 인덱스가되어 있습니다) .

  1. UnityPlayerActivity 라이브러리 프로​​젝트 만들기
  2. 신규 Android 프로젝트 생성
  3. UnityPlayerActivity 라이브러리 프로​​젝트를 포함하여 Android 프로젝트 생성
  4. 생성 된 Android 프로젝트의 메인 Activity에서 UnityPlayerActivity를 상속
  5. (필요한 경우) AndroidManifest.xml 변경
  6. Eclipse 측에서 빌드

 

 

 

 


 

 

【1】 UnityPlayerActivity 라이브러리 프로​​젝트 생성

 Unity에 알맞은 새 프로젝트를 만들고 빌드 대상을 Android로 설정 한 후 빌드를 수행합니다. 그러자 만든 프로젝트 폴더에 "Temp"라는 디렉토리가 만들어지고 그 아래에 "StagingArea"라는 폴더가 생성됩니다.

 StagingArea 폴더를 Eclipse에서 볼 수있는 적당한 디렉토리에 복사하십시오.

 

그림 10 / Temp / StagingArea / 내용

 그런 다음 Eclipse에서 새로운 "Android Project"를 만듭니다. [New Android Project 대화 상자가 표시되었을 때 [Create Project from existing source를 선택하고 방금 복사 한 "StagingArea"폴더를 지정하십시오.

그림 11 "StagingArea"폴더를 지정

 [Build Target을 선택하고 [Finish] 버튼을 누릅니다.

 

그림 12 [Build Target을 "Android 2.2"로...

 그러면 다음과 같이 "UnityPlayerActivity '라는 프로젝트가 Package Explorer]에 표시됩니다.

 

그림 13 자동 빌드로 설정하는 경우는 컴파일 에러가 나오는 경우도 있지만, 무시하고있다

 그런 다음 UnityPlayerActivity 프로젝트의 속성을 변경합니다. 속성 창을 열고 항목 "Android"를 선택하면 "Library"라는 필드가 있으며 [Is Library라는 확인란이 있습니다.이것을 체크하고 [OK] 버튼을 클릭하여 속성 창을 닫습니다.

 

그림 14 [Properties for UnityPlayerActivity 대화

 이상에서 UnityPlayerActivity 라이브러리 프로​​젝트 생성이 완료됩니다.

【2】 신규 Android 프로젝트 생성

 준비로 UnityPlayer 용 라이브러리 JAR 파일을 적당한 디렉토리에 복사합니다.

  • Windows의 경우 
    [Unity 설치 디렉토리] \ Editor \ Data \ PlaybackEngines \ androidplayer \ bin \ classes.jar
  • Mac의 경우 
    / Applications / Unity / Unity.app / Contents / PlaybackEngines / AndroidPlayer / bin / classes.jar

 그런 다음 신규 Android 프로젝트를 생성합니다. 여기서 생성하는 프로젝트에 UnityPlayer이 받아 들여져 APK 파일로 빌드되므로 프로젝트 이름과 패키지 이름은 그럴 생각으로 지정하십시오.

 또한, 패키지 이름은 Unity에서 빌드 설정에서 "Bundle Identifier"고 같아야 있으므로주의하십시오. 여기에서는 「UnitySample}라는 Android 프로젝트를 생성합니다. 각 필드를 입력 한 후 [Finish] 버튼을 눌러 프로젝트를 생성합니다.

 

그림 15 New Android Project 대화

 그런 다음 생성 된 UnitySample 프로젝트 속성을 엽니 다. 항목 [Android]를 열고 Library 설정 란에서 [Add] 버튼을 눌러 생성 된 UnityPlayerActivity 프로젝트를 선택하여 추가합니다. 따라서 Unity의 실행에 필요한 코드가 UnitySample 프로젝트에 포함 할 수 있도록합니다.

 그 때 UnityPlayer에 필요한 라이브러리 JAR 파일을 포함시키기 위해 속성 창에서 Libraries 탭을 엽니 다. [Add External JARs ... 버튼을 클릭하고 먼저 복사 한 'classes.jar'파일을 추가합니다.

 

그림 16 [Properties for UnitySample 대화

 선택 후 [OK] 버튼을 눌러 속성을 닫습니다.

 

그림 17 [Prject Selection 대​​화

 이상으로 프로젝트 생성 완료됩니다.

【3】 Unity 앱 코드 복사

 UnitySample 프로젝트 "assets"폴더에 이전에 생성 한 라이브러리 프로​​젝트 "UnityPlayerActivity"프로젝트의 "assets"폴더의 내용 "bin" "libs"를 드래그 앤 드롭으로 이동합니다. 그러면 아래와 같은 폴더 구조가된다고 생각합니다.

 

그림 18 UnitySample 프로젝트 "assets"폴더에 "bin" "libs"폴더

 이 assets 폴더의 bin 폴더가 Unity에서 만든 프로그램 본체가 들어있는 폴더입니다.Unity에서 앱을 개발하고 다시 빌드했을 때는 Unity 프로젝트 폴더에있는 "Temp / StagingArea / assets / bin"폴더를 해당 폴더에 복사하여 덮어 써서 업데이트됩니다 (덮어 쓰기 후 Eclipse에서 프로젝트를 다시로드 잊지 마세요. 그렇지 않으면 Eclipse에서 새로운 파일을 인식하지 못합니다).

 

그림 19 "Temp / StagingArea / assets / bin"폴더를 UnitySample 프로젝트 "assets / bin"에 덮어

【4】 메인 Activity에서 UnityPlayerActivity의 상

 그런 다음 메인 Activity를 Unity 실행 할 수 있도록 Activity 상속 클래스에서 UnityPlayerActivity 상속 클래스로 변경합니다. 생성 직후에는 아래와 같은 코드가 있다고 생각합니다.

package com.example.sample;
import android.app.Activity; 
import android.os.Bundle;
public  class  UnitySampleActivity  extends  Activity  { 
    / ** Called when the activity is first created. * / 
    @ Override 
    public  void onCreate ( Bundle savedInstanceState )  { 
        super . onCreate ( savedInstanceState ); 
        setContentView ( R . layout . main ); 
    } 
}

 이것을 다음과 같이 상속 클래스를 "Activity"에서 UnityPlayerActivity "클래스로 변경하고 setContentView ()를 삭제합니다.

package com.example.sample;
import com.unity3d.player.UnityPlayerActivity; 
import android.os.Bundle;
public  class  UnitySampleActivity  extends  UnityPlayerActivity  { 
    / ** Called when the activity is first created. * / 
    @ Override 
    public  void onCreate ( Bundle savedInstanceState )  { 
        super . onCreate ( savedInstanceState ); 
    } 
}

 UnityPlayerActivity베이스에 Activity 클래스를 상속하고 있기 때문에이 Activity를 일반 Activity와 같이 변경하여 자유로운 코드 개발이 가능하게됩니다. 물론 다른 Activity를 추가하여 그것을 호출하는 일도 가능합니다.

【5】 AndroidManifest.xml 변경

 AndroidManifest의 변경은 통상의 Android 프로젝트와 같이 만든 UnitySample 프로젝트에 포함 된 "AndroidManifest.xml"를 편집하면 OK입니다.

[6] Eclipse에서 빌드

 마지막으로, 일반 Android 애플리케이션 개발시와 마찬가지로 Eclipse에서 빌드합니다. APK 파일을 생성 에뮬레이터 또는 실기에 전송되고 Unity에서 만든 앱이 실행되는 것입니다.