首页 >Unity3D频道 >【UGUI研究院】 > UGUI研究院之针对IOS平台设置某些图不打图集(二十八)
2017
08-12

UGUI研究院之针对IOS平台设置某些图不打图集(二十八)

最近超累,还得再苦逼一段时间。 抽空更新一下博客,希望大家不要忘记我。前段时间写过一篇处理游戏中的大图片进行压缩的文章。

https://www.xuanyusong.com/archives/4312

在处理大图片的时候,为了保证每张图片都会压缩,所以我会自动给每个图片添加一个packingTag,如此一来即使美术出的图片不是2的幂次方,unity也会将它拉成2的幂次方,在ios平台会自动变成正方形,这样就能保证每张图片都可以进行压缩。

Android采用的是这个方式,单个的大图我都会设置一个packingTag 保证每张图片都可以压缩,效果确实还可以。

https://www.xuanyusong.com/archives/4279

IOS的大图就就略显尴尬,因为有些透明渐变的图片,Android使用上述的压缩方式是没问题的,但是IOS如果直接用pvrtc 4来压缩,效果就不太好了。这时候android可以保持原样,ios希望采取 rgba 16bit 的格式,但是因为前面我们给这个图片设置了packingTag ios上会被拉成正方形,那么rgba 16bit就会比图片原始尺寸更加的占内存。可是如果我把packingTag清空,那么android ios两边无法保持一致,如果打包预处理的话,svn就会发生变化。

所以希望,如果图集中只有一张图片,并且设置了非压缩格式,那么即使设置了packingTag也不对它打图集。研究了一下unity图集的打包过程,可以通过代码来处理。把如下代码放在Editor目录下即可 ,注意代码中注释的部分。

using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

public class CustomPackerPolicySample : UnityEditor.Sprites.IPackerPolicy
{
    protected class Entry
    {
        public Sprite            sprite;
        public UnityEditor.Sprites.AtlasSettings settings;
        public string            atlasName;
        public SpritePackingMode packingMode;
        public int               anisoLevel;
    }

    private const uint kDefaultPaddingPower = 3; // Good for base and two mip levels.

    public virtual int GetVersion() { return 1; }
    protected  string TagPrefix { get { return "[RECT]"; } }
    protected  bool AllowTightWhenTagged { get { return true; } }
    protected  bool AllowRotationFlipping { get { return true; } }

    public static bool IsCompressedFormat(TextureFormat fmt)
    {
        if (fmt >= TextureFormat.DXT1 && fmt <= TextureFormat.DXT5)
            return true;
        if (fmt >= TextureFormat.DXT1Crunched && fmt <= TextureFormat.DXT5Crunched)
            return true;
        if (fmt >= TextureFormat.PVRTC_RGB2 && fmt <= TextureFormat.PVRTC_RGBA4)
            return true;
        if (fmt == TextureFormat.ETC_RGB4)
            return true;
        if (fmt >= TextureFormat.ATC_RGB4 && fmt <= TextureFormat.ATC_RGBA8)
            return true;
        if (fmt >= TextureFormat.EAC_R && fmt <= TextureFormat.EAC_RG_SIGNED)
            return true;
        if (fmt >= TextureFormat.ETC2_RGB && fmt <= TextureFormat.ETC2_RGBA8)
            return true;
        if (fmt >= TextureFormat.ASTC_RGB_4x4 && fmt <= TextureFormat.ASTC_RGBA_12x12)
            return true;
        if (fmt >= TextureFormat.DXT1Crunched && fmt <= TextureFormat.DXT5Crunched)
            return true;
        return false;
    }

