unity3d unity5.0 unity加载assetbundleemanifest包含哪些数据

【讨论】unity3d 5.0的assetbundle增量更新体现在哪里?_unity3d吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:76,334贴子:
【讨论】unity3d 5.0的assetbundle增量更新体现在哪里?收藏
如题:新人发帖,只为求知,欢迎捧场,不胜感激.
达内unity3d培训全程&实战教学&,unity3d金牌讲师授课.免费unity3d课程试听中!到达内unity3d学院学习unity3d,只需4个月速成unity3d游戏工程师.
@xuiopp321@夕影之剑
@懒懒得木马 @甲斐苍龙@AWNUXCVBN @cheng219101还有大家都一起来讨论啊
看来没人先自己撸撸关于unity5.0的优点有以下几个:1、可以直接在编辑器UI上设置操作2、提供了更简便的脚本API3、Unity本身会处理所有的依赖关系4、生成了一种叫做manifest的文件,用于记录资源之间的依赖关系,并以链式结构记录,修改时只需修改链的其中一环5、增量打包功能。
第一点:可以直接在编辑器UI上设置操作通过编辑面板中的New... 和 Remove Unused names可以进行assetbundleName 的 设置
第二点:提供了更简便的脚本API这点针对打包而言,只需要一行代码就可以搞定BuildPipeline.BuildAssetBundles(&Assets/AssetBundle/PC&, BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.StandaloneWindows);相比以前的版本而言,确实是简单多了
第三点:Unity本身会处理所有的依赖关系首先,在unity中,所有的asset在生成的那一刻起,都会在assetdatabase中有依赖信息保存,然后在打包的时候,unity根据这个类 : 1.相同assetbundlename的资源打包进一个assetbundle中 2.每一个assetbundle都对应有一个 manifest文件,此文件记录了 一些信息,最重要的就是 资源依赖信息,这个依赖信息可以定位到依赖资源所在的bundle名3.额外生成一个bundle以及对应的manifest文件。这个bundle名称是你所在的文件夹名称里边有所有打成bundle的依赖信息
unity参考中Mathf.PingPong 描述为让数值t在 0到length之间往返。并给了一个例子transform.position = Vector3(Mathf.PingPong(Time.time, 3), transform.position.y, transform.position.z);表示设置物体x位置在0和3之间循环,请教下为什么用Time.time,它不是表示从游戏开始到到当前所用的时间吗,而且是只读的,想好久也没想明白楼主求教   -这是他的小尾巴,只要咬掉头,应该能吃掉整个身子
登录百度帐号推荐应用温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
2.新建BuildAssetBundlScript.cs脚本,脚本中加入如下函数,编译完成后,可看到Unity3d顶部菜单多了【AssetBundle/BuildAssetBundle】菜单。[MenuItem("AssetBundle/BuildAssetBundle")] public static void CreateAssetBundle() {
BuildPipeline.BuildAssetBundles("Assets\\StreamingAssets\\AssetsResources", BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
//刷新编辑器
//AssetDatabase.Refresh(); }3.在场景中创建一个空实体【GameObject】【Create Empty】,命名为MyFirstObject在这个实体下创建一个Cube和Sphere,分别命名为MyCube和MySphere。4.在Project视图中,在Assets目录下创建文件夹Prefab,将MyFirstObject,拖主Prefab文件夹,完成MyFirstObject.prefab预设体的创建。5.在Project视图中,选中MyFirstObject.prefab,在【Inspector】面板最下方输入AssetBundle的名称myfirstobj(为了区别起见专门与MyFirstObject不一样),回车完成输入,右边的那个暂时不用管。6.在Project视图中,新建文件夹【Assets\StreamingAssets\AssetsResources】,点击菜单【AssetBundle/BuildAssetBundle】完成AssetBundle打包,可以看到在Assets\StreamingAssets\AssetsResources路径下多了四个文件AssetsResources、AssetsResources.manifest、myfirstobj、myfirstobj.manifest。7.新建一个场景,在场景中新建一个名为LoadTest的空实体,给这个实体添加脚本LoadBuildAssetBundle.cs,在脚中加入如下函数。运行程序,点击【Load Assetbundle】按钮,可看到有预设体被创建。void OnGUI() {
if(GUILayout.Button("Load Assetbundle"))
//加载AssetBundle
AssetBundle assetBundle = AssetBundle.LoadFromFile("Assets\\StreamingAssets\\AssetsResources\\myfirstobj");
if(assetBundle == null)
Debug.Log("assetBundle load success!");
//加载预设体并实体化
Instantiate(assetBundle.LoadAsset("MyFirstObject"));
string[] names = assetBundle.GetAllAssetNames();
foreach (var elem in names)
Debug.Log("Asset name is " + elem);
Object[] objs = assetBundle.LoadAllAssets();
foreach (var elem in objs)
Debug.Log("Obj is " + elem.name);
Object[] objsWithSub = assetBundle.LoadAssetWithSubAssets("Obj1");
foreach (var elem in objsWithSub)
Debug.Log("Obj with sub assets is " + elem.name);
//卸载AssetBundle
assetBundle.Unload(true);
阅读(520)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Unity3d5.0打包和读取AssetBundle',
blogAbstract:'1.新建工程。2.新建BuildAssetBundlScript.cs脚本,脚本中加入如下函数,编译完成后,可看到Unity3d顶部菜单多了【AssetBundle/BuildAssetBundle】菜单。[MenuItem(\"AssetBundle/BuildAssetBundle\")]\tpublic static void CreateAssetBundle()\t{\t\tBuildPipeline.BuildAssetBundl',
blogTag:'unity,assetbundle',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:2,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
& &打包和原来一样:在Editor下创建.CS脚本
const string outputDir = "Assets/StreamingAssets";
[MenuItem("AssetBundlePre/Build")]
static void BuildAssetResource()
string plat = GetPlat(EditorUserBuildSettings.activeBuildTarget);
string outputPath = bine(outputDir,plat);
Debug.Log(outputPath);
if (!Directory.Exists(outputPath))
Directory.CreateDirectory(outputPath);
BuildPipeline.BuildAssetBundles(outputPath, 0, EditorUserBuildSettings.activeBuildTarget);
static string GetPlat(BuildTarget tag)
switch (tag)
case BuildTarget.Android:
return "Android";
case BuildTarget.StandaloneWindows:
return "Windows";
case BuildTarget.StandaloneWindows64:
return "Windows";
case BuildTarget.iOS:
return "IOS";
return "Windows";
}加载: #region variable
string manisfeas=#if UNITY_ANDROID "Android";#elif UNITY_IPHONE
"IOS";#elif UNITY_STANDALONE_WIN||UNITY_EDITOR
"Windows";#else
"Windows";#endif
public static readonly string PathURL =#if UNITY_ANDROID
"jar:file://"+Application.dataPath+"!/assets/Android/";#elif UNITY_IPHONE
Application.dataPath+"/Raw/IOS/";#elif UNITY_STANDALONE_WIN||UNITY_EDITOR "file://" + Application.streamingAssetsPath + "/Windows/";#else
string.E#endif
Dictionary&string, WWW& DicAllBundle = new Dictionary&string, WWW&();
# endregion这个是按平台获取,貌似写的有点二,不过不要在意这些细节!!WWW加载AssetBundle我这边是先是预加载的Bundle,当然也可以用的时候加载,这个随便,我就随便写下。 void Awake()
StartCoroutine(LoadAssetsInit());
}IEnumerator LoadAssetsInit()
//获得总的Asste
WWW mwww = WWW.LoadFromCacheOrDownload(PathURL + manisfeas, 0);
if (!string.IsNullOrEmpty(mwww.error))
Debug.LogError(mwww.error);
AssetBundle map = mwww.assetB
//拿到AssetBundleManifest
AssetBundleManifest mainifest = (AssetBundleManifest)map.LoadAsset("AssetBundleManifest");
map.Unload(false);
//从总的集合里面寻找要加载的BUNDLE的所有资源名字
string[] allBundelename = mainifest.GetAllAssetBundles();
for (int i = 0; i & allBundelename.L i++)
string dUrl = PathURL + allBundelename[i];
//开大所有资源路径的bundle添加到临时数组内
将所有bundle打开
WWW dwww = WWW.LoadFromCacheOrDownload(dUrl, mainifest.GetAssetBundleHash(allBundelename[i]));
if (!string.IsNullOrEmpty(dwww.error))
Debug.Log(dwww.error);
if (!DicAllBundle.ContainsKey(allBundelename[i]))
DicAllBundle.Add(allBundelename[i], dwww);
好了该加载的先加载好了,下载去拿里面的东西 void Update()
if (Input.GetMouseButtonDown(0))
Load("2.unity3d", "2");
}我是放在Update里面这样方便点。因为我是先加载了WWW public bool Load(string assetBundleName, string assetName)
bool isDone=
StartCoroutine(LoadAsset(assetBundleName, assetName, isDone));
return isD
} IEnumerator LoadAsset(string assetBundleName, string assetName,bool isDone)
while (!IsDone)
yield return 0;
//异步加载需要BUNDLE
if (DicAllBundle.ContainsKey(assetBundleName))
WWW www = DicAllBundle[assetBundleName];
AssetBundle asstbundle = www.assetB
GameObject obj = asstbundle.LoadAsset(assetName) as GameO
if (obj != null)
Instantiate(obj);
asstbundle.Unload(false);
Debug.Log("this assetname is error");
yield return isDone =
//CloseOneBundle(assetBundleName);
}我这边是没有进行关闭的;所有关闭我也是调用的; public
void CloseAllAssetBundle()
List&string& a = new List&string&(DicAllBundle.Keys);
for (int i = 0; i & a.C i++)
if(DicAllBundle[a[i]].assetBundle!=null)
DicAllBundle[a[i]].assetBundle.Unload(false);
public void CloseOneBundle(string assetBundleName)
if (DicAllBundle.ContainsKey(assetBundleName) && DicAllBundle[assetBundleName].assetBundle != null)
DicAllBundle[assetBundleName].assetBundle.Unload(false);
Debug.Log("this assetname is inexistence or this asser is close");
void Clear()
StopAllCoroutines();
}So &这样就完成了一套加载。自己完善吧;
阅读(918)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'Unity_AssetBundle 5.x 打包和加载(简化)',
blogAbstract:'& & 在开始的时候也写过人家的一个加载打包的工具,不过感觉太多了。而且要求也有。所又写了个简单的。& &打包和原来一样:在Editor下创建.CS脚本
const string outputDir = \"Assets/StreamingAssets\";
[MenuItem(\"AssetBundlePre/Build\")]
static void BuildAssetResource()
string plat = GetPlat(EditorUserBuildSettings.activeBuildTarget);
string outputPath = bine(outputDir,plat);
Debug.Log(outputPath);',
blogTag:'unity',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:7,
publishTime:7,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}Unity3D 5.3 新版AssetBundle使用方案及策略 - 知乎专栏
{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"contributes":[{"sourceColumn":{"lastUpdated":,"description":"编程首先是爱好,其次才是职业。专注前沿技术,热爱开源。深信代码改变世界。没有值得吹嘘的项目,只有不断前行的动力。写有个人博客“嘉栋的Coding Blog”: http://www.chenjd.me","permission":"COLUMN_PUBLIC","memberId":1718122,"contributePermission":"COLUMN_PUBLIC","translatedCommentPermission":"all","canManage":true,"intro":"编程和游戏首先是爱好,其次才是职业。","urlToken":"chenjiadong","id":8818,"imagePath":"v2-c4e393a91e7abcd.png","slug":"chenjiadong","applyReason":"","name":"Runtime","title":"Runtime","url":"/chenjiadong","commentPermission":"COLUMN_ALL_CAN_COMMENT","canPost":true,"created":,"state":"COLUMN_NORMAL","followers":2113,"avatar":{"id":"v2-c4e393a91e7abcd","template":"/{id}_{size}.png"},"activateAuthorRequested":false,"following":false,"imageUrl":"/v2-c4e393a91e7abcd_l.png","articlesCount":47},"state":"accepted","targetPost":{"titleImage":"/5d419ed54b09bada8dce1bc_r.jpg","lastUpdated":,"imagePath":"5d419ed54b09bada8dce1bc.jpg","permission":"ARTICLE_PUBLIC","topics":[,1045],"summary":"1.概览Unity3D 5.0版本之后的AssetBundle机制和之前的4.x版本已经发生了很大的变化,一些曾经常用的流程已经不再使用,甚至一些老的API已经被新的API所取代。 \n因此,本文的主要内容就是分析5.X版本的AssetBundle机制(包括创建资源包、压缩资源包、加载资…","copyPermission":"ARTICLE_COPYABLE","translatedCommentPermission":"all","likes":0,"origAuthorId":0,"publishedTime":"T12:22:28+08:00","sourceUrl":"","urlToken":,"id":767198,"withContent":false,"slug":,"bigTitleImage":false,"title":"Unity3D 5.3 新版AssetBundle使用方案及策略","url":"/p/","commentPermission":"ARTICLE_ALL_CAN_COMMENT","snapshotUrl":"","created":,"comments":0,"columnId":8818,"content":"","parentId":0,"state":"ARTICLE_PUBLISHED","imageUrl":"/5d419ed54b09bada8dce1bc_r.jpg","author":{"bio":"慕容小匹夫 微软MVP《Unity 3D脚本编程 》作者 公众号chenjd01","isFollowing":false,"hash":"2ad398c4fbe830","uid":88,"isOrg":false,"slug":"jiadongchen","isFollowed":false,"description":"编程首先是爱好,其次才是职业。http://www.chenjd.me/\n\n我已加入“维权骑士”()的版权保护计划。","name":"陈嘉栋","profileUrl":"/people/jiadongchen","avatar":{"id":"v2-e3e4f2a706dc7ec9d2866d2aba059c0d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"memberId":1718122,"excerptTitle":"","voteType":"ARTICLE_VOTE_CLEAR"},"id":387237}],"title":"Unity3D 5.3 新版AssetBundle使用方案及策略","author":"jiadongchen","content":"1.概览Unity3D 5.0版本之后的AssetBundle机制和之前的4.x版本已经发生了很大的变化,一些曾经常用的流程已经不再使用,甚至一些老的API已经被新的API所取代。\n因此,本文的主要内容就是分析5.X版本的AssetBundle机制(包括创建资源包、压缩资源包、加载资源包和从资源包中加载/卸载资源等几个方面)及其关键的API使用方式并总结一些对项目的建议(例如根据不同的情景,选择不同的包体加载方案等等)。2.AssetBundle系统的新功能本小节包括:AssetBundle系统的新功能新的AssetBundle系统的优势2.1.AssetBundle系统的新功能在新的AssetBundle系统中,出现了以下的新功能:通过Editor中的UI即可方便的为AssetBundle标记资源。而且一个资源和对应的AssetBundle的映射将会在资源数据库(AssetDatabase)中被创建。在箭头处即可指定该资源所述的AssetBundle,第一个选项为AssetBundle的名字,而后一个选项则是为AssetBundle创建变体,\n例如一些素材需要区分为高清或普通存放在不同的AssetBundle中,那么第二选项就可以以hd和normal来区分。在箭头处即可指定该资源所述的AssetBundle,第一个选项为AssetBundle的名字,而后一个选项则是为AssetBundle创建变体,\n例如一些素材需要区分为高清或普通存放在不同的AssetBundle中,那么第二选项就可以以hd和normal来区分。提供了新的API用来设置资源所属的AssetBundle:设置AssetImporter.assetBundleName的值,即可为该资源指定它所属的AssetBundle。上文中在UI中设置的AssetBundle的名字便是为该值赋值,在资源有了assetBundleName之后,实际上它的信息就已经存在于AssetDataBase里面了。新版本中,创建AssetBundle文件的API变得十分简单了:BuildPipeline.BuildAssetBundles():我们只需要提供一个输出AssetBundle的地址即可。引擎将自动根据资源的assetbundleName属性(即在上文中UI中设置的值)批量打包,自动建立Bundle以及资源之间的依赖关系。新增了一些打包策略/选项,且一些4.x中的旧有策略被默认开启。CompleteAssets ,用于保证资源的完备性,默认开启;CollectDependencies,用于收集资源的依赖项,默认开启;DeterministicAssetBundle,用于为资源维护固定ID,默认开启;ForceRebuildAssetBundle,用于强制重打所有AssetBundle文件,新增;IgnoreTypeTreeChanges,用于判断AssetBundle更新时,是否忽略TypeTree的变化,新增;AppendHashToAssetBundleName,用于将Hash值添加在AssetBundle文件名之后,开启这个选项可以直接通过\n文件名来判断哪些Bundle的内容进行了更新(4.x下普遍需要通过比较二进制等方法来判断,但在某些情况下即使内容不变重新打包,Bundle的二进\n制也会变化),新增。ChunkBasedCompression,用于使用LZ4格式进行压缩,5.3新增。Manifest文件。在4.x版本中,我们通常需要自行维护配置文件,以记录AssetBundle之间的依赖关系,并供运行时使用。而在5.x版本中,使用Manifest文件可以免去4.x版本中的这一过程。而Manifest文件分为两种:单个bundle的Manifest文件,一旦一个新的AssetBundle文件被创建导出,便会对应生成一个.manifest文件,其中包含了校验、依赖文件等信息。所以可以用来做增量更新。实际上在打包的时候,在输出的bundle所在的文件夹内还会生成一个总的manifest文件,叫做[文件夹名].manifest。它包含了\n该文件夹内所有的bundle的信息,以及它们之间互相依赖的信息。所以在我们加载bundle的时候,需要先把总的manifest文件加载进来,以确\n认各个bundle之间的依赖关系。一些在运行时动态加载AssetBundle的API被新的API代替。4.x版本中的AssetBundle.CreateFromFile方法,在5.x版本中变成了AssetBundle.LoadFromFile方法。4.x版本中的AssetBundle.CreateFromMemory方法,在5.x版本中变成了LoadFromMemoryAsync方法。4.x版本中的AssetBundle.CreateFromMemoryImmediate方法,在5.x版本中变成了LoadFromMemory方法。2.2.新的AssetBundle系统的优势由于引擎提供的这些新功能,我们就不再需要像4.x时代那么复杂的用来打包的脚本了。\n同时,资源之间的互相依赖关系不再需要开发者手动维护了,曾经由于不当使用PushAssetDependencies/PopAssetDependencies而可能会造成依赖出现的问题,现在Unity3D已经为我们解决了。\n而且由于引入了清单文件manifest,因此我们可以实现增量更新,即只需要更新有变化的部分,而没有变化的则不必更新。\n举一个例子:\n假设我们有一个cube,它的material有一个材质,我们分别将cube和material打包成cubeBundle和\nmaterialBundle,之后我们修改material上的材质。在过去,我们需要分别重新为cube和material打包,而现在只需要对\nmaterial重新打包即可,cube不受影响。3.AssetBundle文件的创建本小节包括:旧有创建AssetBundle文件的API新的创建AssetBundle文件的API针对项目的建议3.1.旧有创建AssetBundle文件的API在4.x时代,最常用的AssetBundle打包方法主要包括以下两个:BuildPipeline.BuildAssetBundle\n对除Scene以外的资源打包,支持单个和多个资源,需要在方法的参数中指明需要被打入AssetBundle的资源;BuildPipeline.BuildStreamedSceneAssetBundle\n对Scene文件打包,也支持单个和多个。且在4.x时代,打包还需要注意资源之间互相依赖的问题。为了避免资源冗余,同时提高资源加载和卸载的灵活性,因此依赖性打包的重要性不言而喻。老版本中,我们可以使用以下两个方法来实现这种依赖性:BuildPipeline.PushAssetDependenciesBuildPipeline.PopAssetDependencies这种机制并不难理解,简单的说PushAssetDependencies是将资源进栈,PopAssetDependencies是让资源出栈,\n每打一个包,引擎都会检查当前栈中所有的依赖项,查看是否有相同资源已经在栈中。如有,则与其相关的AssetBundle建立依赖关系。3.2.新的创建AssetBundle文件的API在新版本中,Unity3D为我们提供了唯一的API用来打AssetBundle包。即:BuildPipeline.BuildAssetBundles在脚本中调用BuildPipeline.BuildAssetBundles,U3D将自动根据资源的assetbundleName属性批量打包,自动建立Bundle和资源之间的依赖关系。\n在资源的Inpector界面最下方可设置该资源的assetbundleName,每个assetbundleName对应一个Bundle,即assetbundleName相同的资源会打在一个Bundle中。\n如果所依赖的资源设置了不同的assetbundleName,则会自动与之建立依赖关系,避免出现冗余,从而减小Bundle包的大小。\n当然,除了可以指定assetbundleName,我们还可以在Inpector中设置另一个名字,即variant。在打包时,variant会作为\n后缀添加在assetbundleName之后。相同assetbundleName,不同variant的Bundle是可以相互替换的。设置好之后,我们只需要创建一个新的脚本,通过编辑器拓展调用BuildPipeline.BuildAssetBundles方法即可:设置好之后,我们只需要创建一个新的脚本,通过编辑器拓展调用BuildPipeline.BuildAssetBundles方法即可:using UnityE\n\npublic class CreateAssetBundles\n{\n
[MenuItem (\"Assets/Build AssetBundles\")]\n
static void BuildAllAssetBundles ()\n
BuildPipeline.BuildAssetBundles (\"Assets/AssetBundles\");\n
}\n}\nBuildPipeline.BuildAssetBundles方法的参数为bundle的导出目录。当然它有很多重载的版本,可以提供额外的参数来定制符合自己需求的AssetBundle。3.3.针对项目的建议虽然新的AssetBundle简化了打包和处理资源依赖的过程,但是却引入了一个新的复杂度,即需要设置资源的assetbundleName以实现打包的功能。\n因此我们可能需要做的是:提供脚本批量对资源设置assetbundleName规划好assetBundle所对应的资源类型,规划好assetBundle的数量4.AssetBundle的压缩本小节包括:AssetBundle的压缩类型针对项目的建议4.1.AssetBundle的压缩类型Unity3D引擎为我们提供了三种压缩策略来处理AssetBundle的压缩,即:LZMA格式LZ4格式不压缩LZMA格式:\n在默认情况下,打包生成的AssetBundle都会被压缩。在U3D中,AssetBundle的标准压缩格式便是LZMA(LZMA是一种序列化流文\n件),因此在默认情况下,打出的AssetBundle包处于LZMA格式的压缩状态,在使用AssetBundle前需要先解压缩。\n使用LZMA格式压缩的AssetBundle的包体积最小(高压缩比),但是相应的会增加解压缩时的时间。LZ4格式:\nUnity 5.3之后的版本增加了LZ4格式压缩,由于LZ4的压缩比一般,因此经过压缩后的AssetBundle包体的体积较大(该算法基于chunk)。但是,使用LZ4格式的好处在于解压缩的时间相对要短。\n若要使用LZ4格式压缩,只需要在打包的时候开启BuildAssetBundleOptions.ChunkBasedCompression即可。BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, \n
BuildAssetBundleOptions.ChunkBasedCompression);\n不压缩:\n当然,我们也可以不对AssetBundle进行压缩。没有经过压缩的包体积最大,但是访问速度最快。\n若要使用不压缩的策略,只需要在打包的时候开启BuildAssetBundleOptions.UncompressedAssetBundle即可。BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, \n
BuildAssetBundleOptions.UncompressedAssetBundle);\n4.2.针对项目的建议AssetBundle的压缩策略不仅仅和包体的大小、包体的解压速度相关,而且还会关系到AssetBundle在运行时动态加载的API使用。因此,针对不同类型资源的AssetBundle要指定出符合其使用特点的压缩策略。5.AssetBundle的加载和卸载本小节主要包括:新版API动态加载方式对比针对项目的建议5.1 新版API在5.x版本中的新AssetBundle系统中,旧有的一些动态加载API已经被新的API所取代,具体内容如下:4.x版本中的AssetBundle.CreateFromFile方法,在5.x版本中变成了AssetBundle.LoadFromFile方法。4.x版本中的AssetBundle.CreateFromMemory方法,在5.x版本中变成了LoadFromMemoryAsync方法。4.x版本中的AssetBundle.CreateFromMemoryImmediate方法,在5.x版本中变成了LoadFromMemory方法。因此,本小节之后的内容将使用新版API。5.2.动态加载方式对比使用AssetBundle动态加载资源首先要获取AssetBundle对象,第二步才是从AssetBundle中加载目标资源。因此本小节将主要关注如何在运行时获取AssetBundle的对象,关于如何从AssetBundle中加载资源将在下一小节中分析。\n要在运行时加载AssetBundle对象主要可以分为两大类途径:先获取WWW对象,再通过获取AssetBundle对象直接获取AssetBundle下面我们就具体分析一下这两种途径:先获取WWW对象,再通过加载AssetBundle对象:\n在先获取WWW对象,在获取AssetBundle的这种方式中,我们可以使用以下两个API来实现这个功能。public WWW(string url),直接调用WWW类的构造函数,目标AssetBundle所\n在的路径作为其参数,构造WWW对象的过程中会加载Bundle文件并返回一个WWW对象,完成后会在内存中创建较大的WebStream(解压后的内\n容,通常为原Bundle文件的4~5倍大小,纹理资源比例可能更大),因此后续的AssetBundle.LoadAsset可以直接在内存中进行。public static WWW LoadFromCacheOrDownload(string url, int version, uint crc = 0),WWW\n类的一个静态方法,调用该方法同样会加载Bundle文件同时返回一个WWW对象,和上一个直接调用WWW的构造函数的区别在于该方法会将解压形式的\nBundle内容存入磁盘中作为缓存(如果该Bundle已在缓存中,则省去这一步),完成后只会在内存中创建较小的SerializedFile,而后\n续的AssetBundle.LoadAsset需要通过IO从磁盘中的缓存获取。直接加载AssetBundle对象:\n在4.x时代,我们可以通过CreateFromFile或CreateFromMemory方法将磁盘上的文件或内存中的流构造成我们需要的\nAssetBundle对象。但是在5.x版本中,曾经的这两个方法已经被新的LoadFromFile、LoadFromMemory方法所代替(这两\n个方法还有异步的版本),且机制上也有了一些区别。public static AssetBundle LoadFromFile(string path, uint crc = 0):新\n的从文件创建加载AssetBundle方法和4.x中的CreateFromFile方法在机制上有了一些分别,旧的CreateFromFile必须\n使用未压缩的Bundle文件才能在运行时动态创建AssetBundle对象。而新的LoadFromFile方法则没有这个要求,它支持上一节中提到\n的几个压缩格式,针对LZ压缩格式和未压缩的磁盘上的bundle文件,该方法会直接加载。针对使用默认的LZMA压缩格式压缩的bundle文件,该方\n法会在幕后先将bundle文件解压后再加载。这是最快的加载AssetBundle的方式。该方法是同步版本,还有异步版\n本:LoadFromFileAsync。public static AssetBundle LoadFromMemory(byte[] binary, uint crc = 0):从\n内存中获取Bundle的二进制数据,同步地创建AssetBundle对象。该方法一般用在经过加密的数据上,经过加密的流数据经过解密之后我们可以调\n用该方法动态的创建AssetBundle对象。该方法是同步版本,还有异步版本:LoadFromMemoryAsync。以上便是在运行时动态加载AssetBundle对象的方法。下面,我们再从加载过程中内存消耗的角度来对比一下这几种加载AssetBundle对象的方法,下表是Unity3D官方的一个中文版总结。注???:当使用WWW来下载一个bundle时,WebRequest还会有一个8*64KB的缓存区用来存储来自socket的数据。注???:当使用WWW来下载一个bundle时,WebRequest还会有一个8*64KB的缓存区用来存储来自socket的数据。5.3.针对项目的建议由于以上分析的几种加载手段各有各的使用情景和特点。因此建议在我们的项目中按照以下情景使用这些方法:随游戏一同发布的AssetBundle(一般位于StreamingAssets文件夹中):在打AssetBundle包时,使用LZ4压缩格式进行打包(开启BuildAssetBundleOptions.ChunkBasedCompression即可)。在运行时需要加载AssetBundle对象时,使用LoadFromFile方法进行加载。这样做的好处是:即可以将AssetBundle文件压缩,又可以兼顾加载速度,且节约内存。作为更新包,需要从服务端下载的AssetBundle:在打AssetBundle包时,使用默认的LZMA格式压缩。使用方法下载并缓存AssetBundle包文件。这样做的好处是:获得了最大的压缩率,在下载过程中可以减少数据传输量。同时,在本地磁盘创建缓存之后,又可以兼顾之后的加载速度,且节约内存。我们自己进行加密的AssetBundle:在打AssetBundle包时,使用LZ4压缩格式进行打包(开启BuildAssetBundleOptions.ChunkBasedCompression即可)。在运行时需要加载AssetBundle对象时,使用LoadFromMemory方法进行加载。(这也是从内存中使用流数据加载AssetBundle对象的仅有的使用场景。)我们自己压缩的AssetBundle:我们自己也可以使用第三方库或工具对生成的AssetBundle包文件进行压缩,如果需要这样做,则我们最好不要再使用Unity3D对\nAssetBundle进行压缩,因此在打包时选择开启\nBuildAssetBundleOptions.UncompressedAssetBundle。在运行时需要加载AssetBundle对象时,使用LoadFromFileAsync方法进行异步加载。6.资源的加载和卸载本小节包括:从AssetBundle对象中加载资源资源的卸载6.1.从AssetBundle对象中加载资源新旧版的加载和卸载资源的API名称发生了一些变化,但是机制变化不大。\n在旧有的4.X版本中,从AssetBundle对象中加载资源所使用的API主要包括以下几个:Load:从资源包中加载指定的资源LoadAll:加载当前资源包中所有的资源LoadAsync:从资源包中异步加载资源而在新版的AssetBundle中,加载资源的API已经变成了以下的几个:LoadAsset:从资源包中加载指定的资源LoadAllAsset:加载当前资源包中所有的资源LoadAssetAsync:从资源包中异步加载资源6.2.资源卸载资源卸载部分的变化不大,使用的仍然是Unload方法。Unload该方法会卸载运行时内存中包含在bundle中的所有资源。\n当传入的参数为true,则不仅仅内存中的AssetBundle对象包含的资源会被销毁。根据这些资源实例化而来的游戏内的对象也会销毁。\n当传入的参数为false,则仅仅销毁内存中的AssetBundle对象包含的资源。","updated":"T04:22:28.000Z","canComment":false,"commentPermission":"anyone","commentCount":17,"collapsedCount":0,"likeCount":88,"state":"published","isLiked":false,"slug":"","isTitleImageFullScreen":false,"rating":"none","titleImage":"/5d419ed54b09bada8dce1bc_r.jpg","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"Unity(游戏引擎)"},{"url":"/topic/","id":"","name":"C#"},{"url":"/topic/","id":"","name":"游戏开发"}],"adminClosedComment":false,"titleImageSize":{"width":650,"height":380},"href":"/api/posts/","excerptTitle":"","column":{"slug":"chenjiadong","name":"Runtime"},"tipjarState":"inactivated","annotationAction":[],"sourceUrl":"","pageCommentsCount":17,"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T12:22:28+08:00","url":"/p/","lastestLikers":[{"bio":"码农","isFollowing":false,"hash":"df8e8300575","uid":747000,"isOrg":false,"slug":"bu-xiang-tian-30","isFollowed":false,"description":"","name":"不想填","profileUrl":"/people/bu-xiang-tian-30","avatar":{"id":"da8e974dc","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":null,"isFollowing":false,"hash":"7cfae2bb5fdddf5ed500a","uid":08,"isOrg":false,"slug":"liu-wu-25","isFollowed":false,"description":"","name":"刘五","profileUrl":"/people/liu-wu-25","avatar":{"id":"v2-5dfc444ebe760df4b52075b","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"IT程序猿;游戏攻城狮","isFollowing":false,"hash":"fd3d5dbc198c09","uid":80,"isOrg":false,"slug":"ic_leefeng","isFollowed":false,"description":"","name":"李峰","profileUrl":"/people/ic_leefeng","avatar":{"id":"v2-a3fbfd9e990ff95d6c0b9980","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"AR/VR从业者","isFollowing":false,"hash":"c1d2ee0fe37a","uid":60,"isOrg":false,"slug":"geescan","isFollowed":false,"description":"热爱二进制世界里的一切美好,期待和你的不期而遇。","name":"涂启标","profileUrl":"/people/geescan","avatar":{"id":"175ffbe029731ffe8d44a3adf976a1a4","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"Mobile Game Developer","isFollowing":false,"hash":"95dff40b8f7","uid":40,"isOrg":false,"slug":"yangzhenlin","isFollowed":false,"description":"Mobile Game Developer","name":"杨振林","profileUrl":"/people/yangzhenlin","avatar":{"id":"969caef7d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false}],"summary":"1.概览Unity3D 5.0版本之后的AssetBundle机制和之前的4.x版本已经发生了很大的变化,一些曾经常用的流程已经不再使用,甚至一些老的API已经被新的API所取代。 \n因此,本文的主要内容就是分析5.X版本的AssetBundle机制(包括创建资源包、压缩资源包、加载资…","reviewingCommentsCount":0,"meta":{"previous":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/50/9c38a9cbc90d_xl.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"微软(Microsoft)"},{"url":"/topic/","id":"","name":".NET"},{"url":"/topic/","id":"","name":"开源社区"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"慕容小匹夫 微软MVP《Unity 3D脚本编程 》作者 公众号chenjd01","isFollowing":false,"hash":"2ad398c4fbe830","uid":88,"isOrg":false,"slug":"jiadongchen","isFollowed":false,"description":"编程首先是爱好,其次才是职业。http://www.chenjd.me/\n\n我已加入“维权骑士”()的版权保护计划。","name":"陈嘉栋","profileUrl":"/people/jiadongchen","avatar":{"id":"v2-e3e4f2a706dc7ec9d2866d2aba059c0d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"chenjiadong","name":"Runtime"},"content":"文:张善友众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux \n(RHEL)系统上的一流开发平台选项。这个团队已经一起工作好几个月了,RHEL对.NET有许多需求。今天在RedHat 峰会DevNation \n上宣布了.NET Core &
Core 1.0 RTM。Red Hat有一个新的关于在RHEL上更简单的使用.NET \nCore的选项。(DevNation是一场全栈开发大会,将共同探讨开源的最优秀特性。DevNation \n2016由50多场小分会和全体大会组成,其中也有4场涉及到.NET Core相关议题,具体可以访问 \n 。 在微软的Build \n2016开发者大会上Redhat成为了.NET 基金会的技术指导小组成员,Redhat \n同时为.NET开发人员启动了一个新站点:, 微软今天在Redhat的峰会的DevNation上发布 .NET \nCore 以及
Core 1.0,并提供了下载,详见官方发布博文
。三星也加入了.NET基金会。  本次微软放出的下载中(),\n包含了 .NET Core Runtime, .NET Core SDK, .NET Core VS Tooling (包括 Web \n开发工具), .NET Core Windows Server Hosting, 以及更新的 NuGet ASP.NET Core 1.0 和 \nEntity Framework Core 1.0 包。微软还发布了用于创建 .NET Core 项目的 Visual Studio 和 \nVisual Studio Code 扩展,以及 ()。Redhat 也发布了相关博文介绍如何在Redhat 企业版Linux上开发.NET core应用,
\n \n开源方式是人们自由分享想法并且基于他人成果进行开发和创新的协作形式,推动了包括医疗、教育、政府、管理在内的等多个领域的巨大进步。开源促成了多个技\n术领域的数字化转型,并且推动着重大的创新,包括云计算、容器、应用开发、DevOps、大数据、移动、安全等。
\n对于.NET开发人员来说,现在是一个很好的机会去通过你已有的技能来研究各种.Net \n技术。如果你不是一个.NET开发人员,是时候再考虑下.NET了!.NET体系里包括了工具、API和为各类开发人员提供的服务,以方便开发者创建各类\n应用程序。
凌晨的Red Hat DevNation 2016 使用的直播是youtube,很多同学没有看,期待后续在channel9的视频回顾,这里用照片和大家回顾下激动人心的发布: ","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T10:05:42+08:00","url":"/p/","title":".NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布","summary":"文:张善友众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux \n(RHEL)系统上的一流开发平台选项。这个团队已经一起工作好几个月了,RHEL对.NET有许多需求。今天在RedHat 峰会DevNation \n上宣布了.NET Core &
Core 1.0…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":4,"likesCount":19},"next":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/50/70ec7cbe1fe4f601bac490be83aa61b6_xl.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"C#"},{"url":"/topic/","id":"","name":"Unity(游戏引擎)"},{"url":"/topic/","id":"","name":"编程语言"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"慕容小匹夫 微软MVP《Unity 3D脚本编程 》作者 公众号chenjd01","isFollowing":false,"hash":"2ad398c4fbe830","uid":88,"isOrg":false,"slug":"jiadongchen","isFollowed":false,"description":"编程首先是爱好,其次才是职业。http://www.chenjd.me/\n\n我已加入“维权骑士”()的版权保护计划。","name":"陈嘉栋","profileUrl":"/people/jiadongchen","avatar":{"id":"v2-e3e4f2a706dc7ec9d2866d2aba059c0d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"chenjiadong","name":"Runtime"},"content":"0x00 前言众所周知的一点是C#语言是一种强调类型的语言,而C#作为Unity3D中的游戏脚本主流语言,在我们的开发工作中能够驾驭好它的这个特点便十分\n重要。事实上,怎么强调C#的这个特点都不为过,因为它牵涉到编程的很多方面。一个很好的例子便是我们本文要介绍的内容——可空型,它是因何出现的,而它\n的出现又有什么意义呢?以及如何在Unity3D游戏的开发中使用它呢?那么就请各位读者朋友带着这些疑问,通过下面的文字来寻找这些问题的答案吧。0x01 如果没有值?一个了解一点C#基础知识的人都知道,值类型的变量永远不会为null,因为值类型的值是其本身。而对于一个引用类型的变量来说,它的值则是对一个\n对象的引用。那么空引用是表示一个值呢,还是表示没有值呢?如果表示没有值,那么没有值可以算是一种有效的值吗?如果我们根据相关标准中关于引用类型的定\n义,我们其实很容易就可以发现,一个非空的引用值事实上提供了访问一个对象的途径,而空引用(null)当然也表示一个值,只不过它是一个特殊的值,即意\n味着该变量没有引用任何对象。但null在本质上和其他的引用的处理方式是一样的,通过相同的方式在内存中存储,只不过内存会全部使用0来表示null,\n因为这种操作的开销最低,仅仅需要将一块内存清除,这也是为何所有的引用类型的实例默认值都是null的原因。但是,正如在本节一开始说的,值类型的值永远不能是null,但是在我们的开发工作中是否会恰巧遇到一个必须让值类型变量的值既不是负数也不是0,而是真正的不存在的情况呢?答案是是的,很常见。一种最常见的情况是在设计数据库时,是允许将一列的数据类型定义为一个32位整数,同时映射到C#中的Int32这个数据类型。但是,数据库中的一\n列值中是存在为空的可能性的,换言之在该列的某一行上的有可能是没有任何值的,即不是0也不是负无穷,而是实实在在的空。这样会带来很多的隐患,也使得\nC#在处理数据库时变得十分困难,原因上文已经提到过了,在C#中无法将值类型表示为空。当然还有很多种可能的情况,例如在开发手机游戏时需要通过移动手指来滑动选择一块区域内的游戏单位,一次拖动完成之后,显然应该将本次拖动的数据清\n空,以作为开始下一次拖动的开始条件,而往往这些拖动数据在Unity3D的脚本语言中都是作为值类型出现的,因而无法直接设为空,所以也会给开发带来些\n许不便。那么如果没有一个可以让值类型直接表示空的方法出现,我们是否还有别的手段来实现类似的功能呢?下面我们就来聊聊如果没有可空类型,应该如何在逻辑上近似实现值类型表示空的功能。0x02 表示空值的一些方案假设如果真的没有一种可以直接表示空值的方案出现,那么我们是否能想到一些替代方案呢?所以本节就归纳一下三种用来表示空值的方案。1.使用魔值首先我们要知道值类型的值都是它本身,换言之每个值我们都希望是有意义的。而魔值这个概念或者说方案的出现,恰恰是违背这一原则的,即放弃一个有意\n义的值,并且使用它来表示空值,这个值在我们的逻辑中与别的值不同,这便是魔值。因为它让一个有意义的值消失了,例如魔值选为-1000,那么-1000\n这个值便不再表示-1000了,相反,它意味着空。回到刚刚的例子中,在数据库中如果有映射成Int32类型的某列值中恰好有一个是空,那么我们可以选择(牺牲)一个恰当的值来表示空。这样做的好处\n在于不会浪费内存,同样也不需要定义新的类型。但牺牲哪个值来作为魔值便成为了一个需要慎重考虑的事情。因为一旦做出选择,就意味着一个有意义的值的消\n失。当然,使用魔值这种方案在实际的开发中也显得很low,这是因为问题并没有被真正的解决,相反,我们只是耍了一个小聪明来实现暂时蒙混过关。因此我并不十分喜欢这种方案。2 使用标志位如果我们不想浪费或者说牺牲掉一个有意义的值来让它作为魔值来表示空的话,那么只用一个值类型的实例是不够的。这时候我们能想到的一个解决方案就是\n使用额外的bool型变量作为一个标识,来判定对应的值类型实例是否是空值。这种方案具体操作起来有很多种方式,例如我们可以保留两个实例,一个是表示我\n们所需的普通的值的变量,另一个则是标识它是否为空值的bool类型的变量。如下面这段代码所示://使用bool型变量作为标识\n\nusing UnityE\n\nusing S\n\nusing System.Collections.G\n\n \n\npublic class Example : MonoBehaviour {\n\n
private float _realV\n\n
private bool _nullF\n\n \n\n
private void Update()\n\n
this._realValue = Time.\n\n
this._nullFlag =\n\n
this.PrintNum(this._realValue);\n\n
}\n\n \n\n
private void LateUpdate()\n\n
this._nullFlag =\n\n
this.PrintNum(this._realValue);\n\n
}\n\n \n\n
// Use this for initialization\n\n
private void Start () {\n\n \n\n
}\n\n \n\n
private void PrintNum(float number)\n\n
if(this._nullFlag)\n\n
Debug.Log(\"传入的数字为空值\");\n\\n\n
Debug.Log(\"传入的数字为:\" + number); \n\n
}\n\n}\n在这段代码中,我们维护了两个变量,分别是float型的_ realValue,用来表示我们所需的值和bool型的_nullFlag,用来标识此时_ realValue所代表的值是否为空值(当然_ realValue本身不可能为空)。这种使用额外标识的方法要比上一小节中介绍的魔值方案好一些,因为没有牺牲任何有意义的值。但同时维护两个变量,而且这两个变量的关联性很强,因此\n稍有不慎可能就会造成bug,那么除了同时维护两个变量之外,还有别的具体方案可以用来实现标识是否为空值这个需求的吗?答案是有的,一个自然而然的想法\n便是使用结构将这两个值类型封装到一个新的值类型中。我们为这个新的值类型取名为NullableValueStruct。下面我们来看看\nNullableValueStruct值类型的定义://值类型NullableValueStruct的定义\n\nusing S\n\nusing System.C\n\nusing System.Collections.G\n\npublic struct NullableValueStruct\n\n{\n\n
private float _realV\n\n
private bool _nullF\n\n \n\n
public NullableValueStruct(float value, bool isNull)\n\n
this._realValue =\n\n
this._nullFlag = isNull\n\n
}\n\n \n\n
public float Value\n\n
return this._realV\n\n
this._realValue =\n\n
}\n\n \n\n
public bool IsNull\n\n
return this._nullF\n\n
this._nullFlag =\n\n
}\n\n}\n这样,我们就将刚刚要单独维护的两个变量封装到了一个新的类型中。而且由于这个新的类型是struct,换言之它是一个值类型因此也无需担心会产生装箱和拆箱的操作。下面我们就通过一段代码,在我们游戏中使用一下这个新的值类型吧。using UnityE\n\nusing S\n\nusing System.Collections.G\n\n \n\npublic class Example : MonoBehaviour {\n\n
private NullableValueStruct number = new NullableValueStruct(0f, false);\n\n \n\n
private void Update()\n\n
this.number.Value = Time.\n\n
this.number.IsNull =\n\n
this.PrintNum(this.number);\n\n
}\n\n \n\n
private void LateUpdate()\n\n
this.number.IsNull =\n\n
this.PrintNum(this.number);\n\n
}\n\n \n\n
// Use this for initialization\n\n
private void Start () {\n\n \n\n
}\n\n \n\n
private void PrintNum(NullableValueStruct number)\n\n
if(number.IsNull)\n\n
Debug.Log(\"传入的数字为空值\");\n\\n\n
Debug.Log(\"传入的数字为:\" + number.Value); \n\n
}\n\n}\n当然除了这种方式,是否还有别的方案呢?下面我们就来总结一下另一种方案,即使用引用类型来辅助值类型表示空值。3 借助引用类型来表示值类型的空值介绍完前两种为值类型表示空值的方案之后,我们接下来再介绍最后一种方案。当然聪明的读者朋友一定也想到了,既然值类型不能够是null,而引用类\n型却可以是null,那么是否可以借助引用类型来辅助值类型表示null呢?事实上使用引用类型来帮助表示值类型的空值,是一个很好的方向,而具体而言又\n可以分成两种解决思路。如我们所知,C#语言中的所有类型(引用类型和值类型)都是自System.Object类派生而来,虽然值类型不能为null,但是\nSystem.Object类却可以为null,因此在所有使用值类型同时有可能需要值类型表示空值的地方使用System.Object类来代替,便可\n以直接使用null来表示空值了。下面让我们来看一个小例子:using UnityE\n\nusing S\n\nusing System.Collections.G\n\n \n\npublic class Example : MonoBehaviour {\n\n \n\n \n\n
private void Update()\n\n
this.PrintNum(Time.time);\n\n
}\n\n \n\n
// Use this for initialization\n\n
private void Start () {\n\n
}\n\n \n\n
private void PrintNum(object number)\n\n
if(number == null)\n\n
Debug.Log(\"传入的数字为空值\");\n\\n\n
float realNumber = (float)\n\n
Debug.Log(\"传入的数字为:\" + realNumber);\n\n
}\n\n}\n当然,使用这种方式由于会频繁的在引用类型(System.Object)和值类型直接转换,因此会涉及到十分频繁的装箱和拆箱的操作进而产生很多\n垃圾而引发垃圾回收机制,会对游戏的性能产生一些影响。那么是否还有别的方案,不需要涉及到频繁的装箱和拆箱操作呢?答案是直接使用引用类型来表示值类\n型,即将值类型封装成一个引用类型。当然,这么做之后,我们相当于重新创建了一个全新的类型,在这里我们假设我们创建的这个新的类型叫做NullableValueType(当然它事\n实上是引用类型),在NullableValueType类的内部保留一个值类型的实例,该值类型的实例的值便是此时NullableValueType\n类所表示的值,而当需要表示空值时,只需要让NullableValueType类的实例为null即可。下面就让我们通过代码来定义一下\nNullableValueType类吧。// NullableValueType类定义\n\nusing S\n\nusing System.C\n\nusing System.Collections.G\n\npublic class NullableValueType\n\n{\n\n
private float _\n\n \n\n
public NullableValueType(float value)\n\n
this._value =\n\n
}\n\n \n\n
public float Value\n\n
return this._\n\n
this._value =\n\n
}\n\n}\n这样我们就将一个值类型(float)封装成了一个引用类型,所以理论上我们既可以使用引用类型的null来表示空值,也可以借助这个类内部的值类型实例来表示有意义的值。下面我们就使用这种封装的方式来重新实现一下上面的例子。using UnityE\n\nusing S\n\nusing System.Collections.G\n\n \n\npublic class Example : MonoBehaviour {\n\n
private NullableValueT\n\n \n\n \n\n
private void Update()\n\n
this.value.Value = Time.\n\n
this.PrintNum(this.value);\n\n
}\n\n \n\n
// Use this for initialization\n\n
private void Start () {\n\n
this.value = new NullableValueType(0f);\n\n
}\n\n \n\n
private void PrintNum(NullableValueType number)\n\n
if(number == null)\n\n
Debug.Log(\"传入的数字为空值\");\n\\n\n
Debug.Log(\"传入的数字为:\" + number.Value);\n\n
}\n\n}\n如刚刚所说的,在这里我们可以直接判断传入的值是否为null来确定要表达的值是否为空值,如果不是空值,则可以利用类中封装的值类型实例来表示它\n所要表达的值。这样做的优点是无需进行引用类型和值类型之间的转换,换言之能够缓解装箱和拆箱操作的频率,减少垃圾的产生速度。但是缺点同样十分明显,使\n用引用类型对值类型进行封装,本质上是重新定义了一个新的类型,因而代码量将会增加同时增加维护的成本。","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T14:54:10+08:00","url":"/p/","title":"可以为null的值类型,详解可空值类型(一)","summary":"0x00 前言众所周知的一点是C#语言是一种强调类型的语言,而C#作为Unity3D中的游戏脚本主流语言,在我们的开发工作中能够驾驭好它的这个特点便十分\n重要。事实上,怎么强调C#的这个特点都不为过,因为它牵涉到编程的很多方面。一个很好的例子便是我们本文要…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":5,"likesCount":3}},"annotationDetail":null,"commentsCount":17,"likesCount":88,"FULLINFO":true}},"User":{"jiadongchen":{"isFollowed":false,"name":"陈嘉栋","headline":"编程首先是爱好,其次才是职业。http://www.chenjd.me/\n\n我已加入“维权骑士”()的版权保护计划。","avatarUrl":"/v2-e3e4f2a706dc7ec9d2866d2aba059c0d_s.jpg","isFollowing":false,"type":"people","slug":"jiadongchen","bio":"慕容小匹夫 微软MVP《Unity 3D脚本编程 》作者 公众号chenjd01","hash":"2ad398c4fbe830","uid":88,"isOrg":false,"description":"编程首先是爱好,其次才是职业。http://www.chenjd.me/\n\n我已加入“维权骑士”()的版权保护计划。","profileUrl":"/people/jiadongchen","avatar":{"id":"v2-e3e4f2a706dc7ec9d2866d2aba059c0d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"next":{},"chenjiadong":{"following":false,"canManage":false,"href":"/api/columns/chenjiadong","name":"Runtime","creator":{"slug":"jiadongchen"},"url":"/chenjiadong","slug":"chenjiadong","avatar":{"id":"v2-c4e393a91e7abcd","template":"/{id}_{size}.png"}}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{}}}

我要回帖

更多关于 unity3d assetbundle 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信