PR

【Unity】複数解像度スクリーンショットを一括で撮影する拡張機能

Unity複数解像度スクリーンショットを一括で撮影する拡張機能 Unity

転載防止用

TedLab_CaptureScreenshotEditor.cs

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

namespace TedLab
{
    public class CaptureScreenShotWindow : EditorWindow
    {
        private const float settingSizeWaitTime = 1f;

        // iOS用の解像度リスト
        static CaptureResolution[] resolutions_IOS =
        {
            new CaptureResolution(1242, 2208),
            new CaptureResolution(1242, 2688),
            new CaptureResolution(2048, 2732),
        };

        [MenuItem("TedLab Editor/スクリーンショットキャプチャ")]
        static void OpenWindow()
        {
            GetWindow(true, "[TedLab]スクリーンショットキャプチャ");
        }

        [Serializable]
        class CaptureResolution{
            public CaptureResolution(int _width, int _height){
                width = _width;
                height = _height;
            }
            public int width = 1920;
            public int height = 1080;
        }

        static readonly string KeyPrefix = "TedLab_CaptureScreenShot_";
        static readonly string PathKey = KeyPrefix + "Path";
        static readonly string ReverseKey = KeyPrefix + "Reverse";
        static readonly string StopTimeKey = KeyPrefix + "StopTime";
        static readonly string ResoCountKey = KeyPrefix + "ResoCount";
        static readonly string ResoWidthBaseKey = KeyPrefix + "ResoW_";
        static readonly string ResoHeightBaseKey = KeyPrefix + "ResoH_";

        string m_path = "";
        bool m_started = false;
        bool m_reverse = false;
        bool m_stopTime = true;

        [SerializeField] List captureResolutions = null;
        SerializedObject resolutionSerializedObj;
        SerializedProperty resolutionProp;

        EditorCoroutine m_coroutine = null;
        int m_selectedIndexOld = -1;
        GameViewSizeGroupType m_currentGroupType;

        private void OnEnable()
        {
            captureResolutions = new List(32);

            // 読み込み
            m_path = EditorPrefs.GetString( PathKey, "" );
            m_reverse = EditorPrefs.GetBool( ReverseKey, false );
            m_stopTime = EditorPrefs.GetBool( StopTimeKey, true );

            int reso_count = EditorPrefs.GetInt( ResoCountKey, 0 );
            for( int i=0; i<reso_count; ++i ) { string indexText = i.ToString("D2"); int width = EditorPrefs.GetInt( ResoWidthBaseKey + indexText, 0 ); int height = EditorPrefs.GetInt( ResoHeightBaseKey + indexText, 0); if( width > 0 && height > 0 ){
                    captureResolutions.Add( new CaptureResolution(width, height));
                }
            }

            resolutionSerializedObj = new SerializedObject(this);
            resolutionProp= resolutionSerializedObj.FindProperty("captureResolutions");
        }

        private void OnDisable()
        {
            if( m_coroutine != null )
            {
                m_coroutine.Stop();
                m_coroutine = null;
            }

            if( m_timeScaleOld >= 0f ){
                Time.timeScale = m_timeScaleOld;
                m_timeScaleOld = -1f;
            }

            // 保存
            EditorPrefs.SetString( PathKey, m_path );
            EditorPrefs.SetBool( ReverseKey, m_reverse );
            EditorPrefs.SetBool( StopTimeKey, m_stopTime );

            int reso_count = captureResolutions.Count;
            EditorPrefs.SetInt( ResoCountKey, reso_count );
            for( int i=0; i<reso_count; ++i )
            {
                var reso = captureResolutions[i];
                string indexText = i.ToString("D2");
                EditorPrefs.SetInt( ResoWidthBaseKey + indexText, reso.width );
                EditorPrefs.SetInt( ResoHeightBaseKey + indexText, reso.height );
            }
            
        }

        void OnGUI()
        {
            resolutionSerializedObj.Update();

            if( m_started )
            {
                GUILayout.Label("スクリーンショットを撮影中...");
            }
            else
            {
                GUILayout.Label("設定:");
                m_reverse = GUILayout.Toggle( m_reverse, "縦横反転" );
                m_stopTime = GUILayout.Toggle( m_stopTime, "撮影時に止める(TimeScale)" );

                GUILayout.Space(16f);

                GUILayout.Label("解像度リスト:");
                EditorGUILayout.PropertyField(resolutionProp, true);
                resolutionSerializedObj.ApplyModifiedProperties();

                if (GUILayout.Button("解像度をクリア")) {
                    captureResolutions.Clear();
                }

                if (GUILayout.Button("iOS解像度を追加")) {
                    AddResolutions( resolutions_IOS );
                }

                GUILayout.Space(16f);

                GUILayout.Label("保存フォルダ:");
                m_path = GUILayout.TextField(m_path);

                if (GUILayout.Button("フォルダを開く"))
                {
                    string filePath = m_path;
                    filePath = filePath.Replace( '/', Path.DirectorySeparatorChar );
                    EditorUtility.RevealInFinder(filePath);
                }

                if (GUILayout.Button("マニュアルを開く")) {
                    OpenManual();
                }
                if (GUILayout.Button("スクリーンショットを保存")) {
                    Capture();
                }
            }
        }

        void AddResolutions( CaptureResolution[] resolutions )
        {
            foreach( var reso in resolutions ){
                captureResolutions.Add( reso );
            }
        }

        void Capture()
        {
            if( m_started){
                Debug.Log( "Already Started Capture..." );
                return;
            }

            // 解像度指定がない場合はそのまま
            if( captureResolutions.Count <= 0 )
            {
                string dateString = DateTime.Now.ToString("yyyyMMddHHmmss");
                string filePath = string.Format( Path.Combine(m_path, "ScreenCapture_{0}.png"), dateString );
                Debug.Log(string.Format("Saved a new screenshot as {0}", filePath));
                ScreenCapture.CaptureScreenshot(filePath);
            }
            // 複数キャプチャ
            else
            {
                m_coroutine = EditorCoroutine.Start( MultipleCaptureScreen() );
            }
        }

        void OpenManual()
        {
            string url = "https://tedenglish.site/unity-ex-multi-screenshots/";
            Application.OpenURL( url );
        }

        
        float m_timeScaleOld = -1f;

        IEnumerator MultipleCaptureScreen()
        {
            m_started = true;

            string TmpLabel = "TedLabScreenCaptureResoTemp";

            m_timeScaleOld = Time.timeScale;
            if( m_stopTime ){
                Time.timeScale = 0f;
            }

            foreach( CaptureResolution reso in captureResolutions )
            {
                int width = m_reverse ? reso.height : reso.width;
                int height = m_reverse ? reso.width : reso.height;

                string dateString = DateTime.Now.ToString("yyyyMMddHHmmss");
                string filePath = string.Format( Path.Combine(m_path, "ScreenCapture_{0}_{1}.png"), dateString, width.ToString() + "x" +  height.ToString() );
                Debug.Log( string.Format("Save a new screenshot as {0}", filePath) );

                StartCustomSize( TmpLabel, width, height );
                yield return settingSizeWaitTime;

                // キャプチャ&待つ
                ScreenCapture.CaptureScreenshot(string.Format(filePath));

                while( !IsSaved( filePath ) ){
                    yield return 0.1f;
                }

                EndCustomSize( TmpLabel, width, height );
            }

            Debug.Log( "Saved All Captures!!" );

            if( m_stopTime ){
                Time.timeScale = m_timeScaleOld;
                m_timeScaleOld = -1f;
            }

            m_started = false;
            m_coroutine = null;

            yield return 0f;
        }

        // UnityEditor.GameViewSizeTypeと合わせる
        public enum GameViewSizeType
        {
            AspectRatio = 0,
            FixedResolution = 1,
        }

        void StartCustomSize( string name, int width, int height )
        {
            var asm = typeof(UnityEditor.Editor).Assembly;
            Type gameViewType = asm.GetType("UnityEditor.GameView");
            Type gameViewSize = asm.GetType("UnityEditor.GameViewSize");
            Type gameViewSizes = asm.GetType("UnityEditor.GameViewSizes");
            Type gameViewSizeType = asm.GetType("UnityEditor.GameViewSizeType");
            Type gameViewSizeGroup = asm.GetType("UnityEditor.GameViewSizeGroup");

            EditorWindow gameView = EditorWindow.GetWindow(gameViewType, false, "Game", false);

            PropertyInfo currentSizeGroupType = gameViewType.GetProperty("currentSizeGroupType", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            m_currentGroupType = (GameViewSizeGroupType)currentSizeGroupType.GetValue(gameView, null);

            MethodInfo getGroup = gameViewSizes.GetMethod("GetGroup");
            Type scriptableSingleton = typeof(ScriptableSingleton<>).MakeGenericType(gameViewSizes);
            PropertyInfo scriptableSingletonInstance = scriptableSingleton.GetProperty("instance");
            object gameViewSizesInstance = scriptableSingletonInstance.GetValue(null, null);
            object group = getGroup.Invoke(gameViewSizesInstance, new object[] { m_currentGroupType });

            Type[] paramTypes = new Type[] { gameViewSize };
            MethodInfo addCustomSize = gameViewSizeGroup.GetMethod("AddCustomSize", BindingFlags.Public | BindingFlags.Instance, null, paramTypes, null);
            ConstructorInfo constructor = gameViewSize.GetConstructor( new Type[] { gameViewSizeType, typeof(int), typeof(int), typeof(string) } );

            // インデックスを保持しておく
            PropertyInfo selectedSizeIndex = gameViewType.GetProperty("selectedSizeIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            m_selectedIndexOld = (int)selectedSizeIndex.GetValue( gameView, null );

            // 追加
            {
                object newSize = constructor.Invoke(new object[] { (int)GameViewSizeType.FixedResolution, width, height, name });
                addCustomSize.Invoke(group, new object[] { newSize });
            }

            // 追加したのをインデックス設定
            int index = FindSameResolution( group, name, width, height );
            if( index >= 0 ){
                selectedSizeIndex.SetValue( gameView, index, null);
            }
        }

        void EndCustomSize( string name, int width, int height )
        {
            var asm = typeof(UnityEditor.Editor).Assembly;
            Type gameViewSize = asm.GetType("UnityEditor.GameViewSize");
            Type gameViewSizes = asm.GetType("UnityEditor.GameViewSizes");
            Type gameViewSizeType = asm.GetType("UnityEditor.GameViewSizeType");
            Type gameViewSizeGroup = asm.GetType("UnityEditor.GameViewSizeGroup");

            MethodInfo getGroup = gameViewSizes.GetMethod("GetGroup");
            Type scriptableSingleton = typeof(ScriptableSingleton<>).MakeGenericType(gameViewSizes);
            PropertyInfo scriptableSingletonInstance = scriptableSingleton.GetProperty("instance");
            object gameViewSizesInstance = scriptableSingletonInstance.GetValue(null, null);
            object group = getGroup.Invoke(gameViewSizesInstance, new object[] { m_currentGroupType });

            Type[] paramTypes = new Type[] { typeof(int) };
            MethodInfo removeCustomSize = gameViewSizeGroup.GetMethod("RemoveCustomSize", BindingFlags.Public | BindingFlags.Instance, null, paramTypes, null);

            // 同名のインデックスを探す
            int index = FindSameResolution( group, name, width, height );
            if( index >= 0 ){
                removeCustomSize.Invoke( group, new object[] { index } );
            }

            // 元に戻す
            Type gameViewType = asm.GetType("UnityEditor.GameView");
            PropertyInfo selectedSizeIndex = gameViewType.GetProperty("selectedSizeIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            EditorWindow gameView = EditorWindow.GetWindow(gameViewType, false, "Game", false);
            selectedSizeIndex.SetValue( gameView, m_selectedIndexOld, null);
        }

        // 同名のインデックスを探す
        int FindSameResolution( object group, string name, int width, int height )
        {
            int index = -1;
            var getDisplayTexts = group.GetType().GetMethod("GetDisplayTexts");
            var displayTexts = getDisplayTexts.Invoke(group, null) as string[];
            string targetName = string.Format("{0} ({1}x{2})", name, width, height);
            for(int i=0; i<displayTexts.Length; ++i)
            {
                string textTmp = displayTexts[i];
                if( textTmp == targetName ){
                    index = i;
                    break;
                }
            }
            return index;
        }

        // 保存されているか
        bool IsSaved( string path )
        {
            bool isExist = File.Exists( path );
            bool isLocked = false;
            FileStream stream = null;
            try{
                stream = new FileStream( path, FileMode.Open, FileAccess.ReadWrite, FileShare.None );
            }catch{
                isLocked = true;
            }finally{
                if( stream != null ){
                    stream.Close();
                }
            }
            return isExist && !isLocked;
        }

        // コルーチン用
        public class EditorCoroutine
        {
            public static EditorCoroutine Start( IEnumerator _routine )
	        {
		        var coroutine = new EditorCoroutine(_routine);
		        coroutine.Start();
		        return coroutine;
	        }
            readonly IEnumerator routine;
            float _waitTimer = 0f;
            double _prevTime = 0;
		    EditorCoroutine( IEnumerator _routine ){
			    routine = _routine;
		    }

            void Start(){
			    EditorApplication.update += Update;
                _prevTime = EditorApplication.timeSinceStartup;
		    }
		    public void Stop(){
			    EditorApplication.update -= Update;
		    }
		    void Update()
            {
                var time = EditorApplication.timeSinceStartup;
                var deltaTime = (float)(time - _prevTime);
                _prevTime = time;

                if(_waitTimer > 0f){
                    _waitTimer -= deltaTime;
                    if(_waitTimer > 0f){
                        return;
                    }
                }
			    if (routine.MoveNext()){
                    _waitTimer = routine.Current;
                }else{
				    Stop();
			    }
		    }
        }
    }

} // namespace TedLab

 

コメント

スポンサーリンク
Ads Blocker Image Powered by Code Help Pro

Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

タイトルとURLをコピーしました