    public void OnGroupAtlases(BuildTarget target, UnityEditor.Sprites.PackerJob job, int[] textureImporterInstanceIDs)
    {
        List<Entry> entries = new List<Entry>();

        foreach (int instanceID in textureImporterInstanceIDs)
        {
            TextureImporter ti = EditorUtility.InstanceIDToObject(instanceID) as TextureImporter;

            TextureFormat desiredFormat;
            ColorSpace colorSpace;
            int compressionQuality;
            ti.ReadTextureImportInstructions(target, out desiredFormat, out colorSpace, out compressionQuality);

            TextureImporterSettings tis = new TextureImporterSettings();
            ti.ReadTextureSettings(tis);

            Sprite[] sprites =
                AssetDatabase.LoadAllAssetRepresentationsAtPath(ti.assetPath)
                    .Select(x => x as Sprite)
                    .Where(x => x != null)
                    .ToArray();

            foreach (Sprite sprite in sprites)
            {
                Entry entry = new Entry();
                entry.sprite = sprite;
                entry.settings.format = desiredFormat;
                entry.settings.colorSpace = colorSpace;
                // Use Compression Quality for Grouping later only for Compressed Formats. Otherwise leave it Empty.
                entry.settings.compressionQuality = IsCompressedFormat(desiredFormat) ? compressionQuality : 0;
                entry.settings.filterMode = Enum.IsDefined(typeof(FilterMode), ti.filterMode)
                    ? ti.filterMode
                    : FilterMode.Bilinear;
                entry.settings.maxWidth = 2048;
                entry.settings.maxHeight = 2048;
                entry.settings.generateMipMaps = false;
                entry.settings.enableRotation = AllowRotationFlipping;
                entry.settings.paddingPower = (uint)EditorSettings.spritePackerPaddingPower;
                //#if ENABLE_ANDROID_ATLAS_ETC1_COMPRESSION
                entry.settings.allowsAlphaSplitting = ti.GetAllowsAlphaSplitting ();
                //#endif //ENABLE_ANDROID_ATLAS_ETC1_COMPRESSION
                entry.atlasName = ParseAtlasName(ti.spritePackingTag);
                entry.packingMode = GetPackingMode(ti.spritePackingTag, tis.spriteMeshType);
                entry.anisoLevel = ti.anisoLevel;

                entries.Add(entry);
            }
            Resources.UnloadAsset(ti);
        }

        // First split sprites into groups based on atlas name
        var atlasGroups =
            from e in entries
            group e by e.atlasName;
        foreach (var atlasGroup in atlasGroups)
        {
            int page = 0;
            // Then split those groups into smaller groups based on texture settings
            var settingsGroups =
                from t in atlasGroup
                group t by t.settings;
            foreach (var settingsGroup in settingsGroups)
            {
				//已经设置了非压缩的图,不做成图集,避免拉成图集占内存。
				bool skipAtlas = (settingsGroup.Count () == 1 && !IsCompressedFormat (settingsGroup.ElementAt (0).settings.format));
				if (skipAtlas) {
					Debug.Log (settingsGroup.ElementAt (0).sprite.name);
					continue;
				}

				string atlasName = atlasGroup.Key;

				if (settingsGroups.Count() > 1)
                    atlasName += string.Format(" (Group {0})", page);

                UnityEditor.Sprites.AtlasSettings settings = settingsGroup.Key;
                settings.anisoLevel = 1;
                // Use the highest aniso level from all entries in this atlas
                if (settings.generateMipMaps)
                    foreach (Entry entry in settingsGroup)
                        if (entry.anisoLevel > settings.anisoLevel)
                            settings.anisoLevel = entry.anisoLevel;

                job.AddAtlas(atlasName, settings);
                foreach (Entry entry in settingsGroup)
                {
                    job.AssignToAtlas(atlasName, entry.sprite, entry.packingMode, SpritePackingRotation.None);
                }

                ++page;
            }
        }
    }

    protected bool IsTagPrefixed(string packingTag)
    {
        packingTag = packingTag.Trim();
        if (packingTag.Length < TagPrefix.Length)
            return false;
        return (packingTag.Substring(0, TagPrefix.Length) == TagPrefix);
    }

    private string ParseAtlasName(string packingTag)
    {
        string name = packingTag.Trim();
        if (IsTagPrefixed(name))
            name = name.Substring(TagPrefix.Length).Trim();
        return (name.Length == 0) ? "(unnamed)" : name;
    }

    private SpritePackingMode GetPackingMode(string packingTag, SpriteMeshType meshType)
    {
        if (meshType == SpriteMeshType.Tight)
        if (IsTagPrefixed(packingTag) == AllowTightWhenTagged)
            return SpritePackingMode.Tight;
        return SpritePackingMode.Rectangle;
    }
}

图片如果控制不好,还是很占内存的。我们游戏默认就把所有图片全部压缩,如果美术觉得某些图片效果不好,那么就对它单独处理一下即可。

版本unity5.5.2

最后欢迎大家在下面留言一起讨论。。

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

UGUI研究院之针对IOS平台设置某些图不打图集(二十八)》有 4 条评论

  1. Jonny说:

    雨松,请教个问题。我unity发布iOS游戏的时候 怎么操作可以像王者荣耀一样 homebar向上拖动两次才激活系统的最小化?谢谢

  2. 暗夜说:

    您的文章写得很好,我先把你的文章往外面扩展一下,我还有很多同学也是学习这块,都很赞同您说的,因为您说的都很对!

  3. startia说:

    大神,Unity导出的iOS工程可以被制作成iOS的framework让其他APP使用吗?例如导入了这个framework,点一个按钮就可以将Unity视图显示出来,不需要修改原有main函数。谢谢大神!

  4. 王健壮说:

    你的MMORPG上线了吗?

留下一个回复

你的email不会被公开。 必填项已用 * 标注。