iOS项目嵌入Unity的View(Swift )
一、将Unity导出的文件导入到xcode工程中
1、获取unity导出的unity项目后,在项目XXXX.xcworkspace加入unity项目,如图:
二、编译unity项目,产出UnityFramework
2.1、选中unity的Data,设置右侧的Target Membership,选中Unity-iPhone和UnityFramework
2.2、设置unity项目的buildID 、Version 和build 与主工程一致
2.3、选中unity项目,进行编译,编译成功即可
三、把UnityFramework,加入到主工程中,编译使用
3.1、把UnityFramework,加入到主工程中如图
3.2、在桥接文件中引入
#import <Foundation/Foundation.h>
#include <UnityFramework/UnityFramework.h>
3.3、AppDelegate里引入unity,但并不使用,因为我只是在某个页面会跳转到有unityView的页面,这里看需求处理
import UIKit import UnityFramework @main class AppDelegate: UIResponder, UIApplicationDelegate, UnityFrameworkListener { @objc var unityFramework: UnityFramework? var appLaunchOpts: [UIApplication.LaunchOptionsKey: Any]? var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { appLaunchOpts = launchOptions // 2 self.window = UIWindow(frame: UIScreen.main.bounds) self.window?.backgroundColor = UIColor.white // // 3 let nav = UINavigationController(rootViewController: FirstFirstViewController()) if let nativeWindow = self.window { nativeWindow.rootViewController = nav; nativeWindow.makeKeyAndVisible() } return true } func initUnityFramework(){ unityFramework = getUnityFramework() if let unityframework = self.unityFramework { unityframework.setDataBundleId("com.unity3d.framework") unityframework.register(self) unityframework.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: appLaunchOpts) } } private func getUnityFramework() -> UnityFramework? { let bundlePath: String = Bundle.main.bundlePath + "/Frameworks/UnityFramework.framework" let bundle = Bundle(path: bundlePath) if bundle?.isLoaded == false { bundle?.load() } let ufw = bundle?.principalClass?.getInstance() if ufw?.appController() == nil { let machineHeader = UnsafeMutablePointer<MachHeader>.allocate(capacity: 1) machineHeader.pointee = _mh_execute_header ufw!.setExecuteHeader(machineHeader) } return ufw } func unityIsInitialized( ) -> Bool { return (self.unityFramework != nil && self.unityFramework?.appController() != nil) } // 卸载unity private func unloadUnityInternal() { if let unityFramework = self.unityFramework { unityFramework.unregisterFrameworkListener(self) } self.unityFramework = nil } // 监听 func unityDidUnload(_ notification: Notification!) { unloadUnityInternal() } func unityDidQuit(_ notification: Notification!) { unloadUnityInternal() } }
3.4、在跳转有Unity的页面前初始化Unity
import UIKit class FirstViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.view.backgroundColor = .green let pushBtn = UIButton.init(frame: CGRect(x: 0, y: 350, width:200, height: 40)) pushBtn.addTarget(self, action: #selector(pushBtnCLick), for: .touchUpInside) pushBtn.setTitle("去有Unity的VC", for: .normal) pushBtn.backgroundColor = .red self.view.addSubview(pushBtn) } // 去有Unity的VC @objc func pushBtnCLick(){ // 去有Unity的VC前unity初始化 guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } // 这里初始化unity, unity这时候会产生是一个新的window,并覆盖掉默认的window appDelegate.initUnityFramework() // 所以这里要将应用的默认window切换回原来的 window,将unity的window隐藏掉 appDelegate.window?.makeKeyAndVisible() // 去有Unity的VC let vc = NextViewController() self.navigationController?.pushViewController(vc, animated: true) } }
3.5、显示unity画面,把appDelegate.unityFramework?.appController()?.rootView加到想加的view上,调用给的API即可
// 显示unity view @objc func xianShiBtnCLick(){ // 显示 guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } self.delegate = appDelegate // guard let unityRootView = appDelegate.unityFramework?.appController()?.rootView else { return } unityRootView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height) self.view.addSubview(unityRootView) self.view.sendSubviewToBack(unityRootView) }
四、unity的交互与退出
4.1、unity的交互,unity开发会暴露出一些方法,在对应的业务场景调用即可
4.2、unity的退出,这里还是使用appDelegate来执行相关操作,但是并不是真的quit卸载退出,只是unloadApplication
@objc func destoryBtnClick() { DispatchQueue.main.asyncAfter(deadline: .now() + 0) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } appDelegate.unityFramework?.unloadApplication() appDelegate.unityFramework?.appController()?.rootView.removeFromSuperview() appDelegate.unityFramework?.appController().rootViewController.removeFromParent() self.navigationController?.popViewController(animated: true) } }
五、UnityFramework类说明
UnityFramework类 +(UnityFramework*)getInstance :单例类方法,可将实例返回到 UnityFramework。 -(UnityAppController*)appController :返回 UIApplicationDelegate 的 UnityAppController 子类。这是原生端的根 Unity 类,可以访问应用程序的视图相关对象,例如 UIView、UIViewControllers、CADisplayLink 或 DisplayConnection。 -(void)setDataBundleId:(const char*)bundleId:设置捆绑包,Unity 运行时应在其中查找 Data 文件夹。应在调用 runUIApplicationMainWithArgc 或 runEmbeddedWithArgc 之前调用此方法。 -(void)runUIApplicationMainWithArgc:(int)argc argv:(char*[])argv:从没有其他视图的主要方法中运行 Unity 的默认方式。 -(void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts:存在其他视图时,如果需要运行 Unity,需要调用此方法。 -(void)unloadApplication :调用此方法可卸载 Unity,并在卸载完成后接收对 UnityFrameworkListener 的回调。Unity 将释放占用的大部分内存,但不会全部释放。 -(void)registerFrameworkListener:(id)obj :注册监听器对象,用于接收 UnityFramework 生命周期相关事件的回调。 -(void)unregisterFrameworkListener:(id)obj:取消注册监听器对象。 -(void)showUnityWindow:在显示非 Unity 视图时调用此方法,也会显示已经在运行的 Unity 视图。 -(void)pause:(bool)pause:暂停 Unity -(void)setExecuteHeader:(const MachHeader*)header:必须在运行 Unity 之前调用此命令,CrashReporter 才能正常工作。 -(void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg:此方法是 UnitySendMessage 的代理。它通过名称查找游戏对象,并使用单字符串消息参数来调用 functionName。 (void)quitApplication:(int)exitCode:调用此方法可完全卸载 Unity,并在 Unity 退出后接收对 UnityFrameworkListener 的回调。Unity 将释放所有内存。 注意:进行此调用后,将无法在同一进程中再次运行 Unity。可在 AppController 上设置 quitHandler 以覆盖默认进程终止