【C#】定義名に日本語使っていいって知ってた?

最近、知り合いにC#を教えていたのですが、
C#の定義名に日本語を使ってもいいという事をさらっと話したらビックリされました。
日本語表記はデメリットが目立つので毛嫌いされがちです。
しかし、使いどころを間違えなければ個人開発なら便利だという事を
組み込みルールや使い方例などを使ってまとめてみました。
※私個人の使い方ですのであくまで参考程度に・・・

C#は定義名に日本語を使ってもOK

変数やクラス、メソッド名に日本語を使ってもお問題ないです。
以下にソース例を載せておきます

        private void 日本語チェック()
        {
            int 数値 = 1;
            string 文字 = "TEST";
            Console.Write(数値.ToString() + 文字);
        }

こんな感じで、メソッド名や変数名を日本語表記にしても問題なく動きます。

日本語表記は意外とデメリットが多くて推奨されてない

定義名に日本語を使い事のメリットは誰でも分かると思いますが、
実際に日本語表記を使ってみると、意外とデメリットが多いことに気づきます。

・変数名が長くなりがち(日本語だと詳細までかけちゃうから・・・)
・呼び出すときにいちいち
 日本語入力に切り替えて書かないといけない(これはクソだるい!)
・コードの中にチラチラ日本語が混ざると、
 コードが頭に入りずらい(私だけでしょうか?)
・ここは英語でいこうかな?
 みたいなバカみたいな迷いが生じてしまう(私だけ)

初めて使用した時に感じたことはこんなものでしょうか。
皆さんも使ってみるとこんな感覚なんじゃないかなと思います。
ルールを考えずに日本語表記を使うなら、使わない方が断然マシです!
上記のデメリットと向き合い、
日本語表記と上手く向き合うと分かりやすくソースが組めるようになります。
私個人の日本語表記ルールを以下にまとめます。

ルール:日本語は英語を先頭において使う

まずは定義例をご覧ください。

        // 変数定義
        public static int _Quiz_正答率;
        public static int _Quiz_中央値;
        // 定数定義
        public const int Df_Time_ノード消去時間 = 50;
        public const int Df_Pt_リズム_悪 = 0;
        public const int Df_Pt_リズム_並 = 3;
        public const int Df_Pt_リズム_高 = 5;
        // 関数定義
        private bool Is_数値である(){}
        private string Get_今日の日付文字列(){}

こんな感じで定義名を記入します。
グローバル変数には先頭に「 _ 」
・定数には先頭に「 Df_ 」
・判定関数(戻り値がbool)の物には先頭に「 Is_ 」
・値取得関数(戻り値がbool以外)には「 Get_ 」

など、自分でルールを決めて定義名の先頭に英語表記を記入しましょう。
要は、先頭にその定義のカテゴリを英語で表記するということです。
日本語と違い、英語は短い文字で分かりやすいカテゴリを作れます。
この表記法で定義名を見るだけで大雑把説明と詳細説明の2つを行うことができます。
また、最大の利点は日本語入力に切り替えなくても呼びだせます!
呼び出し方法は、先頭の文字を記入してインテリジェンス機能を使えば一発です。
【例】
「Df_Pt_リズム_悪」を呼び出したいときは、
「Df_Pt_」までを記入してインテリジェンス機能を使って呼び出す。
※インテリジェンス機能が分からない方は、
 「 自分の使っているエディター名 インテリジェンス 」で検索!

この表記法によって、呼び出しも簡単になり
インテリジェンス機能を使った組み込みに慣れることもできて一石二鳥です。

日本語もいいけど英語も慣れていかなきゃ・・・

上記にルールを書いておいてなんですが、
私は定数の末尾にしか日本語表記を使っていません。
その理由は、英語使わないと一生覚えれないからです。
定義名を決めるときに、役割に対応する英語を調べるのは英語の勉強になると思います。
関数や変数は、定数と違って汎用的な役割を持っていることが多いです。
よく使う役割に対応する英語ぐらい覚えておきたいので、
日本語表記を使いすぎない!という事も大事だと思います。

