在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代码。

如有任何疑问,请随时与我联系。