Unity3D性能優化之資源原理科普篇


一、Unity的資源(Asset)和對象(UnityEngine.Objects)

資源(Asset)是硬盤中的文件,存儲在Unity工程的Assets文件夾內。例如,紋理(Texture),材質(Material)和FBX文件等,它們都是資源。一些資源的數據格式是Unity原生支持的,例如材質。有些資源則需要轉換為原生的數據格式后才能被Unity使用,例如FBX文件。
對象(UnityEngine.Object)代表序列化數據的集合,表示某個資源的具體實例。它可以是Unity引擎使用的任何類型的資源,例如網格,Sprite,音頻剪輯或動畫剪輯。所有的對象(Object)都是UnityEngine.Object基類的子類。

幾乎所有的對象(Object)類型都是內建的,其中有兩種比較特殊的類型。

ScriptableObject為開發者提供了一套便捷的系統,供開發者自定義數據類型。這些類型可以被Unity直接序列化或反序列化,並在Unity編輯器的檢視器窗口中進行操作。
MonoBehaviour提供了鏈接MonoScript的容器。MonoScript是一種內部數據類型,Unity用它保存對某個特定程序集和命名空間中特定腳本類的引用,MonoScript本身不包含任何實際的可執行代碼。

資源(Asset)與對象(Object)是一種一對多的關系,即一個資源文件可能會包括多個Object。

二、對象之間的引用

所有UnityEngine.Objects都可以引用其他的UnityEngine.Objects。這里“其他的Object”可能存在於相同的資源文件中,或需要從其他資源文件導入。例如,一個材質Object通常有一個或多個紋理Object的引用。這些紋理Object一般是從一個或多個紋理資源文件中導入的(例如PNG或JPG文件)。

序列化后,這些引用由兩部分數據組成:文件GUID和本地ID。文件GUID用於識別資源(Asset)文件中目標資源(Resource)的存儲位置。而本地唯一的ID負責識別單個資源文件中的Object,因為一個資源文件可能會包含多個Object。

比如一個特效做成的Prefab,直接用文本打開prefab和prefab.meta后綴的兩個文件。

GUID:存儲於.meta文件中。Unity會在首次導入資源文件時生成.meta文件,並和資源文件一起存儲在相同的目錄中。GUID提供了文件存儲位置的抽象,這樣一個文件GUID就對應一個具體的文件,因此我們才能隨意移動這個文件而不破壞所有相關Object對這個文件的引用。

fileFormatVersion: 2
guid: 87160fe309c6cd4458c5f56188b57684
timeCreated: 1568165766
licenseType: Pro
NativeFormatImporter:
  externalObjects: {}
  mainObjectFileID: 100100000
  userData: 
  assetBundleName: 
  assetBundleVariant: 

本地ID:是唯一的,使用文本編輯器打開prefab文件,可以看下關於這個預設的所有屬性都在配置里面,100100000就是本地ID。任何資源(Asset)文件中都可能含有(或通過導入產生)多個UnityEngine.Object資源(Resource),因此需要一個本地ID來對其中的Object做明確區分。一個大的預設里面會有多個gameobject,相當於總的預設會記錄子物體的本地ID,這樣才能關聯到每一個子物體。

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
  m_ObjectHideFlags: 1
  serializedVersion: 2
  m_Modification:
    m_TransformParent: {fileID: 0}
    m_Modifications: []
    m_RemovedComponents: []
  m_ParentPrefab: {fileID: 0}
  m_RootGameObject: {fileID: 1891991147743598}
  m_IsPrefabParent: 1
--- !u!1 &1053155390742798

總結:Guid相當於指向一個資源路徑;本地ID相當於指向於具體的游戲對象Object

三、資源引用分析

我們知道了資源和對象的關系后,對資源引用過程已經清楚了。那么怎么分析資源的引用關系呢?

AssetDatabase.AssetPathToGUID(path)
AssetDatabase.GUIDToAssetPath(guid)

