C# 与 C++ 混合开发 && Unity native 跨平台插件制作

C# 与 C++ 混合开发 && Unity native 跨平台插件制作

1: Unity native 插件开发流程:

C++ —> C api —> 打包—> C# 脚本

  1. 在 Safe mode 下 C# 与 C++ 无法直接交互,C++ 需要包装成 C api,由 C# 调用 C api。
  2. 插件包
    Android: 打包成 so。
    IOS:打包成 framework 或者 .a。
    macOS: 打包成 bundle 。
    Windows: 打包成 dll。
  3. C# 封装 C api 中的接口。

2: C# C++ 数据传递,api 调用以及回调方案:

  1. 基本类型传递。
  2. struct 传递。
  3. 指针传递。
  4. 回调方案。

3: 以下为上述概要的完整示例:

c api 头文件

// C api header
#ifndef Bridge_hpp
#define Bridge_hpp
#include 

typedef struct People {
     
    int age;
    const char *name;
} People;

typedef struct PeopleList {
     
    People people1;
    People people2;
} PeopleList;

typedef struct Peoples {
     
    int count;
    People *people;
} Peoples;

typedef void(*Func_Pointer)(int, bool, const char*);

Func_Pointer callback1, callback2;
/**
 Api test.
 */
extern "C" int startLogService();

extern "C" int getSumValue(int value1, int value2);

extern "C" int setPeople(People people);

extern "C" int modifyPeople(People *people);

extern "C" int setPeopleList(PeopleList peopleList);

extern "C" int modifyPeopleList(PeopleList *peopleList);

extern "C" int setPeoples(Peoples peoples);

extern "C" int modifyPeoples(Peoples *peoples);

extern "C" void initCallback(Func_Pointer funcPointer, Func_Pointer funcPointer2);

extern "C" void testCallback();
#endif /* Bridge_hpp */

c api 实现文件:


#include "Bridge.hpp"
#include "LogHelper.h"

#ifndef __cplusplus
extern "C"
#endif
/**
 Api call sample.
 */
LogHelper& logger = LogHelper::getInstance();;

int startLogService() {
     
    logger.startLogService("./wrapper_log.txt");
    return 100;
}

int getSumValue(int value1, int value2) {
     
    return value1 + value2;
}

int setPeople(People people) {
     
    logger.writeLog("Call setPeople from C#: age: %d, name: %s\r\n", people.age, people.name);
    return 100;
}

int modifyPeople(People *people) {
     
    logger.writeLog("Call modfiyPeople from C#: age: %d, name: %s\r\n", people->age, people->name);
    people->age = 30;
    people->name = "I am C++";
    return 100;
}

int setPeoples(Peoples peoples) {
     
    for (int i = 0; i < peoples.count; i ++) {
     
        logger.writeLog("Call setPeoples from C#: age: %d, name: %s\r\n", peoples.people[i].age, peoples.people[i].name);
    }
    return 100;
}

int modifyPeoples(Peoples *peoples) {
     
    logger.writeLog("Call modfiyPeople from C#: count: %d\r\n", peoples->count);
    for (int i = 0; i < peoples->count; i ++) {
     
        peoples->people[i].age = 30;
        peoples->people[i].name = "I am people list from C++";
    }
    return 100;
}

int setPeopleList(PeopleList peopleList) {
     
    logger.writeLog("Call setPeopleList People1 age: %d, name: %s, People2 age: %d, name: %s\r\n", peopleList.people1.age, peopleList.people1.name, peopleList.people2.age, peopleList.people2.name);
    return 100;
}

int modifyPeopleList(PeopleList *peopleList) {
     
    logger.writeLog("Call modifyPeopleList from C#.\r\n");
    peopleList->people1.age = 30;
    peopleList->people1.name = "I am people1 from C++";
    
    peopleList->people2.age = 30;
    peopleList->people2.name = "I am people2 from C++";
    
    return 100;
}

void initCallback(Func_Pointer funcPointer, Func_Pointer funcPointer2) {
     
    callback1 = funcPointer;
    callback2 = funcPointer2;
}

void testCallback() {
     
    callback1(1, true, "I am callback1 from C++");
    callback2(2, false, "I am callback2 from C++");
}
#ifndef __cplusplus
}
#endif

C# 接口文件:

using System.Runtime.InteropServices;
using System;
using AOT;

[StructLayout(LayoutKind.Sequential)]
public struct People {
     

	public int age;
	
	public IntPtr name;
}

[StructLayout(LayoutKind.Sequential)]
public struct PeopleList {
     
	public People people1;

	public People people2;
}

[StructLayout(LayoutKind.Sequential)]
public struct Peoples {
     
	public int count;

	public IntPtr peopleListPtr;
}

public delegate void On_C_Plus_Plus_Callback(int a, bool b, string c);

public class NativeBridge {
     

	public const string libName = "c_sharp_to_c_plus";

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int startLogService();

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int getSumValue(int value1, int value2);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int setPeople(People people);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int modifyPeople(ref People people);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int setPeoples(Peoples peoples);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int getPeoples(ref Peoples peoples);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int setPeopleList(PeopleList peopleList);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern int modifyPeopleList(ref PeopleList peopleList);

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern void testCallback();

	[DllImport(libName, CharSet = CharSet.Ansi)]
	public static extern void initCallback(On_C_Plus_Plus_Callback callback, On_C_Plus_Plus_Callback callback2);
}

调用示例:

using System.Runtime.InteropServices;
using UnityEngine;
using AOT;

public class HelloUnity : MonoBehaviour {
     

    // Use this for initialization
    void Start()
    {
     
		People people = new People();
		people.age = 50;
		people.name = Marshal.StringToHGlobalAnsi("Bobo");

		int ret1 = NativeBridge.startLogService();
		Debug.LogFormat("startLogService ret1 = {0} ", ret1);

		int ret2 = NativeBridge.getSumValue(100, 200);
		Debug.LogFormat("startLogService ret2 = {0} ", ret2);

		int ret3 = NativeBridge.setPeople(people);
		Debug.LogFormat("setPeople ret3 = {0} ", ret3);

		int ret4 = NativeBridge.modifyPeople(ref people);
		Debug.LogFormat("modifyPeople ret4 = {0},  age = {1}  ,name = {2} ", ret4, people.age, Marshal.PtrToStringAnsi(people.name));

		People p1 = new People();
		p1.age = 50;
		p1.name = Marshal.StringToHGlobalAnsi("bobo");

		People p2 = new People();
		p2.age = 100;
		p2.name = Marshal.StringToHGlobalAnsi("bobo2");

		PeopleList peopleList = new PeopleList();
		peopleList.people1 = p1;
		peopleList.people2 = p2;

		int ret5 = NativeBridge.setPeopleList(peopleList);
		Debug.LogFormat("setPeopleList ret5 = {0} ", ret5);

		int ret6 = NativeBridge.modifyPeopleList(ref peopleList);
		Debug.LogFormat("setPeopleList ret6 = {0} ", ret6);

		NativeBridge.initCallback(On_C_Plus_Plue_Callback_1, On_C_Plus_Plue_Callback_2);

		NativeBridge.testCallback();
    }

	[MonoPInvokeCallback(typeof(On_C_Plus_Plus_Callback))]
	public static void On_C_Plus_Plue_Callback_1(int a, bool b, string c) {
     
		Debug.LogFormat("On_C_Plus_Plue_Callback1 a: {0}, b: {1}, c: {2}", a, b, c);
	}

	[MonoPInvokeCallback(typeof(On_C_Plus_Plus_Callback))]
	public static void On_C_Plus_Plue_Callback_2(int a, bool b, string c)
	{
     
		Debug.LogFormat("On_C_Plus_Plue_Callback1 a: {0}, b: {1}, c: {2}", a, b, c);
	}
}

输出结果:

Unity C# 层 log:
C# 与 C++ 混合开发 && Unity native 跨平台插件制作_第1张图片
C++ 层调用 log:
C# 与 C++ 混合开发 && Unity native 跨平台插件制作_第2张图片

基础项目链接:
Sample github 链接.

全平台高级进阶项目:
全平台(Android, iOS, Windows, macOS 全平台插件高级进阶项目 github 链接).

你可能感兴趣的:(C#,&&,C++,混合开发,c#,c++,unity3d,plugin)