Unity性能优化之字体篇

Posted Metaverse达爷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity性能优化之字体篇相关的知识,希望对你有一定的参考价值。

使用引擎:Unity

使用软件:FontSubsetGUI,FontCreator

前言

首先我们要知道Unity导出安装包时是把游戏场景所应用的资源打包导出,而字体就是其中的一种,但Unity使用的都是默认的Arial动态字体,这字体是微软系统自带的一个字体库,它的大小可能十几MB左右,在一般情况下是可以忽略它的大小,但是如果是手机游戏,微信小游戏这一类对安装包有极大要求的游戏,就需要减少安装包的大小,而字体就好比一个仓库,默认情况下他会把所有的文字都包含在库里,可实际上游戏开发时,大部分的文字我们并没有用到,但是Unity会把这些字体跟着安装包一块打包导出,这无形增加了安装包的大小,为此我们需要对字体进行优化也就是“裁剪”“。

接下来讲一下具体的优化步骤!

1.首先我们需要下载两个软件,一个是FontSubsetGUI,一个是FontCreator。Unity本身对ogg和ttf格式的字体都支持,而我们的软件可以对ttf进行裁剪,为此我们使用ttf字体。字体一般都是ogg格式的,因此我们需要把字体从ogg格式转为ttf格式,这就需要FontCreator进行转换。

2.打开FontCreator软件,点【文件】—【打开】—【字体文件】,找到你要转换的字体,等它完全打开后点【文件】—【另存为】保存成ttf格式就可以了。 

3.接下来打开FontSubsetGUI软件,并新建一个txt文本格式的文件,把游戏里所需要用到的所有文字放进去,然后我来介绍一下FontSubsetGUI的界面。

  • Source Font:这里放我们需要裁剪的ttf字体。
  • New Font:这里选择我们裁剪字体后要保存的位置。
  • Char List:这里放我们之前新建的保留有游戏里所需要用到的所有文字的txt文件。
  • Proceed:这是导出按钮,上述操作做完后点击这个就可以正式导出了。

最后导出的字体就是我们精心裁剪过的字体了,这里面只包含游戏里需要演示的字体,当然如果后续要更改得重新按照上述步骤操作。

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包,这样会造成包体变大,加载重复资源的问题。

所以我们应该根据这些特性或者说机制,来分析资源引用的关系,再来进行引用的优化。所以针对这种情况会不同的资源先进行分析,最后再根据分析的情况来进行优化。



以上是关于Unity性能优化之字体篇的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D性能优化之内存

[Unity优化] Unity CPU性能优化 (难度3 推荐4)

手游引擎unity和cocos各有啥优劣

Unity 优化篇 | 优化专栏《导航帖》,全面学习Unity优化技巧,让我们的Unity技术上升一个档次

手游引擎怎么购买?

基于Unity3d 引擎的Android游戏优化(转)