惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
T
Tenable Blog
博客园_首页
S
Securelist
Spread Privacy
Spread Privacy
Google Online Security Blog
Google Online Security Blog
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
U
Unit 42
L
LINUX DO - 热门话题
量子位
T
Threat Research - Cisco Blogs
博客园 - 【当耐特】
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
Martin Fowler
Martin Fowler
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Security Latest
Security Latest
Scott Helme
Scott Helme
V
Vulnerabilities – Threatpost
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
InfoQ
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
V2EX - 技术
V2EX - 技术
T
Tailwind CSS Blog
月光博客
月光博客
Recent Announcements
Recent Announcements
G
Google Developers Blog
F
Full Disclosure
W
WeLiveSecurity
宝玉的分享
宝玉的分享
腾讯CDC
G
GRAHAM CLULEY
Vercel News
Vercel News
Simon Willison's Weblog
Simon Willison's Weblog
美团技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Help Net Security
Help Net Security

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
Unity3D 游戏开发之 C++ 插件接入 - 元视角
飞鸿踏雪 · 2015-11-21 · via 元视角

各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://qinyuanpei.com。虽然 Unity3D 引擎依靠强大的跨平台能力睥睨高手林立的游戏引擎世界,我们在使用 Unity3D 游戏引擎的时候基本上不会去接触底层的东西,可是有时候面对某些奇葩的要求的时候,我们就不得不考虑使用 C++这样的语言来为其编写相关的插件。你如果问我是什么样的奇葩要求,比如接入蓝牙手柄来控制游戏、接入类似街机的设备来控制游戏、接入同一个游戏到两个不同的设备上并响应不同的控制……诸如此类的种种问题,可能目前在 Unity3D 引擎中找不到解决方案,这个时候写 C++插件就变成了一种刚性需求,这就是我们今天要来一起探讨的问题。

Unity3D 主要使用 C#进行开发,所以为 Unity3D 编写插件本质上就是让 C#调用 C++代码。目前主要有 C++ CLR 和 C++ Native 两种实现方法,其中 C++ CLR 可以理解为运行在.Net CLR 即公共语言运行库上的 C++代码,这种代码是托管的 C++代码,目前并没有被 C++标准承认,因为它更像是 C++和 C#两种语言的混合代码,这种代码的优势是可以像普通的.NET 库一样被 C#调用,考虑到 Unity3D 建立在和.Net 类似的 Mono 上,因此这种方式应该是我们的最佳实践方案;C++ Native 则是指传统的 C++ 动态链接库,通过 DllImport 在 C#中进行包装后在 C#中进行调用,相对地这种方式调用的是非托管的 C++代码,这种方式相信接触过 Windows 开发的朋友应该不会感到陌生啦,它是一种更为普遍的方法,例如我们要接入苹果官方 SDK 的时候,需要对 Object C 的代码进行封装后交给 C#去调用,而这里使用的方法就是 DllImport 了。

好了,下面我们来看看两种方式各自是如何实现的吧!这里博主使用的开发环境是 Windows 8.1 32bit 和 Visual Studio 2012,Unity3D 的版本为 4.6 版本。

C++ CLR

创建一个 C++ CLR 类库项目

首先我们按照下图中的步骤创建一个 C++ CLR 项目:

截图是件讨厌的事情,虽然懒惰的人们都喜欢 截图是件讨厌的事情,虽然懒惰的人们都喜欢

请注意.Net 版本问题,重要的事情说三遍,不认真看这里的人出现问题就不要到我这里来评论了,我最讨厌连文章都没有看明白就来和你纠缠不清的人,谢谢。创建好项目后请打开项目属性窗口设置【公共语言运行时支持】节点的值为【安全 MSIL 公共语言运行时支持(/clr:safe)】好了,下面我们找到 CLR4Unity.h 文件,添加 ExampleClass 声明:

/// <summary>
/// 一个简单的托管C++示例类
/// </summary>
public  ref  class ExampleClass
{
	public:

	/// <summary>
	/// 产生一个介于min和max之间的整型随机数
	/// <returns>整型随机数</returns>
	/// <param name="min">最小值</param>
	/// <param name="max">最大值</param>
	/// </summary>
	static int  Random(int min,int max)
	{
		//注意在托管的C++中使用gcnew来代替new
		//我承认C++写CLR代码略显奇葩像是C++和C#语法的混合
		return (gcnew System::Random)->Next(min,max);
	}

	/// <summary>
	/// 计算一个整数的平方
	/// <returns>整型数值</returns>
	/// <param name="a">需要平方的数值</param>
	/// </summary>
	static int Square(int a)
	{
		return a * a;
	}

	/// <summary>
	/// 返回两个数中的最大值
    /// <returns>整型数值</returns>
	/// <param name="a">参数1</param>
	/// <param name="b">参数2</param>
	/// </summary>
	static int Max(int a,int b)
	{
		if(a<=b){
			return b;
		}else{
			return a;
		}
	}
};

显然我们这里定义了三个简单的方法,注意到第一个方法 Random 依赖于 System.Rnadom 类,而在托管的 C++中是使用 gcnew 来代替 new 这个关键字的,所以请尽情感受 C#和 C++的混搭语法风格吧!这样我们就可以编译得到 CLR4Unity.dll 这个类库,将这个文件复制到 Unity3D 项目中的 Plugins 目录下下,然后将其加入项目引用列表。如果你以为引用就是:

呵呵,我严重怀疑你对.Net 的熟悉程度。你没有添加对 CLR4Unity.dll 的引用,你到底在 using 什么啊?

先添加引用然后using 先添加引用然后using

如果你对.NET 熟悉到足以无视这里的一切,请闭上眼接着往下看,哈哈!

在 C#中添加引用及方法调用

接下来我们在 Unity3D 中创建一个脚本 PluginTest.cs,然后在 OnGUI 方法增加下列代码。可是你要以为这些代码就应该写在 OnGUI 方法中,抱歉请你先去了解 MonoBehaviour 这个类。什么?添加了这些代码报错?没有 using 的请自行面壁:

//调用C++ CLR中的方法
if(GUILayout.Button("调用C++ CLR中的方法", GUILayout.Height (30))) 
{
	Debug.Log("调用C++ CLR中的方法Random(0,10):" + ExampleClass.Random(0,10));
	Debug.Log("调用C++ CLR中的方法Max(5,10):" + ExampleClass.Max(5,10));
	Debug.Log("调用C++ CLR中的方法Square(5):" + ExampleClass.Square(5));
}

C++ Native

创建一个 C++动态链接库项目

首先我们按照下图中的步骤来创建一个 C++ Win32 项目:

不要问我从哪里来 不要问我从哪里来

我的故乡在远方 我的故乡在远方

好了,接下来我们找到 Native4Unity.cpp 写入下列代码:

// Native4Unity.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
//为了使用rand()函数引入C++标准库
#include "stdlib.h"

/// <summary>
/// 产生一个介于min和max之间的整型随机数
/// <returns>整型随机数</returns>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// </summary>
extern "C" __declspec(dllexport) int Random(int min,int max)
{
	return rand() % (max - min + 1) + min;
}

/// <summary>
/// 返回两个数中的最大值
/// <returns>整型数值</returns>
/// <param name="a">参数1</param>
/// <param name="b">参数2</param>
/// </summary>
extern "C" __declspec(dllexport) int Max(int a ,int b)
{
	if(a<=b){
	   return b;
    }else{
	   return a;
	}
}

/// <summary>
/// 计算一个整数的平方
/// <returns>整型数值</returns>
/// <param name="a">需要平方的数值</param>
/// </summary>
extern "C" __declspec(dllexport) int Square(int a)
{
	return a * a;
}

和 C++ CLR 类似,我们使用标准的 C++语言来实现同样的功能。注意到 rand()这个函数是 C++标准库里的内容,所以我们在文件开头增加了对 stdlib.h 这个头文件的引用。这里需要注意的一点是:所有希望使用 DllImport 引入 C#的 C++方法都应该在方法声明中增加__declspec(dllexport)关键字,除非它在.def 文件中对这些方法进行显示声明。关于.def 文件的相关定义大家可以到 MSDN 上检索,这些都是属于 C++编译器的内容,这里不再详细说了。

在 C#中使用 DllImport 封装方法

将编译好的 Native4Unity.dll 复制到 Plugins 目录中后,下面我们要做的事情就是在 C#里对这些方法进行封装或者说是声明:

 [DllImport("Native4Unity")]
 private extern static int Random(int min, int max);

 [DllImport("Native4Unity")]
 private extern static int Max(int a, int b);

 [DllImport("Native4Unity")]
 private extern static int Square(int a);

然后就是简单地调用啦:

//调用C++ Native中的方法
if(GUILayout.Button("调用C++ Native中的方法", GUILayout.Height (30))) 
{
    Debug.Log("调用C++ Native中的方法Random(0,10):" + Random(0, 10));
    Debug.Log("调用C++ Native的方法Max(5,10):" + Max(5, 10));
    Debug.Log("调用C++ Native中的方法Square(5):" + Square(5));
}

最终程序的运行效果如图:

这个结果来之不易请大家珍惜 这个结果来之不易请大家珍惜