簡體   English   中英

在自己的 ViewController 中嵌入 Unity

[英]Embed Unity inside iOS in own ViewController

使用 Unity 2019.3.0f3 及其Unity as a library功能,我嘗試將 Unity 項目嵌入到我的 iOS 應用程序中。

Unity 官方只支持全屏渲染。 盡管如此,我正在尋找解決該限制的方法。
在以前的 Unity 版本中,我成功地使用了swift-unity來進行集成。 在這種方法中,很容易獲得 Unity 渲染的視圖(使用UnityGetGLView() )。 我在穩定性或資源方面沒有問題。

使用新的庫方法,每次我嘗試訪問UnityView ,unity 都會強制它完成Window作為keyWindow

我嘗試使用訪問我自己的 ViewController 中的 UnityView

if let unityView = UnityFramework.getInstance()?.appController()?.rootViewController.view {
    // insert subview at index 0 ensures unity view is behind current UI view
    view?.insertSubview(unityView, at: 0)
}

但這會立即激活完整的統一窗口並隱藏我的育兒UITabBarController

試圖使UnityFramework.getInstance()?.appController()?.rootViewController成為我的UITabBarController的孩子失敗,結果相同。

此外,無法添加子ViewController 似乎只能添加子視圖。

有誰知道該窗口行為位於何處,或者我如何訪問UnityView (或 RootViewController)並自由使用它?

從統一論壇找到了基於這種方法的問題的解決方案。 使用這種方法,我可以將UnityViewController用作我自己的TabBarController

該方法適用於 Unity 2019.3.0f3,但我不確定它是否適用於未來版本。 感覺 Unity 試圖積極阻止這種使用。 然后我再次在庫代碼的注釋中找到提示,表明至少考慮了修改后的 ViewController-Hierarchy,例如在UnityAppController+ViewHandling.h 但是說明不清楚,並且不存在帶有提示名稱的方法。


解決方案

1. 創建UnityEmbeddedSwift.swift

Unity 提供官方示例 App真是一團糟。 我最終使用了鏈接論壇帖子中UnityEmbeddedSwift.swift並添加了暫停功能。 此類將所有與 Unity 相關的功能封裝在一個干凈的類中。

//
//  UnityEmbeddedSwift.swift
//  Native
//
//  Created by NSWell on 2019/12/19.
//  Copyright © 2019 WEACW. All rights reserved.
//

//
//  Created by Simon Tysland on 19/08/2019.
//  Copyright © 2019 Simon Tysland. All rights reserved.
//

import Foundation
import UnityFramework

class UnityEmbeddedSwift: UIResponder, UIApplicationDelegate, UnityFrameworkListener {

    private struct UnityMessage {
        let objectName : String?
        let methodName : String?
        let messageBody : String?
    }

    private static var instance : UnityEmbeddedSwift!
    private var ufw : UnityFramework!
    private static var hostMainWindow : UIWindow! // Window to return to when exiting Unity window
    private static var launchOpts : [UIApplication.LaunchOptionsKey: Any]?

    private static var cachedMessages = [UnityMessage]()

    // MARK: - Static functions (that can be called from other scripts)

    static func getUnityRootViewController() -> UIViewController! {
        return instance.ufw.appController()?.rootViewController
    }

    static func getUnityView() -> UIView! {
        return instance.ufw.appController()?.rootViewController?.view
    }

    static func setHostMainWindow(_ hostMainWindow : UIWindow?) {
        UnityEmbeddedSwift.hostMainWindow = hostMainWindow
        let value = UIInterfaceOrientation.landscapeLeft.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
    }

    static func setLaunchinOptions(_ launchingOptions :  [UIApplication.LaunchOptionsKey: Any]?) {
        UnityEmbeddedSwift.launchOpts = launchingOptions
    }

    static func showUnity() {
        if(UnityEmbeddedSwift.instance == nil || UnityEmbeddedSwift.instance.unityIsInitialized() == false) {
            UnityEmbeddedSwift().initUnityWindow()
        }
        else {
            UnityEmbeddedSwift.instance.showUnityWindow()
        }
    }

    static func hideUnity() {
        UnityEmbeddedSwift.instance?.hideUnityWindow()
    }

    static func pauseUnity() {
        UnityEmbeddedSwift.instance?.pauseUnityWindow()
    }

    static func unpauseUnity() {
        UnityEmbeddedSwift.instance?.unpauseUnityWindow()
    }

    static func unloadUnity() {
        UnityEmbeddedSwift.instance?.unloadUnityWindow()
    }

    static func sendUnityMessage(_ objectName : String, methodName : String, message : String) {
        let msg : UnityMessage = UnityMessage(objectName: objectName, methodName: methodName, messageBody: message)

        // Send the message right away if Unity is initialized, else cache it
        if(UnityEmbeddedSwift.instance != nil && UnityEmbeddedSwift.instance.unityIsInitialized()) {
            UnityEmbeddedSwift.instance.ufw.sendMessageToGO(withName: msg.objectName, functionName: msg.methodName, message: msg.messageBody)
        }
        else {
            UnityEmbeddedSwift.cachedMessages.append(msg)
        }
    }

    // MARK - Callback from UnityFrameworkListener

