[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

相关文章

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

利用Python脚本实现批量将图片转换为WebP格式

《利用Python脚本实现批量将图片转换为WebP格式》Python语言的简洁语法和库支持使其成为图像处理的理想选择,本文将介绍如何利用Python实现批量将图片转换为WebP格式的脚本,WebP作为... 目录简介1. python在图像处理中的应用2. WebP格式的原理和优势2.1 WebP格式与传统

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再