20200323のUnityに関する記事は9件です。

カメラワークについて

格ゲーなるものを作るべくそのためにどう技を見せるか考えたときにたどり着いたこと

unityのシネマソーンを使って画像を載せながらと思っていましたが、思いのほか長くなっったので別の記事にまた改めて書きます。
覚えるためだけのメモです。

基本的なカメラワーク 

FIX(フィックス)

カメラを固定して撮影する。全体の迫力自体はないが動いているものに集中するため、細かな動作や表情を取るにに向いている。

PAN/PANNING(パン/パンニング)

撮影者自身は動かないがカメラを左右に動かす撮影方法
主に素早く動くものを取ったり、周囲の全容を移す、場面転換で用いられる。
個人的に遠近感を出せて走り去るときに使ったら迫力出そう。

TILT(ティルト)

pan同様撮影者は動かないが、カメラをパンと違い上下に動かす撮影方法

下から上に上がるようにとるのをTiltUP(ティルトアップ)といい、スケール間や空の面積が広がるような動きになるため、希望などを感じさせたいときに使うといいらしい。
手振れ的なのも入れたらよくなりそう。
あと、作品の当事者になったようになる効果もあるっぽい(?)。

TiltDown(ティルトダウン)
上から下にカメラが下がっていく撮影方法
徐々に全容が秋からになっていくときに使える。

Zoom In Out

こちらはおなじみズームイン。ズームアウト
被写体に注目したいときや被写体の状況を徐々に明らかにするときに使える。

TRACK(トラック) 追跡

カメラを一定の距離を保ちながら、被写体とともに移動する撮影方法。

移動スピードを体感したり、迫力のあるショットを取るときに用いられる撮影方法らしい。
TPS(三人称)的な感じ

DOLLY(ドリー) 台車撮影

カメラを台車に乗せて、滑らかに水平移動させる撮影方法。
別に台車を使用していなくてもドリーと呼べるらしい。要は滑らかな水平移動。
あえて上下にブレさせて臨場感を出しているもののある。

DOLLY IN OUT(ドリーイン/アウト)

カメラが前にすすんでいく撮影方法。ズームインとは違い遠近のゆがみや被写体と背景のサイズが変化していく。
アウトも同様で、逆に下がっていく撮影方法。

SPINNING SHOT(スピンショット) 被写体を軸に回転

 被写体を軸にコンパスのように周囲から撮影する方法。
被写体をしっかりとらえつつ、背景は素早く動くため、被写体と周囲の状況を伝えるときに用いられるらしい。
パニックや焦燥、包囲されていることを伝えるために用いられる時もある。

DUTCH ANGLE(ダッチアングル) 斜斜

わざと角度を付けて心理的な不安や緊張感を表現するときに用いられる撮影方法。

HIGH ANGLE(ハイアングル) 上から撮影

目線より上から撮影する方法。
 強者の目線(?)なので迫力を伝えるには向かないらしい。
逆に無力さを強調したいときに効果が出る。

LOW ANGLE(ローアングル) 下から撮影

目線より下から撮影する方法。被写体を強そうに、迫力を与えたいときや、威厳を表現するときに用いられる。

FOCUS IN(フォーカスイン) 焦点を合わせる

 焦点を外した状態から徐々に焦点を合わせていく撮影方法
意識を取り戻したときの演出に使われる。

FOUCUS OUT(フォーカスアウト)

逆で焦点が合っている状態から徐々に焦点をぼかしていく。
階層しーへの移行や気を失うときの演出に用いられる。

FOUCU PULL(プルフォーカス) 焦点の対象が変化する

焦点の対象が途中でほかのものに変わっていく撮影方法。
注目の順序をコントロールすることができる。

ほかにもドローン撮影などもあるがこちらはいま必要ないので割愛します。

参考URL https://asobo-design.com/nex/blog-830-22726.html
     https://asobo-design.com/nex/blog-831-22734.html

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

Unityで簡単なゲームを作ってみた

