ScreenPocket - 画面の隙間

Unityエンジニアの日々の雑記。たまにpython3、DirectX、PhotoshopScript(JavaScript)も触ります

OnGUI()内でEditorGUILayout.TextField()を呼ぶと例外

タイトルで完結してしまいましたが、この間やった凡ミスのメモ書き。

using UnityEngine;
using UnityEditor;

#if DEVELOPMENT_BUILD || UNITY_EDITOR

namespace ScreenPocket
{
	public class Hoge : MonoBehaviour
	{
		string inputText;
		void OnGUI()
		{
			inputText = EditorGUILayout.TextField(inputText);
		}
	}
}
#endif

と書いてしまって、NullReferenceExceptionを起こしてしまったので同じミスをしないようにメモ。
すぐには例外が出なくて、クリックした時に内部で例外を発するので気づくのが遅れた;

EditorGUILayout → GUILayout
にしましょう。

大きなサイズのpngテクスチャをMultipleSpriteで分割する

お久しぶりです。
マイルストーンやら転職やら、色々ありまして、半年ほど時期が開いてしまいました。

現在転職前の有給消化期間なので、趣味のプログラムを組んでいたのですが、
こちらのアセット
https://assetstore.unity.com/packages/2d/gui/cartoon-ui-pack-200-sprites-70518
を買った所、めちゃくちゃデカい画像だったので、ちょっと分割したいなぁと思い、Unity上で分割できないかどうかを試してみることにしました。

問題点は下記
・サイズが 3981x16104px と膨大
・UnityのTexture2Dアセットとして、Assetdatabase.LoadAsset()で読み込むと、2048(とか4096とかの設定した最大サイズ)で読み込まれてしまう。。
・どうせ分割するなら、解像度も担保したいし元々のサイズを基準に分割したい!

という事で、下記のスクリプトを作成しました。

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

namespace ScreenPocket
{
	public class TextureUtility
	{
		[MenuItem("Assets/ScreenPocket/Texture/DivideBySprite")]
		static void DivideTextureBySprite()
		{
			Object[] selections = Selection.GetFiltered(typeof(Object), SelectionMode.Assets | SelectionMode.DeepAssets);
			List<Texture2D> targets = new List<Texture2D>(selections.Length);
			foreach (var @object in selections)
			{
				var texture = @object as Texture2D;
				if (texture == null)
				{
					continue;
				}
				targets.Add(texture);
			}

			if (targets.Count == 0)
			{
				EditorUtility.DisplayDialog("Error", "NotFound Texture", "OK");
				return;
			}

			foreach ( var targetTexture in targets)
			{
				var path = AssetDatabase.GetAssetPath(targetTexture);
				var importer = (TextureImporter)TextureImporter.GetAtPath( path );
				if (importer.spriteImportMode != SpriteImportMode.Multiple)
				{
					EditorUtility.DisplayDialog("Error", "Not Multiple Speite" + importer.name, "OK");
					continue;
				}

				//テクスチャ名と同一名のディレクトリを追加する
				var createDirectryPath = Path.GetDirectoryName(path) + "/" + Path.GetFileNameWithoutExtension(path);
				if (!Directory.Exists(createDirectryPath))
				{
					Directory.CreateDirectory(createDirectryPath);
				}

				var isReadable = importer.isReadable;
				importer.isReadable = true;
				if (!isReadable)
				{
					importer.SaveAndReimport();
				}

				var originalPngTexture = ReadPng(path);

				var spriteMetaDatas = importer.spritesheet;
				foreach (var spriteMetaData in spriteMetaDatas)
				{
					var spriteRect = spriteMetaData.rect;
					int x = Mathf.RoundToInt(spriteRect.x);
					int y = Mathf.RoundToInt(spriteRect.y);
					int w = Mathf.RoundToInt(spriteRect.width);
					int h = Mathf.RoundToInt(spriteRect.height);

					Texture2D newSpriteTexture = new Texture2D( w, h );
					newSpriteTexture.SetPixels(originalPngTexture.GetPixels(x,y,w,h));

					var pngData = newSpriteTexture.EncodeToPNG();
					string filePath = GetSavePath(createDirectryPath, spriteMetaData.name);
					File.WriteAllBytes(filePath, pngData);
				}

				//Readableを戻す
				importer.isReadable = isReadable;
				if (!isReadable)
				{
					importer.SaveAndReimport();
				}

				//TextureImporterを作るためにリフレッシュ
				AssetDatabase.Refresh();

				//BorderとPivotを元のSpriteと合わせる
				foreach (var spriteMetaData in spriteMetaDatas)
				{
					string filePath = GetSavePath( createDirectryPath, spriteMetaData.name);
					var newImporter = (TextureImporter)TextureImporter.GetAtPath(filePath);

					bool isNeedSave = false;
					if (newImporter.spriteBorder != spriteMetaData.border)
					{
						newImporter.spriteBorder = spriteMetaData.border;
						isNeedSave = true;
					}
					TextureImporterSettings settings = new TextureImporterSettings();
					newImporter.ReadTextureSettings(settings);
					if (settings.spritePivot != spriteMetaData.pivot)
					{
						settings.spritePivot = spriteMetaData.pivot;
						settings.spriteAlignment = spriteMetaData.alignment;
						newImporter.SetTextureSettings(settings);
						isNeedSave = true;
					}

					//大本のテクスチャのImporterと設定を合わせたいなら、ここで値を代入する処理を追加する

					if (isNeedSave)
					{
						newImporter.SaveAndReimport();
					}
				}
			}

			AssetDatabase.SaveAssets();
		}

		private static string GetSavePath(string parentDirectryName, string spriteMetaDataName)
		{
			return parentDirectryName + "/" + spriteMetaDataName + ".png";
		}

		/// <summary>
		/// pngファイルをそのまま読み込む
		/// ↓参考サイト
		/// http://macomu.sakura.ne.jp/blog/?p=55
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		static byte[] ReadPngFile(string path)
		{
			FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
			BinaryReader bin = new BinaryReader(fileStream);
			byte[] values = bin.ReadBytes((int)bin.BaseStream.Length);
			bin.Close();
			return values;
		}

		/// <summary>
		/// 対象パスのpngをそのまま読み込んでTexture2Dにする
		/// ※Texture2Dアセットを読み込むわけではないのでテクスチャサイズの制限を越えて取得できる
		/// ↓参考サイト
		/// https://qiita.com/r-ngtm/items/6cff25643a1a6ba82a6c
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		static Texture2D ReadPng(string path)
		{
			byte[] readBinary = ReadPngFile(path);

			int pos = 16; // 16バイトから開始

			int width = 0;
			for (int i = 0; i < 4; i++)
			{
				width = width * 256 + readBinary[pos++];
			}

			int height = 0;
			for (int i = 0; i < 4; i++)
			{
				height = height * 256 + readBinary[pos++];
			}

			Texture2D texture = new Texture2D(width, height);
			texture.LoadImage(readBinary);

			return texture;
		}
	}
}

使用する際はProjectツリーで該当のテクスチャを右クリックして ScreenPocket > Texture > DivideBySprite を実行して下さい。
MultipleSpriteのテクスチャと同名のディレクトリを作成し、その中にスプライト名をファイル名としたpngを列挙します。

参考サイトは下記
UnityでPNGファイルを動的に読み込む方法 | ma_comu雑記帳
【Unity】pngファイルを読み込み同サイズのTexture2Dを生成する - Qiita

pngをそもそもTexture2Dアセットではなく、直接読み込んだ上でTexture2D化したい、という事で、上記のサイトのコードを使わせていただきました。
とりあえず上記でファイルを分割しつつ、MultipleSprite時のBorderとか、Pivotを引き継いだテクスチャが作成されるかと思います。

上記のコードで懸念としては、
Mathf.RoundToInt()で、ピクセルが少しずれてしまわないか?という点と、
Importer設定を完全に引き継いでいないので、もうちょっとしっかりコピーしたい点でしょうか。

使用する際は良い感じに書き換えてあげて下さいmm

CEPHtmlEngineによる高い使用率による問題、の解決策ファイル(DL224.zip)のある場所

全くプログラム関係ないですが、新年早々イライラ案件があったのでメモ。

家のPCでやけにCPU使用率が高く、Adobe製品を立ち上げた覚えもないのに CEPHtmlEngine タスクの使用率が高いことがわかりました。
Photoshopのみインストール済み

で、見つけた記事がこちら。
helpx.adobe.com

解決策1の
>%USERPROFILE%\AppData\Local\Adobe\Creative Cloud Libraries\Logs
フォルダはなかったので、解決策2を参照。

指示通りにフォルダをバックアップを取って、DL224.zipをDLしようとしたら404が…。

どうせ日本語訳の際のリンク貼りミスだろう…、と思ったら案の定、元になったっぽいページから取得することが出来ました。
helpx.adobe.com
↑このページ内のDL224.zipリンクは生きてるっぽい

同じ探す苦労をする人が少しでも減ると良いですね;

SharedBetweenAnimatorsAttribute メモ

SharedBetweenAnimatorsAttribute を指定すると、メモリフットプリントを削減できるそうな。

docs.unity3d.com

ただ、変数の変更が他のAnimatorにも影響しちゃうみたい?
なので、振る舞いだけを書いた StateMachineBehaviour派生クラスだったら指定してみるのも良いかもしれませんね。

Photoshop クリッピングマスクをOn/Offするスクリプト

ScriptListenerでレコったのをメモ。

選択中レイヤーのクリッピングマスクON

var idGrpL = charIDToTypeID( "GrpL" );
    var desc13 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref7 = new ActionReference();
        var idLyr = charIDToTypeID( "Lyr " );
        var idOrdn = charIDToTypeID( "Ordn" );
        var idTrgt = charIDToTypeID( "Trgt" );
        ref7.putEnumerated( idLyr, idOrdn, idTrgt );
    desc13.putReference( idnull, ref7 );
executeAction( idGrpL, desc13, DialogModes.NO );

選択中レイヤーのクリッピングマスクOFF

var idUngr = charIDToTypeID( "Ungr" );
    var desc11 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref6 = new ActionReference();
        var idLyr = charIDToTypeID( "Lyr " );
        var idOrdn = charIDToTypeID( "Ordn" );
        var idTrgt = charIDToTypeID( "Trgt" );
        ref6.putEnumerated( idLyr, idOrdn, idTrgt );
    desc11.putReference( idnull, ref6 );
executeAction( idUngr, desc11, DialogModes.NO );

後は、選択中のArtLayerがクリッピングマスクかどうか判定できればトグルも作れそうですね

Photoshop CC用Scripting Listenerのダウンロード

地味に迷ったのでリンク紹介だけ

Adobe Photoshop Scripting | Adobe Developer Connection

現状このページの中段くらいにあります。

と思ったら、こっちにもあった。

helpx.adobe.com

後は

qiita.com

この辺とか

オリジナルのスクリプトの作り方 | Dearps

この辺を見ればインストールできるんじゃないかと。

Eclipseから、自作ExtensionBuilderプロジェクトをPhotoshop2015.5で実行する方法

またしてもPhotoshopネタ。
EclipseからPhotoshopのExtensionBuilderの実行に手間取ったのでメモ。

ここに書いておるとおりに、
labs.adobe.com
から、ExtensionBuilder3をダウンロードする。
※zipが落ちてくるけど解凍は不要

  • Eclipseをダウンロードしろ…と書いてあるけれども、その前にJDKをダウンロード(後で必要になるので)

Java SE - Downloads | Oracle Technology Network | Oracle

www.eclipse.org

  • 書いてある通り Help > Install New Software
  • Add... > Archive 選択して先ほどダウンロードしたzipを選択
  • チェックを入れてインストール
  • この辺でEclipse再起動がかかる
  • File > New > Project... 別ウィンドウ開く > Extension Builder 3 > Application Extension Project
  • プロジェクト設定してプロジェクトを作成
  • で、書いてある通りプロジェクトフォルダを右クリックして Run As を選択したのですがエラーダイアログが…

f:id:ScreenPocket:20160918014511p:plain

  • ここで詰まったのですが、結論としては 64bit Windows の場合はパスを入れなおさなければならないっぽい。

forums.adobe.com
f:id:ScreenPocket:20160918020532p:plain

  • 更にエラーが出るのでもういっちょパス合わせ

f:id:ScreenPocket:20160918014935p:plain
CEPServiceBilder4はなかったけど、CEPフォルダで良いっぽいので、それを指定。
赤枠を付け忘れたけど、下のユーザ用フォルダもCEPフォルダに変えておく

コレでとりあえず実行だけはできるようにはなりました…が、まだ自作プロジェクトのエクステンションパネルは出ない模様。

色々調べた結果、マニフェストのバージョンID指定が不足しているようでした。

blogs.adobe.com
を参考に、

  • プロジェクト右クリック→Adobe ExtensionBuilder3 > ManifestEditor
  • ウィンドウが開くのでmanifest.xmlタブを選択し、右クリックXmlEditorを起動

HTML Panel Tips #21: Photoshop CC 2015.5 survival guide | Photoshop, etc.

  • こちらを参考に "14.0" に書き換え(ついでにマニフェストバーションも5.0に)

で、完了!
f:id:ScreenPocket:20160918025058p:plain
ようやくスタートラインです;