C++をAndroid NDKでビルドしてUnityで使ってみる

# はじめに
私はUnity開発者です。C++のライブラリをUnityで動かしてよと言われたので勉強中です。
手順を記載していきますが、内容については深く触れません。

作業したプロジェクトはこちらになります。わかりづらい部分はこちらを参考にしてください。
https://github.com/Kazu-num/CppToUnity

PCはMac(M1)を使いました。今回は簡単な処理のビルドになりますが、C++ライブラリのビルドになってくると第三者のビルドツールや手順にお世話になることが多く、Mac/Linuxユーザーが多い印象です。

# C++をAndroid NDKでビルド
Cmakeを使う方法など他の方法もありますが、
今回はmakeファイルを使った方法を採用します。

## AndroidNDKのインストール
今回は最新のr25を使用しますが、ビルドしたいライブラリによってはNDKのバージョンが指定されていることが多いので注意してください。
https://developer.android.com/ndk/downloads?hl=ja
![スクリーンショット 2023-06-10 8.17.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/a3112b12-9898-0884-3292-461c39404f18.png)

古いバージョンを使用する場合はこちらからインストールできます。
https://github.com/android/ndk/wiki/Unsupported-Downloads
※上記にも記載されていないほど古いバージョン(r8など)も、URLを手直しするとDLできたりします。

利用規約に同意してダウンロードすると、最新のバージョンだと.dmgのイメージファイルが手に入ると思います。

手に入れたNDKを使いやすい位置に配置します。
私は「/Users/${Username}/Tools」の位置にAndroidNDKフォルダを作りました。
```
mkdir ~/Tools
cd ~/Tools
mkdir AndroidNDK
cd AndroidNDK 
```
NDKが.dmgの場合はここにアプリケーションファイルをドラッグ&ドロップします。

NDKの場所を環境変数に指定します。
.dmgの場合はFinder上で右クリックして「パッケージの内容を表示」を押すとアプリケーションファイルの中を見ることができます。
![スクリーンショット 2023-06-10 8.47.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/65eed5b2-a37d-1af7-890d-abc463df3043.png)
ndk-buildを使いたいので格納されているフォルダを探してください。
r25では「/Users/${Username}/Tools/AndroidNDK/AndroidNDK9519653.app/Contents/NDK」にありました。
NDKの場所は一般的にANDROID_NDKとして指定しますが、今回はNDKのバージョンを変えやすいようにANDROID_NDK25として指定しました。

ndk-buildが動作するか一度確認します。
NDKがアプリケーションファイルの場合は動かす前に一度ダブルクリックで開いてください。
インターネットからダウンロードしたアプリに対する警告が出ます。開くを押しましょう。
下記のようなコマンドでndk-buildが動くか確認します。
```
$ANDROID_NDK25/ndk-build -v
```

## 今回ビルドするC++コード
```Cpp:CppToUnity.cpp
extern "C" {
    int nativeAdd(int a, int b)
    {
        return a + b;
    }
}
```
ただの足し算です。

## makeファイルを作成
ドキュメントに従って、２つファイルを作成します。
```makefile:Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := CppToUnity
LOCAL_SRC_FILES := CppToUnity.cpp

include $(BUILD_SHARED_LIBRARY)
```
参考：https://developer.android.com/ndk/guides/android_mk?hl=ja

```makefile:Application.mk
APP_ABI := arm64-v8a
```
参考：https://developer.android.com/ndk/guides/application_mk?hl=ja

## build
今回は次のようなフォルダ構成で各ファイルを配置します。
```
Android
|- jni
   |- Android.mk
   |- Application.mk
   |- CppToUnity.cpp
```

Androidフォルダでndk-buildを実行します。
```
$ANDROID_NDK25/ndk-build
```

Androidフォルダ以下にlibs、objフォルダが出来ていれば成功です。
Android/libs/arm64-v8a/libCppToUnity.soをUnityで使います。

# Unityで使ってみる
Unity2021.3.19f１を使用しました。
## ProjectSettings
Native処理を利用するので、OtherSettings > ScriptCoplilationのAllow 'unsafe' Codeにチェックを入れます。
![スクリーンショット 2023-06-10 9.59.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/8efb09ae-d524-4846-baf8-15eb82537310.png)

arm64-v8aを使用したいので、OtherSettings > ConfigurationのScriptingBackendをIL2CPPに、Target ArchitecturesをARM64にします。
![スクリーンショット 2023-06-11 16.13.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/1d07a770-7a16-82d1-0e92-d7aee20e54fb.png)

## C++ライブラリを配置する
Assets/Plugins/Android/armeabi-v8a/libCppToUnity.soとなるように、配置します。
libCppToUnity.soのInspectorでPlatformがAndroid、CPUがARM64になっていることを確認します。
![スクリーンショット 2023-06-11 16.41.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/867fe796-c6da-8698-eb61-b87cb54dbca1.png)

## C#スクリプトを書く
ライブラリの処理を呼び出すスクリプトを書きます。
```cs:CppToUnity.cs
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;

public class CppToUnity : MonoBehaviour
{
    [SerializeField]
    InputField inputFieldA;
    [SerializeField]
    InputField inputFieldB;
    [SerializeField]
    Text anserText;

    public void Calc()
    {
        var aIsInt = int.TryParse(inputFieldA.text, out int a);
        var bIsInt = int.TryParse(inputFieldB.text, out int b);
        if (aIsInt && bIsInt)
        {
            // anserText.text = (a + b).ToString();
            var ans = nativeAdd(a, b);
            anserText.text = ans.ToString();
            Debug.LogFormat("{0} + {1} = {2}", a, b, ans);
        }
    }

    [DllImport("CppToUnity")]
    private static extern int nativeAdd(int a, int b);
}
```
※Editor実行するとDllNotFoundのエラーが出ます。動作は実機で確認してください。

C++の処理を呼び出しているのはここになります。
```cs
    [DllImport("CppToUnity")]
    private static extern int nativeAdd(int a, int b);
```

## シーンの実装・ビルド
省略します。はじめに記載したGitHubの”CppToUnity”フォルダのUnityプロジェクトを参考にしてください。

![Screenshot_20230611-161247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657999/dbce277a-6da6-2114-23e8-2f59b29e051b.png)

足し算出来てます。

# 最後に
正直ほとんど理解しきれていませんが、まずは第一歩ということで足し算が出来ました。
これから理解を進めてOSSライブラリをビルドしていかないといけません。
ビルドマスターに俺はなる．．．！