1、特殊文件夹(unity doc : Special Folders)
Unity工程根目录下,有三个特殊文件夹:Assets、Library、ProjectSettings
Assets
Unity工程中所用到的所有Asset都放在该文件夹中,是资源文件的根目录,很多API都是基于这个文件目录的,查找目录都需要带上Assets,比如AssetDatabase。
Library
Unity会把Asset下支持的资源导入成自身识别的格式,以及编译代码成为DLL文件,都放在Library文件夹中。
ProjectSettings
下面都是存在Assets目录下的文件的了。
Editor
为Unity编辑器扩展程序的目录,可以在根目录下,也可以在子目录下,只要名字叫“Editor”,而且数量不限。Editor下面放的所有资源文件和脚本文件都不会被打进包中,而且脚本只能在编辑器模式下使用。一般会把扩展的编辑器放在这里,或只是编辑器程序用到的dll库,比如任务编辑器、角色编辑器、技能编辑器、战斗编辑器……以及各种小工具。
Editor Default Resources
名字带空格,必须在Assets目录下,里面放编辑器程序用到的一些资源,比如图片,文本文件等。不会被打进包内,可以直接通过EditorGUIUtility.Load去读取该文件夹下的资源。
Gizmos
Gizmos.DrawIcon在场景中某个位置绘制一张图片,该图片必须是在Gizmos文件夹下。
void OnDrawGizmos() { Gizmos.DrawIcon(transform.position, "0.png", true); }
1
2
3
OnDrawGizmos是MonoBehaviour的生命周期函数,但是只在编辑器模式下每一帧都会执行。Gizmos类能完成多种在场景视图中绘制需求,做编辑器或调试的时候经常会用到,比如在场景视图中绘制一条辅助线。(用Debug.DrawLine,Debug.DrawRay也可以绘制简单的东西)
Plugins
该文件夹一般会放置几种文件,第三方包、工具代码、sdk。
plugin分为两种:Managed plugins and Native plugins Managed plugins:就是.NET编写的工具,运行于.NET平台(包括mono)的代码库,可以是脚本文件,也可以本身是DLL。NGUI源码就放在该文件夹下面的。 Native plugins:原生代码编写的库,比如第三方sdk,一般是dll、so、jar等等。
该文件夹下的东西会在standard compiler时编译(最先编译),以保证在其它地方使用时能找到。
Resources
存放资源的特殊文件夹,可以在根目录下,也可以在子目录下,只要名字叫“Resources”就行,比如目录:/xxx/xxx/Resources 和 /Resources 是一样的,而且可有多个叫Resources的文件夹。Resources文件夹下的资源不管用还是不用都会被打包进.apk或者.ipa,因为Unity无法判断脚本有没有访问了其中的资源。需要注意的是项目中可以有多个Resources文件夹,所以如果不同目录的Resources存在同名资源,在打包的时候就会报错。
Resources中全部资源会被打包成一个缺省的AssetBundle(resources.assets)。
在该文件夹下的资源,可以通过Resources类进行加载使用。API地址
Standard Assets
存放导入的第三方资源包。
StreamingAssets
该文件夹也会在打包的时候全部打进包中,但它是“原封不动”的打包进去(直接拷贝到的包里)。游戏运行时只能读不能写。
不同的平台最后的路径也不同,可以使用unity提供的Application.streamingAssetsPath,它会根据平台返回正确的路径,如下:
Mac OS or Windows:path = Application.dataPath + “/StreamingAssets”; IOS:path = Application.dataPath + “/Raw”; Android:path = “jar:file://” + Application.dataPath + “!/assets/”;
我们一般会把初始的AssetBundle资源放在该文件夹下,并且通过WWW或AssetBundle.LoadFromFile加载使用。
Hide Assets
隐藏文件夹和文件 以”.”开头 以”~”结尾 名字为”cvs” 扩展名为”.tmp”
Asset(doc)
An asset is representation of any item that can be used in your game or project. An asset may come from a file created outside of Unity, such as a 3D model, an audio file, an image, or any of the other types of file that Unity supports. There are also some asset types that can be created within Unity, such as an Animator Controller, an Audio Mixer or a Render Texture.
asset就是游戏中所用的资源,可以是用其它软件创建的,如3D model、audio、image等,和一些unity可创建的,如animator、audio mixer、render texture…
一些通常的Asset类型
Image: 支持绝大多数的image type,例如BMP、JPG、TIF、TGA、PSD Model:eg、.max、.blend、.mb、.ma,它们将通过FBX插件导入。或者直接在3D app导出FBX放到unity project中 Mesh and Animations:unity支持绝大多数流行的3D app的model(Maya、Cinema 4D、3ds Max、Cheetah3D、Modo、Lightwave、Blender、SketchUp) Audio Files:如果是非压缩的audio,unity将会根据import setting压缩导入(更多) Other:
Asset Store
里面有很多免费和收费的插件,可以供开发者下载使用。
导入
unity会自动导入Asset目录下的资源,可以是unity支持的,也可以是不支持的,而在程序中用到的(比如二进制文件)。
当在Asset下进行保存、移动、删除等修改文件的操作,unity都会自动导入。
自定义导入
导入结果( doc)
导入资源之后,除了要生成.meta文件,unity并不是直接使用这些资源的,而是在导入的过程中,生成了unity内部特定的格式(unity可识别)文件在游戏中使用,储存在Library目录下,而原始资源不变,仍然放在原来位置。当然,每次修改原始文件,unity都会重新导入一次,才能在unity中看到改过之后的样子。
正因为Library存放了导入资源的结果,所以每次删除Library或里面某个文件,都会让unity重新导入相应的资源(生成内部格式),但对工程没有影响。
最终那些资源的导入结果就在metadata文件夹里
“the results of the import process are stored in a folder named for the first two digits of the Asset’s File GUID. This folder is stored inside the Library/metadata/ folder. The individual Objects are serialized into a single binary file that has a name identical to the Asset’s File GUID.” import的结果存储在Library/metadata/文件夹下,并且把File GUID的前两位bit作为文件夹名,以File GUID作为文件名字。
.meta文件
Asset中的所有文件、文件夹,经过unity的导入过程后,会为每个都生成一个.meta文件,这个文件是unity内部管理文件的重要内容,里面记录着一些信息。
你知道unity是怎么管理资源依赖关系的吗?可以试着更改一个挂在prefab上的脚本的目录或者名字,而这些prefab依然可以正常的调用那些脚本。
unity在第一次导入新文件的时候,会生成一个Unique ID,用来标志这个asset,它就是unity内部用来区分asset的。Unique ID是全局唯一的,保存在.meta文件中。
在unity中资源间的依赖关系引用都是用Unique ID来实现的,如果一个资源丢失了.meta文件,那依赖它的资源就找不到它了。
If an asset loses its meta file (for example, if you moved or renamed the asset outside of Unity, without moving/renaming the corresponding .meta file), any reference to that asset will be broken. Unity would generate a new .meta file for the moved/renamed asset as if it were a brand new asset, and delete the old “orphaned” .meta file.
For example, in the case of a texture asset losing its .meta file, any Materials which used that Texture will now have no reference to that texture. To fix it you would have to manually re-assign that texture to any materials which required it.
In the case of a script asset losing its .meta file, any Game Objects or Prefabs which had that script assigned would end up with an “unassaigned script” component, and would lose their functionality. You would have to manually re-assign the script to these objects to fix this.
脚本
unity支持三种脚本语言,分别是C#、JavaScript、Boo,最常用的是前两种,当然还有后来扩展的支持Lua脚本的库(slua、ulua)。
1.编译顺序
编译顺序的原则是在第一个引用之前编译它,参考 官网文档可以知道,Unity中的可以将脚本代码放在Assets文件夹下任何位置,但是不同的位置会有不同的编译顺序。规则如下:
The phases of compilation are as follows: Phase 1: Runtime scripts in folders called Standard Assets, Pro Standard Assets and Plugins. Phase 2: Editor scripts in folders called Editor that are anywhere inside top-level folders called Standard Assets, Pro Standard Assets and Plugins. Phase 3: All other scripts that are not inside a folder called Editor. Phase 4: All remaining scripts (those that are inside a folder called Editor).
(1) 首先编译Standard Assets,Pro Standard Assets,Plugins文件夹(除Editor,可以是一级子目录或是更深的目录)下的脚本; (2) 接着编译Standard Assets,Pro Standard Assets,Plugins文件夹下(可以是一级子目录或是更深的目录)的Editor目录下的脚本; (3) 然后编译Assets文件夹下,不在Editor目录的所有脚本; (4) 最后编译Editor下的脚本(不在Standard Assets,Pro Standard Assets,Plugins文件夹下的);
基于以上编译顺序,一般来说,我们直接在Assets下建立一个Scripts文件夹放置脚本文件,它处于编译的“第三位”。
2.编译结果:
项目工程文件夹中会生成类似如下几个文件, 按顺序分别对应着上述四个编译顺序:(GameTool是项目名称) GameTool.CSharp.Plugins.csproj GameTool.CSharp.Editor.Plugins.csproj GameTool.CSharp.csproj GameTool.CSharp.Editor.csproj
所有脚本被编译成几个DLL文件,位于工程根目录 / Library / ScriptAssemblies。 生成如下三个dll: Assembly-CSharp-Editor.dll:包含所有Editor下的脚本 Assembly-CSharp-firstpass.dll:包含Standard Assets、Pro Standard Assets、Plugins文件夹下的脚本 Assembly-CSharp.dll:包含除以上两种,在Assets目录下的脚本。
Plugins(doc)
内容包括了Plugin导入设置、怎样创建使用两种Plugin、怎样利用底层渲染接口以及一些基础知识。
在打包的时候,会把plugin里面的各种库,拷贝到包体中相应的位置(不同平台不一样,具体在可以把工程分别打成几个平台的包)
分平台打包,就需要对不同平台的plugin区分,方法是在Plugins目录下建立相应平台的文件夹,unity在为不同平台打包的时候,除了会将相应平台的plugin里的脚本编译成Assembly-CSharp-firstpass.dll,还会把已经是dll、so等库直接拷贝到包内相应位置。 Plugins/x86:win32位平台plugin Plugins/x86_64:win64位平台plugin Plugins/Android:Android平台 Plugins/iOS:iOS平台
Object
UnityEngine.Object是所有类的基类,它描述了Asset上使用的所有resource的序列化数据,它有几个重要的派生类:GameObject,Component,MonoBehaviour
GameObject
GameObject是组件的容器,所有Component都在可以挂在上面,Unity以组价化思想构建,所有功能拆分成各个组件,需要某个功能只需挂上相应的组件,组件之间相互独立,逻辑互补交叉。当然组件式开发也有最大的弊端就是组件之间的交互。
Component
Component作为组件的基类,unity中有大量的组件,Transform、Renderer、Collider、MeshFilter都是组件。
MonoBehaviour
开发时创建的脚本,需要挂在GameObject上的脚本都是继承自MonoBehaviour。
ScriptableObject
自定义可被Unity识别的资源类型,可打成AssetBundle,可通过Resources or AssetBundle加载。
序列化
Asset和Object的关系
Object作为Asset的序列化数据,比如以Texture导入一张图片,那么就用Texture对象记录描述了该图片。 Asset可能有多个Object,比如prefab的GameObject上挂着多个组件,这样Asset和Object就是一对多的关系。那么问题来了,同一个Object怎么区分分别挂在不同GameObject上的对象的?等等,这里是一定要区分的,因为它们要包含序列化数据(在Inspector视图设置的),而不是在游戏运行中再new。
Class ID 和 File ID(object id)
先梳理一下关系,unity通过guid找到asset,其中asset上可能又挂了很多组件,每个组件又对应着一个class,而在序列化的时候是对象。Class ID是unity定义好的(传送),File ID是为对象生成的id,也就是说,我用guid + (class id 可有) + file id 就能确定某个资源上的组件对象。
YMAL
是一种标记语言,如果不了解语言格式可以看网站。
Text-Based Scene Files
和二进制文件一样,unity还提供了基于文本的场景文件,使用YAML标记语言,通过文本描述了asset和object组件之间的关系是怎么关联、保存数据等。
下面是一个GameObejct的YAML document:
%YAML 1.1%TAG !u! tag:unity3d.com,2011:--- !u!1001 &100100000Prefab: m_ObjectHideFlags: 1 serializedVersion: 2 m_Modification: m_TransformParent: {fileID: 0} m_Modifications: [] m_RemovedComponents: [] m_ParentPrefab: {fileID: 0} m_RootGameObject: {fileID: 1000010512509832} m_IsPrefabParent: 1--- !u!1 &1000010512509832GameObject: m_ObjectHideFlags: 0 m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 100100000} serializedVersion: 4 m_Component: - 4: {fileID: 4000012302714536} - 33: {fileID: 33000011309778356} - 23: {fileID: 23000013678005954} - 114: {fileID: 114000012372194584} m_Layer: 0 m_Name: Cube m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1--- !u!4 &4000012302714536Transform: m_ObjectHideFlags: 1 m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 100100000} m_GameObject: {fileID: 1000010512509832} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0
第三行的字符串“— !u!1001 &100100000”,“!u!”后面的“1001”是Object的Class ID,“&”后面的“100100000”是File ID。 YAML Class ID Reference
接着第四行是Prefab,是Class Name;下面“m_”开头的是它的序列化属性,请看“m_RootGameObject”,记录着Prefab对象的根节点的GameObject对象,继续看后面“{fileID: 1000010512509832}”,file ID指向的刚好是第14行定义的GameObject对象;第20行m_Component属性,可以看出gameObject上挂了四个组件,通过前面的Class ID就可以查到是什么组件了。
Instance ID
在前面已经介绍了GUID、Class ID、File ID,先做个小结。Unity通过GUID找到asset文件,以至于我们可以随意更改asset文件的位置,但是.meta文件丢失就会导致找不到相应的资源,最常见的就是丢失脚本;接着在游戏中加载某个资源的时候,通过File ID找到组件的Object,如果Object没有被加载就找不到,则通过File ID找到数据源,通过Class ID找到类,然后实例化对象,初始化序列数据,并分配Instance ID,Object就加载好了。
但是每一个资源,这样每一次加载都需要对GUID和File ID遍历查找比较判断对应的Object是否加载,这样就会带来性能问题。所以基于这个原因,所以利用GUID和File ID生成一个Instance ID,在Unity内部维护了一个Instance ID的映射缓存来标识各个Object,每当有新的Objects添加到缓存中时,Instance ID以简单的单调递增的方式进行赋值。
另外Instance ID的生命周期和Object的创建销毁是一致的。
参考文章
特殊文件夹的编译顺序 Unity脚本执行顺序和编译顺序 Unity文件夹 Unity文件夹 Special Folder unity资源