在Unity中实现Swift
这是我以前的文章中关于与ARKit一起在Unity中实现CoreML的后续文章。
学习完本教程后,您可以做什么。
- 您可以在Unity项目中使用任何Swift代码。
- 您可以从Unity调用Swift void,但也可以调用实际的返回类型。
- 您可以将变量从Unity传递给Swift函数。
- 您可以从Swift字符串转换为C#字符串(听起来更复杂)。
首先,在Unity中使用Swift。
让我们从Unity的Swift开始,最简单最重要的事情开始。 我之前的许多人已经做到了这一点,尤其是对于Unity 5.6及更低版本。 最近人们很难使其在Unity 2017或更高版本中工作。 在许多教程的帮助下,我使它可以在Unity 2017及更高版本中使用,一直到Unity 2018.2 beta为止。
所以我们开始:
步骤1:建立可可触控框架
打开Xcode时,创建一个新项目,然后选择“ Cocoa Touch Framework”并命名为任意名称。 我将其命名为“ SwiftPlugin”。 当然,语言是“快速”,您可以保留“包括单元测试”。
为了使事情井井有条,请在“ SwiftPlugin”文件夹中创建一个新文件夹。 并将其称为“来源”。
步骤2:开始编写您的Swift代码。
首先,我们在“ Source”文件夹中创建一个Swift文件。 我称我为“ SwiftForUnity.swift”。
将以下代码粘贴到您的.swift文件中:
进口基金会
导入UIKit
@objc公共类SwiftForUnity:UIViewController {
@objc静态让共享= SwiftForUnity()
@objc func SayHiToUnity()->字符串{
返回“嗨,我是斯威夫特”
}
}
@objc表示该类和函数可以在Objective-c中使用,我们稍后需要在C#中使用它。 同样,您希望对Objective-C可见的所有类都需要从NSObject继承,因为UIViewController已经从NSObject继承了,所以无需在这里再次添加它。
编写Swift代码后,您需要制作一个Objective-C ++文件并将其命名为“ SwiftForUnityBridge.mm”。 您需要将其从“ SwiftForUnityBridge.m”重命名为“ SwiftForUnityBridge.mm”。
现在,将以下代码添加到.mm文件中:
#include“ SwiftPlugin-Swift.h”
#pragma mark-C接口
extern“ C” {
char * _sayHiToUnity(){
NSString * returnString = [[SwiftForUnity共享] SayHiToUnity];
char * cStringCopy(const char * string);
return cStringCopy([returnString UTF8String]);
}
}
char * cStringCopy(const char * string){
如果(string == NULL){
返回NULL;
}
char * res =(char *)malloc(strlen(string)+1);
strcpy(res,string);
返回资源;
}
这将给您带来一些错误,因为“ SwiftPlugin-Swift.h”文件尚不存在,这也是它告诉您找不到“ SwiftForUnity共享”的原因。 构建后,将自动创建此文件。 如果只想执行void,则可以使用以下命令:
#include“ SwiftPlugin-Swift.h”
#pragma mark-C接口
extern“ C” {
无效_sayHiToUnity(){
[[SwiftForUnity分享了] SayHiToUnity];
}
}
cStringCopy函数用于更改stringformatting,以使其对于C#可读,所有其他返回类型的工作方式与void完全相同。
如果要将一些变量传递给Swift函数,它将看起来像这样:
在这种情况下,我传递了PixelBuffer(CoreML需要)和一个字符串。
步骤3:创建桥接头
在我们的源文件夹中,我们创建一个新的头文件,并将其命名为“ SwiftPlugin-Bridging-Header.h”。
Xcode要求桥接头的格式始终如下:[PROJECTNAME-Bridging-Header]。
将以下内容添加到头文件中:
# 导入
# 导入
所以看起来像这样:
完成之后,就可以将框架构建到模拟器设备上。 构建将失败,但是在产品下,您将拥有.framework文件。 右键单击它,然后按在查找器中显示。
将文件复制到可以找到它的位置,因为在Unity中不需要它,以后在Unity生成的Xcode项目中将需要它。
步骤4:Unity项目。
让我们从创建一个新的Unity项目开始,我们都知道该如何做。 在Unity项目的资产文件夹中,我们创建一个名为“ Plugins”的新文件夹,在“ Plugins”中创建一个名为“ iOS”的文件夹,然后在其中创建一个名为“ SwiftPlugin”的文件夹,在其中我们创建2个文件夹,其中一个名为“ Editor” ”,另一个名为“源”。 它看起来应该像这样:
在源代码中,我们拖动“ SwiftForUnity.Swift”文件,“ SwiftForUnityBridge.mm”文件和“ SwiftPlugin-Bridging-Header.h”文件。 现在,我们在Source文件夹中创建一个名为“ SwiftForUnity.cs”的C#文件。
它应该是这样的:
使用系统;
使用System.Runtime.InteropServices;
使用UnityEngine;
公共类SwiftForUnity:MonoBehaviour {
#region声明外部C接口
#if UNITY_IOS &&!UNITY_EDITOR
[DllImport(“ __ Internal”)]
私有静态外部字符串_sayHiToUnity();
#万一
#endregion
#region包装的方法和属性
公共静态字符串HiFromSwift()
{
#if UNITY_IOS &&!UNITY_EDITOR
返回_sayHiToUnity();
#其他
返回“未找到Swift!”;
#万一
}
#endregion
#region Singleton实现
私有静态SwiftForUnity _instance;
公共静态SwiftForUnity实例
{
得到
{
如果(_instance == null)
{
var obj = new GameObject(“ SwiftUnity”);
_instance = obj.AddComponent ();
}
返回_instance;
}
}
私人void Awake()
{
如果(_instance!= null)
{
销毁(gameObject);
返回;
}
DontDestroyOnLoad(gameObject);
}
#endregion
}
我们需要System.Runtime.InteropsServices导入外部函数。 还请确保使用[DllImport…。]正确地拼写.mm函数,如果不正确,它将无法正常工作。
[DllImport(“ __ Internal”)]
私有静态外部字符串_sayHiToUnity();
#万一
#endregion
#region包装的方法和属性
公共静态字符串HiFromSwift()
{
#if UNITY_IOS &&!UNITY_EDITOR
返回_sayHiToUnity();
#其他
返回“未找到Swift!”;
#万一
}
#endregion
必须在两个位置正确拼写“ _sayHiToUnity()”。
现在,您可以在任何喜欢的地方使用“ SwiftForUnity.HiFromSwift()”,它将返回您的Swift代码中指定的字符串。
对于更多的返回类型,以及当您希望将变量传递给swift时,这看起来是这样的:
请注意,尽管Swift中的String在Objective C ++中是char *,但在C#中仍是字符串,这可能是传递最混乱的变量,这就是为什么我将其用作示例,这样您应该可以弄清楚淘汰所有其他东西。
提示:请记住我告诉过您的.framework文件放在您想起的地方。 您实际上可以将其放在Unity的“ StreamingAssets”文件夹中。 这样,它将不会与其他文件一起编译,但仍将在您的Xcode项目中。
步骤5:关键部分。
好的,您已经在Unity中实现了一些Swift功能,并渴望对其进行测试,因此您构建了Xcode项目,将其打开并尝试对其进行构建……但是请继续……如此之多的错误! 最重要的部分尚未发生。 为了使此工作正常进行,后期处理必须在Xcode中调整几个设置。
请记住,我们在“ SwiftPlugin”中创建了一个名为“ Editor”的文件夹,现在该使用它了。 在这里,我们添加了一个名为以下脚本的C#脚本:“ SwiftPostProcess.cs”
该文件将包含以下代码:
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
使用UnityEditor;
使用UnityEditor.Callbacks;
使用UnityEditor.iOS.Xcode;
使用System.Diagnostics;
使用System.IO;
使用System.Linq;
公共静态类SwiftPostProcess {
[PostProcessBuild]
公共静态无效OnPostProcessBuild(BuildTarget buildTarget,字符串buildPath)
{
如果(buildTarget == BuildTarget。iOS)
{
var projPath = buildPath +“ /Unity-Iphone.xcodeproj/project.pbxproj”;
var proj = new PBXProject();
proj.ReadFromFile(projPath);
var targetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
proj.SetBuildProperty(targetGuid,“ ENABLE_BITCODE”,“ NO”);
proj.SetBuildProperty(targetGuid,“ SWIFT_OBJC_BRIDGING_HEADER”,“库/插件/iOS/SwiftPlugin/Source/SwiftPlugin-Bridging-Header.h”);
proj.SetBuildProperty(targetGuid,“ SWIFT_OBJC_INTERFACE_HEADER_NAME”,“ SwiftPlugin-Swift.h”);
proj.AddBuildProperty(targetGuid,“ LD_RUNPATH_SEARCH_PATHS”,“ @ executable_path / Framework $(PROJECT_DIR)/ lib / $(CONFIGURATION)$(继承)”);
proj.AddBuildProperty(targetGuid,“ FRAMERWORK_SEARCH_PATHS”,
“ $(继承)$(PROJECT_DIR)$(PROJECT_DIR)/ Framework”);
proj.AddBuildProperty(targetGuid,“ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES”,“是”);
proj.AddBuildProperty(targetGuid,“ DYLIB_INSTALL_NAME_BASE”,“ @rpath”);
proj.AddBuildProperty(targetGuid,“ LD_DYLIB_INSTALL_NAME”,
“ @executable_path /../ Frameworks / $(EXECUTABLE_PATH)”);
proj.AddBuildProperty(targetGuid,“ DEFINES_MODULE”,“是”);
proj.AddBuildProperty(targetGuid,“ SWIFT_VERSION”,“ 4.0”);
proj.AddBuildProperty(targetGuid,“ COREML_CODEGEN_LANGUAGE”,“ Swift”);
proj.WriteToFile(projPath);
}
}
}
注意:如果使用其他名称,请确保在脚本编写的后处理中检查头文件名,然后再使用。
它所做的基本上是更改整个Xcode设置。 如果您不使用ARKit或CoreML,则无需更改其中的许多内容,但不会造成任何伤害。 添加完成后,就可以开始构建了。
一旦打开Xcode项目,就必须对包标识符进行常规操作,而且还必须向应用程序中添加.framework。 因为我们将其放在“ StreamingAssets”文件夹中,所以我们可以在Xcode项目中找到它,并将其移到该文件夹中的Framework文件夹中。
好了,现在您应该能够在Unity中构建和使用Swift代码。
如有任何疑问,请随时与我联系。