Open3

Unityのコードメモ

s_ryuukis_ryuuki

UnityのEditor拡張でコンソールコマンドを動かす方法。
Editorが固まらないようにTaskで実行する。
コマンドの「console_test」部分を変更して試しましょう。

using System;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using Debug = UnityEngine.Debug;

public class TestConsole
{
   private const int DefaultTimeoutInMilliseconds = 500000;
   
   private class ProcessRunnerResult
   {
       public bool Success { get; set; }
       public string Output { get; set; }
       public string Error { get; set; }
   }
   
   [MenuItem("Hoge/Test Console1")]
   static void TestConsole1()
   {
      Debug.Log("RunTask start");
      RunTask((log) =>
      {
          Debug.Log(log);
      });
      Debug.Log("RunTask End");
   }
   
   static async void RunTask(Action<string> finish)
   {
      var processStartInfo = new ProcessStartInfo
      {
          FileName = "console_test",
          Arguments = "",
          UseShellExecute = false,
          RedirectStandardOutput = true,
          RedirectStandardError = true,
          CreateNoWindow = true
      };

      try
      {
          var result = await StartAndWaitForExitAsync(processStartInfo, 50000, Debug.Log);
          Debug.Log($"Process execution {(result.Success ? "succeeded" : "failed")}");
          Debug.Log($"Output: {result.Output}");
          Debug.Log($"Error: {result.Error}");
          finish("finish!");
      }
      catch (Exception e)
      {
          Debug.LogError($"Error executing process: {e.Message}");
      }
   }
   
   static async Task<ProcessRunnerResult> StartAndWaitForExitAsync(ProcessStartInfo processStartInfo,
       int timeoutMs = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null)
   {
       var tcs = new TaskCompletionSource<ProcessRunnerResult>();
       
       await Task.Run(() =>
       {
           using var process = new Process();
           process.StartInfo = processStartInfo;
           var sbOutput = new StringBuilder();
           var sbError = new StringBuilder();
           
           process.OutputDataReceived += (_, e) =>
           {
               if (e.Data == null) return;
               sbOutput.AppendLine(e.Data);
               onOutputReceived?.Invoke(e.Data);
           };
           process.ErrorDataReceived += (_, e) =>
           {
               if (e.Data == null) return;
               sbError.AppendLine(e.Data);
           };
           
           process.Start();
           process.BeginOutputReadLine();
           process.BeginErrorReadLine();
           
           if (process.WaitForExit(timeoutMs))
           {
               tcs.SetResult(new ProcessRunnerResult
               {
                   Success = true,
                   Error = sbError.ToString(),
                   Output = sbOutput.ToString()
               });
               return;
           }
           
           try
           {
               process.Kill();
           }
           catch
           {
               // プロセスの終了に失敗した場合は無視
           }
           
           tcs.SetResult(new ProcessRunnerResult
           {
               Success = false,
               Error = "Process timed out",
               Output = sbOutput.ToString()
           });
       });
       
       return await tcs.Task;
   }
}
s_ryuukis_ryuuki

概要

Addressableで内部的に割り当てられるdependencyKeyに関するメモ。
Addressable Groupにフォルダ登録で登録したり、Pack Together設定をするとdependencyKeyが同じになる。Pack Separately設定でもフォルダ登録した場合はdependencyKeyが同じになる。

参考

コード

コードはAddressables | 1.22.2を参照している。

Library/PackageCache/com.unity.addressables@1.22.2/Runtime/ResourceLocators/ContentCatalogData.cs

m_DependencyにdependencyKeyが入っている。

        class CompactLocation : IResourceLocation
        {
            ResourceLocationMap m_Locator;
            string m_InternalId;
            string m_ProviderId;
            object m_Dependency;  //これに入っている。getメソッドを作りCatalogダンプ時に表示する
            object m_Data;

m_Dependencyを使用してAssetを引いている

            public IList<IResourceLocation> Dependencies
            {
                get
                {
                    if (m_Dependency == null)
                        return null;
                    IList<IResourceLocation> results;
                    m_Locator.Locate(m_Dependency, typeof(object), out results); //ここ
                    return results;
                }
            }

dependencyKeyの初期化

            public CompactLocation(ResourceLocationMap locator, string internalId, string providerId, object dependencyKey, object data, int depHash, string primaryKey, Type type)
            {
                m_Locator = locator;
                m_InternalId = internalId;
                m_ProviderId = providerId;
                m_Dependency = dependencyKey; //ここでdependencyKeyが初期化されている
                m_Data = data;
                m_HashCode = internalId.GetHashCode() * 31 + providerId.GetHashCode();
                m_DependencyHashCode = depHash;
                m_PrimaryKey = primaryKey;
                m_Type = type == null ? typeof(object) : type;
            }
        }

ここでカタログをロードしている。ここにログを仕込むことでdependencyKeyがどのAssetのセットかを確認できる。

        public ResourceLocationMap CreateLocator(string providerSuffix = null)
        {
            var bucketData = Convert.FromBase64String(m_BucketDataString);
            int bucketCount = BitConverter.ToInt32(bucketData, 0);
            var buckets = new Bucket[bucketCount];
〜中略〜
            for (int i = 0; i < buckets.Length; i++)
            {
                var bucket = buckets[i];
                var key = keys[i];
                var locs = new IResourceLocation[bucket.entries.Length];
                for (int b = 0; b < bucket.entries.Length; b++)
                    locs[b] = locations[bucket.entries[b]]; //locs[b]をCompactLocationにキャストして変数をダンプする
                locator.Add(key, locs);
            }

            return locator;

Assetをロードした時に、dependencyKeyでロードされるAssetリストが取得できる。

        public bool Locate(object key, Type type, out IList<IResourceLocation> locations)
        {
            IList<IResourceLocation> locs = null;
            if (!this.locations.TryGetValue(key, out locs))
            {
                locations = null;
                return false;
            }
//ここでlocsをダンプする

Library/PackageCache/com.unity.addressables@1.22.2/Runtime/AddressablesImpl.cs

Assetロード時に呼び出される箇所。ここにログを仕込めばよい。
こで出力したログの次にLocateのログと合わせて見ると依存関係が確認できる。

        public AsyncOperationHandle<TObject> LoadAssetAsync<TObject>(IResourceLocation location)
        {
            QueueEditorUpdateIfNeeded();
            if (ShouldChainRequest)
                return TrackHandle(LoadAssetWithChain<TObject>(ChainOperation, location));
            return TrackHandle(ResourceManager.ProvideResource<TObject>(location));
        }
        public AsyncOperationHandle<TObject> LoadAssetAsync<TObject>(object key)
        {
            QueueEditorUpdateIfNeeded();
            if (ShouldChainRequest)
                return TrackHandle(LoadAssetWithChain<TObject>(ChainOperation, key));