20200211のUnityに関する記事は8件です。

UnityのSRPについて調べてみたことについて調べてみる。

Unityのレンダーパイプラインについて調べてみたことについて調べていきます。

レンダリングパイプライン(HDRP、URP)

Unityで利用できるレンダリングパイプラインは、現時点で3つあります。
・コアエンジンに含まれるビルトインパイプライン
・HDRP
・URP

レンダリングパイプラインとはメッシュ、シェーダーなど情報から、ゲーム画面に描画するプログラム集団の事

レンダリングパイプラインとは、3D CGの処理において頂点やジオメトリ、ラスタライズやピクセルといった各処理の繋がりと流れについて定義したものです。これらの処理にはGPUハードウェアのシェーダー、ミドルのライブラリなどが関連し、ユーザプログラムでカスタマイズできる仕組みもあります。詳細についてはDirectXかOpenGLのドキュメントを読むとわかりやすいです。

DirectXのグラフィックスレンダリングパイプラインのドキュメント

OpenGLのレンダリングパイプラインオーバービュー

Shader graphが使える
これはSRP(HDRP、URP)でしか使えません。

ShaderGraphのパッケージにある説明文では「今は」と書かれているので、将来に期待が持てます。
Currently, both the High Definition Rendering Pipeline and the Universal Rendering Pipeline support Shader Graph.

ノードを使って誰でも簡単にシェーダーが描ける

ShaderGraphを使ったからといって、必ずしも簡単にシェーダーが描けるとは限りません。コードで書くやり方がわかっていないと、ノードベースでどのようにロジックを組み立てればいいのかを理解するのは難易度が上がります。
シェーダーを描くには数学、プログラミング、3D CGといった広範なCSの知識が求められます。たいていの物事には、銀の弾丸は存在しません。

HDRPは、グラフィックがとても綺麗で、コンピューターゲーム向け。

HDRPはハイエンドなグラフィックを実現するために用意されました。Unity社はHDRPが自動車、製造業、映像産業、ゲーム用途などで利用されることを想定しています。

Getting started with ray tracing

URPは、従来のパイプラインに比べ、軽いので、モバイル向け。軽いにもかかわらず、グラフィックの違いはよく見ないと分からないぐらいでしかないです。

重いか軽いかはどのようにコンテンツを作ったかに依存する部分が多く、パイプラインの違いをのみこの理由にするのは望ましくありません。複雑なメッシュ、計算量の多いシェーダーやレイトレース、パーティクルなど、重くなる要因は様々です。また、モバイルベースのVRデバイスではハイエンドなPCと比較すると非力なため、アプリケーションの作り方次第では容易に重くなるという、採用したプラットフォームの要因もあります。
このため、いかにパフォーマンスチューニングするか、またルックや体験の質を落とすことなくコンテンツから重い要因を取り除くことは、デベロッパーにとって重要な仕事です。

HDRPやURPはSRP(スクリプタブルレンダーパイプライン)が使われているので、改造出来ます。
これによって、Shader graphなどで、描画したものを加工したり、独自のポストエフェクトを使えます。

パイプラインをカスタマイズできるということは、文字通りパイプラインに手を入れることが可能になっているということであり、単一のシェーダーをノードベースで作成できるShaderGraphが使えるということと直接的な関連はありません。
ビジュアルの加工についても、独自のポストエフェクトについても従来から可能なことです。
繰り返しますがパイプラインに手を加えることと、挙げられたメリットに関連はありません。

最近出来たものだし、使い勝手が悪いので、情報が少ないです。

Unity公式がマニュアルを公開していることと、利用ガイド的なUnityブログがあり、情報が少ないとは言い切れません。日本語で読めるブログが少ないというならあっています。
また、この文に「使い勝手が悪い」と挿入された箇所について前後の繋がりが理解できません。

SRPでは色々な理由で、シェーダーをプログラムで書くのがとても難しいです。
私はずっとプログラムで書くために、たくさん調べてきましたが、SRPに伴い、シェーダーを書くコードがShader labでなくなったみたい?で、まだ書けていません。

他のプログラミング言語と比較した場合に、シェーダー言語にはハードルがあることは確かです。ここに余計なことをさらに付け加えると、そもそもプログラミング言語をはじめるということにもハードルがあります。
本題に戻ると、SRPではHLSL言語でシェーダーを記述することが可能で、これはカスタムポストプロセスやShaderGraphでのカスタムファンクションノードにテキストで書いて使います。また、いわゆる従来のShaderLABで使用する言語はCg/HLSL言語であり、書法、書式としては共通するところが多いものとなっています。
つまり、ここでロジックを整理してみると、もしもShaderLABでHLSLを描いてきたのであれば、SRPでもHLSLを描くことができます。
念のため逆についても書いておくと、SRPでHLSLを描けないのであれば、おそらくShaderLABでHLSLを描くことができないと推測できます。