以上です。
今回は、集団開発から見たデメリットは一切書いてません!
(だって集団開発なら、規約で日本語なんか確実に禁止になってるから意味ないもん)
個人開発なら日本語表記は全然便利だよ。という話でした。

【ココナラ】スロット出玉サイトのスクレイピングをツール化してみた

Pythonスクレイピングをやっていて、
暇つぶしと実益を兼ねてスロットの出玉情報の取得・解析を行う
ツールを作ってみました。

オプションとしてソースの公開も行ってますので
広告させていただきます。

作ってみて思ったことは、
意外と曜日の癖ってあるんだなぁ~ということでした。

興味のある方は無料版を配布しますのでココナラからご連絡ください。

スロット出玉データサイトのスクレイピング行います 誰でも簡単!ツールで一発!好きなホールの出玉データを収集解析

【C#】クラス型変数をコピーする方法

C#で変数をコピーしたとき、
自分の想定では値をコピーしたものだと思っていたのに
いざ結果を見てみると、
コピーした変数の値を変更したら
コピー元の変数の値も変わっていた!

という経験が誰しもあると思います。
私だけじゃないと思います・・・たぶん
という事で、今回は参照型変数についての話と
参照型になるクラスの値をコピーする方法のご紹介です

参照型とは?intやstringとは違うの?

参照型と値型の違いについては別記事で語ってます。
以下のリンクをご確認ください
apuridasuo.hatenablog.com

参照型の変数を普通にコピーした時

では実際に参照型となる自作クラスをコピーしてみます

        /*******************************************
           コピーする自作クラス
        ********************************************/
        public class TestClass
        {
            public int _Id;
            public string _Name;
            public string[] _Array01;
            //=========================
            // 通常のコンストラクタ
            //=========================
            public TestClass()
            {
                _Id = 0;
                _Name = "";
                _Array01 = new string[2] { "", "" };
            }
        }

        /*******************************************
           コピー処理を行う関数
        ********************************************/
        private void Main()
        {
            // コピーする変数を作成
            TestClass TestClassBef = new TestClass();
            TestClassBef._Id = 1;
            TestClassBef._Name = "コピー前";
            TestClassBef._Array01[0] = "ま";
            TestClassBef._Array01[1] = "え";
            // コピーしてコピー後の変数の値を変えてみる
            TestClass TestClassCopy = TestClassBef;
            TestClassCopy._Id = 2;
            TestClassCopy._Name = "コピー後";
            TestClassCopy._Array01[0] = "あ";
            TestClassCopy._Array01[1] = "と";
        }

こちらが自作クラス「TestClass 」の型の変数「TestClassBef 」を作成後、
別の変数「TestClassCopy 」にコピーして、値を変更するソースです。
処理後の変数の中身をメッセージ出力した結果が以下になります

            // コピー前の変数の中身
            TestClassBef._Id = 2
            TestClassBef._Name = "コピー後"
            TestClassBef._Array01[0] = "あ"
            TestClassBef._Array01[1] = "と"
            // コピー後の変数の中身
            TestClassCopy._Id = 2
            TestClassCopy._Name = "コピー後"
            TestClassCopy._Array01[0] = "あ"
            TestClassCopy._Array01[1] = "と"

コピー前の変数も値が変わってしまっていますね。
これが参照型変数の特徴なんです。

クラス型の変数を別変数としてコピーする方法

コピー後の変数を変更しても、
コピー前の変数に影響を与えない方法をご紹介します。
自作クラスのコンストラクタを追加します!

        /****************************
           コピーしたいクラス
        *****************************/
        public class TestClass
        {
            public int _Id;
            public string _Name;
            public string[] _Array01;
            //====================
            // 通常のコンストラクタ
            //====================
            public TestClass()
            {
                _Id = 0;
                _Name = "";
                _Array01 = new string[2] { "", "" };
            }
            //====================
            // コピー用コンストラクタ
            //====================
            public TestClass(TestClass CopyClass)
            {
                _Id = CopyClass._Id;
                _Name = CopyClass._Name;
                _Array01 = new string[2] { "", "" };
                Array.Copy(CopyClass._Array01, _Array01, CopyClass._Array01.Length);
            }
        }

        /*******************************************
           呼び出す関数
        ********************************************/
        private void Main()
        {
            TestClass TestClassBef = new TestClass();
            TestClassBef._Id = 1;
            TestClassBef._Name = "コピー前";
            TestClassBef._Array01[0] = "ま";
            TestClassBef._Array01[1] = "え";
            // コンストラクタを使って変数をコピーする
            TestClass TestClassCopy = new TestClass(TestClassBef);
            TestClassCopy._Id = 2;
            TestClassCopy._Name = "コピー後";
            TestClassCopy._Array01[0] = "あ";
            TestClassCopy._Array01[1] = "と";
        }

以上がソースになります。
通常コピーで書いたソースと見比べてみてください。
まず自作クラスの方にコンストラクタを追加しています。
※1つのクラスに、引数を変えれば複数のコンストラクタを作成できます
自分のクラス型を引数にしたコンストラクタです。
このコンストラクタの中で、
コピー元の変数の中身を取得する処理を記入しましょう!
そして、コピーする変数「TestClassCopy」作成時の記述も変わってます
「new TestClass(TestClassBef);」と定義することで、
コピー前と違ったアドレスを持った空の変数ができます。
そして、作成したコンストラクタによってコピー前の中身を持ってきます。
ソースの実行結果が以下になります

            // コピー前の変数の中身
            TestClassBef._Id = 1
            TestClassBef._Name = "コピー前"
            TestClassBef._Array01[0] = "ま"
            TestClassBef._Array01[1] = "え"
            // コピー後の変数の中身
            TestClassCopy._Id = 2
            TestClassCopy._Name = "コピー後"
            TestClassCopy._Array01[0] = "あ"
            TestClassCopy._Array01[1] = "と"

ちゃんとコピー後変数の変更がコピー前の変数に影響を与えていません。
このように、
参照型の変数は手順を踏んでコピーを行わないと
簡単に想定外の不具合が起きます!

私のコピーの仕方はほんの一例でしかありません。
参照型というものは不具合に遭遇して経験を積まないと
ぱっと理解できるものじゃないと思います。
今後もバグらせまくると思いますので、
変なバグをやっちゃったらまとめたいと思います。

【C#】stringが参照型だって知ってた?

最近、stringが参照型だということを知って
そう言われるとそうじゃないとおかしいよなー!という納得と
参照型だったらおかしくね!?と思う事があったのでまとめます

参照型と値型の違い

araramistudio.jimdo.com
リンク丸投げですみません!
上記リンクはメモリが図示されていて分かりやすかったのでご参考にしてください
簡単に説明すると
値型は、変数の中に値が格納されていて、
参照型は、変数の中に値が格納されているアドレスが格納されています。
実際にソースの動きで見てみると分かりやすいかも

        private void SetValueTest()
        {
            int testInt = 0;                                    // 値型変数代表
            StringBuilder testStrb = new StringBuilder("000");  // 参照型変数代表
            AddValues(testInt, testStrb);
        }
        private void AddValues(int testInt2, StringBuilder testStrb2)
        {
            testInt2+= 222;
            testStrb2.Append("222");
        }

例として、値型・参照型それぞれの変数を定義。
外部関数の引数として呼び出して関数内で値を変更。
結果、2つの変数にどのような変化が起きるか検証してみます
以下が結果になります

testInt = 0
testStrb ="000222"