作成したゲーム

  • 上から降ってくる岩を打ち落として、得点を稼ぐシューティングゲーム。岩が画面外に出てしまったらゲームオーバー。

  • Unityのバージョンは2018.4.19f1

  • プレイヤー移動はキーボード入力

  • 弾が岩に当たったら爆発のエフェクト

  • 弾を発射した際、Trail Rendererを使って弾の軌跡を出す

  • UIで得点を表示し、岩が画面外に行ったときにゲームオーバーを表示を出す

用意したもの

  • プレイヤー、障害物(岩とか)、弾、弾の軌跡、背景

使用した素材

ソースコード

プレイヤー(ロケット)を動かすスクリプト

RocketController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RocketController : MonoBehaviour
{
    public GameObject bulletPrefab;

    void Update()
    {
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.Translate(-0.1f, 0, 0);
        }

        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.Translate(0.1f, 0, 0);
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Instantiate(bulletPrefab, transform.position, Quaternion.identity);
        }
    }
}

Instantiate関数

  Instantiate(bulletPrefab,transform.position,Quaternion.identify);
//Instantiate(第1関数,第2関数,第3関数);
  • この関数ではPrefabからインスタンスを作り、任意の位置に生成する関数です
  • 第1関数にPrefab、第2関数にインスタンスを作成したい位置、第3関数にはインスタンスの回転角を指定します
  • 今回は弾のPrefab(bulletPrefab)を複製して発射したいのでこれを第1関数に、これをプレイヤーの位置に生成したい ので第2関数はtransform.position、第3関数の回転はQuaternion.identifyにすることで無回転になります

岩の制御

  • 岩を落とすスクリプト
RockController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RockController : MonoBehaviour
{
    float fallSpeed;
    float rotSpeed;

    void Start()
    {
        this.fallSpeed = 0.01f + 0.1f * Random.value;
        this.rotSpeed = 5f + 3f * Random.value;
    }


    void Update()
    {
        transform.Translate(0, -fallSpeed, 0, Space.World);
        transform.Rotate(0, 0, rotSpeed);

        if (transform.position.y < -5.5f)
        {
            GameObject.Find("Canvas").GetComponent<UIController>().GameOver();
            Destroy(gameObject);
        }
    }
}

Translate関数

  transform.Translate(0, -fallSpeed, 0, Space.World);
//transform.Translate(x, y, z, relative to);
  • 最初の3つの値はxyzどの軸に沿って移動するかという意味で、最後のrelative toにはSpace.WorldやSpace.Selfなどが入ります。Space.Worldにした場合、ワールド座標になります。ワールド座標とは原点から見た座標のことです。
  • 今回の場合、fallSpeedに従って落ちてきてほしいのでy軸のみ値が入っています

  • ランダムに岩を生成するスクリプト
RockGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RockGenerator : MonoBehaviour
{
    public GameObject rockPrefab;

    void Start()
    {
        InvokeRepeating("GenRock", 1, 1);
    }

    void GenRock()
    {
        Instantiate(rockPrefab, new Vector3(-2.5f + 5 * Random.value, 6, 0), Quaternion.identity);
    }
}

InvokeRepeating関数

InvokeRepeating("GenRock", 1, 1);
//InvokeRepeating(第1関数, n, m);
  • この関数はとても便利な関数です。第1関数に指定したものを、n秒後にm回呼び出す関数です。
  • 今回はGenRockという関数を1秒に1回呼び出したいのでこのようになります

背景を動かすスクリプト

BackgroundController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BackgroundController : MonoBehaviour
{   
    void Update()
    {
        transform.Translate(0, -0.03f, 0);
        if (transform.position.y < -4.9f)
        {
            transform.position = new Vector3(0, 4.9f, 0);
        }
    }
}
  • 背景の大きさは決まっているのである一定のところまで移動したら、戻るようにif文で設定しています

発射する弾の制御

