ヨイチカの冒険譚

日々の思い付きを日記のように書き留めていく

PowerShellのインストール 初期設定

前置き

  • 最近PowerShellを本を読みながら学び始めたのであまり触らない初期設定やインストールなどを忘れたときに見返せるようにメモ程度に書き残しておきます。
    ある程度プログラミング言語に触ったことがある前提で話しています。
    Windows10の場合のインストール方法です。 Windows10には標準でWindowsPowerShellが入っていますが、WindowsPowerShellには入っていない処理だったりクロスプラットフォームではなかったりがあるため最新のPowerShellを使う方法になります。

PowerShellとは

自分と相手が対話しているような操作性から対話型ホスト、コンソールと呼ばれているCUIになります。
PowerShellの特徴としてコマンドプロンプトと同じ「シェル環境」であること。
スクリプトに処理を記述できること。
実行した結果がオブジェクトとして返ってくることだと思っています。
逆にpowershell-coreにはない機能があり、windowsに密接にかかわる機能は使うことができません。そちらを扱う際はWindowsPowerShellを使います。
まだ学び始めたばかりでぜんぜん解釈間違っているかもしれないです

PowerShellのインストール

Chocoを使ってインストールするためChocoが入っていない場合は以下の方法でインストールする
  1. 管理者権限を使ってWindowsPowerShellを起動
  2. 公式サイトより下記コードをコピペ
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

WindowsPowerShellに張り付けて実行
色々聞かれるのでyで承諾していく

Chocoを使ってPowerShellをインストール
  1. WindowsPowerShell上で choco install powershell-core を実行
  2. インストールが完了したらパスを通す
    GUIでシステムの詳細設定 > 環境変数の追加で変数名をpwshにしてパスをPowerShellをインストールしたフォルダのpwsh.exeを指定
    コマンドでパスを通す場合
powershell -command [Enviroment] : : SetEnviromentVariable('PATH',([Enviroment] : : GetEnviromentVariable('PATH','Machine') + ';' + 'インストール場所'),'Machine')

確認としてコマンドプロンプトなりを起動して pwsh --versionでインストールされているバージョンを確認してインストールされているか確認する

VS CodePowerShellをつかえるようにする

VS Code拡張機能powershellと検索して拡張をインストール
コマンドパレットを開き powershell show session Menu と入力して Current Session PowerShellを選択
再起動すると使えるようになります。

Unityのc#スクリプトのテンプレートを変更する

前書き


専門学校のテストや就活等であまりブログを更新できなかったので久しぶりに投稿しておこうと思い自分の知っている小技を一つ紹介します。

バージョンごとのテンプレートの変更


何も設定せずにスクリプトを生成するとスタートとアップデートがあらかじめ用意されたものができます。
今回はこれを独自に変更して、生成した後にいらない部分を削除して。。。なんてのを省けるようにして好きなようにテンプレートを変えます

方法


1. UnityHubのEditorから任意のバージョンのファイルを開く
\Unity\Hub\Editor\201X.X.Xf1\Editor\Data\Resources\ScriptTemplates

テンプレートがいろいろあると思いますが、その中の81-C# Script-NewBehaviourScript.cs.txtを開きます

2. テンプレートを変更する

開いたファイルは以下のようになっているので好きなように変更して保存します

中身

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

public class #SCRIPTNAME# : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        #NOTRIM#
    }

    // Update is called once per frame
    void Update()
    {
        #NOTRIM#
    }
}

3. 再起動する

Unityを再起動すると反映されていますのでこれでバージョンにおけるテンプレートの変更は完了です


プロジェクトごとにテンプレートを用意する


上記の方法ではチームで作成していた場合いきなりテンプレートが変われば問題になります。
そのため問題が起きないようにAssets/以下にテンプレートを置くことで複数のテンプレートをバージョンごとではなくプロジェクト単位で使用できます。

方法


Assets/以下にScriptTemplatesフォルダを作成してテキストファイルを作成

f:id:yoitika:20210204022402p:plain
テキストファイル作成時には以下の規則に合っていないと反映されないので注意です
<表示順>-<Create欄に表示する名前>-<生成されるファイル名>.txt
例 79-Custom__表示名.cs.txt
クリエイトに表示される名前に__で階層化できます
f:id:yoitika:20210204022437p:plain

作成が終わったら再起動する

反映されます
f:id:yoitika:20210204022201p:plain

 public class hoge : MonoBehaviour
    {
        /// <summary>
        /// 初期化処理
        /// </summary>
        private void Start()
        {
            
        }
    }

2020年の振り返り

1年間で学んだこと


言語、ライブラリ

  • C++言語
    学校の授業で学びました。c#を学んだあとだったので割とすっと頭の中に入ってきた。初心者の壁の一つであるポインタもc#でunsafeで使ったことがあったので壁にならなかったぜ・・
  • JavaScript
    初めて動的型付け言語を学びなぜconstでいいのか困惑した。静的型付け言語との違いをここで初めて知りました。
  • C#
    GCとかLinqとかc#のより深い機能を学びました。GCの機能のありがたみと、アロケーションの危険度(?)を学べたのが大きかったです。
  • Unity
    拡張とかタイムラインだったり、ShaderGraphだったり、VFXGraphだったり新機能を多く学び、パフォーマンスのチェックだったり、本当に多く学びました。
  • UE4
    UE4はまだブループリントと少しC++を触った程度で全然なので21年頑張ってゲーム作りたい。
  • React
    Reactはyoutubeのおススメでやっすんさんの入門がでてきて面白そうだったので学んでみました。(動機が謎ですね)
  • Google Apps Script
    GASは英語を学ぶときにスプレッドシートに書き込んでUnityで単語帳作るために学んだのですが、結構躓いたことが多くて大変でした。
その他

  • git
    githubの使い方完全に理解した(理解してない) github Action使いこなせるようになりたい
  • デザインパターン
    最近読み始めてGofの23パターン理解した。あとはクリーンアーキテクチャだったりMVP、MVCとかを学びました。
  • 読書(読む習慣をつける)
    一日1時間読むように時間の管理をちゃんとしました。Trelloというタスク管理アプリで読む本と読んだ本を分けて後で何を読んだかわかるようにしたら満足感で続けることができました。
    モチベーションの維持はどんな物事にも言えますが大切ですね。
  • 英語
    高校の時から苦手で避けてきましたが、できないと読みたい論文があっても英語で読めない・・となることが多くあり、中学生で習うようなことから学びなおしています。1日15単語覚えてスプレッドシートにメモを繰り返して、今では350ほど単語覚えることができました。 これからも続けていきます。
  • 数学
    三角関数微分積分など3Dで使う数学を学びなおしていました。
    読んでいた本たち
    実例で学ぶゲーム数学
    Unityでわかるゲーム数学

楽しかったこと


ゲーム最高!Apexが特に楽しかった!
今年で20になり、酒が飲めるようになって最高!(あんま強くないけど)

まとめ


大事なことは継続だと思いました。人間の記憶は一週間で覚えたことの7から8割忘れてしまうといいます。そのため、復習することで脳に定着させて忘れないようにする必要がありますね。

オブジェクトプール

前置き

オブジェクトプールを学んだので備忘録。
最近デザインパターンの本よく読んでだいぶ知見たまってきたのでブログどんどん更新していく(予定)

オブジェクトプールとは

オブジェクトを生成してそのオブジェクトを使いまわすことで重い処理である生成や破棄を抑えて負荷を下げようというときに使うものです。(※間違っていたらすいません)
プールしすぎるとメモリを多く使うことになるのでそこは注意が必要かもしれません。

汎用的なオブジェクトプールのロジック

Unityではオブジェクトプールはプールするオブジェクトの親として空のオブジェクトを生成しておき、そこから子としてプーリングしていく感じが多い印象です。
使う際はアクティブと非アクティブを切り替えて再利用しています。

コード
 public class MyObjectPool : MonoBehaviour
    {
        //プールとなる親オブジェクト
        private Transform _pool;
        
        private void Start()
        {
            _pool = new GameObject($"Pool").transform;
        }
     
        void GetPoolObject(GameObject gameObject)
        {
            foreach (Transform transform in _pool)
            {
                //オブジェクトが非アクティブなら使いまわす
                if (!transform.gameObject.activeSelf)
                {
                    //処理
                    transform.gameObject.SetActive(true);
                }
                
                //非アクティブがない時は生成する
                Instantiate(gameObject,_pool);
            }
        }
    }

まとめ

オブジェクトプールの汎用的なものを作ろうと考えて試行錯誤していたのですが、結局なにをプールするかで処理が全然違ってくるため、汎用化は自分のレベルでは無理でした…
ジェネリクスを使って継承する形でオーバーライドで処理を書いていくのを思いつくのがやっとでした。

継承したバージョン(そんな変わってない)

  public class MyObjectPool<T> : MonoBehaviour
    {
        //プールとなる親オブジェクト
        protected Transform _pool;

        [SerializeField, Header("生成数")] protected int _generateCount;
        
        private void Start()
        {
            _pool = new GameObject($"{typeof(T).Name}Pool").transform;
        }

        protected virtual void GenerateObject()
        {
            for (int current = 0; current < _generateCount; current++)
            {
                //生成処理
            }
        }

        protected virtual void GetPoolObject(GameObject gameObject)
        {
            foreach (Transform transform in _pool)
            {
                //オブジェクトが非アクティブなら使いまわす
                if (!transform.gameObject.activeSelf && transform)
                {
                    //処理
                    transform.gameObject.SetActive(true);
                }
            }
        }
    }

自動バリデーションの実装

シーン内のMissingをチェックする

f:id:yoitika:20201216102812p:plain

ソースコード

namespace EditorTest
{
    public class MissingOrNone : EditorWindow
    {
        //初期チェック
        private bool _ini = false;
        
        //スクロール位置
        private Vector2 _scrollPosition = Vector2.zero;
        
        //エディターウィンドウの作成
        [MenuItem("Tool/MissingOrNoneCheck")]
        public static void OpenWindow()
        {
            MissingOrNone window = (MissingOrNone)GetWindow(typeof(MissingOrNone));
            window.Show();
        }
        
        private void OnGUI()
        {
            GUI.contentColor = new Color(0.85f, 0.25f, 0.22f);
            
             GUILayout.Label("Missing None を直してください!!", GUILayout.Height(30));
             
             if (!_ini)
             {
                 _ini = true;
                 return;
             }

             //描画領域が足りないときにスクロールする
             _scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
             
             //ミッシング None を表示する
             GameObject[] hierarchyObjects = HierarchyGetter.GetHierarchyObject();
                         
             foreach (var hierarchyObject in hierarchyObjects)
             {
                 MissingGetter.MissingButton(hierarchyObject);
             }
             
             //スクロールのエンド位置
             GUILayout.EndScrollView();
        }
    }


namespace EditorTest
{
    public class HierarchyGetter
    {
        
        /// <summary>
        /// ヒエラルキーのオブジェクトをすべて取得する
        /// Resourcesからオブジェクトを取得している
        /// 取得ができない場合はnullを返してエラーを吐く
        /// </summary>
        /// <returns></returns>
        public static GameObject[] GetHierarchyObject()
        {
            GameObject[] hierarchyObject = Resources.FindObjectsOfTypeAll<GameObject>();

            if (hierarchyObject == null)
            {
                Debug.LogError("ヒエラルキーのオブジェクトを取得できませんでした。");
                return null;
            }
            
            return hierarchyObject;
        }
    }
}

namespace EditorTest
{
    public static class MissingGetter
    {
        //Unityを開いたときに実行される
        [InitializeOnLoadMethod]
        public static void SceneSetting()
        {
            //シーン保存時にミッシングを検索してダイアログを出すようにする
            EditorSceneManager.sceneSaving += MissingGet;
        }
        
        
        /// <summary>
        /// シーン保存時に実行する処理
        /// ウィンドウを表示する
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="path"></param>
        static void MissingGet(Scene scene,string path)
        {
            MissingOrNone.OpenWindow();
        }
        
        

        /// <summary>
        /// ヒエラルキーからMissingとNoneを見つけてリスト化して表示する
        /// ボタンを生成してMissingやNoneを選択できるようになっている
        /// </summary>
        /// <param name="hierarchyObject"></param>
        public static void MissingButton(GameObject hierarchyObject)
        {
           var components = hierarchyObject.GetComponents<Component>().Where(c => c != null);
           
           foreach (var component in components)
           {
               var so = new SerializedObject(component);
               var sp = so.GetIterator();

               while (sp.NextVisible(true))
               {
                   //エラーチェック
                   if(sp.propertyType != SerializedPropertyType.ObjectReference) continue;
                   if(sp.objectReferenceValue != null) continue;
                   if(!sp.hasChildren) continue;
                   
                   
                   var fileId = sp.FindPropertyRelative("m_FileID");
                   if(fileId == null) continue;
                   
                   //fileIDが0の場合はNoneのためNoneをウィンドウに表示する
                   if (fileId.intValue == 0)
                   {
                       continue;
                   }
                   
                   
                   
                   //一つ一つをボックスとして扱う
                   GUILayout.BeginHorizontal(GUI.skin.box);
                   
                   
                   
                     //色を変える  背景を青に近い色にして見やすく
                     //文字を白くして読みやすくしています
                    GUI.backgroundColor = new Color(0.38f, 0.54f, 1f);
                    GUI.contentColor = Color.white;

                    
                    
                   //fileIDがあり、中身がない場合MissingになっているためMissingをウィンドウに表示する
                   GUILayout.Label($"{component.gameObject.name}のMissing");
                   
                   
                   if (GUILayout.Button($"選択",GUILayout.Width(100)))
                   {
                       Selection.activeObject = component.gameObject;
                   }
                   
                   
                   GUILayout.EndHorizontal();
               }
           } 
        }
    }
}


自動バリデーションとは

バリデーションとは簡単に言えば入力値のチェックのことを言います。
そして自動バリデーションは入力値のチェックを自動で行ってくれる仕組みのことを言います。
今回の実装ではシーン保存時に自動的にヒエラルキー内のオブジェクトを探索してMissingObjectがあった場合に専用windowを開いてMissingを選択できるようにしています。


シーン保存時に処理を実行する

シーン保存時に処理を実行したい場合はUnityが用意しているイベントを使うことで簡単にできます。
        public static void SceneSetting()
        {
            //シーン保存時にミッシングを検索してダイアログを出すようにする
            EditorSceneManager.sceneSaving += MissingGet;
        }
[Unityが用意してくれているイベント](https://docs.unity3d.com/ja/2018.4/ScriptReference/SceneManagement.EditorSceneManager.html)
これで登録するためのメソッドは用意できます。ですが、これを実行時に追加しても意味がありません。そのためUnity起動時に追加するようにします。
Unity起動時に処理を実行するためにはメソッドに以下の属性をつけてあげます。
`[InitializeOnLoadMethod]`
イベント登録の全体コード
 //Unityを開いたときに実行される
        [InitializeOnLoadMethod]
        public static void SceneSetting()
        {
            //シーン保存時にミッシングを検索してダイアログを出すようにする
            EditorSceneManager.sceneSaving += MissingGet;
        }
        
        
        /// <summary>
        /// シーン保存時に実行する処理
        /// ウィンドウを表示する
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="path"></param>
        static void MissingGet(Scene scene,string path)
        {
            MissingOrNone.OpenWindow();
        }


Missingを見つけてウィンドウを表示する

メインの処理であるMissingを見つけてウィンドウを表示する処理です。
そもそもですが、Missingとはオブジェクトがアタッチされていたものが消えた際に発生します。


ヒエラルキー内のオブジェクトを探索する
今回はヒエラルキー内のオブジェクトのアクティブ、非アクティブ関係なく探索したいため Resources.FindObjectsOfTypeAll<>()を使い実装しました。
処理内容はヒエラルキー内のゲームオブジェクトを配列に格納して渡すようにしています。
メソッド
public static GameObject[] GetHierarchyObject()
        {
            GameObject[] hierarchyObject = Resources.FindObjectsOfTypeAll<GameObject>();

        if (hierarchyObject == null)
        {
            Debug.LogError(&#34;ヒエラルキーのオブジェクトを取得できませんでした。&#34;);
            return null;
        }

        return hierarchyObject;
    }</pre>


Missingを表示する
ヒエラルキーから得られたオブジェクトに対してMissingがあるかを見てあった場合はwindowに追加するようにしています。
メソッド
  public static void MissingButton(GameObject hierarchyObject)
        {
           var components = hierarchyObject.GetComponents<Component>().Where(c => c != null);

       foreach (var component in components)
       {
           var so = new SerializedObject(component);
           var sp = so.GetIterator();

           while (sp.NextVisible(true))
           {
               //エラーチェック
               if(sp.propertyType != SerializedPropertyType.ObjectReference) continue;
               if(sp.objectReferenceValue != null) continue;
               if(!sp.hasChildren) continue;


               var fileId = sp.FindPropertyRelative(&#34;m_FileID&#34;);
               if(fileId == null) continue;

               //fileIDが0の場合はNoneのためNoneをウィンドウに表示する
               if (fileId.intValue == 0)
               {
                   continue;
               }



               //一つ一つをボックスとして扱う
               GUILayout.BeginHorizontal(GUI.skin.box);



                 //色を変える  背景を青に近い色にして見やすく
                 //文字を白くして読みやすくしています
                GUI.backgroundColor = new Color(0.38f, 0.54f, 1f);
                GUI.contentColor = Color.white;



               //fileIDがあり、中身がない場合MissingになっているためMissingをウィンドウに表示する
               GUILayout.Label($&#34;{component.gameObject.name}のMissing&#34;);


               if (GUILayout.Button($&#34;選択&#34;,GUILayout.Width(100)))
               {
                   Selection.activeObject = component.gameObject;
               }


               GUILayout.EndHorizontal();
           }
       } 
    }</pre>


windowを表示する
ここはおまけみたいなものですがエディタ拡張でMissingを表示するためのwindowを表示します。
windowを表示
 public class MissingWindow : EditorWindow
    {
        //初期チェック
        private bool _ini = false;

    //スクロール位置
    private Vector2 _scrollPosition = Vector2.zero;

    //エディターウィンドウの作成
    [MenuItem(&#34;Tool/MissingOrNoneCheck&#34;)]
    public static void OpenWindow()
    {
        MissingWindow window = (MissingWindow)GetWindow(typeof(MissingWindow));
        window.Show();
    }

    private void OnGUI()
    {
        GUI.contentColor = new Color(0.85f, 0.25f, 0.22f);

         GUILayout.Label(&#34;Missing None を直してください!!&#34;, GUILayout.Height(30));

         if (!_ini)
         {
             _ini = true;
             return;
         }

         //描画領域が足りないときにスクロールする
         _scrollPosition = GUILayout.BeginScrollView(_scrollPosition);

         //ミッシングを表示する
         GameObject[] hierarchyObjects = HierarchyGetter.GetHierarchyObject();

         foreach (var hierarchyObject in hierarchyObjects)
         {
             MissingGetter.MissingButton(hierarchyObject);
         }

         //スクロールのエンド位置
         GUILayout.EndScrollView();
    }
}</pre>

小技ですが、コードにある_iniの役割はメインの処理が長くなる時全体の表示も遅くなってしまうので一度描画してから処理が終わり次第表示するようにするためのものです。

ドラッグ&ドロップの実装

完成したもの

f:id:yoitika:20201018005005g:plain


準備


コードを書いていく前にシーンにイメージを配置してわかりやすいよう画像を分けておきます。

f:id:yoitika:20201018005512p:plain


実装


ドラッグ
  • ItemオブジェクトにDragAndDrop(名前は適当)を作成してアタッチしてスクリプトを開きます。
  • 初めはオブジェクトのドラッグから実装していきます。 ドラッグに必要なものはドラッグ中のマウスの位置になります。それを取得するための方法は簡単でUnityが用意しているDragHandlerで簡単に取得できます。
    f:id:yoitika:20201018010725p:plain
    ハンドラを実装したら中身を書いていきます。
    処理内容は自身の位置をマウスのポジションに合わせて移動させるものになります。
    f:id:yoitika:20201018011405p:plain
    参考API
    anchoredPosition
    delta
    これでドラッグが完成しました。 ですが、試してみるとわかりますがただ位置を移動させるだけでスナップも初期位置にも戻りません。

初期位置に戻す

次に初期位置に戻すための処理を記述していきます。
初期位置に戻すために必要なものは動かす前の初期座標とドラッグを離したことがわかることです。
初期座標はフィールドを使い値を保持しておきます。
ドラッグを離したことを知るためにはEndDragHandlerを使い取得します。
f:id:yoitika:20201018012750p:plain


スナップ 

最後はスナップの処理を書いていきます。
スナップ処理に必要なものはFieldの上にドラッグしたオブジェクトがあるかどうかです。
これを得るためにはFieldにDropHandlerを実装していきます。
中の処理にはオブジェクトがあるならそのオブジェクトをFieldに合わせるという処理になります。
f:id:yoitika:20201018013446p:plain
これでほぼ完成ですがこのままだとオブジェクトがFieldにあるのにEndDragの元の位置に戻る処理が走ってしまいます。
そのため修正してドラッグを離したときにレイを飛ばしてItem以外に合ったっているオブジェクトがない場合のみ元に戻るようにします。
f:id:yoitika:20201018013856p:plain
これで完成になります。
ここからはおまけでわかりやすくItemにcanvasGroupを付けてドラッグ中は透明度を下げてRayが貫通するようにしておき離したら元に戻るようにしておきます。

f:id:yoitika:20201018014545p:plain

TODO

ほかにはこのままだと大きさはそのままなのでFieldに合わせて調節したり音を付けて移動させている感を出せばそれなりに見えると思います。

MarkDownの練習

MarkDown記法 h1 見出しは#

h2

h3

h4

h5
h6


* 強調 *する文字はアスタリスクで囲む 太字 にしたいときはアスタリスク2個で囲む
改行は <br>

  • リストは伸ばし棒
    • ネストもできる
      • ネスト
  • リスト

  • 番号付きリスト    1. ネスト  

    引用文章 だいなり?をつかう

    ネストもできる

    ne


水平線は伸ばし棒3つでできる

リンクの文字 リンク文字は[リンク文字]に(URL)でできる

画像表示 リンク文字とほぼ同じで前に!マークを付けることで画像になる

ソースコードはバッククォートで囲む バッククォートはシフト+@ 複数行は3つ付ける

var tte= 10;

num = 0
while num < 2 do
   print("num = ", num)
end
print("End")

目次
//[:contents]//

シンタックスハイライト
バッククォートの後に言語名を入れることでその言語の体裁に合わせられます //```ruby

num = 0
while num < 2 do
   print("num = ", num)
end
print("End")

取り消し線
~~で囲みます
文字

コンテンツを折りたためるようにする

<details>
<summary>概要</summary>
内容
</details>

概要 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa