[Unity]将所有 TGA、TIFF、PSD 和 BMP(可自定义)纹理转换为 PNG,以减小项目大小,而不会在 Unity 中造成任何质量损失

本文主要是介绍[Unity]将所有 TGA、TIFF、PSD 和 BMP(可自定义)纹理转换为 PNG,以减小项目大小,而不会在 Unity 中造成任何质量损失,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如何使用
只需在“项目”窗口中创建一个名为“编辑器”的文件夹,然后在其中添加此脚本即可。然后,打开窗口-Convert Textures to PNG,配置参数并点击“Convert to PNG! ”。

就我而言,它已将某些 3D 资源的总文件大小从 1.08 GB 减少到 510 MB。

只要禁用“Keep Original Files”或将项目的资源序列化模式设置为“强制文本”,就会保留对转换后的纹理的引用。
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;public class ConvertTexturesToPNG : EditorWindow
{private const string DUMMY_TEXTURE_PATH = "Assets/convert_dummyy_texturee.png";private const bool REMOVE_MATTE_FROM_PSD_BY_DEFAULT = true;private readonly GUIContent[] maxTextureSizeStrings = { new GUIContent( "32" ), new GUIContent( "64" ), new GUIContent( "128" ), new GUIContent( "256" ), new GUIContent( "512" ), new GUIContent( "1024" ), new GUIContent( "2048" ), new GUIContent( "4096" ), new GUIContent( "8192" ), new GUIContent( "16384" ) };private readonly int[] maxTextureSizeValues = { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };private readonly GUIContent rootPathContent = new GUIContent( "Root Path:", "Textures inside this folder (recursive) will be converted" );private readonly GUIContent textureExtensionsContent = new GUIContent( "Textures to Convert:", "Only Textures with these extensions will be converted (';' separated)" );private readonly GUIContent excludedDirectoriesContent = new GUIContent( "Excluded Directories:", "Textures inside these directories won't be converted (';' separated)" );private readonly GUIContent keepOriginalFilesContent = new GUIContent( "Keep Original Files:", "If selected, original Texture files won't be deleted after the conversion" );private readonly GUIContent maxTextureSizeContent = new GUIContent( "Max Texture Size:", "Textures larger than this size will be downscaled to this size" );private readonly GUIContent optiPNGPathContent = new GUIContent( "OptiPNG Path (Optional):", "If 'optipng.exe' is selected, it will be used to reduce the image sizes even further (roughly 20%) but the process will take more time" );private readonly GUIContent optiPNGOptimizationContent = new GUIContent( "OptiPNG Optimization:", "Determines how many trials OptiPNG will do to optimize the image sizes. As this value increases, computation time will increase exponentially" );private readonly GUIContent optiPNGURL = new GUIContent( "...", "http://optipng.sourceforge.net/" );private readonly GUILayoutOption GL_WIDTH_25 = GUILayout.Width( 25f );private string rootPath = "";private string textureExtensions = ".tga;.psd;.tiff;.tif;.bmp";private string excludedDirectories = "";private bool keepOriginalFiles = false;private int maxTextureSize = 8192;private string optiPNGPath = "";private int optiPNGOptimization = 3;private Vector2 scrollPos;[MenuItem( "Window/Convert Textures to PNG" )]private static void Init(){ConvertTexturesToPNG window = GetWindow<ConvertTexturesToPNG>();window.titleContent = new GUIContent( "Convert to PNG" );window.minSize = new Vector2( 285f, 160f );window.Show();}private void OnEnable(){// By default, Root Path points to this project's Assets folderif( string.IsNullOrEmpty( rootPath ) )rootPath = Application.dataPath;}private void OnGUI(){scrollPos = GUILayout.BeginScrollView( scrollPos );rootPath = PathField( rootPathContent, rootPath, true, "Choose target directory" );textureExtensions = EditorGUILayout.TextField( textureExtensionsContent, textureExtensions );excludedDirectories = EditorGUILayout.TextField( excludedDirectoriesContent, excludedDirectories );keepOriginalFiles = EditorGUILayout.Toggle( keepOriginalFilesContent, keepOriginalFiles );maxTextureSize = EditorGUILayout.IntPopup( maxTextureSizeContent, maxTextureSize, maxTextureSizeStrings, maxTextureSizeValues );optiPNGPath = PathField( optiPNGPathContent, optiPNGPath, false, "Choose optipng.exe path", optiPNGURL );if( !string.IsNullOrEmpty( optiPNGPath ) ){EditorGUI.indentLevel++;optiPNGOptimization = EditorGUILayout.IntSlider( optiPNGOptimizationContent, optiPNGOptimization, 2, 7 );EditorGUI.indentLevel--;}EditorGUILayout.Space();// Convert Textures to PNGif( GUILayout.Button( "Convert to PNG!" ) ){double startTime = EditorApplication.timeSinceStartup;List<string> convertedPaths = new List<string>( 128 );long originalTotalSize = 0L, convertedTotalSize = 0L, convertedTotalSizeOptiPNG = 0L;try{rootPath = rootPath.Trim();excludedDirectories = excludedDirectories.Trim();textureExtensions = textureExtensions.ToLowerInvariant().Replace( ".png", "" ).Trim();optiPNGPath = optiPNGPath.Trim();if( rootPath.Length == 0 )rootPath = Application.dataPath;if( optiPNGPath.Length > 0 && !File.Exists( optiPNGPath ) )Debug.LogWarning( "OptiPNG doesn't exist at path: " + optiPNGPath );string[] paths = FindTexturesToConvert();string pathsLengthStr = paths.Length.ToString();float progressMultiplier = paths.Length > 0 ? ( 1f / paths.Length ) : 1f;CreateDummyTexture(); // Dummy Texture is used while reading Textures' pixelsfor( int i = 0; i < paths.Length; i++ ){if( EditorUtility.DisplayCancelableProgressBar( "Please wait...", string.Concat( "Converting: ", ( i + 1 ).ToString(), "/", pathsLengthStr ), ( i + 1 ) * progressMultiplier ) )throw new Exception( "Conversion aborted" );string pngFile = Path.ChangeExtension( paths[i], ".png" );string pngMetaFile = pngFile + ".meta";string originalMetaFile = paths[i] + ".meta";bool isPSDImage = Path.GetExtension( paths[i] ).ToLowerInvariant() == ".psd";// Make sure to respect PSD assets' "Remove Matte (PSD)" optionif( isPSDImage ){bool removeMatte = REMOVE_MATTE_FROM_PSD_BY_DEFAULT;if( File.Exists( originalMetaFile ) ){const string removeMatteOption = "pSDRemoveMatte: ";string metaContents = File.ReadAllText( originalMetaFile );int removeMatteIndex = metaContents.IndexOf( removeMatteOption );if( removeMatteIndex >= 0 )removeMatte = metaContents[removeMatteIndex + removeMatteOption.Length] != '0';}SerializedProperty removeMatteProp = new SerializedObject( AssetImporter.GetAtPath( DUMMY_TEXTURE_PATH ) ).FindProperty( "m_PSDRemoveMatte" );if( removeMatteProp != null && removeMatteProp.boolValue != removeMatte ){removeMatteProp.boolValue = removeMatte;removeMatteProp.serializedObject.ApplyModifiedPropertiesWithoutUndo();}}// Temporarily copy the image file to Assets folder to create a read-write enabled Texture from itFile.Copy( paths[i], DUMMY_TEXTURE_PATH, true );AssetDatabase.ImportAsset( DUMMY_TEXTURE_PATH, ImportAssetOptions.ForceUpdate );// Convert the Texture to PNG and save itbyte[] pngBytes = AssetDatabase.LoadAssetAtPath<Texture2D>( DUMMY_TEXTURE_PATH ).EncodeToPNG();File.WriteAllBytes( pngFile, pngBytes );originalTotalSize += new FileInfo( paths[i] ).Length;convertedTotalSize += new FileInfo( pngFile ).Length;// Run OptiPNG to optimize the PNGif( optiPNGPath.Length > 0 && File.Exists( optiPNGPath ) ){try{Process.Start( new ProcessStartInfo( optiPNGPath ){Arguments = string.Concat( "-o ", optiPNGOptimization.ToString(), " \"", pngFile, "\"" ),CreateNoWindow = true,UseShellExecute = false} ).WaitForExit();}catch( Exception e ){Debug.LogException( e );}convertedTotalSizeOptiPNG += new FileInfo( pngFile ).Length;}// If .meta file exists, copy it to PNG imageif( File.Exists( originalMetaFile ) ){File.Copy( originalMetaFile, pngMetaFile, true );// Try changing original meta file's GUID to avoid collisions with PNG (Credit: https://gist.github.com/ZimM-LostPolygon/7e2f8a3e5a1be183ac19)if( keepOriginalFiles ){string metaContents = File.ReadAllText( originalMetaFile );int guidIndex = metaContents.IndexOf( "guid: " );if( guidIndex >= 0 ){string guid = metaContents.Substring( guidIndex + 6, 32 );string newGuid = Guid.NewGuid().ToString( "N" );metaContents = metaContents.Replace( guid, newGuid );File.WriteAllText( originalMetaFile, metaContents );}}// Don't show "Remote Matte (PSD)" option for converted Texturesif( isPSDImage ){string metaContents = File.ReadAllText( pngMetaFile );bool modifiedMeta = false;if( metaContents.Contains( "pSDShowRemoveMatteOption: 1" ) ){metaContents = metaContents.Replace( "pSDShowRemoveMatteOption: 1", "pSDShowRemoveMatteOption: 0" );modifiedMeta = true;}if( metaContents.Contains( "pSDRemoveMatte: 1" ) ){metaContents = metaContents.Replace( "pSDRemoveMatte: 1", "pSDRemoveMatte: 0" );modifiedMeta = true;}if( modifiedMeta )File.WriteAllText( pngMetaFile, metaContents );}}if( !keepOriginalFiles ){File.Delete( paths[i] );if( File.Exists( originalMetaFile ) )File.Delete( originalMetaFile );}convertedPaths.Add( paths[i] );}}catch( Exception e ){Debug.LogException( e );}finally{EditorUtility.ClearProgressBar();if( File.Exists( DUMMY_TEXTURE_PATH ) )AssetDatabase.DeleteAsset( DUMMY_TEXTURE_PATH );// Force Unity to import PNG images (otherwise we'd have to minimize Unity and then maximize it)AssetDatabase.Refresh();// Print information to ConsoleStringBuilder sb = new StringBuilder( 100 + convertedPaths.Count * 75 );sb.Append( "Converted " ).Append( convertedPaths.Count ).Append( " Texture(s) to PNG in " ).Append( ( EditorApplication.timeSinceStartup - startTime ).ToString( "F2" ) ).Append( " seconds (" ).Append( EditorUtility.FormatBytes( originalTotalSize ) ).Append( " -> " ).Append( EditorUtility.FormatBytes( convertedTotalSize ) );if( convertedTotalSizeOptiPNG > 0L )sb.Append( " -> " ).Append( EditorUtility.FormatBytes( convertedTotalSizeOptiPNG ) ).Append( " with OptiPNG" );sb.AppendLine( "):" );for( int i = 0; i < convertedPaths.Count; i++ )sb.Append( "- " ).AppendLine( convertedPaths[i] );Debug.Log( sb.ToString() );}}GUILayout.EndScrollView();}private string PathField( GUIContent label, string path, bool isDirectory, string title, GUIContent downloadURL = null ){GUILayout.BeginHorizontal();path = EditorGUILayout.TextField( label, path );if( GUILayout.Button( "o", GL_WIDTH_25 ) ){string selectedPath = isDirectory ? EditorUtility.OpenFolderPanel( title, "", "" ) : EditorUtility.OpenFilePanel( title, "", "exe" );if( !string.IsNullOrEmpty( selectedPath ) )path = selectedPath;GUIUtility.keyboardControl = 0; // Remove focus from active text field}if( downloadURL != null && GUILayout.Button( downloadURL, GL_WIDTH_25 ) )Application.OpenURL( downloadURL.tooltip );GUILayout.EndHorizontal();return path;}private string[] FindTexturesToConvert(){HashSet<string> texturePaths = new HashSet<string>();HashSet<string> targetExtensions = new HashSet<string>( textureExtensions.Split( ';' ) );// Get directories to excludestring[] excludedPaths = excludedDirectories.Split( ';' );for( int i = 0; i < excludedPaths.Length; i++ ){excludedPaths[i] = excludedPaths[i].Trim();if( excludedPaths[i].Length == 0 )excludedPaths[i] = "NULL/";else{excludedPaths[i] = Path.GetFullPath( excludedPaths[i] );// Make sure excluded directory paths end with directory separator charif( Directory.Exists( excludedPaths[i] ) && !excludedPaths[i].EndsWith( Path.DirectorySeparatorChar.ToString() ) )excludedPaths[i] += Path.DirectorySeparatorChar;}}// Iterate through all files in Root Pathstring[] allFiles = Directory.GetFiles( rootPath, "*.*", SearchOption.AllDirectories );for( int i = 0; i < allFiles.Length; i++ ){// Only process filtered image filesif( targetExtensions.Contains( Path.GetExtension( allFiles[i] ).ToLowerInvariant() ) ){bool isExcluded = false;if( excludedPaths.Length > 0 ){// Make sure the image file isn't part of an excluded directorystring fileFullPath = Path.GetFullPath( allFiles[i] );for( int j = 0; j < excludedPaths.Length; j++ ){if( fileFullPath.StartsWith( excludedPaths[j] ) ){isExcluded = true;break;}}}if( !isExcluded )texturePaths.Add( allFiles[i] );}}string[] result = new string[texturePaths.Count];texturePaths.CopyTo( result );return result;}// Creates dummy Texture asset that will be used to read Textures' pixelsprivate void CreateDummyTexture(){if( !File.Exists( DUMMY_TEXTURE_PATH ) ){File.WriteAllBytes( DUMMY_TEXTURE_PATH, new Texture2D( 2, 2 ).EncodeToPNG() );AssetDatabase.ImportAsset( DUMMY_TEXTURE_PATH, ImportAssetOptions.ForceUpdate );}TextureImporter textureImporter = AssetImporter.GetAtPath( DUMMY_TEXTURE_PATH ) as TextureImporter;textureImporter.maxTextureSize = maxTextureSize;textureImporter.isReadable = true;textureImporter.filterMode = FilterMode.Point;textureImporter.mipmapEnabled = false;textureImporter.alphaSource = TextureImporterAlphaSource.FromInput;textureImporter.npotScale = TextureImporterNPOTScale.None;textureImporter.textureCompression = TextureImporterCompression.Uncompressed;textureImporter.SaveAndReimport();}
}

这篇关于[Unity]将所有 TGA、TIFF、PSD 和 BMP(可自定义)纹理转换为 PNG,以减小项目大小,而不会在 Unity 中造成任何质量损失的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/282715

相关文章

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

C#中图片如何自适应pictureBox大小

《C#中图片如何自适应pictureBox大小》文章描述了如何在C#中实现图片自适应pictureBox大小,并展示修改前后的效果,修改步骤包括两步,作者分享了个人经验,希望对大家有所帮助... 目录C#图片自适应pictureBox大小编程修改步骤总结C#图片自适应pictureBox大小上图中“z轴

通过C#获取PDF中指定文本或所有文本的字体信息

《通过C#获取PDF中指定文本或所有文本的字体信息》在设计和出版行业中,字体的选择和使用对最终作品的质量有着重要影响,然而,有时我们可能会遇到包含未知字体的PDF文件,这使得我们无法准确地复制或修改文... 目录引言C# 获取PDF中指定文本的字体信息C# 获取PDF文档中用到的所有字体信息引言在设计和出

Python 中 requests 与 aiohttp 在实际项目中的选择策略详解

《Python中requests与aiohttp在实际项目中的选择策略详解》本文主要介绍了Python爬虫开发中常用的两个库requests和aiohttp的使用方法及其区别,通过实际项目案... 目录一、requests 库二、aiohttp 库三、requests 和 aiohttp 的比较四、requ

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne