C、C++ ソース コード インタラクションのための Unity 開発、クロスプラットフォームのサポート

序文

会社の新しいプロジェクトは、C++ アルゴリズムを行う人々と協力して開発する必要があります. 最初は、プロジェクトは Windows プラットフォームに設定されていました. C++ と DLL の間の相互作用に関する情報を読んだ後、DLL を生成するための一連のインターフェイスを作成しました.その後、プロジェクトの執筆計画は、機器の都合で、再度 Android プラットフォームに移行することにしました.パッケージ化を試みた後、DLL を Android プラットフォームにパッケージ化できませんでした.DLL の名前を変更して、.dll としてパッケージ化しようとしました. ABパッケージしてからAssembly.Loadを使うのですが、この方法はC#のDLLにしか適用できません 色々調べた結果、某qグループでこの質問を投げたらC++のソースコードを操作する方法があることを知りましたが失敗しました数回の試行の後、それを呼び出して、対話のために Android プラットフォームでそのようにコンパイルしようとすることをほとんどあきらめました. 元旦の後、ある日夢の中でこの質問を考えました. q グループに移動して、これを投げ続けます最後に、2日間の絶え間ない努力の後、Cインターフェイスを作成し、パッケージングプロセスのさまざまな問題を解決しようとし、最終的に過去数日間の結果をまとめました。


需要分析

最初の写真

ここに画像の説明を挿入
これは Unity レンダリングに基づくデジタル ヒューマン プロジェクトです. フロント エンドは Unity を使用して表示と対話を行います. 一部の入力とデジタル ヒューマンのパフォーマンスは C# レイヤーで開発されます. アニメーション システム, マイク入力, 音声データ同期リップなど.バックエンドアルゴリズムテキストと音声の変換、AI、アルゴリズムモデルのトレーニングなど、主にC++で開発されており、C#とC++の間の相互作用、つまり相互呼び出しの問題が関係しています。

だから私の最終的な流れは

  1. Unity はマイクを使用してプレイヤーのスピーチを監視し、そのスピーチを C++ アルゴリズムに送信します。
  2. C++ はテキストを取得し、インテリジェントな対話システムをトレーニングします
  3. トレーニングのダイアログ テキストを取得したら、テキストを音声に変換して Unity に送信します。
  4. Unity は、唇の変化や体の動きなど、音声データに基づいてスピーチを駆動します
  5. AI Numerical Homo sapiens が応答した後、ステップ 1へのサイクルを開始します。

実装

1. C# レイヤー インターフェイスの定義

C# レイヤー コード: 関数は生成された C++ コードにリンクされているため、_P/Invoke 呼び出し元の個別の DLL はありません。したがって、次の例のように、「__Internal」キーワードを DLL 名の代わりに使用して、実行時に関数をロードするのではなく、関数を解決する責任を C++ リンカに持たせることができます。

  • 初期化関数を定義し、C# レイヤー インターフェイスを C++ に渡し、それを C++ でキャッシュして、Unity のログ出力、Unity で音声データを受信する方法など、C# コールバックを使用して、C++ が C# コールバック関数 (委任) を呼び出すことができるようにします。 ) 能力をキャッシュした後。
[DllImport("__Internal")]
    static extern int Init(
        ULogCallback logCallback
        ,UReceivesAI_DialogueCallback diaogueCallback
   );
    • 2 つの初期化は、コールバック パラメーターとして使用され、それ自体で展開され、C++ 用にキャッシュされます. そのうち、ULogCallback は Unity のログ コールバックのデリゲートであり、diaogueCallback は AI ダイアログの受信のデリゲートです. ULogCallback のプロトタイプは次のとおりです:
 /// <summary>
    /// Unity日志调用的回调委托
    /// </summary>
    /// <param name="level"></param>
    /// <param name="msg"></param>
    public delegate void ULogCallback(LogLevel level, string msg);

    public enum LogLevel
    {
        Info,
        Warn,
        Error
    };
    • diaogueCallback のプロトタイプは次のとおりです。
 /// <summary>
    /// Unity接收的AI对话语音的回调委托
    /// </summary>
    /// <param name="voiceDatas">语音数据</param>
    public delegate void UReceivesAI_DialogueCallback(byte[] voiceDatas);
    • 呼び出すとき、デフォルトで Unity のログ デリゲートを送信します. InitDLL メソッドをカプセル化し、コールバック パラメータを自分で展開できますが、C++ インターフェイスに対応するように変更する必要があります。
[MonoPInvokeCallback(typeof(void))]
    /// <summary>
    /// 输出日志
    /// </summary>
    /// <param name="level">等级</param>
    /// <param name="msg">消息</param>
    static void UnityLog(LogLevel level, string msg)
    {
        msg = $"Unity回调收到C++输出日志:{msg}";
        if (level == LogLevel.Info)
        {
            Debug.Log(msg);
        }
        else if (level == LogLevel.Warn)
        {
            Debug.LogWarning(msg);
        }
        else
        {
            Debug.LogError(msg);
        }
    }

public static void InitDLL(UReceivesAI_DialogueCallback UDialogueCallback)
    {
        /*int init = Init(
            Marshal.GetFunctionPointerForDelegate((Delegate)(ULogCallback)UnityLog),
            Marshal.GetFunctionPointerForDelegate((Delegate)UDialogueCallback)
            );*/

        int init = Init(
            UnityLog//这个方法回调基本不变,不做参数
            ,UDialogueCallback
            );
    }