従来のシェーダーが使えない
既存のアセットの一部が使えない、使いにくい

アプリケーションのバージョンが上がったり、新しい概念での何らかの仕組みが登場した時に互換性の問題はつきものですが、ものづくりをする立場であれば選択肢はふたつかとは思います。

・自力でセットアップする
・対応したものが出てくるのを待つ
マテリアルで前者のやり方は、URPであればシェーダーの変換について扱った日本語のブログ記事が存在します。HDRPについてはそもそも物理ベースで扱うという概念に変わっているので、自分がやりたい表現にHDRPが最適かどうかを先に考慮するべきです。

従来のシェーダーが違うために、GaiaやAura2などグラフィック系のアセットが使えないです。

SRPではVolumeという機能が追加されているので、まずはそれをどう使うのかを調べるべきでしょう。これはつまり、サードパーティに追加の費用を支払うことなく、標準の機能で実現できる可能性が増えたということであり、エンジンが進化したことを喜ぶべきです。
これら従来のアセットは今後、エンジンの標準機能をサポートする役割としてバージョンアップするか、または役割を終えたと判断して開発を終了するかのどちらかになることでしょう。

結論から言うと使うべきではないと思います。

世界はたいへんに複雑であり、〇〇である、〇〇ではないといった断言できることはそれほど多くありません。
私はテキストを書く時に文末を断定で終わらせることは多くありますが、実のところ常に迷ってます。果たして、私にとって正しいと思うことが、他者にとってもそうであるかという疑問です。
完全にこれは個人の思いではありますが、他者に対してべからずの結論を伝えるよりも前に、何かもっと考慮するべきことがあるように感じられてならないのです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity 宴 別のシーンから「宴」のラベルを呼び出す

はじめに

「宴」とはビジュアルノベルや会話シーンを手軽に作成するためのUnityアセットです。
この記事ではUnityの別シーン上のC#スクリプトから宴のシナリオの中の特定のラベルを呼び出す方法を記述します。

具体的にどのようなことに応用できるかというと…
・キャラクターセレクト
・画像によるチャプターセレクト
・購入済みのシナリオのみを選択できるようにする
など応用範囲の広いテクニックです。

開発環境

・Windows 10
・Unity 2019.3.0f6
・「宴」3.8.0

手順

ここでは最初に上げた「キャラクターセレクト」をイメージした手順を記述します。
2020-02-11_All2.png

1.「宴」のセットアップ(プロジェクトの作成)を行ってください。
ここでは「Main」という名前のプロジェクトの作成を行ったと仮定します。
セットアップ(プロジェクトの作成)方法は「宴」の公式サイトのチュートリアルを参照してください。

2.「宴」用シナリオスクリプト(Excelファイル)を用意する
今回は以下のようなテスト用シナリオスクリプトを用意しました。
シナリオスクリプトの詳細な記述方法は「宴」の公式サイトを参照してください。
2020-02-11_16h24_58.png

3.以下の3つのC#スクリプトを用意してください。
C#スクリプトを置く場所はどこでもいいです。

ExecuteUtage.cs
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;


public class ExecuteUtage : MonoBehaviour
{
    // 他シーンのUtage呼び出し
    public void Execute(string utageSceneName, string label)
    {
        // 実行するラベル名を保存
        ExecuteScenario.Label = label;

        StartCoroutine(ExecuteScenarioCoroutine(utageSceneName));
    }

    // シーン読み込みが完了したか
    private bool IsSceneLoaded;

    private void Start()
    {
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    // シーン読み込み完了イベント
    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        IsSceneLoaded = true;
    }

    // シナリオの呼び出しCorotine
    private IEnumerator ExecuteScenarioCoroutine(string utageSceneName)
    {
        IsSceneLoaded = false;
        SceneManager.LoadScene(utageSceneName);
        yield return new WaitUntil(() => IsSceneLoaded);
    }
}
SelectScenario.cs
using System.Collections;
using UnityEngine;
using Utage;


public class SelectScenario : MonoBehaviour
{
    [SerializeField] public AdvEngine Engine;
    [SerializeField] public UtageUguiTitle Title;
    [SerializeField] public UtageUguiMainGame MainGame;


    private void Start()
    {
        StartCoroutine(Execute());
    }

    // シナリオを呼び出す
    private IEnumerator Execute()
    {
        // Utage Engine の起動を待つ
        yield return new WaitUntil(() => !Engine.IsLoading);
        // 機能呼び出し
        yield return StartCoroutine(ExecuteLabel());
    }

    // 機能呼び出し
    private IEnumerator ExecuteLabel()
    {
        // 起動ラベルのラベル名を取得
        string label = ExecuteScenario.Label;

        // 機能呼び出し
        Title.Close();
        MainGame.OpenStartLabel(label);

        yield return null;
    }
}
ExecuteScenario.cs
public class ExecuteScenario
{
    // 呼び出すラベル
    public static string Label;
}

4.Mainシーンのヒエラルキーの中に「SelectScenario」という名前の空のオブジェクトを作成して「SelectScenario.cs」スクリプトをアタッチします。

5. 作成した「SelectScenario」コンポーネントのプロパティに以下のオブジェクトを設定します。
2020-02-11_16h32_48.png

プロパティー名 オブジェクト名 オブジェクトの位置
Engine AdvEngine ヒエラルキー直下
Title Title Canvas-AdvUI/Title
Main Game MainGame Canvas-AdvUI/MainGame

6.「Main」シーンを保存してください。

7.Unityでキャラクターセレクトを行うシーンを作成してください。
ここでは「Title」という名前のシーンを作ったと仮定します。

8.今回はキャラクターセレクトということでキャラクターの画像が載ったButtonUIを設置しました。
このボタンに「ExecuteUtage.cs」スクリプトをアタッチします。
これで「宴」のラベルを呼び出す準備ができました。

9.この「ExecuteUtage」コンポーネントの...

public void Execute(string utageSceneName, string label)

関数を呼び出せば「宴」シナリオスクリプト内のラベルが呼び出せます。

引数名 設定する値
string utageSceneName 宴のあるシーン名
string label 呼び出すラベル名

10.今回はサンプルとして以下のC#スクリプトを用意しました。
このC#スクリプトを各ボタンにアタッチしてください。

EventCharacterSelection.cs
using UnityEngine;


[RequireComponent(typeof(ExecuteUtage))]
public class EventCharacterSelection : MonoBehaviour
{
    private ExecuteUtage ExecuteUtageComponent;

    private void Start()
    {
        ExecuteUtageComponent = gameObject.GetComponent<ExecuteUtage>();
    }

    public void CharacterSelect(string label)
    {
        ExecuteUtageComponent.Execute("Main", label);
    }
}

11.各ボタンの「On Click ()」を以下の画像のように設定してください。
これでボタンを押すと「宴」のラベルが呼び出されます。
2020-02-11_16h39_57.png

13.以上で完了です。
シーンが変わる際に「メニュー」画面がちらっと見えることがありますが「宴」のシーンの「Canvas-AdvUI/Title」にUIが配置されていますので非表示にするなりローディング画面を出すなり好きにしてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityからSpineのプロジェクトを指定してエクスポートとプレハブ作成を行う

初投稿です!
分かりづらい、読みにくいなどあると思いますがお手柔らかにお願いします。

最近仕事で触っていたSpineですが、コマンドラインの使い方については情報が少ないですね
そして公式のマニュアルってちょっと分かりにくいですよね
だれかまとめてくれないかなあ...

僕が調べた成果として、
Unityからエクスポートを実行させて、ついでにプレハブを作成する
という自作のエディタ拡張機能をご紹介します

UnityでSpine使うのであれば、いつか役に立つかもしれません

目的

Unityエディタ上からSpineのファイルを指定して、
あらかじめ用意しておいた設定に基づいてデータを作成・更新する

特定のフォルダ以下のSpineファイルを一気に変換・更新する

動作環境

インストール手順などは省略します

Windows 10 Pro (Macが手元になくて・・・、パスやコマンドライン実行部分を良い感じに変えてください)
Unity 2019.3.0f6 Personal (自宅用なのでPersonal)
Spine 3.8.84 Pro
Spine-Unity spine-unity-3.8-2020-02-06.unitypackage

Spineのパスを確認する

まずはコマンドラインを使うためにSpineのパスを調べます
デスクトップなどにあるSpineのアイコンを右クリックして「ファイルの場所を開く」を選択して、
表示されたエクスプローラのパスをメモしておきましょう

素直にインストールした人はこちらだと思います
windows
C:\Program Files (x86)\Spine\Spine.com
mac
/Applications/Spine/Spine.app/Contents/MacOs/Spine

エクスポート設定ファイルの作成

次にSpineのエクスポート設定ファイルを作成します
Spineを起動して左上のロゴをクリックしてエクスポートを選択しましょう

設定はプロジェクトの方針で決めてください (公式マニュアル-エクスポート)
Unityで使う場合には以下の2点に注意してください

  • バイナリ形式を使う場合は拡張子を.skel.bytesにする
  • アトラスファイルの拡張子を.atlas.txtにする

設定を変更したら、保存してパスを覚えておいてください
今回はプロジェクト内にAssets/Editor/SpineUtility/export_settings.jsonとして保存しました

export_settings.json
export_settings.json
{
"class": "export-binary",
"name": "Binary",
"project": "C:\\Program Files (x86)\\Spine\\examples\\spineboy\\spineboy-pro.spine",
"output": "D:/work",
"open": false,
"extension": ".skel.bytes",
"nonessential": false,
"cleanUp": true,
"packAtlas": {
    "stripWhitespaceX": true,
    "stripWhitespaceY": true,
    "rotation": true,
    "alias": true,
    "ignoreBlankImages": false,
    "alphaThreshold": 3,
    "minWidth": 16,
    "minHeight": 16,
    "maxWidth": 2048,
    "maxHeight": 2048,
    "pot": true,
    "multipleOfFour": false,
    "square": false,
    "outputFormat": "png",
    "jpegQuality": 0.9,
    "premultiplyAlpha": true,
    "bleed": false,
    "scale": [ 1 ],
    "scaleSuffix": [ "" ],
    "scaleResampling": [ "bicubic" ],
    "paddingX": 2,
    "paddingY": 2,
    "edgePadding": true,
    "duplicatePadding": false,
    "filterMin": "Linear",
    "filterMag": "Linear",
    "wrapX": "ClampToEdge",
    "wrapY": "ClampToEdge",
    "format": "RGBA8888",
    "atlasExtension": ".atlas.txt",
    "combineSubdirectories": false,
    "flattenPaths": false,
    "useIndexes": false,
    "debug": false,
    "fast": false,
    "limitMemory": true,
    "currentProject": true,
    "packing": "rectangles",
    "silent": false,
    "ignore": false,
    "bleedIterations": 2
},
"packSource": "attachments",
"packTarget": "perskeleton",
"warnings": true
}

UnityでC#スクリプトを作成

Assets/Editor/SpineUtility/SpineConfig.cs

Spineのパスやバージョンなどの定義を記載しています

SpineConfig.cs
SpineConfig.cs
using UnityEngine;
namespace SpineUtility
{
    public static class SpineConfig
    {
        //Spineのパス、空白が含まれるため使用する際に""で囲む
        public static readonly string SpinePath = "C:/Program Files (x86)/Spine/Spine.com";

        //Spineのバージョン latestか3.8.84など
        public static readonly string SpineVersion = "latest";

        //Spineエクスポート設定の絶対パス
        public static readonly string ExportSetting = $"{Application.dataPath}/Editor/SpineUtility/export_settings.json";
    }
}

Assets/Editor/SpineUtility/SpineExporterWindow.cs

エディタウィンドウの表示部分です

SpineExporterWindow.cs
SpineExporterWindow.cs
using System.IO;
using UnityEngine;
using UnityEditor;

namespace SpineUtility
{
    public class SpineExporterWindow : EditorWindow 
    {
        private Vector2 _scrollPosition;
        private string _spineProjectPath = string.Empty;
        private string _outputDirectory = string.Empty;
        private string _spineProjectsRootDirectory = string.Empty;
        private string _outputRootDirectory = string.Empty;
        private SpineExporter _spineExporter = new SpineExporter();

        [MenuItem("SpineUtility/SpineExporterWindow")]
        private static void ShowWindow() {
            var window = GetWindow<SpineExporterWindow>("SpineExporterWindow");
            window.Show();
        }

        private void OnGUI() {
            using (var scroll = new EditorGUILayout.ScrollViewScope(_scrollPosition, GUI.skin.box))
            {
                _scrollPosition = scroll.scrollPosition;

                SelectSpineProject();
                EditorGUILayout.Space();

                SelectOutputDirectory();
                EditorGUILayout.Space();

                ExportButton();
                EditorGUILayout.Space();
                EditorGUILayout.Space();

                EditorGUILayout.LabelField("このフォルダ以下のSpineプロジェクトをエクスポートします");
                SelectSpineProjectsRoot();
                EditorGUILayout.Space();

                EditorGUILayout.LabelField("このフォルダ以下にフォルダ構成に沿って出力します");
                SelectOutputRootDirectory();
                EditorGUILayout.Space();

                ExportAllButton();
                EditorGUILayout.Space();
          }
        }

        /// <summary>
        /// プロジェクトファイル選択GUI
        /// </summary>
        private void SelectSpineProject()
        {
            using(new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                _spineProjectPath = EditorGUILayout.TextField("Spineプロジェクトファイル", _spineProjectPath);
                if(GUILayout.Button("Spineプロジェクト選択"))
                {
                    string path = EditorUtility.OpenFilePanel("Spineプロジェクト選択", _spineProjectPath, "spine");
                    if(!string.IsNullOrEmpty(path))
                    {
                        _spineProjectPath = path.Replace("\\", "/");
                        Repaint();
                    }
                }
            }
        }

        /// <summary>
        /// 出力先選択GUI
        /// </summary>
        private void SelectOutputDirectory()
        {
            using(new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                _outputDirectory = EditorGUILayout.TextField("エクスポートフォルダ", _outputDirectory);
                if(GUILayout.Button("エクスポートフォルダ選択"))
                {
                    string path = EditorUtility.OpenFolderPanel("エクスポートフォルダ選択", _outputDirectory, "");
                    if(!string.IsNullOrEmpty(path))
                    {
                        _outputDirectory = path.Replace("\\", "/");
                        Repaint();
                    }
                }
            }
        }

        /// <summary>
        /// エクスポート実行ボタンGUI
        /// </summary>
         private void ExportButton()
        {
            bool disabled = string.IsNullOrEmpty(_spineProjectPath) || string.IsNullOrEmpty(_outputDirectory);
            using(new EditorGUI.DisabledScope(disabled))
            {
                if(GUILayout.Button("エクスポート実行"))
                {
                    _spineExporter.Export(_spineProjectPath, _outputDirectory);
                }
            }
        }


        /// <summary>
        /// プロジェクトファイル選択GUI
        /// </summary>
        private void SelectSpineProjectsRoot()
        {
            using(new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                _spineProjectsRootDirectory = EditorGUILayout.TextField("Spineプロジェクトルート", _spineProjectsRootDirectory);
                if(GUILayout.Button("Spineプロジェクトルートフォルダ選択"))
                {
                    string path = EditorUtility.OpenFolderPanel("Spineプロジェクトルートフォルダ選択", _spineProjectsRootDirectory, "");
                    if(!string.IsNullOrEmpty(path))
                    {
                        _spineProjectsRootDirectory = path.Replace("\\", "/");
                        Repaint();
                    }
                }
            }
         ;   
        }

        /// <summary>
        /// 出力先選択GUI
        /// </summary>
        private void SelectOutputRootDirectory()
        {
            using(new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                _outputRootDirectory = EditorGUILayout.TextField("エクスポートフォルダルート", _outputRootDirectory);
                if(GUILayout.Button("エクスポートフォルダルート選択"))
                {
                    string path = EditorUtility.OpenFolderPanel("エクスポートフォルダルート選択", _outputRootDirectory, "");
                    if(!string.IsNullOrEmpty(path))
                    {
                        _outputRootDirectory = path.Replace("\\", "/");
                        Repaint();
                   }
                }
            }
        }

        /// <summary>
        /// エクスポート実行ボタンGUI
        /// </summary>
        private void ExportAllButton()
        {
            bool disabled = string.IsNullOrEmpty(_spineProjectsRootDirectory) || string.IsNullOrEmpty(_outputRootDirectory);
            using(new EditorGUI.DisabledScope(disabled))
            {
                if(GUILayout.Button("全ファイルエクスポート実行"))
                {
                    //  指定ディレクトリ以下の.spineファイルを取得
                    string[] files = System.IO.Directory.GetFiles(_spineProjectsRootDirectory, "*.spine", System.IO.SearchOption.AllDirectories);
                    foreach(var file in files)
                    {
                        //  Windowsではディレクトリの区切り文字が違うので変換して使う
                        var filepath = file.Replace("\\", "/");
                        var dir = Path.GetDirectoryName(filepath).Replace("\\", "/");

                        //  出力先ディレクトリを作成
                        string outputDir = dir.Replace(_spineProjectsRootDirectory, _outputRootDirectory); 
                        if(!Directory.Exists(outputDir))
                        {
                            Directory.CreateDirectory(outputDir);
                        }

                        //  変換実行
                        _spineExporter.Export(filepath, outputDir);
                    }
                }
            }
        }
    }
}

Assets/Editor/SpineUtility/SpineExporter.cs

エクスポートコマンドの呼び出し、プレハブの生成を行います
今回は省略してますが、テクスチャの設定やRendererの設定、初期アニメーションを変更する、
などを変換処理の中に追加しておくといちいち変更しなくてすみます

SpineExporter.cs
SpineExporter.cs
using System.IO;
using UnityEngine;
using UnityEditor;
using Spine.Unity;

namespace SpineUtility
{
    public class SpineExporter
    {
        /// <summary>
        /// SpineプロジェクトからUnityで必要なアセットをエクスポートしてプレハブを作成
        /// </summary>
        /// <param name="projectPath">.spineファイル</param>
        /// <param name="outputDirectory">出力先ディレクトリ</param>
        public void Export(string projectPath, string outputDirectory)
        {
            try
            {
                EditorUtility.DisplayProgressBar("SpineExporter", "エクスポート中", 0);

                if(ExportAssets(projectPath, outputDirectory) != 0)
                {
                    EditorUtility.DisplayDialog("エラー", "エクスポートコマンドの実行に失敗しました", "ok");
                    return;
                }

                //  エクスポートファイルがインポートされる
                AssetDatabase.Refresh();
                //  必要があればここでテクスチャの設定を変更したり、Mixの変更をしてりするとよい

                CreatePrefab(projectPath, outputDirectory);
            }
            finally
            {
                EditorUtility.ClearProgressBar();
            }
        }

        /// <summary>
        /// Unityで使用するSpineのアセットを出力
        /// </summary>
        /// <param name="projectPath">.spineファイル</param>
        /// <param name="outputDirectory">出力先ディレクトリ</param>
        /// <returns></returns>
        private int ExportAssets(string projectPath, string outputDirectory)
        {
            string command  = $"\"{SpineConfig.SpinePath}\" "
                            + $"-u \"{SpineConfig.SpineVersion}\" "
                            + $"-i \"{projectPath}\" "
                            + $"-m -o \"{outputDirectory}\" "
                            + $"-e \"{SpineConfig.ExportSetting}\" ";
            return CommandLine.Run(command);    
        }

        /// <summary>
        /// SkeletonAnimationのプレハブを作成する
        /// </summary>
        /// <param name="projectPath">.spineファイル</param>
        /// <param name="outputDirectory">出力先ディレクトリ</param>
        private void CreatePrefab(string projectPath, string outputDirectory)
        {
            //UnityのAPIを呼ぶ際はプロジェクトフォルダからのパスにする
            string outDir = outputDirectory.Replace(Application.dataPath, "Assets");
            string filename = Path.GetFileNameWithoutExtension(projectPath);

            string dataAssetPath = $"{outDir}/{filename}_SkeletonData.asset";
            var dataAsset = AssetDatabase.LoadAssetAtPath<SkeletonDataAsset>(dataAssetPath);
            if(dataAsset == null)
            {
                UnityEngine.Debug.LogError("SkeletonDataAssetが見つかりません");
                return;
            }

            //  すでにプレハブがあるかもしれない
            if(FindPrefab(filename, dataAsset) != null)
            {
                return;
            }

            var skeletonAnimation = SkeletonAnimation.NewSkeletonAnimationGameObject(dataAsset);
            //  必要があればコンポーネントの設定を変更する


            //  プレハブの保存
            bool success = false;
            var prefab = PrefabUtility.SaveAsPrefabAsset(skeletonAnimation.gameObject, $"{outDir}/{filename}.prefab", out success);
            if(success)
            {
                EditorGUIUtility.PingObject(prefab);
            }
            else
            {
                UnityEngine.Debug.LogError("プレハブの作成に失敗しました", dataAsset);
            }
            Object.DestroyImmediate(skeletonAnimation.gameObject);
        }


        /// <summary>
        /// スケルトンが使用されているアセットを探す
        /// </summary>
        /// <param name="filename">名前</param>
        /// <param name="dataAsset">SkeletonDataAsset</param>
        /// <returns>見つかったプレハブ</returns>
        private GameObject FindPrefab(string filename, SkeletonDataAsset dataAsset)
        {
            var guids = AssetDatabase.FindAssets($"t:Prefab {filename}");
            foreach(var guid in guids)
            {
                var path = AssetDatabase.GUIDToAssetPath(guid);
                var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
                var skeletonAnimations = prefab.GetComponentsInChildren<SkeletonAnimation>(true);
                foreach(var skeletonAnimation in skeletonAnimations)
                {
                    if(skeletonAnimation.skeletonDataAsset == dataAsset)
                    {
                        EditorGUIUtility.PingObject(prefab);
                        UnityEngine.Debug.Log("すでにプレハブが作成されていました", prefab);
                        return prefab;
                    }
                }
            }
            return null;
        }
    }
}

Assets/Editor/SpineUtility/CommandLine.cs

コマンド実行を行います
ここはあんまり詳しくないです

CommandLine.cs
CommandLine.cs
using System.Diagnostics;

namespace SpineUtility
{
    public static class CommandLine
    {
        /// <summary>
        /// コマンドプロンプトでコマンドを実行する
        /// </summary>
        /// <param name="command">実行コマンド</param>
        /// <returns>終了ステータス</returns>
        public static int Run(string command)
        {
            var process = new Process
            {
                StartInfo =
                {
                    FileName = "cmd.exe",
                    Arguments = $"/c \"{command}\"",
                    CreateNoWindow = true,
                    UseShellExecute = false, 
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                }
            };
            process.Start();
            process.WaitForExit();

            string output = process.StandardOutput.ReadToEnd();
            if(!string.IsNullOrEmpty(output))
            {
                UnityEngine.Debug.Log(output);
            }
            string error = process.StandardError.ReadToEnd();
            if(!string.IsNullOrEmpty(error))
            {
                UnityEngine.Debug.LogError(error);
            }
            int exitCode = process.ExitCode;

            process.Close();
            return exitCode;
        }
    }
}

使い方

メニューの「SpineUtility/SpineExporterWindow」を選択してウィンドウを表示させます

1ファイルの時は上の方の2か所を入力して3番目のボタンを押します

特定フォルダ以下を全部変換する際は下の2か所を入力して一番下のボタンを押します
指定したフォルダの構成に沿ってフォルダを作って出力します

spine_exporter.png

最後に

実際のプロジェクトではパラメータ設定やフォルダ構成が異なると思います
キャラはここでこんな設定、ステージはここであんな設定とかとか
エクスポート設定ファイルや出力先の選択ルールが決まっていれば自動化も可能だと思います

ちなみにSpineエディタの起動が一番時間がかかっている部分なので、
複数ファイルでコマンドをまとめて送ると速くなったりします
全ファイルの書き出しが成功すること前提です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity2019でScriptableObject 12/1

2020/02/10 構成を記述

こんにちは、コロナです。

adventcalenderの初日としてunityの再度学習としてscriptableobjectから進めていくために、こちらのタイトルにいたしました。

unityのscriptableobjectを作成して、スクショや動画を作成する

scriptableobjectとは

データ入力用のオブジェクトとして利用することが多いものです。
キャラクターのパラメータなどの雛形として参考になるものとなります。

本家のリンクはこちらから
https://docs.unity3d.com/jp/460/ScriptReference/ScriptableObject.html

ここからは、後日中身を記述していく

scriptableobjectを作成してみる

プログラム側から参照する

動作しているスクショを作成

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity 2019.3 でJDK、NDK、SDKが無いエラーが出る

Unity でAndroid開発、あるいはOculus開発しようとしたら、 UnityException: JDK not found が出ましたか?
自分は同じミスを二度ほどして学びました。

エラー内容

UnityException: JDK not found
Java Development Kit (JDK) directory is not set or invalid. Please, fix it in Preferences -> External Tools
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at /Users/builduser/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:187)

Preferences を見ると、JDK周りに警告が出ている。
スクリーンショット 2020-02-11 1.15.17.png

対応方法

Unity の Andorid ビルドをインストールした時に、JDK周りのツールがオプションのため、インストールされていないことが原因なので、UnityHubから追加します。

  1. Unity Hub を起動して、該当のUnityのバージョンの・をクリックします。
    スクリーンショット 2020-02-11 1.35.18.jpg
  2. モジュールを加えるを選択します。
    スクリーンショット 2020-02-11 1.35.31.png
  3. Android SDK & NDK ToolsOpenJDK の両方にチェックを入れて、実行。
    スクリーンショット 2020-02-11 1.36.00.png

概ねこれで対応完了です。Preferences をみてまだ警告が出ているようであれば、手動で設定すれば解消できるはずです。

参考

https://qiita.com/akiya/items/d3921bf7321e3488548f

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity 2019.3 でJDK、NDK、SDKが無いエラーが出る場合の対応方法

Unity でAndroid開発、あるいはOculus開発しようとしたら、 UnityException: JDK not found が出ましたか?
自分は同じミスを二度ほどして学びました。

エラー内容

UnityException: JDK not found
Java Development Kit (JDK) directory is not set or invalid. Please, fix it in Preferences -> External Tools
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at /Users/builduser/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:187)

Preferences を見ると、JDK周りに警告が出ている。
スクリーンショット 2020-02-11 1.15.17.png

対応方法

Unity の Andorid ビルドをインストールした時に、JDK周りのツールがオプションのため、インストールされていないことが原因なので、UnityHubから追加します。

  1. Unity Hub を起動して、該当のUnityのバージョンの・をクリックします。
    スクリーンショット 2020-02-11 1.35.18.jpg
  2. モジュールを加えるを選択します。
    スクリーンショット 2020-02-11 1.35.31.png
  3. Android SDK & NDK ToolsOpenJDK の両方にチェックを入れて、実行。
    スクリーンショット 2020-02-11 1.36.00.png

概ねこれで対応完了です。Preferences をみてまだ警告が出ているようであれば、手動で設定すれば解消できるはずです。

参考

https://qiita.com/akiya/items/d3921bf7321e3488548f

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】型指定でPrefabをインスタンスできるようにする

概要

シングルトンをシーンに配置しなくても使えるようにしたいと思い、作成中のゲームで実装した方法です

  • ScriptableObjectにシングルトンを持ったPrefabを事前登録
  • SingletonMonoBehaviourを継承 1
  • 外部から呼ばれた際、継承先の型から一致するPrefabを見つけ、インスタンスする

コード

Prefabs.cs
    [CreateAssetMenu(fileName = "Prefabs")]
    public class Prefabs : ScriptableObject
    {
        [SerializeField]
        private GameObject[] _prefabs;

        private static Prefabs _instance;
        public static Prefabs Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = Resources.Load<Prefabs>("Prefabs");
                }

                return _instance;
            }
        }

        public static GameObject GetPrefabFromType<T>()
        {
            GameObject result = null;

            foreach (var prefab in Prefabs.Instance._prefabs)
            {
                if (prefab.GetComponent<T>() != null)
                {
                    result = prefab;
                    return result;
                }
            }

            Debug.LogError(typeof(T) + " は登録されていないPrefabです");
            return null;
        }
    }

使い方

  • ResourcesフォルダにこのScriptableObjectを作成
    1.png

  • SingletonMonoBehaviour内で、instanceがない時にGetPrefabFromTypeを呼ぶようにする

SingletonMonoBehaviour.cs
public class SingletonMonoBehaviour<T> : MonoBehaviourWithInit where T : MonoBehaviourWithInit
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = (T)FindObjectOfType(typeof(T));

                if (_instance == null)
                {
                    _instance = GameObject.Instantiate(Prefabs.GetPrefabFromType<T>().GetComponent<T>());
                }

                _instance.InitIfNeeded();
            }
            return _instance;
        }
    }

中略...

}

まとめ

これでシングルトンをシーンに配置せずとも、必要なときにインスタンスしてくれるようになります。
参照先をScriptableObjectで一元管理しているので、クラスごとにPrefabへの参照を持つ必要がありません。
また、シングルトン以外の用途でも、Prefabさえ登録しておけばどこからでも型指定で呼べるので、いろんな使い方ができます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unityとサーバーで連携してネツエンエンジニアもどきをしてみる

2020/02/10 記事の動作構成だけ作成

こんにちは、コロナです。

今回はネットで「ネツエン」という単語が目に入っていいなーとおもったので、いろいろ考えたりしたいと思ってテーマにしました

元ネタ

https://cloud.watch.impress.co.jp/docs/special/1221923.html

ここから、ネットワークのケーブルとかつながっているのいいなーという軽率な思い付きです。

アプリ構成(予定)

unity → コンピュータの見た目で、つながっているときにかっこいいエフェクトをだしたかっただけ

サーバ → これから、ネットワーク構成情報がとれるものを調査して作っていく

今後の課題

サーバーを決めて、仮ネットワークを組んだりする

ネットワーク構成図を張り付ける

アプリを組んだ時のスクショを張り付けたりする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む