BulletController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletController : MonoBehaviour
{
    public GameObject explosionPrefab;      //爆発エフェクトのPrefab

    void Update()
    {
        transform.Translate(0, 0.2f, 0);

        if (transform.position.y > 5)
        {
            Destroy(gameObject);
        }
    }

    void OnTriggerEnter2D(Collider2D coll)
    {
        //衝突したときにスコアを更新する
        GameObject.Find("Canvas").GetComponent<UIController>().AddScore();

        //爆発エフェクトを生成
        GameObject effect = Instantiate(explosionPrefab, transform.position, Quaternion.identity) as GameObject;
        Destroy(effect, 1.0f);

        Destroy(coll.gameObject);
        Destroy(gameObject);
    }
}

ややこしい部分の解説

//爆発エフェクトを生成
 GameObject effect = Instantiate(explosionPrefab, transform.position, Quaternion.identity) as GameObject;
 Destroy(effect, 1.0f);
  • Instantiate関数でGameObjectとしてキャストすることで、爆発エフェクトを加える
  • その後のDestroy関数でそのオブジェクトを消す。第2引数はオブジェクトを破壊するまでの時間(秒)です

UIの制御

UIController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIController : MonoBehaviour
{
    int score = 0;
    GameObject scoreText;
    GameObject gameOverText;
    public void GameOver()
    {
        this.gameOverText.GetComponent<Text>().text = "GameOver";
    }
    public void AddScore()
    {
        this.score += 10;
    }

    void Start()
    {
        this.scoreText = GameObject.Find("Score");
        this.gameOverText = GameObject.Find("GameOver");
    }

    void Update()
    {
        scoreText.GetComponent<Text>().text = "Score:" + score.ToString("D4");
    }
}

スコアの更新

scoreText.GetComponent<Text>().text = "Score:" + score.ToString("D4");
  • UIのテキスト部分に「Score:0000」と表示するためにscoreText.GetComponent.textで情報を入手する
  • score.ToString("D4")は整数を4桁表示するという意味です

作成したゲーム

簡単なゲーム制作2D.gif

問題点

  • 同じ場所で弾を連射すると、弾同士がぶつかってしまいそこに爆発エフェクトがでてしまう。

参考させていただいたサイト

おもちゃラボ 【Unity入門】60分でつくるシューティングゲーム

追記

  • GitHubにコード上げました!

GitHub

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

PrefabからEntityを生成する方法メモ(Entities v0.8.0)

はじめに

DOTS以前のUnityでは、

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnPrefab : MonoBehaviour {

    [SerializeField] private GameObject prefab;

    private void Start () {
        Instantiate (prefab);
    }
}

のようなスクリプトを作成し、何らかのオブジェクトにアタッチしておけば、実行時にPrefabからGameObjectが生成されていました。

これと同じことをHybrid ECSで行う工程をメモとして残しておきたいと思い、この記事を書きました。

環境

  • Unity 2019.3.6f1
  • Entities 0.8.0
  • Unity Physics 0.2.5
  • Hybrid Renderer 0.4.0

Prefabの準備

生成したいGameObjectにConvertToEntityをアタッチし、必要に応じてAuthoring、PhysicsShape、PhysicsBody等をつけた後、Prefab化します。
(旧アーキテクチャのColliderがついている場合は消しておきましょう)

必要なComponentDataの作成

using Unity.Entities;

/// <summary>
/// 生成するPrefabに対応するEntityの情報
/// </summary>
public struct SpawnerData : IComponentData
{
    public Entity PrefabEntity;
}
using Unity.Entities;

/// <summary>
/// Spawnerを識別するためのタグコンポーネント
/// </summary>
public struct Spawner : IComponentData
{
}

using Unity.Entities;

/// <summary>
/// Spawnをするタイミングを伝えるためのタグコンポーネント
/// </summary>
public struct SpawnTag : IComponentData
{
}

Spawnerオブジェクトの作成

空のゲームオブジェクトを作成し、ConvertToEntityを付けます。

SpawnerAuthoringの作成

using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