違いが分かるでしょうか?
値型の「testInt」は外部関数で変更があっても変わらず0のまま。
参照型の「testStrb」は外部の変更に影響されて"000222"になってます。
つまり、
値型は引数として外部関数に渡される際に新しい変数が作成される。
そのため、外部関数で変更があっても影響しない。
参照型は引数としてアドレスが外部関数に渡される
※同じアドレスが格納された別の変数「testStrb2」が作成されるという事です
外部関数の変更は「testStrb2」に格納されたアドレスの中身に対する変更になる。
つまり、「testStrb」に格納されたアドレスの中身に対する変更でもあるのです。
そのため、外部関数で変更があると、元の変数も影響を受けるという事です。
これが、値型と参照型の違いとなっています。

値型と参照型の分類

ufcpp.net
上記サイトの「C# の型の分類」という図がとても分かりやすいです。
この分類を見ると、
文字列型(string型)は参照型になってる!!
なんてこった!/(^o^)\
今までなんとなくで使ってたstringですが、
経験上stringは値型だと考えて組み込みを行ってました!
これには本当に驚いたとともに、様々な疑問が浮かんできました・・・
なんとなく解決しましたのでそれを以下にまとめます

参照型のstringの挙動はどうなってるの?

参照型・値型の違いを説明する時に用いたコードを
string型の変数で実行してみましょう

        private void SetValueTest()
        {
            string testStr = "000";  // 文字列型変数
            AddValues(testStr);
        }
        private void AddValues(string testStr )
        {
            testStr += "222";
        }

関数の構成は全く同じです。
以下が結果になります

testStr ="000"

外部関数の変更の影響を受けてません・・・
挙動としては値型の挙動になってます。
そうなんです!普段stringを使用していると分かると思うのですが
stringの挙動は値型と同じ挙動をしているのです。不思議です・・・
stringが参照型なのに挙動が値型の理由を以下で掘り下げていきます。

string型にはコンストラクタが存在していた!

docs.microsoft.com
上記サイトに、string型のコンストラクタに関する説明が書かれています。
中でも、以下の記述に注目です!
String(Char[])
 String クラスの新しいインスタンスを、
 指定した文字配列で示された Unicode 文字に初期化します。

簡単にソースで説明すると、

string testStr = "000"; 
string testStr2 = testStr ; 

こうやって記述することは

string testStr = "000"; 
string testStr2 = new string (testStr); 

という事と同義になる。という事です。
普通の参照型変数は変数をコピーするとアドレスをコピーするのですが。
string型変数をコピーすると
別アドレスにコピー元のアドレスの中身をコピーして、
コピー後の変数はコピー後のアドレスになっている
という事です。
参照型なのに無理やり値型の挙動を再現している感じですね。。。
つまり、
string型はコンストラクタによって、
変数コピーの際は値型の挙動をとる!
という事です。
これが参照型のstringが値型のような挙動をとる理由ですね。

なぜstring型は参照型なのに値型みたいに扱おうとしているの?

※ここから先は事実じゃないです。ただの暇つぶしの考察です
まず大前提として、string型の中身はchar型の配列です。
つまり、int型等の値型と違って中身のサイズが不定になってしまいます。
これが、string型が参照型にならざるを得ない理由だと思います。
しかし、参照型は気軽に扱う標準搭載の型としては使い勝手が悪すぎる!
そして、string型はクラス型のように複数の不定の型の集合体ではなく、
char型の配列と、決まった型となっています。
なので、コントラクタを使って値型の挙動を再現できるのだと思います。

以上が参照型云々の話でした。
長々と書いてしまいましたが
string型のコントラクタを発見する場でには
もっと長々とした道中があったのでご勘弁ください。。。

【Unity】Textの文字を動く虹色にする

UnityのTextオブジェクトは、タグによってある程度自由な表現ができます。
今回はタグのテストみたいなもので文字を動く虹色に出来るスクリプトを作りました。

実際の挙動

f:id:apuridasuo:20200131134323g:plain
図:スクリプトの実挙動
こんな感じです。
Textに記入した文字に自動でタグ付けをして一文字づつ色を変えます。
追加機能で、Inspectorの設定で
方向転換・変色スピードの設定変更もできる様にしています。

ソースコード

スクリプトファイルをGitHubにアップしてますのでご自由にどうぞ
github.com

落とすのがメンドイ方は以下にソースを記入しますのでコピペして下さい

public class TextRainbow : MonoBehaviour
{
    // Inspectorで設定する変数
    public bool IsTokeiMawari = true;
    public int ChangeSpeed = 5;
    // 変換管理変数
    public static int _ChangeCnt = 0;
    public static int _NijiStartId = 0;
    // 変換管理定数
    public const string Df_Tag_Hedder = "<color=#Value>";
    public const string Df_Tag_Footer = "</color>";
    // 虹色の中身 ※ 2桁づつで区切って[00 00 00]=[ R, G, B ]の値になっている
    //       ※ この配列の要素を追加・編集したらオリジナルの動く色が作れる
    public static string[] Df_ColorTag = new string[] 
    {
        "ff0000",
        "ffff00",
        "00ff00",
        "00ffff",
        "0000ff",
        "ff00ff",
    };

    /// <summary>
    /// 毎割り込みイベント
    /// </summary>
    void Update()
    {
        // 設定したスピードに合わせて色変更処理を呼ぶ
        _ChangeCnt--;
        if (_ChangeCnt <= 0)
        {
            _ChangeCnt = ChangeSpeed;
            SetTextColorChange(IsTokeiMawari, this.GetComponent<Text>());
        }
    }

    /// <summary>
    /// テキスト色を変える処理(毎割り込み)
    /// </summary>
    /// <param name="TxtSet">変更するテキストオブジェクト</param>
    public static void SetTextColorChange(bool IsVecLR,Text TxtSet)
    {
        // テキストの文字を取得※Tag文字を取り除く
        string textNoTag = TxtSet.text;
        textNoTag = textNoTag.Replace(Df_Tag_Footer, "");
        for (int i_ColorId = 0; i_ColorId < Df_ColorTag.Length; i_ColorId++)
        {
            textNoTag = textNoTag.Replace(Df_Tag_Hedder.Replace("Value", Df_ColorTag[i_ColorId]), "");
        }
        // 一文字ずつ色を設定
        int setColorId = _NijiStartId;
        StringBuilder textSet = new StringBuilder();
        for (int i_Word = 0; i_Word < textNoTag.Length; i_Word++)
        {
            textSet.Append(Df_Tag_Hedder.Replace("Value", Df_ColorTag[setColorId]));
            textSet.Append(textNoTag.Substring(i_Word, 1));
            textSet.Append(Df_Tag_Footer);
            if(IsVecLR)
            {
                setColorId--;
                if (setColorId <0)
                {
                    setColorId = Df_ColorTag.Length-1;
                }
            }
            else
            {
                setColorId++;
                if (setColorId >= Df_ColorTag.Length)
                {
                    setColorId = 0;
                }
            }
        }
        // 次回の開始色を更新
        _NijiStartId++;
        if (_NijiStartId >= Df_ColorTag.Length)
        {
            _NijiStartId = 0;
        }
        // テキスト文字を変更
        TxtSet.text = textSet.ToString();
    }
}

ソースの中で、「虹色の中身」の部分に注目してください。
この配列が設定する文字の色です。
現在は虹に見える様に6色に設定しているのですが、
自由に要素を増やしたり、色データの値を変更すれば
オリジナルの配色にできる様になってます。

使い方

1)虹にしたいTextオブジェクトにスクリプトファイルを追加
2)オブジェクトのTextコンポーネント
 「Rich Text」項目にチェックが入ってなければチェックを入れる
3)ビルドしたら虹になるので、スピード・方向を設定すればOK!

Textのタグのついて

Textに記入している文字を「<○○>表示文字<○○>」のように囲むと
囲んだ文字(表示文字)が変化して表示されるという機能です。
【例】文字「AAA」を赤色でサイズを50にして表示する

