C 파일로 된, 최소 플러그인 (너무 최소다..)
float FooPluginFunction() { return 0.5f }
using UnityEngine; using System.Runtime.InteropServices; class FooInterface : MonoBehaviour { #if UNITY_IPHONE || UNITY_XBOX360 // iOS나 Xbox360에서 플러그인은 정적으로 // 실행파일들에 연결되어 있기 때문에 // __Internal 을 라이브러리 이름으로 사용해야 합니다. // __Internal이 파일 이름인가? "__Internal" 문자열 그대로 쓰라는 것인가? [DllImport ("__Internal")] #else // 다른 플랫폼들은 플러그인을 여러가지 방법으로 로드하기 때문에 // 플러그인의 다이나믹 라이브러리의 이름을 사용합니다. // <PluginName>이 파일 이름인가? "PluginName" 문자열 그대로 쓰라는 것인가? [DllImport ("<PluginName>")] #endif private static extern float FooPluginFunction(); void Awake () { // 플러그인안에 FooPluginFunction 을 호출하고 // 콘솔에 5를 출력합니다 print (FooPluginFunction ()); } }
extern "C" { float FooPluginFunction (); }
사용할 라이브러리를 Assets \ Plugins \ Android 폴더에 복사.
아래 형태로 함수를 선언해 두면 유니티에서는 이름으로 라이브러릴를 찾는다.
[DllImport("<PluginName")] private static extern float FooPluginFunction();
* %%<PluginName>%% : "lib" 같은 라이브러리 접두사, "so" 같은 확장자를 넣지 않는다. * **Application.platform**으로 플랫폼을 확인 하고 각 실제 디바이스에서 실행 되어야 한다. * 에디터상에서 실행되는 경우 더미 값을 리턴하도록 작성
방법1 : JDK로 “.java” 파일을 “javac”로 컴파일 하는 방법. 컴파일된 “.class” 파일을 “jar” 커맨드툴로 묶는 것.
방법2 : 이클립스에서 ADT로 컴파일하는 방법
만들어둔 Java plugin (.jar) 파일을 Assets \ Plugins \ Android 폴더에 복사.
Java 코드를 찾기 위해서 JavaVM에 접근해야 하는데, C/C++ 코드로는 아래 처럼 사용한다.
// 그런데 이거 어디에 두는거지.. jint JIN_OnLoad( JavaVM* vmPtr_, void* reserved_) { JNIEnv* jni_env_ptr = 0; vmPtr_->AttachCurrentThread(&jni_env_ptr, 0); }
JNI 상세. (별로 알고 싶지 않지만, Java Native Interface Spec)
// 클래스와 클래스 생성자를 찾고(얻고) // 인스턴스를 생성하고 --> 이렇게 쓰는 모양 jobject createJavaObject( JNIEnv* jni_env ) { // find class definition jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class"); // find constructor method jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V"); // create object instance jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass); // return object with a global reference return jni_env->NewGlobalRef(obj_JavaClass); }
환경변수
환경변수:옵션
환경변수:Path
설치프로그램
설치프로그램:옵션
icon 이름 변경
ic_launcher.png 파일명을 app_icon.png 로 변경해야함 (이유는 모르겟네, 인터넷 검색하면 나오려나. 이 이름이 안맞으면 안된다네)
프로젝트의 AndroidManifest.xml 파일 수정
최초의 생성된 코드, 아래와 같은,를 수정해서 필요한 부분만 남긴다.
// 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, menu); return true; } }
삭제 이후
// 아래 부분 삭제 // 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, menu); return true; }
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); } }
APIInterfaceUnityPluginActivity.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_, String callFunctionName, String param_) { UnityPlayer.UnitySendMessage(unityObjectName_, callFunctionName, param_); } }
최소 빌드 규격을 결정
Clean (메뉴 : Project → Clean…) 화면에서, 빌드 순서가 제대로 조정되지 않으면 빌드 에러가 날 수 있으니 Start a build immediately는 비추라네.
자 이젠 빌드
에러가 나는 경우 : “unity3d plugin error:Error retrieving parent for item: No resource found that matches”
Bin 폴더에 JAR 파일이 생겼는지 확인!
유니티의 Assets \ Plugins \ Android 폴더에
유니티에 추가하는 플러그인 사용 코드 |
---|
using UnityEngine; using System.Collections; public class AndroidPluginInterface : MonoBehaviour { #if UNITY_ANDROID AndroidJavaObject curActivity; void Awake() { AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); curActivity = jc.GetStatic<AndroidJavaObject>("currentActivity"); } void Destroy() { if(curActivity != null) curActivity.Dispose(); } public void CallFunctionName(string msg_) { strLabelReceiveMessage = msg_; Debug.Log(msg_); } int init() { Debug.Log("init()"); AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); if(jc != null) { curActivity = jc.GetStatic<AndroidJavaObject>("currentActivity"); jc.Dispose(); if(curActivity != null) { Debug.Log("init success"); return 1; } } Debug.Log("init failed"); return 0; } float CallIntValue() { Debug.Log("CallIntValue()"); if(curActivity != null) { int _left = Random.Range(1, 100); int _right = Random.Range(10, 90); float n_val = curActivity.Call<float>("GetPluginNumber", _left, _right); return n_val; } else { Debug.Log("CallIntValue(): androidPlugin is null"); } return 0; } void requestCheckPluginMessage() { Debug.Log("requestCheckPluginMessage()"); string[] msg_list = new string[] { "abcde", "hello", "세번째 메시지", "랜덤메시지" }; if(curActivity != null) { int _n = Random.Range(0, msg_list.Length - 1); curActivity.Call("SendMessageToUnity", "AndroidManager", "CallFunctionName", msg_list[_n]); } else { Debug.Log("requestCheckPluginMessage(): androidPlugin is null"); } } string strLabelReturnInt = ""; string strLabelReceiveMessage = ""; void OnGUI() { GUILayout.BeginHorizontal(); { if(GUILayout.Button("Called IntValue", GUILayout.Width(200), GUILayout.Height(100))) { float _fv = CallIntValue(); strLabelReturnInt = _fv.ToString(); } GUILayout.Label(string.Format("Return Int : {0}", strLabelReturnInt)); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); { if(GUILayout.Button("Called Request Message", GUILayout.Width(200), GUILayout.Height(100))) { requestCheckPluginMessage(); } GUILayout.Label(string.Format("RecvMsg : {0}", strLabelReceiveMessage)); } GUILayout.EndHorizontal(); } #endif } |
Exception: JNI: Init'd AndroidJavaClass with null ptr! UnityEngine.AndroidJavaClass..ctor (IntPtr jclass) (at C:/BuildAgent/work/d3d49558e4d408f4/Runtime/Export/AndroidJavaImpl.cs:539) UnityEngine.AndroidJavaObject.get_JavaLangClass () (at C:/BuildAgent/work/d3d49558e4d408f4/Runtime/Export/AndroidJavaImpl.cs:517) UnityEngine.AndroidJavaObject.FindClass (System.String name) (at C:/BuildAgent/work/d3d49558e4d408f4/Runtime/Export/AndroidJavaImpl.cs:50) UnityEngine.AndroidJavaClass._AndroidJavaClass (System.String className) (at C:/BuildAgent/work/d3d49558e4d408f4/Runtime/Export/AndroidJavaImpl.cs:528) UnityEngine.AndroidJavaClass..ctor (System.String className) (at C:/BuildAgent/work/d3d49558e4d408f4/artifacts/EditorGenerated/AndroidJava.cs:92) AndroidPluginInterface.Awake () (at Assets/AppJavaPlugin/AndroidPluginInterface.cs:13)
Unable to find unity activity in manifest. You need to make sure orientation attribut is set to portrait manually. UnityEditor.BuildPlayerWindow:BuildPlayerAndRun()
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test.jvplugin" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/app_icon" android:label="@string/app_name"> <activity android:name="com.test.jvplugin.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>