[DisallowMultipleComponent]
[RequiresEntityConversion]
public class SpawnerAuthoring : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
{
    [SerializeField] private GameObject prefab;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponentData(entity, new Spawner());

        dstManager.AddComponentData(entity, new SpawnerData()
        {
            PrefabEntity = conversionSystem.GetPrimaryEntity(prefab)
        });

        // 今回は簡単な例を考え、実行時にEntityを生成することにします。なのでここでSpawnTagをつけます
        dstManager.AddComponentData(entity, new SpawnTag());
    }

    public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(prefab);
    }
}

これをSpawnerオブジェクトにアタッチし、最初に作成したPrefabをエディタ上のPrefabフィールドに代入します。

SpawnSystemの作成

using Unity.Entities;

/// <summary>
/// Prefabを生成するSystem
/// </summary>
public class SpawnSystem : SystemBase
{
    private EntityCommandBufferSystem _entityCommandBufferSystem;

    protected override void OnCreate()
    {
        _entityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        var concurrent = _entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent();

        Entities
            .WithBurst()
            .WithAll<SpawnTag, Spawner>()
            .ForEach(
            (Entity entity, int entityInQueryIndex, in SpawnerData spawnerData) =>
            {
                concurrent.Instantiate(entityInQueryIndex, spawnerData.PrefabEntity);

                // Entityを2つ以上生成しないために、ここでSpawnTagを剥がします
                // これをしないと、毎フレームEntityが生成されてしまうので
                concurrent.RemoveComponent<SpawnTag>(entityInQueryIndex, entity);
            }).ScheduleParallel();

        _entityCommandBufferSystem.AddJobHandleForProducer(Dependency);
    }
}

これで実行時にPrefabからEntityが生成されます。

ここでは、実行時にEntityを生成する簡単な場合を考えましたが、好きなタイミングでEntityを生成したい場合は、そのタイミングでSpawnTag ComponentDataをSpawnerのEntityに付ければokです。

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

対応するOSのBuildSupportをインストールしてるはずなのに「Unsupported Build Target」と出てくる時の対処法。

問題の発生

Unityを立ち上げProjectを開こうとしたら、こんなエラーが出ました。
要約すると「Android Build Supportを入れてないじゃろ」といったところですかね。
「さっきまで使ってたはずなのに何故???」
となったことが何度かあります。
スクリーンショット 2020-03-23 16.00.17.png

Unsupported Build Target
Build Target AndroidPlayer is not supported. Switch to MacStandaloneSupport or exit Unity?

iOSとかだと文言が Build Target iOSPlayer is not supported.とかになるんですかね?

対処法

  1. 新規プロジェクトを作成し開く。(仮にTempProjectって名前でもつけてあげますか)
  2. File -> BuildSettings を開く。
  3. Platformを任意のOSを選択し、SwitchPlatform を押す。今回の場合はAndroidを選択しました。
  4. TempProjectを閉じる。
  5. 「Unsupported Build Target」のエラーがでたプロジェクトを開き直す。
  6. 開けるはず!

こんな時に発生しやすい

・BuildTarget が Android または iOS の状態で作業をしてる際にUnityをクラッシュさせる。
 その後にプロジェクトを開き直すとたまに出てきちゃう。

環境

Mojave 10.14.5
Unity 2018.3.11f1

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

UnityのTextMeshProで日本語を使いたい

UnityでTextMeshProに日本語を利用したいメモ
日本語フォントがないと駄目のようでしたので

日本語フォントをダウンロード

https://www.google.com/get/noto/#sans-jpan
NoName_2020-3-23_15-48-29_No-00.png

Unityへフォントのファイル設置

NoName_2020-3-23_16-8-55_No-00.png

Font Asset Createrでフォント生成

Font Settingは添付画像のようにしました

Custum Charactor Listには下記フォントデータの文字列をコピペ
https://gist.github.com/kgsi/ed2f1c5696a2211c1fd1e1e198c96ee4

NoName_2020-3-23_15-54-10_No-00.png

生成したデータを別名保存(めっちゃ時間かかった)