<size=50><color=#ff0000>AAA</color></size>

※上記をTextにコピペ!
こんな感じで、AAAをタグでくくることで表示が変更されます
タグの詳細は以下のリファレンスを参照
docs.unity3d.com

以上です。
正直、文字を綺麗に動かすなら「TextMeshPro」一択です!
しかし、単調な動きならスクリプトで汎用化させておくと
疲れた時に楽できるのでとても重宝します。。。


P.S.初めてGIF貼り付けしてみたけど面倒くさすぎて泣けた・・・

【python】tryの中で呼び出した関数がエラーになった場合の挙動

pythonの組み込みで必ず使用するtryを使った例外処理の話です。

tryの中で呼び出した関数でエラー

tryの中で外部関数を呼び出した場合、
・外部関数でエラーになった時は呼び出し元のtryが動いてくれるのか?
・ちゃんとexcept以下の処理が動くのか?
・外部関数で外部関数を呼び出した時も同じ挙動になるのか?
・外部関数の中でtryが組み込まれていた場合はどうなるのか?

これらの挙動を簡単なソースで確認してみました。

【外部にtryなし】外部でエラーが起きた場合の挙動

A )外部の外部の外部でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    print("01")
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
    SetDisp_03()
#******************************************************************************
# 呼び出す外部関数_3rd
def SetDisp_03():
    print('03')
    print(1+"")# こいつがエラーになる行( int+str でエラー)   
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("エラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01を呼ぶ→SetDisp_02を呼ぶ→SetDisp_03を呼ぶ→エラー
という挙動になるように組み込みました。
実行結果

01
02
03
エラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12308>)

結果を見る限り、
外部でエラーになってもちゃんとtryが機能していて処理が中止されることはない。
ちゃんとエラー発生後にexcept以下の処理が実行されている
という事がわかりました。
B )外部でエラーが起きた場合、その後の処理は中断されるのか?

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    print("01")
    print(1+"")# こいつがエラーになる行( int+str でエラー)
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("エラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01を呼ぶ→エラー(の後、SetDisp_02を呼ぶ)
という挙動になるように組み込みました。
実行結果

01
エラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB126C8>)

結果を見る限り、
外部でエラーになってもエラー発生後にすぐ
except以下の処理が実行されているから、エラー後の処理は行われない。
という事がわかりました。

まとめ
・外部でエラーになってもちゃんとtryが機能していて処理が中止されることはない。
・ちゃんとエラー発生後にexcept以下の処理が実行されている
・外部でエラーになってもエラー発生後にすぐ
 except以下の処理が実行されているから、エラー後の処理は行われない。

【外部にtryあり】外部でエラーが起きた場合の挙動

A )外部のtryの中でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    try:
        print("01")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子01のエラー")
        print(sys.exc_info())
        pass
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    try:
        print("02")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子02のエラー")
        print(sys.exc_info())
        pass    
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("親のエラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01(tryの中でエラー発生)→SetDisp_02(tryの中でエラー発生)
という挙動になるように組み込みました。
実行結果

0101のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12788>)
0202のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B08>)
正常

B )外部のtryの外でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    try:
        print("01")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子のエラー")
        print(sys.exc_info())
        pass
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
    print(1+"")# こいつがエラーになる行( int+str でエラー)
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("親のエラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01(tryの中でエラー発生)→SetDisp_02(tryの外でエラー発生)
という挙動になるように組み込みました。
実行結果

01
子のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B48>)
02
親のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B88>)

まとめ
A)とB)の結果を比較して、
・親tryの中で呼んだ外部関数のtry(以下、子try)の中でエラーが起きたら、
そのエラーは子tryの中で完結するので親tryから見たら正常扱いとなる。
・子tryはその中で完結しているので、子try外でのエラーは親tryのエラーとなる。
そのため、エラー発生時に処理は中断されて親tryのexcept以下の処理が実行される。

以上です。
組み込んでて色々気になることを検証しているのですが、
説明がめんどくさくて記事に出来ない事ばっかりなので難しいですね。
今回もこんなに記事が長くなるとは思いませんでした。。。まとめ力が成長しない。。。

【Unity】バージョンアップ時にプロジェクトが開けなくなった

昨日、unityを起動したら急に発生した現象です。

アホみたいなことを繰り返してようやく復旧できたので

対処法をまとめておきます。

(ネットに全く参考記事が無くて孤独死しそうでした・・・)

 

 

 バグった現象

Unityプロジェクト起動後に、
今まで問題なかったプロジェクトのコンポーネントがほぼエラー状態になっていた。
※自作スクリプトではなくUnityに元からあるものもエラーになっていた

f:id:apuridasuo:20191129153335p:plain

図:壊れたプロジェクトのエディター表示

f:id:apuridasuo:20191129153416p:plain

図:VSでスプリクトを開いた時のエラー内容(一部抜粋)

色々確認していったのですが、

何かしらが原因でUnityEngineのdll参照が壊れているのかなー?

ぐらいしか分かりませんでした。

原因(予想)

原因は分からないのですが、心当たりがいくつかあるのでリスト化

・UnityHubの方のバージョンアップ通知があったので更新して

 更新後にプロジェクトを開いた。

 ※これが原因だと思ってます。

 でも、エディターでもないHubの更新でプロジェクトが壊れるとは思えない・・・

・当該PJは、当初2017バージョンのUnityエディタで制作していて

 1ヶ月前ぐらいに2019バージョン(2019.2.11)に切り替えていた

 ※切り替え後も上手く動いていて、2019バージョンで

 アプリリリースも行ってますので関係ないとは思う

上記の2つの要因を掛け合わせて予想した原因は、

UnityHubの更新を行ったことで

2017verエディタで作成したプロジェクトの2019verへの互換性がなくなった!

という事だと思ってます。

対応したこと(失敗編)

結局直りませんでしたが、行ったことをまとめておきます

・PCを再起動

 →変わらず・

・UnityHub、Unityエディターを再インストール

 →変わらず・・

・最新のエディター(2019.2.14)をインストールしてPJ(プロジェクト)を開く

 →変わらず・・・(*_*;

・壊れたPJのAssetsフォルダ全てをエクスポートして新規作成PJにインポート

 →新規作成PJは問題なく動くが、インポートしたとたん

 「'csc'は、内部コマンドまたは外部コマンド、
 操作可能なプログラムまたはバッチ ファイルとして認識されていません」

 というエラーが出てビルドできなかった・・・(; ・`д・´)

・PJのバックアップ(2017のエディターに対応)を引っ張り出してきて

 2019バージョンのエディタで起動

 →起動したPJが壊れていた・・・(*´з`)

 この対応で、

 「もしかしてHubの更新が原因で

  2019verのエディタと2017verエディタの互換性がなくなったんじゃ・・・?」

 と思いつきました。

対応したこと(解決編)

・PJのバックアップ(2017のエディターに対応)を2018.4.13のエディタで起動

2019バージョンのエディタで起動したら壊れたPJですが、

2018バージョンなら壊れずに正常に起動できました。

※2017バージョンでも起動できましたが、

 APIレベル28以上の基準が満たせないので2018バージョンで起動

 

あとは、エディター周りの設定を一度削除してしまっていたので

AndroidSDK、NDKの環境を整えて上手くビルドできる様になりました。

 

 分かったこと(大事!)

・UnityHubの更新でもバージョンの互換性は変化する

・バージョンごとに互換性があるため、安易に対応エディタのver更新をしない

・バックアップは偉大・・・( ..)φメモメモ

・2017verのプロジェクトと2018verのプロジェクトは互換性がある

 

いじょうです。

同じ境遇の方がいましたらご参考にしてみてください!