    func unityDidUnload(_ notification: Notification!) {
        ufw.unregisterFrameworkListener(self)
        ufw = nil
        UnityEmbeddedSwift.hostMainWindow?.makeKeyAndVisible()
    }

    // MARK: - Private functions (called within the class)

    private func unityIsInitialized() -> Bool {
        return ufw != nil && (ufw.appController() != nil)
    }

    private func initUnityWindow() {
        if unityIsInitialized() {
            showUnityWindow()
            return
        }

        ufw = UnityFrameworkLoad()!
        ufw.setDataBundleId("com.unity3d.framework")
        ufw.register(self)
//        NSClassFromString("FrameworkLibAPI")?.registerAPIforNativeCalls(self)

        ufw.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: UnityEmbeddedSwift.launchOpts)

        sendUnityMessageToGameObject()

        UnityEmbeddedSwift.instance = self
    }

    private func showUnityWindow() {
        if unityIsInitialized() {
            ufw.showUnityWindow()
            sendUnityMessageToGameObject()
        }
    }

    private func hideUnityWindow() {
        if(UnityEmbeddedSwift.hostMainWindow == nil) {
            print("WARNING: hostMainWindow is nil! Cannot switch from Unity window to previous window")
        }
        else {
            UnityEmbeddedSwift.hostMainWindow?.makeKeyAndVisible()
        }
    }

    private func pauseUnityWindow() {
        ufw.pause(true)
    }

    private func unpauseUnityWindow() {
        ufw.pause(false)
    }

    private func unloadUnityWindow() {
        if unityIsInitialized() {
            UnityEmbeddedSwift.cachedMessages.removeAll()
            ufw.unloadApplication()
        }
    }

    private func sendUnityMessageToGameObject() {
        if (UnityEmbeddedSwift.cachedMessages.count >= 0 && unityIsInitialized())
        {
            for msg in UnityEmbeddedSwift.cachedMessages {
                ufw.sendMessageToGO(withName: msg.objectName, functionName: msg.methodName, message: msg.messageBody)
            }

            UnityEmbeddedSwift.cachedMessages.removeAll()
        }
    }

    private func UnityFrameworkLoad() -> 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 {
            // unity is not initialized
            //            ufw?.executeHeader = &mh_execute_header

            let machineHeader = UnsafeMutablePointer<MachHeader>.allocate(capacity: 1)
            machineHeader.pointee = _mh_execute_header

            ufw!.setExecuteHeader(machineHeader)
        }
        return ufw
    }
}

2.修改AppDelegate.swift

設置UnityEmbeddedSwift所需的窗口和啟動選項

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        UnityEmbeddedSwift.setHostMainWindow(window)
        UnityEmbeddedSwift.setLaunchinOptions(launchOptions)

        return true
    }

3. 創建RootTabBarController.swift

這個類設置層次結構。
在調用UnityEmbeddedSwift.showUnity()后立即使用UnityRootViewController很重要。
Tab-Switching 不是很好,但如果缺少它,Unity 將在加載過程中暫停(或凍結?)。 時間似乎取決於 Unity-Projects 加載時間。 對於小型項目,它可能會更快,而對於大型項目則需要更多時間。

import UIKit

class RootTabBarController: UITabBarController, UITabBarControllerDelegate {

    var unityNC: UINavigationController?
    var nativeNC: UINavigationController?

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate = self

        // start unity and immediatly set as rootViewController
        // this loophole makes it possible to run unity in the same window
        UnityEmbeddedSwift.showUnity()
        let unityViewController = UnityEmbeddedSwift.getUnityRootViewController()!
        unityViewController.navigationItem.title = "Unity"

        unityNC = UINavigationController.init(rootViewController: unityViewController)
        unityNC?.tabBarItem.title = "Unity"

        let nativeViewController = UIViewController.init()
        nativeViewController.view.backgroundColor = UIColor.darkGray
        nativeViewController.navigationItem.title = "Native"

        nativeNC = UINavigationController.init(rootViewController: nativeViewController)
        nativeNC?.tabBarItem.title = "Native"

        viewControllers = [unityNC!, nativeNC!]

        // select other tab and reselect first tab to unfreeze unity-loading
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
            self.selectedIndex = 1

            DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
                self.selectedIndex = 0
            })
        })
    }

    // MARK: - UITabBarControllerDelegate

    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        // pause unity if unity-tab is not selected
        if viewController != unityNC {
            UnityEmbeddedSwift.pauseUnity()
        } else {
            UnityEmbeddedSwift.unpauseUnity()
        }
    }
}

4.修改Main.storyboard

修改故事板以從RootTabBarController開始。

故事板:TabBarController 中的 Unity

對於仍然對防止凍結感興趣的任何人,我都建立在aalmigthy 的回答之上:

不需要在標簽之間添加的TabBar控制器和開關。 您需要做的就是:

  • 將 Unity 視圖添加為子視圖
  • 將子視圖發送回

這是修改后的ViewController類(不需要標簽欄):

import UIKit

class HybridViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        UnityEmbeddedSwift.showUnity()
        
        let uView = UnityEmbeddedSwift.getUnityView()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
            self.view.addSubview(uView!)
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
                self.view.sendSubviewToBack(uView!)
            })
        })
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM