using UnityEditor; using System.IO; /// <summary> /// /// </summary> public class AssetBundleEditor { //1.編譯階段插件聲明 [MenuItem("Assets/Build AssetBundles")] static void BuildAssetBundles() { string dir = "AssetBundles"; if (!Directory.Exists(dir)) { //2.在工程根目錄下創建dir目錄 Directory.CreateDirectory(dir); } //3.構建AssetBundle資源,AB資源包是一個壓縮文件,可以把它看成是一個壓縮的文件夾,裏面 //可能包含多個文件,預製件,材質,貼圖,聲音。 BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.iOS); } }
設置需要打包的資源
/// <summary> ///讀取原生沙盒Documents/AssetsBundle目錄下的文件,Documents/AssetsBundle下的文件通過Native原生下載的資源 /// </summary> /// <param name="abName">Documents/AssetsBundle下的ab文件</param> /// <returns>讀取到的字符串</returns> public static AssetBundle GetNativeAssetFromDocumentsOnProDownLoad(string abName) { string localPath = ""; if (Application.platform == RuntimePlatform.Android) { localPath = "jar:file://" + Application.persistentDataPath + "/AssetsBundle/" + abName; } else { localPath = "file://" + Application.persistentDataPath + "/AssetsBundle/" + abName; } UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(localPath); var operation = request.SendWebRequest(); while (!operation.isDone) { } if (request.result == UnityWebRequest.Result.ConnectionError) { Debug.Log(request.error); return null; } else { AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); return assetBundle; } //UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(localPath); //yield return request.Send(); //AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); //return assetBundle; }
注意:當離開Unity容器時需要卸載裏面加載的ab包
public void TestUnLoadGameObject() { UnLoadGameObjectWithTag("NFT"); } public void UnLoadGameObjectWithTag(string tagName) { GameObject go = GameObject.FindWithTag(tagName); if (go) { Destroy(go, 0.5f); } else { Debug.Log(go); } } public void UnLoadAllGameObjectWithTag(string tagName) { GameObject[] gos = GameObject.FindGameObjectsWithTag(tagName); foreach (GameObject go in gos) { Destroy(go, 0.5f); } }
public void TestLoadStreamingAssetBundle() { LoadStreamingAssetBundleWithABName("cube.ab", "Cube", "NFT"); } public void LoadStreamingAssetBundleWithABName(string abName, string gameObjectName, string tagName) { AssetBundle ab = FileUtility.GetNativeAssetFromStreamingAssets(abName); GameObject profab = ab.LoadAsset<GameObject>(gameObjectName); profab.tag = tagName; Instantiate(profab); GestureController gc = GameObject.FindObjectOfType<GestureController>(); gc.target = profab.transform; ab.Unload(false); }
Unity場景切換的腳本實現:
//接收原生事件:切換場景 public void SwitchScene(string parmas) { Debug.Log(parmas); Param param = new Param(); Param res = JsonDataContractJsonSerializer.JsonToObject(parmas, param) as Param; Debug.Log(res.name); Debug.Log("------------"); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); Debug.Log(scene.name); } SceneManager.LoadScene(res.name, LoadSceneMode.Single); Debug.Log("------------"); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); Debug.Log(scene.name); } }
#import <Foundation/Foundation.h> @protocol NativeCallsProtocol @required /// Unity調用原生 /// - Parameter params: {"FeatureName":"下載資源", "params": "參數"} - (void)callNative:(NSString *)params; @end __attribute__ ((visibility("default"))) @interface NativeCallProxy : NSObject // call it any time after UnityFrameworkLoad to set object implementing NativeCallsProtocol methods + (void)registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi; @end
NativeCallProxy.mm文件實現如下:
#import "NativeCallProxy.h" @implementation NativeCallProxy id<NativeCallsProtocol> api = NULL; + (void)registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi { api = aApi; } @end extern "C" { void callNative(const char * value); } void callNative(const char * value){ return [api callNative:[NSString stringWithUTF8String:value]]; }
原生的Delegate的實現
#pragma mark - NativeCallsProtocol - (void)callNative:(NSString *)params { NSLog(@"收到Unity的調用:%@",params); }
//重要聲明,聲明在iOS原生中存在下面的方法,然後C#中可以直接進行調用 [DllImport("__Internal")] static extern void callNative(string value); public void changeLabel(string textString) { tmpText.text = textString; } public void btnClick() { Debug.Log(tmpInput.text); callNative(tmpInput.text); }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class Param { public string packageName { get; set; } public string name { get; set; } public string tag { get; set; } public string type { get; set; } public string isAll { get; set; } } public class DispatchGO : MonoBehaviour { //接收原生事件 public void DispatchEvent(string parmas) { Debug.Log(parmas); //事件分發 ChangeLabel cl = GameObject.FindObjectOfType<ChangeLabel>(); cl.changeLabel(parmas); } //接收原生事件:加載模型 public void LoadModel(string parmas) { Debug.Log(parmas); Param param = new Param(); Param res = JsonDataContractJsonSerializer.JsonToObject(parmas, param) as Param; Debug.Log(res.packageName); Debug.Log(res.name); Debug.Log(res.tag); Debug.Log(res.type); if (res.type == "0") { LoadAssetUtility laUnity = GameObject.FindObjectOfType<LoadAssetUtility>(); laUnity.LoadStreamingAssetBundleWithABName(res.packageName, res.name, res.tag); } else { LoadAssetUtility laUnity = GameObject.FindObjectOfType<LoadAssetUtility>(); laUnity.LoadNativeAssetBundleWithABName(res.packageName, res.name, res.tag); } } //接收原生事件:卸載模型 public void UnLoadModel(string parmas) { Debug.Log(parmas); Param param = new Param(); Param res = JsonDataContractJsonSerializer.JsonToObject(parmas, param) as Param; UnLoadAssetUtility unLAUnity = GameObject.FindObjectOfType<UnLoadAssetUtility>(); if (res.isAll == "1") { unLAUnity.UnLoadAllGameObjectWithTag(res.tag); } else { unLAUnity.UnLoadGameObjectWithTag(res.tag); } } //接收原生事件:切換場景 public void SwitchScene(string parmas) { Debug.Log(parmas); Param param = new Param(); Param res = JsonDataContractJsonSerializer.JsonToObject(parmas, param) as Param; Debug.Log(res.name); Debug.Log("------------"); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); Debug.Log(scene.name); } SceneManager.LoadScene(res.name, LoadSceneMode.Single); Debug.Log("------------"); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); Debug.Log(scene.name); } } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } }
在iOS原生側,本地通過使用unityFramework的sendMessageToGOWithName方法從原生想Unity發送消息。
case 103: { NSDictionary *params = @{ @"tag":@"NFT", @"isAll":@"1" }; [ad.unityFramework sendMessageToGOWithName:"DispatchGO" functionName:"UnLoadModel" message:[self serialJsonToStr:params]]; } break; case 104: { NSDictionary *params = @{ @"name":@"DemoScene" }; [ad.unityFramework sendMessageToGOWithName:"DispatchGO" functionName:"SwitchScene" message:[self serialJsonToStr:params]]; } break;
Unity通過調用iOS中協議聲明的方法void callNative(string value); 進行調用。
//重要聲明,聲明在iOS原生中存在下面的方法,然後C#中可以直接進行調用 [DllImport("__Internal")] static extern void callNative(string value); public void btnClick() { Debug.Log(tmpInput.text); callNative(tmpInput.text); }
原生端創建Unity容器
@implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [UnitySceneManager sharedInstance].launchOptions = launchOptions; [[UnitySceneManager sharedInstance] Init]; return YES; }
UnitySceneManager的主要實現邏輯如下:#import "UnitySceneManager.h"#import <UnityFramework/NativeCallProxy.h>
extern int argcApp; extern char ** argvApp; @interface UnitySceneManager()<UnityFrameworkListener, NativeCallsProtocol> @end @implementation UnitySceneManager #pragma mark - Life Cycle + (instancetype)sharedInstance { static UnitySceneManager *shareObj; static dispatch_once_t onceKey; dispatch_once(&onceKey, ^{ shareObj = [[super allocWithZone:nil] init]; }); return shareObj; } + (instancetype)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } - (instancetype)copyWithZone:(struct _NSZone *)zone { return self; } #pragma mark - Private Method - (void)Init { [self initUnityFramework]; [NativeCallProxy registerAPIforNativeCalls:self]; } - (void)unloadUnityInternal { if (self.unityFramework) { [self.unityFramework unregisterFrameworkListener:self]; } self.unityFramework = nil; } - (BOOL)unityIsInitialized { return (self.unityFramework && self.unityFramework.appController); } // MARK: overwrite #pragma mark - Public Method - (void)initUnityFramework { UnityFramework *unityFramework = [self getUnityFramework]; self.unityFramework = unityFramework; [unityFramework setDataBundleId:"com.zhfei.framework"]; [unityFramework registerFrameworkListener:self]; [unityFramework runEmbeddedWithArgc:argcApp argv:argvApp appLaunchOpts:self.launchOptions]; } - (UnityFramework *)getUnityFramework { NSString* bundlePath = nil; bundlePath = [[NSBundle mainBundle] bundlePath]; bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"]; NSBundle* bundle = [NSBundle bundleWithPath: bundlePath]; if ([bundle isLoaded] == false) [bundle load]; UnityFramework* ufw = [bundle.principalClass getInstance]; if (![ufw appController]) { // unity is not initialized [ufw setExecuteHeader: &_mh_execute_header]; } return ufw; } #pragma mark - Event #pragma mark - Delegate #pragma mark - UnityFrameworkListener - (void)unityDidUnload:(NSNotification*)notification { } - (void)unityDidQuit:(NSNotification*)notification { } #pragma mark - NativeCallsProtocol - (void)callNative:(NSString *)params { NSLog(@"收到Unity的調用:%@",params); } #pragma mark - Getter, Setter #pragma mark - NSCopying #pragma mark - NSObject #pragma mark - AppDelegate生命週期綁定 - (void)applicationWillResignActive { [[self.unityFramework appController] applicationWillResignActive: [UIApplication sharedApplication]]; } - (void)applicationDidEnterBackground { [[self.unityFramework appController] applicationDidEnterBackground: [UIApplication sharedApplication]]; } - (void)applicationWillEnterForeground { [[self.unityFramework appController] applicationWillEnterForeground: [UIApplication sharedApplication]]; } - (void)applicationDidBecomeActive { [[self.unityFramework appController] applicationDidBecomeActive: [UIApplication sharedApplication]]; } - (void)applicationWillTerminate { [[self.unityFramework appController] applicationWillTerminate: [UIApplication sharedApplication]]; } @end
#import "UnityContainerViewController.h" #import "UnitySceneManager.h" @interface UnityContainerViewController () @end @implementation UnityContainerViewController #pragma mark - Life Cycle - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self setupUI]; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; UnitySceneManager *ad = [UnitySceneManager sharedInstance]; ad.unityFramework.appController.rootView.frame = self.view.bounds; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; UnitySceneManager *ad = [UnitySceneManager sharedInstance]; [ad.unityFramework pause:NO]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; UnitySceneManager *ad = [UnitySceneManager sharedInstance]; [ad.unityFramework pause:YES]; } #pragma mark - Private Method - (void)setupUI { self.view.backgroundColor = [UIColor whiteColor]; UnitySceneManager *ad = [UnitySceneManager sharedInstance]; UIView *rootView = ad.unityFramework.appController.rootView; rootView.frame = [UIScreen mainScreen].bounds; [self.view addSubview:rootView]; [self.view sendSubviewToBack:rootView]; }