生成終了後の画面
NoName_2020-3-23_16-5-53_No-00.png

別名保存後
NoName_2020-3-23_16-9-4_No-00.png

TextMeshProで指定

NoName_2020-3-23_16-6-52_No-00.png

参考サイト

UnityのText Mesh Proでほぼ全ての日本語を表示させる
https://qiita.com/kgsi/items/08a1c78b3bee71136156

kgsi/japanese_full.txt
https://gist.github.com/kgsi/ed2f1c5696a2211c1fd1e1e198c96ee4

Noto Sans CJK JP
https://www.google.com/get/noto/#sans-jpan

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

MessagePack for C#とEntitiesを両方入れた時にエラーが出る

環境

エラーの内容

MessagePack for C# とEntitiesをInstallすると次のようにエラーが出ます。

スクリーンショット 2020-03-22 16.28.18.png

PrecompiledAssemblyException: Multiple precompiled assemblies with the same name System.
Runtime.CompilerServices.Unsafe.dll included for the current platform. 
Only one assembly with the same name is allowed per platform. 
Assembly paths: Assets/Plugins/System.Runtime.CompilerServices.Unsafe.dll
Assets/Scripts/MessagePack/SequenceReaderExtensions.cs(39,21): error CS0433: 
The type 'Unsafe' exists in both 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' 
and 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Assets/Scripts/MessagePack/Formatters/GenericEnumFormatter`1.cs(39,107): error CS0433: The type 'Unsafe' exists in both 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' and 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

これらのエラーはSystem.Runtime.CompilerServices.Unsafeが重複して存在することに起因しているようです。

解決法

Library/PackageCache/com.unity.collections@0.7.0-preview.2/ 以下にある

  • System.Runtime.CompilerServices.Unsafe.dll
  • System.Runtime.CompilerServices.Unsafe.dll.meta

を削除します。

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

C#:列挙型の解説

Sample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 列挙型とは?
// * コードを読みやすくするためのもの(可読性をあげる)
// * 制限の中から選択可能にするもの

public class Sample : MonoBehaviour
{
    enum DIRECTION {
        UP,
        DOWN,
        RIGHT,
        LEFT,
    }

    DIRECTION direction = DIRECTION.DOWN;
    int directionInt = 0; // 0:上

    void Start()
    {

        if (directionInt == 0)
        {
            Debug.Log("下");
        }
        else if(directionInt == 1)
        {
            Debug.Log("上");
        }

        switch (direction)
        {
            case DIRECTION.DOWN:
                Debug.Log("下");
                break;
            case DIRECTION.UP:
                Debug.Log("下");
                break;
            case DIRECTION.RIGHT:
                Debug.Log("下");
                break;
            case DIRECTION.LEFT:
                Debug.Log("下");
                break;
        }

        /*
        if (direction == DIRECTION.Down)
        {
            Debug.Log("下");
        }
        else if (direction == DIRECTION.Up)
        {
            Debug.Log("上");

        }
        else if (direction == DIRECTION.RIGHT)
        {
            Debug.Log("右");

        }
        else if (direction == DIRECTION.LEFT)
        {
            Debug.Log("左");
        }
        */

    }
}

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

C#:2次元配列の解説

スクリーンショット 2020-03-23 9.51.49.png

Sample.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 2次元配列
// * Table/表

public class Sample : MonoBehaviour
{
    // 宣言方法
    // 1次元配列
    int[] arrayInt = new int[3];
    // 2次元配列
    int[,] tabelInt = new int[3, 2];
    void Start()
    {
        // 代入方法
        tabelInt[0, 0] = 1;
        tabelInt[2,1] = 10;
        // 取得方法
        //int x = tabelInt[0, 0];

        //長さ
        //Debug.Log(arrayInt.Length);
        Debug.Log(tabelInt.GetLength(0));
        Debug.Log(tabelInt.GetLength(1));
        for (int x = 0; x<tabelInt.GetLength(0); x++)
        {
            for (int y = 0; y < tabelInt.GetLength(1); y++)
            {
                Debug.Log(tabelInt[x,y]);
            }
        }
        for (int y = 0; y < tabelInt.GetLength(1); y++)
        {
            for (int x = 0; x < tabelInt.GetLength(0); x++)
            {
                Debug.Log(tabelInt[x, y]);
            }
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】ScriptableObjectのAssetファイルを自動で出力生成するエディタ拡張

※この方法だと値がリセットされてしまうため、回避方法を模索中です。

概要

置いておくだけでScriptableObjectのAssetファイルを自動で出力・更新してくれるエディタ拡張を作成しました。

ScriptableObjectの生成について

UnityでScriptableObjectのスクリプトを作ったとき、[CreateAssetMenu]属性などでAssetファイルを手動出力する必要があります。

(例:Data.cs→CreateAsset→Data.asset)

これが1回ならいいのですが、パラメータ用の変数追加等で再出力するとなると何回も出力作業をするしかないため非常に億劫です。
(しかも、名前が「new Data1.asset」みたいになったりする! やだやだ!)
自作ウィンドウやコンテキストメニュー等、色んな所に出力手段を作っている記事はありますが、やっぱりどうしても面倒です。

自動で出力したい……!

機能

というわけで、特定の入力ディレクトリと出力ディレクトリを指定し

  • Unity起動時
  • フォルダやスクリプト生成、素材インポートなどのアセット更新時
  • メニューから手動で実行時

にプロジェクト内のScriptableObjectのAssetを一括で正しく配置(生成・上書き・削除)してくれるエディタ拡張を作りました。

"Assets/Scripts/Data"
にスクリプトを作っておくと

"Assets/Resources/Data"
に同じフォルダ構成でアセットが自動で作られる!

みたいな感じです。

使い方

  • ScriptableObjectCreator.csをAssets/Editorに入れてください
  • その時点でアセットの更新が入るので、ScriptableObjectのAssetが必要に応じて出力ディレクトリに配置されます
  • あとは置いておけば、起動時、アセット更新時(ファイル作成や更新時)にScriptableObjectのAssetが整理されます
  • 手動で再配置を実行したいときは、Unityのメニューから「Tool」→「Create ScriptableObject」を選択してください

余計なアセット(対応する.csがないやつ)があれば削除、必要なところにはアセットを生成してくれます。
常に整理がなされるため、常に適切な箇所にアセットが置かれている状態を保つことができます。

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

// 起動時に実行.
[InitializeOnLoad]
public class ScriptableObjectCreator : EditorWindow
{
    // スクリプトの入力パス.
    private static string inputPath = "Assets/Scripts/Data";
    // アセットの出力パス.
    private static string outputPath = "Assets/Resources/Data";

    // スクリプトの拡張子.
    public static readonly string scriptExtension = ".cs";
    // アセットの拡張子.
    public static readonly string assetExtension = ".asset";

    // コンストラクタ(起動時に呼び出される).
    static ScriptableObjectCreator()
    {
        // 処理を呼び出す.
        UpdateAssets();
    }

    // アセット更新時に実行.
    public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetsPath)
    {
        // 処理を呼び出す.
        UpdateAssets();
    }

    // メニューにアイテムを追加.
    [MenuItem("Tools/Create ScriptableObject")]
    // 呼び出す関数.
    public static void UpdateAssets()
    {
        // アセットを削除する.
        DeleteAll(outputPath);

        // アセットを生成する.
        CreateAll(inputPath);

        // データベースをリフレッシュする.
        AssetDatabase.Refresh();
    }

    // 出力先をチェック.
    public static void DeleteAll(string targetPath)
    {
        // ディレクトリパスの配列.
        string[] directories = Directory.GetDirectories(targetPath);
        // ファイルパスの配列.
        string[] files = Directory.GetFiles(targetPath);

        // ファイルを削除.
        foreach (var file in files)
        {
            DeleteAsset(file);
        }

        // さらに深い階層を探索.
        foreach (var directory in directories)
        {
            DeleteAll(directory);
        }
    }

    // 入力元をチェック.
    public static void CreateAll(string targetPath)
    {
        // ディレクトリパスの配列.
        string[] directories = Directory.GetDirectories(targetPath);
        // ファイルパスの配列.
        string[] files = Directory.GetFiles(targetPath);

        // ファイルを生成.
        foreach (var file in files)
        {
            CreateAsset(file);
        }

        // さらに深い階層を探索.
        foreach (var directory in directories)
        {
            CreateAll(directory);
        }
    }

    // アセットを削除.
    public static void DeleteAsset(string targetPath)
    {
        // ファイル情報.
        string directoryName = Path.GetDirectoryName(targetPath);
        string fileName = Path.GetFileName(targetPath);
        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
        string extension = Path.GetExtension(targetPath);

        // スクリプトのパス.
        string scriptPath = inputPath.Replace("/", "\\");
        // アセットのパス.
        string assetPath = outputPath.Replace("/", "\\");

        // アセットファイルである.
        if (extension == assetExtension)
        {
            // アセットのパス.
            string filePath = targetPath.Replace("/", "\\");
            // スクリプトのパス.
            string checkPath = Path.Combine(directoryName.Replace(assetPath, scriptPath), fileNameWithoutExtension + scriptExtension);

            // 対応するファイルが存在しない.
            if (!File.Exists(checkPath))
            {
                // 削除.
                AssetDatabase.DeleteAsset(filePath);

                Debug.Log("ScriptableObject Deleteed : " + filePath);
            }
        }
    }

    // アセットを生成.
    public static void CreateAsset(string targetPath)
    {
        // ファイル情報.
        string directoryName = Path.GetDirectoryName(targetPath);
        string fileName = Path.GetFileName(targetPath);
        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
        string extension = Path.GetExtension(targetPath);

        // スクリプトのパス.
        string scriptPath = inputPath.Replace("/", "\\");
        // アセットのパス.
        string assetPath = outputPath.Replace("/", "\\");

        // スクリプトファイルである.
        if (extension == scriptExtension)
        {
            // 生成.
            var obj = CreateInstance(fileNameWithoutExtension);
            string filePath = Path.Combine(directoryName.Replace(scriptPath, assetPath), fileNameWithoutExtension + assetExtension);
            CreateAssetWithOverwrite(obj, filePath);

            Debug.Log("ScriptableObject Created : " + filePath);
        }
    }

    // アセットを上書きで作成する(metaデータはそのまま).
    public static void CreateAssetWithOverwrite(UnityEngine.Object asset, string exportPath)
    {
        // アセットが存在しない場合はそのまま作成(metaファイルも新規作成).
        if (!File.Exists(exportPath))
        {
            AssetDatabase.CreateAsset(asset, exportPath);
            return;
        }

        // 仮ファイルを作るためのディレクトリを作成.
        var fileName = Path.GetFileName(exportPath);
        var tmpDirectoryPath = Path.Combine(exportPath.Replace(fileName, ""), "tmp");
        Directory.CreateDirectory(tmpDirectoryPath);

        // 仮ファイルを保存.
        var tmpFilePath = Path.Combine(tmpDirectoryPath, fileName);
        AssetDatabase.CreateAsset(asset, tmpFilePath);

        // 仮ファイルを既存のファイルに上書き(metaデータはそのまま).
        FileUtil.ReplaceFile(tmpFilePath, exportPath);

        // 仮ディレクトリとファイルを削除.
        AssetDatabase.DeleteAsset(tmpDirectoryPath);

        // データ変更をUnityに伝えるためインポートしなおし.
        AssetDatabase.ImportAsset(exportPath);
    }
}

これですっきり。
ご拝読、ありがとうございました。

こちらを参考にさせていただきました
https://qiita.com/JGS_Developer/items/2b7e9cc91b0113c477fc
https://kan-kikuchi.hatenablog.com/entry/CreateAssetWithOverwrite

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