看代碼就大概了解,這兩個方法可以將guid和path進行互相轉換,因此可以根據預設里引用到的所有guid值,進而可以找到這個資源所引用到對象。

四、Unity資源引用的機制

我們在Unity如果創建一個Materials,然后指定一個有兩張圖片屬性的Shader,接着引用了兩張紋理。這時候打開材質可以看到是有引用到兩個紋理m_Texture。可以看下原先的屬性:

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
  serializedVersion: 6
  m_ObjectHideFlags: 0
  m_PrefabParentObject: {fileID: 0}
  m_PrefabInternal: {fileID: 0}
  m_Name: 5400_fangkuai
  m_Shader: {fileID: 4800000, guid: b5f72fe4d91f50b47920a7498eeaf32a, type: 3}
  m_ShaderKeywords: 
  m_LightmapFlags: 4
  m_EnableInstancingVariants: 0
  m_DoubleSidedGI: 0
  m_CustomRenderQueue: -1
  stringTagMap: {}
  disabledShaderPasses: []
  m_SavedProperties:
    serializedVersion: 3
    m_TexEnvs:
    - _AmitTex:
        m_Texture: {fileID: 2800000, guid: 35a133778d8d5f64788367aa21dd2589, type: 3}
        m_Scale: {x: 0.5, y: 1}
        m_Offset: {x: 0, y: 0}
    - _FlowTex:
        m_Texture: {fileID: 2800000, guid: 4cccfa2e1ba52a342b545da00de76a29, type: 3}
        m_Scale: {x: -2, y: -2}
        m_Offset: {x: 0, y: 0}
    m_Floats:
    - _AmitIntensity: 2
    - _FlowIntensity: 4
    - _FlowTexUSpeed: 0.5
    - _FlowTexVSpeed: -0.8
    m_Colors:
    - _Color: {r: 0.32352942, g: 0.5521299, b: 1, a: 1}
    - _FlowColor: {r: 0, g: 0.28539556, b: 0.61764705, a: 1}

如果這時我改一下Shader,而且這個Shader只引用一張紋理,我不賦值。這個材質引用的紋理就變成了三張,前面Shader引用到的紋理依舊在引用。

m_TexEnvs:
    - _AmitTex:
        m_Texture: {fileID: 2800000, guid: 35a133778d8d5f64788367aa21dd2589, type: 3}
        m_Scale: {x: 0.5, y: 1}
        m_Offset: {x: 0, y: 0}
    - _Amitex:
        m_Texture: {fileID: 0}
        m_Scale: {x: 1, y: 1}
        m_Offset: {x: 0, y: 0}
    - _FlowTex:
        m_Texture: {fileID: 2800000, guid: 4cccfa2e1ba52a342b545da00de76a29, type: 3}
        m_Scale: {x: -2, y: -2}
        m_Offset: {x: 0, y: 0}

總結:這個引用機制就是每次改變時,只增加新的引用,舊的引用不會去刪除。

這個機制的好處:是當又改成之前的shader時,會自動引用到紋理和設置屬性。

這個機制的壞處:資源會導致冗余,打包時會打到多余的資源對象,加載時也會加載多余的資源對象。

五、Unity資源冗余

我們知道了引用機制,那么可以發現一些應該避免和注意的問題。

  • 當有對象被刪除時,引用此對象的依賴依然存在,應該處理。
  • 材質:材質會引用紋理,而紋理是根據材質所引用的Shader決定,所以應該針對所有材質進行處理紋理的引用。
  • 粒子系統(Particle System):粒子系統可以引用FBX和材質,在Renderer中可以引用到FBX網格,這部分也會引起FBX和紋理引用冗余。

六、資源打包冗余

打包冗余是指相同的對象,比如紋理被重復打進多個AB包,這樣會造成包體變大,加載重復資源的問題。

所以我們應該根據這些特性或者說機制,來分析資源引用的關系,再來進行引用的優化。所以針對這種情況會不同的資源先進行分析,最后再根據分析的情況來進行優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM