#はじめに
Assetを購入した時によくある 「テクスチャでっか!!!」 問題。
ビルド時のサイズはTextureのImporter設定で減らすことが出来ますが、
それだと元データのサイズが大きいままなので、サーバにアップする時にもデータサイズが不要に大きいままになってしまい容量がかさんでしまいます。。
GitにPushする前にTextureのアセット原本自体を半分の解像度にしてデータサイズを減らしたい・・・
けど わざわざpngを一つ一つPhotoshopで開いて解像度を半分にして保存していく手間は掛けたくない・・・
というわがままを叶えるために、Unityのエディタ拡張でテクスチャアセットの解像度を半分にするスクリプトを作成しました。
コード
という事でコードです
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace ScreenPocket.Editor
{
public static class ResizeTexture
{
/// <summary>
/// 選択中のTextureアセットをすべて半分の解像度にする
/// </summary>
[MenuItem("Assets/ScreenPocket/Texture/ResizeHalfSize")]
private static void ResizeTextureHalfSize()
{
//↓この3行 var targets = Selection.GetFiltered(typeof(Texture2D), SelectionMode.Assets | SelectionMode.DeepAssets); の1行でまとめられそう
var selections = Selection.GetFiltered(typeof(Object), SelectionMode.Assets | SelectionMode.DeepAssets);
var targets = new List<Texture2D>(selections.Length);
targets.AddRange(selections.OfType<Texture2D>());
if (targets.Count == 0)
{
EditorUtility.DisplayDialog("Error", "NotFound Texture", "OK");
return;
}
foreach (var targetTexture in targets)
{
ResizeTextureHalfSize(targetTexture);
}
//最後に保存
AssetDatabase.SaveAssets();
}
/// <summary>
/// 渡されたTextureを半分の解像度にして上書きする
/// </summary>
/// <param name="targetTexture"></param>
private static void ResizeTextureHalfSize(Texture2D targetTexture)
{
//パス取得
var targetTexturePath = AssetDatabase.GetAssetPath(targetTexture);
//設定変更用にImporterを確保
var textureAssetImporter = AssetImporter.GetAtPath(targetTexturePath);
if (textureAssetImporter is not TextureImporter textureImporter)
{
return;
}
//設定を元に戻す用に保持しておく
var compression = textureImporter.textureCompression;
var textureType = textureImporter.textureType;
//設定変更
//念のため無圧縮に
textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
//Defaultに変えておかないとNormalMapが変色してしまう
textureImporter.textureType = TextureImporterType.Default;
textureImporter.SaveAndReimport();
//半分サイズ
var halfWidth = targetTexture.width / 2;
var halfHeight = targetTexture.height / 2;
var rt = new RenderTexture(halfWidth, halfHeight,0);
rt.Create();
var renderTargetActive = RenderTexture.active;
RenderTexture.active = rt;
UnityEngine.Graphics.Blit(targetTexture, rt);
var newTexture = new Texture2D(halfWidth, halfHeight, targetTexture.format, textureImporter.mipmapEnabled);
//読み取る
newTexture.ReadPixels(new Rect(0,0,halfWidth,halfHeight),0,0);
newTexture.Apply();
RenderTexture.active = renderTargetActive;
rt.Release();
var bytes = newTexture.EncodeToPNG();
//データは吸いだし完了したので即座に削除
Object.DestroyImmediate( newTexture );
//元のテクスチャPathにpngデータを書き出し
File.WriteAllBytes(targetTexturePath, bytes);
//Importし直し(直後にtextureImporter.SaveAndReimport()があるので不要かも)
AssetDatabase.ImportAsset(targetTexturePath);
//設定を元に戻す
textureImporter.textureType = textureType;
textureImporter.textureCompression = compression;
textureImporter.SaveAndReimport();
}
}
}
ポイントは
- コピー前に textureImporter で色々設定が必要だった
- 圧縮したうえで縮小する事で想定以上に画像が汚くなってしますことを避けるために無圧縮設定
- NormalMapで変色する現象が起こってしまったので一旦Defaultタイプに戻す
- 現状だとSpriteの領域などは壊れてしまう事が予想される。各座標値を半分にすれば良い感じになると思うのでカスタマイズしてください(未検証)
- コピーはRenderTextureにBlitして、ReadPixelする事で達成可能だった
- Graphics.ConvertTexture()が使えるかな?と思ったんだけど、どうやらGPU側にしか作用せずCPU側処理(EncordToPNG()など)には作用できないっぽい
- Graphics.CopyTexture()も無理っぽかった。
- 仕方が無いので参考資料のRenderTextureを使うコードで対応
- 当然非可逆です
- もし解像度を下げてはならない可能性があるなら、処理実行前に元に戻せるようにしておくように気を付けましょう
- と言ってもリサイズ前にPushしてしまうと結局サーバに容量が掛かってしまうので、例えばPackageManagerなどから上書きで戻せるようにしておくとか…
- もし解像度を下げてはならない可能性があるなら、処理実行前に元に戻せるようにしておくように気を付けましょう
- pngのみ対応
- tgaやjpg、bmpはカスタムしてご対応ください
な感じでしょうか。
使用する際は「Projectツリービューでテクスチャを選択 > 右クリック > ScreenPocket > Texture > ResizeHalf」を選択してください。
#終わりに
ランタイム&ビルド時のサイズ調整はTextureImporterからできるのですが、そもそものサイズを減らしたい時の一助になれば幸いです。
使いもしないサンプルデータに、余分にデータサイズ取られるのも勿体ないですしね。
#参考資料