Unity AssetBundle 教程

AssetBundle打包

设置AssetBundle名字

手动设置
打包之前按照上图所示的方法,设置一下AssetBundle的名字。
自动设置
将需要进行AssetBundle打包的图片按照“UI_”的前缀命名,然后根据图片的父目录来设置AssetBundle的名字。如下所示
然后新建一个ImageImporter.cs文件放入Editor目录下
    
    
using UnityEngine ; using UnityEditor ; /// <summary> /// 根据名字前缀自动化设置图片的格式以及TAG等设置 /// </summary> public class ImageImporter : AssetPostprocessor { /// <summary> /// 图片导入之前调用,可设置图片的格式、spritePackingTag、assetBundleName等信息 /// </summary> void OnPreprocessTexture ( ) { TextureImporter importer = ( TextureImporter ) assetImporter ; string path = importer . assetPath ; string [ ] pathArray = importer . assetPath . Split ( '/' ) ; if ( pathArray . Length <= 2 ) { Debug . LogError ( "获取路径名失败" ) ; return ; } string imageName = pathArray [ pathArray . Length - 1 ] ; string packTag = pathArray [ pathArray . Length - 2 ] ; if ( imageName . StartsWith ( "UI_" ) ) { importer . textureType = TextureImporterType . Sprite ; importer . mipmapEnabled = false ; //设置spritePackingTag importer . spritePackingTag = packTag ; //设置assetBundleName importer . assetBundleName = packTag ; } } }
接着执行Reimport操作即可自动设置好图片的AssetBundle的名字。

AssetBundle打包

新建一个QAssetBundleEditor.cs脚本放入Editor文件夹下面,代码如下
    
    
using System . IO ; using UnityEditor ; /// <summary> /// AssetBundle打包脚本Editor /// </summary> public class QAssetBundleEditor { static string OUT_PATH_WIN64 = "AssetBundles/Win64/AssetBundles" ; static string OUT_PATH_IOS = "AssetBundles/IOS/AssetBundles" ; static string OUT_PATH_Android = "AssetBundles/Android/AssetBundles" ; /// <summary> /// BuildWin64 /// </summary> [ MenuItem ( "AssetBundle/BuildWin64" ) ] public static void BuildAssetBundle_Win64 ( ) { BuildAssetBundles ( OUT_PATH_WIN64 , BuildTarget . StandaloneWindows64 ) ; } /// <summary> /// BuildWin64 /// </summary> [ MenuItem ( "AssetBundle/BuildIOS" ) ] public static void BuildAssetBundle_IOS ( ) { BuildAssetBundles ( OUT_PATH_IOS , BuildTarget . iOS ) ; } /// <summary> /// BuildWin64 /// </summary> [ MenuItem ( "AssetBundle/BuildAndroid" ) ] public static void BuildAssetBundle_Android ( ) { BuildAssetBundles ( OUT_PATH_Android , BuildTarget . Android ) ; } public static void BuildAssetBundles ( string outPath , BuildTarget buildTarget ) { if ( Directory . Exists ( outPath ) ) { Directory . Delete ( outPath , true ) ; } Directory . CreateDirectory ( outPath ) ; BuildPipeline . BuildAssetBundles ( outPath , BuildAssetBundleOptions . UncompressedAssetBundle , buildTarget ) ; AssetDatabase . Refresh ( ) ; } }
然后通过Unity的菜单栏进行AssetBundle打包操作,选择“AssetBundle/BuildWin64”打出一个Windows平台使用的AssetBundle。
在Asset同级目录生成了AssetBundles目录
文件如下所示,有一个以文件夹名字“AssetBundles”命名的AssetBundle,可以理解为主AssetBundle,下面的uibackground就是将几张图片打出来的AssetBundle包。
AssetBundles.manifest文件的“AssetBundleInfos”保存了当前打出的所有AssetBundle的文件名称。
uibackground.manifest文件的“Assets”保存了当前打出的AssetBundle里面包含的所有图片名称。

AssetBundle加载

我们将打出来的所有AssetBundle放入Unity的StreamingAssets目录
接下来就可以通过代码加载AssetBundle了。
新建一个QAssetBundleManager.cs文件,代码如下
    
    
using UnityEngine ; using System . Collections . Generic ; public class QAssetBundleManager { static AssetBundle assetbundle = null ; static Dictionary < string , AssetBundle > DicAssetBundle = new Dictionary < string , AssetBundle > ( ) ; public static T LoadResource < T > ( string assetBundleName , string assetBundleGroupName ) where T : Object { if ( string . IsNullOrEmpty ( assetBundleGroupName ) ) { return default ( T ) ; } if ( ! DicAssetBundle . TryGetValue ( assetBundleGroupName , out assetbundle ) ) { assetbundle = AssetBundle . LoadFromFile ( GetStreamingAssetsPath ( ) + assetBundleGroupName ) ; //+ ".assetbundle" DicAssetBundle . Add ( assetBundleGroupName , assetbundle ) ; } object obj = assetbundle . LoadAsset ( assetBundleName , typeof ( T ) ) ; var one = obj as T ; return one ; } public static void UnLoadResource ( string assetBundleGroupName ) { if ( DicAssetBundle . TryGetValue ( assetBundleGroupName , out assetbundle ) ) { assetbundle . Unload ( false ) ; if ( assetbundle != null ) { assetbundle = null ; } DicAssetBundle . Remove ( assetBundleGroupName ) ; Resources . UnloadUnusedAssets ( ) ; } } public static string GetStreamingAssetsPath ( ) { string StreamingAssetsPath = # if UNITY_EDITOR Application . streamingAssetsPath + "/" ; # elif UNITY_ANDROID "jar:file://" + Application . dataPath + "!/assets/" ; # elif UNITY_IPHONE Application . dataPath + "/Raw/" ; # else string . Empty ; # endif return StreamingAssetsPath ; } }
核心代码就是下面这两行代码
    
    
assetbundle = AssetBundle . LoadFromFile ( GetStreamingAssetsPath ( ) + assetBundleGroupName ) ; object obj = assetbundle . LoadAsset ( assetBundleName , typeof ( T ) ) ;
通过AssetBundle.LoadFromFile加载出AssetBundle,然后根据图片的名字从AssetBundle里面取出即可,接口为
    
    
assetbundle . LoadAsset
下面通过一个例子来查看如何使用
我们创建两个按钮,分别用来加载两张图片,按钮的回调函数就是加载出某个AssetBundle(uibackground)下面的某个图片(UI_1002)
    
    
QAssetBundleManager . LoadResource < Sprite > ( "UI_1002" , "uibackground" ) ;
具体代码如下:
    
    
using UnityEngine . UI ; using UnityEngine ; using UnityEngine . Networking ; using System . Collections ; public class QAssetBundleSample : MonoBehaviour { public Button Button_Load ; public Button Button_Load2 ; public Image Image_BackGround ; private void Awake ( ) { Button_Load . onClick . AddListener ( OnClickLoad ) ; Button_Load2 . onClick . AddListener ( OnClickLoad2 ) ; } void OnClickLoad ( ) { Image_BackGround . overrideSprite = QAssetBundleManager . LoadResource < Sprite > ( "UI_1002" , "uibackground" ) ; } void OnClickLoad2 ( ) { Image_BackGround . overrideSprite = QAssetBundleManager . LoadResource < Sprite > ( "UI_1003" , "uibackground" ) ; } }
运行Unity,结果如下

AssetBundle卸载

    
    
assetbundle . Unload ( false ) ; assetbundle . Unload ( true ) ;
Unload(false) 只卸载内存镜像;
Unload(true) 卸载内存镜像以及Asset的内存实例;
一般AssetBundle的卸载是放在切场景的时候,或者低内存的时候,手动调用该接口,执行完Unload之后assetbundle就会被Unity置为null。QAssetBundleManager.cs增加如下函数即可。
    
    
public static void UnLoadResource ( string assetBundleGroupName ) { if ( DicAssetBundle . TryGetValue ( assetBundleGroupName , out assetbundle ) ) { assetbundle . Unload ( false ) ; DicAssetBundle . Remove ( assetBundleGroupName ) ; Resources . UnloadUnusedAssets ( ) ; } }

AssetBundle下载

方式:在登录游戏的时候检查更新,下载对应的AssetBundle到本地。然后运行的时候直接从本地加载AssetBundle,游戏运行中不从网络上下载AssetBundle。

搭建本地服务器

为了测试下载,这里通过nodejs搭建一个本地服务器,将打出来的AssetBundle放入本地服务器中,通过C#去下载即可方便测试。
安装 Node.js 。 在安装完成后再安装 http-server ,命令行输入:
    
    
npm install http - server - g
等待下载完成,我们到AssetBundle生成的目录下,开启命令行,输入
    
    
http - server
即可在当前目录开启服务。
我们在浏览器中输入http://127.0.0.1:8080/即可访问本地的服务器
下面我们开始通过C#代码来下载对应的AssetBundle文件。

UnityWebRequest

我们在界面中增加一个“WebLoad”按钮,当点击按钮的时候从服务器中下载对应的AssetBundle文件,保存到本地之后按照之前的流程加载即可。这里我们使用Unity自带的UnityWebRequest函数来进行下载操作。
先下载主AssetBundle也就是“AssetBundles”文件,该文件中保存了本地所有的AssetBundle的名字以及AssetBundle的依赖关系。然后下载所有的AssetBundle文件同时保存到本地。
    
    
AssetBundle mainAssetBundle = AssetBundle . LoadFromFile ( localPath ) ; if ( mainAssetBundle == null ) yield break ; //获取AssetBundleManifest文件 AssetBundleManifest manifest = mainAssetBundle . LoadAsset < AssetBundleManifest > ( "AssetBundleManifest" ) ; //获取AssetBundleManifest中的所有AssetBundle的名称信息 string [ ] assets = manifest . GetAllAssetBundles ( ) ; for ( int i = 0 ; i < assets . Length ; i ++ ) { Debug . Log ( AssetBundlePath + assets [ i ] ) ; //开启协程下载所有的 StartCoroutine ( DownloadAssetBundleAndSave ( AssetBundlePath , assets [ i ] , ( ) => { //下载完成,按照之前的方法,从本地加载AssetBundle并设置。 Image_BackGround . overrideSprite = QAssetBundleManager . LoadResource < Sprite > ( "UI_1003" , "uibackground" ) ; } ) ) ; }
完整代码如下
    
    
void OnClickWebLoad ( ) { StartCoroutine ( DownloadAssetBundles ( ) ) ; } /// <summary> /// 下载根目录AssetBundle文件 /// </summary> /// <returns></returns> IEnumerator DownloadAssetBundles ( ) { using ( UnityWebRequest www = UnityWebRequest . Get ( AssetBundlePath + MainAssetBundleName ) ) { yield return www . SendWebRequest ( ) ; if ( www . isNetworkError ) { yield break ; } byte [ ] datas = www . downloadHandler . data ; SaveAssetBundle ( MainAssetBundleName , datas ) ; string localPath = QAssetBundleManager . GetApplicationdataPath ( ) + MainAssetBundleName ; AssetBundle mainAssetBundle = AssetBundle . LoadFromFile ( localPath ) ; if ( mainAssetBundle == null ) yield break ; //获取AssetBundleManifest文件 AssetBundleManifest manifest = mainAssetBundle . LoadAsset < AssetBundleManifest > ( "AssetBundleManifest" ) ; //获取AssetBundleManifest中的所有AssetBundle的名称信息 string [ ] assets = manifest . GetAllAssetBundles ( ) ; for ( int i = 0 ; i < assets . Length ; i ++ ) { Debug . Log ( AssetBundlePath + assets [ i ] ) ; //开启协程下载所有的 StartCoroutine ( DownloadAssetBundleAndSave ( AssetBundlePath , assets [ i ] , ( ) => { //下载完成,按照之前的方法,从本地加载AssetBundle并设置。 Image_BackGround . overrideSprite = QAssetBundleManager . LoadResource < Sprite > ( "UI_1003" , "uibackground" ) ; } ) ) ; } } } IEnumerator DownloadAssetBundleAndSave ( string url , string name , Action saveLocalComplate = null ) { WWW www = new WWW ( url + name ) ; yield return www ; if ( www . isDone ) { SaveAssetBundle ( name , www . bytes , saveLocalComplate ) ; } } void SaveAssetBundle ( string fileName , byte [ ] bytes , Action saveLocalComplate = null ) { string path = QAssetBundleManager . GetApplicationdataPath ( ) + fileName ; FileInfo fileInfo = new FileInfo ( path ) ; FileStream fs = fileInfo . Create ( ) ; fs . Write ( bytes , 0 , bytes . Length ) ; fs . Flush ( ) ; fs . Close ( ) ; fs . Dispose ( ) ; if ( saveLocalComplate != null ) { saveLocalComplate ( ) ; } }
运行游戏,结果如下所示。

AssetBundle文件比对

既然能够通过服务器下载AssetBundle了,那么我们需要根据AssetBundle的文件不同来选择是否需要重新下载,这里我们通过文件的MD5来进行文件比对,如果MD5相同就不需要重新下载。这里简单写下思路。首先我们将打好的AssetBundle文件的MD5全部读取出来,然后将AssetBundle名字和对应的MD5写入“FileList.txt”文件中,然后将该文件上传至服务器,根据服务器的文件来和本地的“FileList.txt”进行比对,有MD5不一致的就需要下载,下载完了再覆盖掉本地的“FileList.txt”。当然也可以增加一个“Version.txt”文件,里面用来记录版本号,通过版本号来判断是否进行文件更新。
如何创建这个"FileList.txt"呢?增加两个编辑器函数即可
    
    
/// <summary> /// Create FileList /// </summary> static void CreateFileList ( string outPath ) { string filePath = outPath + FILE_LIST_NAME ; if ( File . Exists ( filePath ) ) { File . Delete ( filePath ) ; } StreamWriter streamWriter = new StreamWriter ( filePath ) ; string [ ] files = Directory . GetFiles ( outPath ) ; for ( int i = 0 ; i < files . Length ; i ++ ) { string tmpfilePath = files [ i ] ; if ( tmpfilePath . Equals ( filePath ) || tmpfilePath . EndsWith ( ".manifest" ) ) continue ; Debug . Log ( tmpfilePath ) ; tmpfilePath . Replace ( "\\" , "/" ) ; streamWriter . WriteLine ( tmpfilePath . Substring ( outPath . Length ) + "|" + GetFileMD5 ( tmpfilePath ) ) ; } streamWriter . Close ( ) ; streamWriter . Dispose ( ) ; AssetDatabase . Refresh ( ) ; } /// <summary> /// 获取文件的MD5 /// </summary> static System . Security . Cryptography . MD5 MD5 = new System . Security . Cryptography . MD5CryptoServiceProvider ( ) ; static System . Text . StringBuilder stringBuilder = new System . Text . StringBuilder ( ) ; static string GetFileMD5 ( string filePath ) { FileStream fileStream = new FileStream ( filePath , FileMode . Open ) ; byte [ ] bytes = MD5 . ComputeHash ( fileStream ) ; fileStream . Close ( ) ; for ( int i = 0 ; i < bytes . Length ; i ++ ) { stringBuilder . Append ( bytes [ i ] . ToString ( "x2" ) ) ; } return stringBuilder . ToString ( ) ; }
名字和MD5之间通过“|”分隔即可。

欢迎Star。如果本文对你有所帮助,欢迎赞赏~~~

《游戏AI程序设计实战》作者 微信公众号:Unity游戏开发笔记 Github:https://github.com/onelei
优快云:https://blog.youkuaiyun.com/onelei1994
QQ交流群:754814245 欢迎支持我的新书《游戏AI程序设计实战》 https://item.jd.com/12520141.html
内容概要:本文详细介绍了一个基于Java和Vue的联邦学习隐私保护推荐系统的设计与实现。系统采用联邦学习架构,使用户数据在本地完成模型训练,仅上传加密后的模型参数或梯度,通过中心服务器进行联邦平均聚合,从而实现数据隐私保护与协同建模的双重目标。项目涵盖完整的系统架构设计,包括本地模型训练、中心参数聚合、安全通信、前后端解耦、推荐算法插件化等模块,并结合差分隐私与同态加密等技术强化安全性。同时,系统通过Vue前端实现用户行为采集与个性化推荐展示,Java后端支撑高并发服务与日志处理,形成“本地训练—参数上传—全局聚合—模型下发—个性化微调”的完整闭环。文中还提供了关键模块的代码示例,如特征提取、模型聚合、加密上传等,增强了项目的可实施性与工程参考价值。 适合人群:具备一定Java和Vue开发基础,熟悉Spring Boot、RESTful API、分布式系统或机器学习相关技术,从事推荐系统、隐私计算或全栈开发方向的研发人员。 使用场景及目标:①学习联邦学习在推荐系统中的工程落地方法;②掌握隐私保护机制(如加密传输、差分隐私)与模型聚合技术的集成;③构建高安全、可扩展的分布式推荐系统原型;④实现前后端协同的个性化推荐闭环系统。 阅读建议:建议结合代码示例深入理解联邦学习流程,重点关注本地训练与全局聚合的协同逻辑,同时可基于项目架构进行算法替换与功能扩展,适用于科研验证与工业级系统原型开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值