予防:

ここで注意すべき問題があります. DLL との対話を使用する場合は問題ありませんが、ソース コードとの対話ではエラーが発生します: NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method.定義. マーシャリングしようとしているメソッドは..., 情報を確認したところ、渡されたデリゲート関数に [MonoPInvokeCallback(typeof(...))] を追加する必要があることがわかりました.内部に入力するか、関数パラメーターが 1 つしかない場合はそのパラメーターの型を入力します。

参考:
ここに画像の説明を挿入

  • 音声データを C++ に送信するためのインターフェイスが定義されています. 以下のインターフェイスのカプセル化は比較的単純なので、あまり導入する必要はありません.
 [DllImport("__Internal")]
    public static extern void ReceivingMicrophoneSpeech(byte[] voiceDatas);
  • テキスト テストを C++ に送信するためのインターフェイスを定義します。
[DllImport("__Internal")]
    public static extern void TEST_Call(string msg);

2. C/C++ ダイナミック リンク ライブラリ プロジェクトを作成する

以前 C++ DLL とやり取りしてダイナミック リンク ライブラリ プロジェクトを作成したので、Unity 内の C++ コードはリンク ライブラリ プロジェクトからコピーされます (後で NativeCode を CInterface に名前変更しました) 新しいリンク ライブラリ プロジェクトの作成方法については、こちらを参照してください。記事の最後へ リンク: Unity の C# と C++/C の相互作用 ポインター関数 ポインター構造の相互作用
ここに画像の説明を挿入

3. C++ レイヤーは、C# レイヤー定義インターフェイスに対応します。

IL2CPP スクリプティング バックエンドを使用すると、C++ (.cpp) コード ファイルを Unity プロジェクトに直接追加できます。これらの C++ ファイルは、Plugin Inspector でプラグインとして機能します。Windows Player と互換性があるように構成されている場合、Unity はマネージ アセンブリから生成された C++ コードを使用して C++ ファイルをコンパイルします。.cpp ファイルをクリックし、インスペクター ウィンドウの [プラットフォーム設定] セクションでプラットフォーム設定を選択します。
ここに画像の説明を挿入

C# 層で対話型インターフェイスを定義した後、C/C++ 側の対応するデータ構造を同時に定義する必要があります. C# の定義と厳密に 1 対 1 である必要があります. リンクを参照できます.記事の最後に: C# と C++の型
の対応CInterface.h

#ifndef __NativeCode_H__
#define __NativeCode_H__
//#ifndef EXPORT_DLL
//#define EXPORT_DLL  __declspec(dllexport) //导出dll声明
//#endif

enum class LogLevel {
	Info,
	Warn,
	Error
};


//定义回调函数指针
typedef void(__stdcall* ULogCallback)(LogLevel level ,const char*);//Unity日志输出函数
typedef void(__stdcall* UReceivesAI_DialogueCallback)(unsigned char voiceDatas[]);//Unity需要播放的AI对话函数


extern "C" {

	int Init(ULogCallback logCallback ,UReceivesAI_DialogueCallback diaogueCallback);//初始化注册Unity回调函数

	void ReceivingMicrophoneSpeech(unsigned char voiceDatas[]);
	
	void TEST_Call(char* char_Str);//测试方法
}

#endif//__NativeCode_H__

CInterface.cpp

//#include "pch.h"
#include "CInterface.h"

ULogCallback ULog;
UReceivesAI_DialogueCallback UReceivesAI_Dialogue;

extern "C" {
	int Init(ULogCallback logCallback ,UReceivesAI_DialogueCallback diaogueCallback)
	{
		ULog = logCallback;
		UReceivesAI_Dialogue = diaogueCallback;

		//TODO:logCallback支持中文字符
		ULog(LogLevel::Info, "12345");
		ULog(LogLevel::Info, "abc");
		

		return 0;
	}

	void TEST_Call(char * char_Str) {
		ULog(LogLevel::Info, char_Str);
	}

	void ReceivingMicrophoneSpeech(unsigned char voiceDatas[]) {
		ULog(LogLevel::Info, "Call ReceivingMicrophoneSpeech");
	}
}

予防:

パッケージングの際、cpp のコンパイル時に次のエラーが表示されます。

  • CInterface.h は __declspec をサポートしていないため、DLL が対話するときに __declspec(dllexport) キーワードが残されます。
  • CInterface.cpp での DLL のやり取りにより、ヘッダー ファイル #include "pch.h" が見つかりません。コメント アウトするだけです。
  • すべての C++ インターフェイスには extern "C" が必要です
  • cpp ソース コードを Unity インタラクションに統合するには、ProjectSetting-Player-OtherSetting-Configuration-ScriptingBackend で IL2CPP を選択する必要があります

要約する

CPPのソースコードを使うとUnityエンジンをパッケージ化した後、対応するプラットフォームのコードにコンパイルできますスクリプトバックエンドはIL2CPPを選択する前提ですが、この方法はエディタモードでは使えないので、dllを使う予定です後でエディターモードでのやり取り. 判断するプラットフォームを追加し、後のプロセスが成熟した後にまとめを続けます.


参考文献

Windows Player: IL2CPP 用の C++ ソース コード プラグイン

Unity の C# および C++/C のインタラクティブなポインター関数ポインター構造の相互作用

C# と C++ の型の対応

おすすめ

転載: blog.csdn.net/qq_42541751/article/details/128552441