language:unity:plugin:android
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판이전 판다음 판 | 이전 판 | ||
language:unity:plugin:android [2014/03/05 15:55] – [따라가기] kieuns | language:unity:plugin:android [2024/04/23 22:45] (현재) – 바깥 편집 127.0.0.1 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
+ | ====== Unity 플러그인 ====== | ||
+ | 문서 출처 : [[http:// | ||
+ | |||
+ | ===== 기능 ===== | ||
+ | * 다른 navtive 라이브러리, | ||
+ | |||
+ | ===== 제한 ===== | ||
+ | * Pro, Mobile 지원 | ||
+ | * 웹은 지원하지 않음 | ||
+ | |||
+ | ===== 요구사항 ===== | ||
+ | * C 기반 언어로 플러그인을 작성하고 라이브러리로 컴파일 | ||
+ | * 라이브러리의 함수를 호출하기 위한 C# 스크립트 작성 | ||
+ | |||
+ | ===== Native C 를 사용한 경우 ===== | ||
+ | ==== 간단한 예제 ==== | ||
+ | |||
+ | C 파일로 된, 최소 플러그인 (너무 최소다..) | ||
+ | |||
+ | <code c> | ||
+ | float FooPluginFunction() { return 0.5f } | ||
+ | </ | ||
+ | |||
+ | ==== 플러그인 사용을 위한 C# 스크립트 ==== | ||
+ | |||
+ | <code csharp> | ||
+ | using UnityEngine; | ||
+ | using System.Runtime.InteropServices; | ||
+ | |||
+ | class FooInterface : MonoBehaviour | ||
+ | { | ||
+ | #if UNITY_IPHONE || UNITY_XBOX360 | ||
+ | // iOS나 Xbox360에서 플러그인은 정적으로 | ||
+ | // 실행파일들에 연결되어 있기 때문에 | ||
+ | // __Internal 을 라이브러리 이름으로 사용해야 합니다. | ||
+ | | ||
+ | // __Internal이 파일 이름인가? | ||
+ | | ||
+ | [DllImport (" | ||
+ | #else | ||
+ | // 다른 플랫폼들은 플러그인을 여러가지 방법으로 로드하기 때문에 | ||
+ | // 플러그인의 다이나믹 라이브러리의 이름을 사용합니다. | ||
+ | | ||
+ | | ||
+ | // < | ||
+ | | ||
+ | [DllImport ("< | ||
+ | #endif | ||
+ | |||
+ | private static extern float FooPluginFunction(); | ||
+ | | ||
+ | void Awake () { | ||
+ | // 플러그인안에 FooPluginFunction 을 호출하고 | ||
+ | // 콘솔에 5를 출력합니다 | ||
+ | print (FooPluginFunction ()); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====== 안드로이드 플러그인 ====== | ||
+ | |||
+ | ===== 요구사항 ===== | ||
+ | * [[http:// | ||
+ | * NDK로 공유 라이브러리를 만드는 방법 | ||
+ | |||
+ | ===== 주의사항 ===== | ||
+ | * C++ 로 작성한다면 C linkage 형태의 %%extern " | ||
+ | <code csharp> | ||
+ | extern " | ||
+ | float FooPluginFunction (); | ||
+ | } | ||
+ | </ | ||
+ | ===== C#에서 플러그인 사용 ===== | ||
+ | 사용할 라이브러리를 **Assets \ Plugins \ Android** 폴더에 복사. | ||
+ | |||
+ | 아래 형태로 함수를 선언해 두면 유니티에서는 이름으로 라이브러릴를 찾는다. | ||
+ | |||
+ | * %%< | ||
+ | |||
+ | <code csharp> | ||
+ | [DllImport("< | ||
+ | private static extern float FooPluginFunction(); | ||
+ | </ | ||
+ | |||
+ | * %%< | ||
+ | * **Application.platform**으로 플랫폼을 확인 하고 각 실제 디바이스에서 실행 되어야 한다. | ||
+ | * 에디터상에서 실행되는 경우 더미 값을 리턴하도록 작성 | ||
+ | |||
+ | ===== 안드로이드 라이브러리 ===== | ||
+ | |||
+ | * jar 형태로 만들어진 안드로이드 라이브러리를 **Assets \ Plugins \ Android**에 복사한다. " | ||
+ | |||
+ | ==== 배치 ==== | ||
+ | * 크로스 플랫폼을 지원하려면, | ||
+ | * libPlugin.so : for Android | ||
+ | * Plugin.bundle : for MAC | ||
+ | * Plugin.dll : for Windows | ||
+ | * 플랫폼에 맞는 라이브러리는 자동선택된다. | ||
+ | |||
+ | ===== 자바플러그인 ===== | ||
+ | |||
+ | ==== 빌드 ==== | ||
+ | |||
+ | * 빌드 방법에 상관 없이 .class 파일을 포함하는 " | ||
+ | |||
+ | 방법1 : JDK로 " | ||
+ | |||
+ | 방법2 : 이클립스에서 ADT로 컴파일하는 방법 | ||
+ | |||
+ | |||
+ | ==== NativeCode에서 Java 코드 사용하기 ==== | ||
+ | |||
+ | 만들어둔 Java plugin (.jar) 파일을 **Assets \ Plugins \ Android** 폴더에 복사. | ||
+ | * JNI (Java Native Code) 에 의해 Java 코드에 접근할 수 있다. | ||
+ | * JNI 는 Java <-> NativeCode , 접근을 위해 사용된다. | ||
+ | |||
+ | Java 코드를 찾기 위해서 JavaVM에 접근해야 하는데, C/C++ 코드로는 아래 처럼 사용한다. | ||
+ | |||
+ | <code java> | ||
+ | // 그런데 이거 어디에 두는거지.. | ||
+ | jint JIN_OnLoad( JavaVM* vmPtr_, void* reserved_) { | ||
+ | JNIEnv* jni_env_ptr = 0; | ||
+ | vmPtr_-> | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | JNI 상세. (별로 알고 싶지 않지만, [[http:// | ||
+ | |||
+ | <code java> | ||
+ | // 클래스와 클래스 생성자를 찾고(얻고) | ||
+ | // 인스턴스를 생성하고 --> 이렇게 쓰는 모양 | ||
+ | jobject createJavaObject( JNIEnv* jni_env ) { | ||
+ | // find class definition | ||
+ | jclass cls_JavaClass = jni_env-> | ||
+ | | ||
+ | // find constructor method | ||
+ | jmethodID mid_JavaClass = jni_env-> | ||
+ | | ||
+ | // create object instance | ||
+ | jobject obj_JavaClass = jni_env-> | ||
+ | | ||
+ | // return object with a global reference | ||
+ | return jni_env-> | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Using Your Java Plugin with helper classes ==== | ||
+ | |||
+ | - **UnityEngine.AndroidJNIHelper**, | ||
+ | - **UnityEngine.AndroidJavaObject**, | ||
+ | * **UnityEngine.AndroidJNIHelper**, | ||
+ | * 내부에 자동화를 위한 로직이 포함되어 있고 (뭐 어때?) | ||
+ | * 자바클래스의 static 멤버를 | ||
+ | |||
+ | |||
+ | ====== 컴파일 할때 있어야할 것들 ====== | ||
+ | |||
+ | ===== Windows ===== | ||
+ | |||
+ | 환경변수 | ||
+ | * ANDROID_SDK_ROOT : ADK 경로 | ||
+ | * JDK_ROOT : JDK 경로 | ||
+ | * NDKROOT : Android NDK 경로 | ||
+ | * JAVA_HOME : Java RLE 경로. JDK를 설치하면 필요없지만, | ||
+ | 환경변수: | ||
+ | * ADT_ROOT : 언제 생긴걸까? | ||
+ | 환경변수: | ||
+ | * %%" | ||
+ | |||
+ | 설치프로그램 | ||
+ | * ADK : Android SDK | ||
+ | * NDK : Android NDK | ||
+ | * JDK : Java SDK | ||
+ | * JRE : Java Runtime | ||
+ | * keytool.exe : JDK, JRE에 있다. | ||
+ | * openssl.exe : 별도 설치. | ||
+ | 설치프로그램: | ||
+ | * bash | ||
+ | |||
+ | ====== 따라가기 ====== | ||
+ | |||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | |||
+ | ===== 신규 프로젝트 설정 ===== | ||
+ | |||
+ | | {{: | ||
+ | | {{: | ||
+ | | PackageName 은 나중에 유니티에 이것과 동일하게 (앱자체의 패키지 이름이라면야) | | ||
+ | | {{: | ||
+ | | **Create custom launcher icon** : 체크 끄기 | | ||
+ | | {{: | ||
+ | | **Blank Activity** 선택. 나중에 바꾸려면 골치아므니.. | ||
+ | | {{: | ||
+ | | Activity 이름 설정. (메인 함수 같은 느낌인데) \\ Navigation Type : None | | ||
+ | |||
+ | ===== 유니티의 안드로이드용 라이브러리(External JAR)추가 ===== | ||
+ | |||
+ | | {{: | ||
+ | | {{: | ||
+ | | {{: | ||
+ | | {{: | ||
+ | |||
+ | ===== 파일명 변경 ===== | ||
+ | |||
+ | ==== ic_launcher.png 파일명 변경 ==== | ||
+ | |||
+ | icon 이름 변경 | ||
+ | |||
+ | ic_launcher.png 파일명을 app_icon.png 로 변경해야함 (이유는 모르겟네, | ||
+ | |||
+ | * 프로젝트의 **res** 폴더의 **drawable-??? | ||
+ | * 일괄 변경을 위해서 Rename 기능 활용. | ||
+ | * 바뀌지 않은게 있는지 수동 확인. | ||
+ | |||
+ | | {{: | ||
+ | | {{: | ||
+ | |||
+ | ==== AndroidManifest.xml 파일에서 ic_launcher.png 파일명 남아 있는지 확인 ==== | ||
+ | |||
+ | 프로젝트의 AndroidManifest.xml 파일 수정 | ||
+ | |||
+ | * **Applicaiont 탭**에서 **Icon** 항목이 **@drawable/ | ||
+ | |||
+ | | {{: | ||
+ | | {{: | ||
+ | |||
+ | ===== 코드 수정 ===== | ||
+ | |||
+ | 최초의 생성된 코드, 아래와 같은,를 수정해서 필요한 부분만 남긴다. | ||
+ | |||
+ | * **extends Activity** -> **extends UnityPlayerActivity** | ||
+ | * **UnityPlayerActivity** 마우스 우클릭, \\ 자동 교정 메뉴로 **import com.unity3d.player.UnityPlayerActivity; | ||
+ | * 아래 부분 삭제 - 미사용 부분 | ||
+ | |||
+ | <code java> | ||
+ | // com.test.apiinterface --> com.test.jvplugin 으로 이름 변경 | ||
+ | package com.test.apiinterface; | ||
+ | |||
+ | import android.os.Bundle; | ||
+ | import android.app.Activity; | ||
+ | import android.view.Menu; | ||
+ | |||
+ | public class APIInterfaceUnityPluginActivity extends Activity | ||
+ | { | ||
+ | @Override | ||
+ | protected void onCreate(Bundle savedInstanceState) { | ||
+ | super.onCreate(savedInstanceState); | ||
+ | setContentView(R.layout.activity_apiinterface_unity_plugin); | ||
+ | } | ||
+ | | ||
+ | @Override | ||
+ | public boolean onCreateOptionsMenu(Menu menu) { | ||
+ | // Inflate the menu; this adds items to the action bar if it is present. | ||
+ | getMenuInflater().inflate(R.menu.apiinterface_unity_plugin, | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | 삭제 이후 | ||
+ | |||
+ | <code java> | ||
+ | // 아래 부분 삭제 | ||
+ | // | ||
+ | setContentView(R.layout.activity_apiinterface_unity_plugin); | ||
+ | //... | ||
+ | public boolean onCreateOptionsMenu(Menu menu) | ||
+ | { | ||
+ | // Inflate the menu; this adds items to the action bar if it is present. | ||
+ | getMenuInflater().inflate(R.menu.apiinterface_unity_plugin, | ||
+ | return true; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * 정리 후에는 (Ctrl + Shift + O, 뭐하는 단축키?) : 패키지와 import 상태 정리. -> 필요한 패키지가 import 되어 있다. | ||
+ | |||
+ | <code java> | ||
+ | package com.test.apiinterface; | ||
+ | |||
+ | import android.os.Bundle; | ||
+ | |||
+ | import com.unity3d.player.UnityPlayerActivity; | ||
+ | |||
+ | public class APIInterfaceUnityPluginActivity extends UnityPlayerActivity | ||
+ | { | ||
+ | @Override | ||
+ | protected void onCreate(Bundle savedInstanceState) { | ||
+ | super.onCreate(savedInstanceState); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== 참고 ==== | ||
+ | |||
+ | * UnityPlayerActivity 항목을 상속 받는 대신, Activity 상속 받고 UnityPlayerActivity 를 직접 구현해도 된다.(는데) | ||
+ | * 지금의 UnityPlayerActivity 가 구현된 상태를 참고하는 것은 | ||
+ | * < | ||
+ | * 같은 폴더의 파일들은 | ||
+ | * UnityPlayerProxyActivity 가 | ||
+ | * Android Version >= 2.3 : UnityPlayerNativeActivity 사용 | ||
+ | * Android Version < 2.3 : UnityPlayerActivity 사용 | ||
+ | * 하도록 한다는데 (그냥 넘어가자) | ||
+ | * AndroidMenifest.xml 에서 MainActivity 에 정해진, 플러스, 이 MainActivity가 UnityPlayerActivity를 상속 받았다면 해당 Activity를 사용한다. | ||
+ | |||
+ | ===== Java에 테스트 코드 추가 ===== | ||
+ | |||
+ | **APIInterfaceUnityPluginActivity.java**에 테스트 코드를 추가. | ||
+ | |||
+ | * GetPluginNumber() | ||
+ | * PluginToUnitySendMessage() | ||
+ | |||
+ | 테스트 함수 추가. | ||
+ | |||
+ | <code java> | ||
+ | // com.test.apiinterface --> com.test.jvplugin 으로 이름 변경 | ||
+ | package com.test.jvplugin; | ||
+ | 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); | ||
+ | } | ||
+ | | ||
+ | float GetPluginNumber( int left_, int right_ ) | ||
+ | { | ||
+ | return left_ + right_ / 1.23f; | ||
+ | } | ||
+ | | ||
+ | void SendMessageToUnity( String unityObjectName_, | ||
+ | { | ||
+ | UnityPlayer.UnitySendMessage(unityObjectName_, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Java 빌드 ===== | ||
+ | |||
+ | 최소 빌드 규격을 결정 | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Clean (메뉴 : Project -> Clean...) 화면에서, | ||
+ | |||
+ | 자 이젠 빌드 | ||
+ | | ||
+ | 에러가 나는 경우 : " | ||
+ | |||
+ | * 테마가 없다고 에러가 나는 경우, | ||
+ | * 다시 컴파일 해보기. | ||
+ | * Project -> Clean 메뉴에서 자동 빌드 옵션 끄고, 다시 리빌드. | ||
+ | * 프로젝트의 AndroidManifest.xml 파일에서 **Theme** 항목을 비울 것 \\ {{: | ||
+ | |||
+ | Bin 폴더에 JAR 파일이 생겼는지 확인! | ||
+ | |||
+ | ===== 유니티 작업 ===== | ||
+ | |||
+ | ==== Jar 파일 복사 ==== | ||
+ | |||
+ | 유니티의 **Assets \ Plugins \ Android** 폴더에 | ||
+ | * AndroidManifest.xml | ||
+ | * %%< | ||
+ | |||
+ | < | ||
+ | |||
+ | ==== 코드 추가 ==== | ||
+ | |||
+ | ^ 유니티에 추가하는 플러그인 사용 코드 ^ | ||
+ | | <code csharp> | ||
+ | using UnityEngine; | ||
+ | using System.Collections; | ||
+ | |||
+ | public class AndroidPluginInterface : MonoBehaviour | ||
+ | { | ||
+ | #if UNITY_ANDROID | ||
+ | |||
+ | AndroidJavaObject curActivity; | ||
+ | |||
+ | void Awake() | ||
+ | { | ||
+ | AndroidJavaClass jc = new AndroidJavaClass(" | ||
+ | curActivity = jc.GetStatic< | ||
+ | } | ||
+ | |||
+ | void Destroy() | ||
+ | { | ||
+ | if(curActivity != null) curActivity.Dispose(); | ||
+ | } | ||
+ | |||
+ | public void CallFunctionName(string msg_) | ||
+ | { | ||
+ | strLabelReceiveMessage = msg_; | ||
+ | Debug.Log(msg_); | ||
+ | } | ||
+ | |||
+ | int init() | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | AndroidJavaClass jc = new AndroidJavaClass(" | ||
+ | if(jc != null) | ||
+ | { | ||
+ | curActivity = jc.GetStatic< | ||
+ | jc.Dispose(); | ||
+ | if(curActivity != null) | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | return 1; | ||
+ | } | ||
+ | } | ||
+ | Debug.Log(" | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | float CallIntValue() | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | if(curActivity != null) | ||
+ | { | ||
+ | int _left = Random.Range(1, | ||
+ | int _right = Random.Range(10, | ||
+ | float n_val = curActivity.Call< | ||
+ | return n_val; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | void requestCheckPluginMessage() | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | | ||
+ | string[] msg_list = new string[] | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }; | ||
+ | | ||
+ | if(curActivity != null) | ||
+ | { | ||
+ | int _n = Random.Range(0, | ||
+ | curActivity.Call(" | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Debug.Log(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | string strLabelReturnInt = ""; | ||
+ | string strLabelReceiveMessage = ""; | ||
+ | | ||
+ | void OnGUI() | ||
+ | { | ||
+ | GUILayout.BeginHorizontal(); | ||
+ | { | ||
+ | if(GUILayout.Button(" | ||
+ | { | ||
+ | float _fv = CallIntValue(); | ||
+ | strLabelReturnInt = _fv.ToString(); | ||
+ | } | ||
+ | GUILayout.Label(string.Format(" | ||
+ | } | ||
+ | GUILayout.EndHorizontal(); | ||
+ | | ||
+ | GUILayout.BeginHorizontal(); | ||
+ | { | ||
+ | if(GUILayout.Button(" | ||
+ | { | ||
+ | requestCheckPluginMessage(); | ||
+ | } | ||
+ | GUILayout.Label(string.Format(" | ||
+ | } | ||
+ | GUILayout.EndHorizontal(); | ||
+ | } | ||
+ | |||
+ | #endif | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | |||
+ | |||
+ | ====== 에러처리 ====== | ||
+ | |||
+ | ===== Exception: JNI: Init'd AndroidJavaClass with null ptr! ===== | ||
+ | |||
+ | < | ||
+ | Exception: JNI: Init'd AndroidJavaClass with null ptr! | ||
+ | UnityEngine.AndroidJavaClass..ctor (IntPtr jclass) (at C:/ | ||
+ | UnityEngine.AndroidJavaObject.get_JavaLangClass () (at C:/ | ||
+ | UnityEngine.AndroidJavaObject.FindClass (System.String name) (at C:/ | ||
+ | UnityEngine.AndroidJavaClass._AndroidJavaClass (System.String className) (at C:/ | ||
+ | UnityEngine.AndroidJavaClass..ctor (System.String className) (at C:/ | ||
+ | AndroidPluginInterface.Awake () (at Assets/ | ||
+ | </ | ||
+ | |||
+ | * 에디터상에서, | ||
+ | * 적어도 에러를 감추는 방법은 없을까? | ||
+ | * 인터넷으로 찾아보니 디파인으로 처리하랜다. | ||
+ | |||
+ | ===== Unable to find unity activity in manifest ===== | ||
+ | |||
+ | < | ||
+ | Unable to find unity activity in manifest. You need to make sure orientation attribut is set to portrait manually. | ||
+ | UnityEditor.BuildPlayerWindow: | ||
+ | </ | ||
+ | |||
+ | * 액티비트 클래스가 유니티에서 정한 것이 아니라서 뜨는 워닝이라 무시해도 된다는데, | ||
+ | * 감출 수는 없을까? | ||
+ | |||
+ | ===== 앱이 제대로 설치되지 않는다 ===== | ||
+ | |||
+ | * 용량(심카드가 없는)문제로 설치 안되는 경우도 있으니 | ||
+ | |||
+ | ===== 앱이 제대로 설치되지 않거나 실행 안되는 경우 ===== | ||
+ | |||
+ | * 용량 문제가 아니라면 **AndroidManifest.xml** 파일 설정에 패키지 이름이 맞는지 확인 | ||
+ | |||
+ | <code xml> | ||
+ | <?xml version=" | ||
+ | < | ||
+ | package=" | ||
+ | android: | ||
+ | android: | ||
+ | | ||
+ | < | ||
+ | | ||
+ | < | ||
+ | android: | ||
+ | android: | ||
+ | < | ||
+ | < | ||
+ | <action android: | ||
+ | | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ====== 첨부파일 ====== | ||
+ | |||
+ | {{: |