のにっき

【Unity】Androidアプリの起動が遅い時の対処

ちびちび作ってたアプリがようやく完成間近の段階になってブラッシュアップ中の事。
改善必須の厄介な現象につかまりました。
検索しても出てこなかったので色々試行錯誤して、やっと解決したのでまとめます

環境と現象

Androidアプリでの現象
実機デバッグを行った際に、起動後に「Unityロゴ」が出てから
タイトル画面の表示が描画されるまでに「15秒」もかかった

f:id:apuridasuo:20200512150908p:plain
図:表示するタイトル画面の構成
今回は、上記の遅すぎる起動をどのように解決したかまとめていきます
※今回は、対応としては設定1つの変更で解決したのですが、
解決までの過程で色々対応してどんな効果があったのか、ためになったので紹介します。
※解決法だけ知りたい方は以下の「解決法(結論)」の項目だけお読みください

解決法(結論)サウンドの読み込みによる遅延

遅延の原因は、オブジェクトの描画でも、Startイベントで行っている
オブジェクトキャッシュ処理でもありませんでした。
アプリ起動時にサウンドデータを読み込む処理が遅かっただけでした。
サウンドデータをバックグラウンドで読み込むことで解決します

f:id:apuridasuo:20200512152019p:plain
図:サウンドデータの設定方法
上図のように、サウンドデータを選択して、
「Load In Background」にチェックするだけ!
これで起動が「 15秒 → 1秒未満 」に改善されました
音ゲー作ってたので大量の音データの読み込みに時間がかかってたみたいです。。。

※タイトル画面で使用する音(すぐに出力するBGM等)は、
チェックを入れると音の出力が遅れてしまうのでチェックを入れないように!!

(過程)以外と起動の遅延には関係なかった者たち

遅延と聞いて原因を予想する時、何を考えるでしょうか?
今回の様に、サウンドデータのことを思いつく人なんて天才かよ!?って感じです。
私はまず「Startイベントのキャッシュ処理」「3Dオブジェクトの描画」
この2点が原因で起こった遅延だと思っていました。
(そのせいで、解決するまでクソほど遠回りしちゃいました泣)
そこで行った対応が以下になります
・3dオブジェクトを非表示にしてビルド
・3dオブジェクトを削除してビルド
・Startイベントを全てコメントアウトしてビルド

この3つの対応をすれば、とりあえず起動は早くなるだろうから
そこから原因を深堀していけばいいや・・・と思っていました。
結果は、起動時間は全く変わりませんでした
以外だったのは、少しも速くならなかったことです。

◆Startイベントって起動の遅延に影響ないの?

改めて、Startイベントのタイミングを調べてみたのですが、
Unity - マニュアル: イベント関数の実行順
上記サイトの記入では
▼ Startイベントのタイミング
スクリプトインスタンスが有効になると、最初のフレームのアップデート前に呼ぶ
との事でした。
最初のフレームのアップデート前という事は、
やはりStartイベントの内容は描画前に呼ばれているイベントだと思います。
実際はキャッシュ処理で起動が遅くなっているのでしょうが、
まったく気にしなくていいぐらいの遅延だったのでしょう。
以外とキャッシュ処理って起動に影響しないんだ。。。とビックリしました

◆3Dオブジェクト大量においても遅延ないの?

3Dオブジェクトの描画で起動時間が遅れることはないのか!?
気になったので、今のタイトル画面にUnityちゃんを102体設定してビルドしてみました

f:id:apuridasuo:20200512155954j:plain
図:51vs51のラップバトル
実際のタイトル画面です。ラップバトルをイメージしたタイトル画面なのに
ちょっとした戦争ものみたいになっちゃいましたw
実際に起動したときの挙動は、
・起動時間は0.5秒→3秒ぐらいに長くなった(遅延した)
・起動の遅延よりも起動後の操作遅延がえぐかった
・0.5秒でBGMはなったので、Startイベントの処理は描画の遅延に関わらず終わった
こんな感じでした。
やはり、3Dオブジェクトの描画は起動の遅延にも影響があるみたいですね
ただ、オブジェクトの描画による遅延は今回のデータ読み込み遅延と違って
Startのイベントはすぐに完了する
という部分が大事だと思いました。
処理的には終わってるけど描画が追い付いていないって感じなんでしょうね
A. Startイベントはすぐ終わる起動遅延=オブジェクト描画が原因
B. Startイベント終わってない起動遅延=データ読み込み等の他の原因

こんな感じで、起動遅延の原因追及がなんとなく調べやすくなりました!!

以上です、アプリが出来たら下にリンク張るので是非遊んでね!

この問題を経て作ったアプリ

やっとやっとできたので紹介します!
リズムゲームの要素で、押すボタンがクイズになってる感じです。
操作自体は簡単なので、ぜひプレイしてみてください。
play.google.com

今後、実写映像化して欲しいコミック紹介

最近仕事が忙しくて全然アプリ作れてない

 →アプリ作れてないから新しい発見が無い

  →発見無いから記事書くことない

   →じゃあマンガ紹介するか!

という事で今回は、実写で映像化して欲しいコミックの紹介です。

最近多くなってきたコミック、ラノベの実写化ですが、

全体的にあまりいい評判を聞かないですね・・・

今回は、実写化について思うところがあるので

酔っぱらいジジイがごとく物申しながらコミック紹介したいと思います。

 

実写が批判されるのは二番煎じだからだろ!

これは常々思ってました!

コミックが映像化されるのはアニメ、実写それぞれに良さがあるはずです。

しかし最近の実写系はアニメで受けが良かった作品の実写化が多い!

アニメで受けがいい=アニメで良さが出る版権という事なので

実写で良さが出る作品とは限らないのです!

しかも、後追いするせいで二番煎じ感が出るし

ブームも終息し始めた頃の発表になってしまうので

見る前からコレジャナイ感がもの凄い印象になってしまいます!

そんなもんどれだけ版権が良くて面白くても初動が伸びるわけない!

どうせ実写化するなら、新鮮な版権を自分たちで見つけ出して

ブームを作る側になって欲しい!実写だって面白いんだから。。。

と、いうわけで以下から

アニメ化よりも実写化してほしい作品紹介していきます

 

しおりエクスペリエンス【 続14巻 】

 ・ジャンル 

バンドもの、登場人物みんな好きになる、アツコメ(熱い話だけどコメディもあるよ)

 ・あらすじ

地味な高校教師のシオリ(27)に、ある日幽霊がとりついた!

その幽霊はなんとジミーヘンドリクス!!!

ジミーに憑りつかれたシオリ(27)はある呪いにかかっていた。。。

「27歳が終わるまでに音楽で伝説を残さなければ死ぬ」

この物語は、地味な先生がジミーの呪いを糧に、

様々な思いを抱える生徒たちと伝説のバンドを目指す物語。

 ・感想 

この漫画はそもそもの設定がギャグなのでギャグマンガだと思ってしまいがちです。

(実際、1巻だけ見たらギャグ系かなー?って思っちゃいます)

しかしながら実際は登場人物全員に厚い人間ドラマがあり、

熱い成長ドラマがしっかり描写されたアツコメ成長物語でした。

音楽系のマンガで大事になるのは演奏描写です。

この漫画はシオリたちが成長していく話に合わせた描写を行っているので

ストーリーに感動した心をぐっと引き込む演奏描写になっています。

また、この漫画の大きな特徴だと思うのが

登場人物全員に分厚いストーリーがあり、それをきちんと描写している。

さらに、そのストーリーをシオリたちの成長に絡ませるすごい構成力です。

伏線単体で感動できるのに、それを回収しちゃうの!?と思って

出てくる人みんなに感情移入してみんなに感動しちゃいます。

実写に似合うなと思ったところは、

人間ドラマと演奏描写はアニメよりも実写で見たいから!

  

シネマこんぷれっくす【 続5巻 】 

 ・ジャンル 

高校日常もの、映画ウンチクつめこみました

 ・あらすじ 

映画が好きで、映画みたいに熱い青春を送りたがっている高校入学したてのガクト。

そこに現れたのは映画オタクたちが集う映画研究会の面々。

ガクトは映画を語りたいんじゃなくて映画みたいな青春を送りたい!

この物語は、映研に無理やり入部させられたガクトが

映画馬鹿たちに翻弄されながらも面白おかしな非日常な青春をお送りする物語

 ・感想 

この作品は、作者の趣味を漫画にした作品なのですが新しいです!

普通は、キャンプ趣味を漫画にしたらキャンプ漫画。

釣りの趣味を マンガにしたら釣り漫画になるでしょう。。。

では、映画趣味を漫画にしたこの作品はどうなるのか?

この作品は、キャラたちが色々な映画のテーマに沿った行動を起こすのです!

ホラー作品を語るときはゴーストバスターやったり!

ジャッキー語りたかったらカンフーやったり!!

任侠語りたかったら極道生徒会長でてきたり!!

映画という一つの趣味から色々なジャンルに派生するので

見ていて飽きない。常に新しくて面白いことをやってるのに、

映画ウンチクは欠かさず入っているから芯がぶれてないオリジナリティーです。

実写に似合うなと思ったところは、

・他作品の紹介が必須なので、実写なら何とかなる

・この作品を見ると、色々な映画が見たくなる紹介物として人気が出そう

 

特攻の拓【 終27巻 】

特攻の拓 新装版 コミック 1-27巻セット (ヤングマガジンKC)
 

 ・ジャンル 

ヤンキーもの、友情・努力・勝利?の究極完全体

 ・あらすじ 

いじめられっ子の拓は転校してきた最強ヤンキーの秀人に出会う。

いじめっ子とは違った男気のある秀人に憧れた拓。

秀人に関わったせいでボコボコにされたのに、

それでも秀人を裏切らなかった拓を見て秀人も拓に惹かれたのだった。

拓と秀人は転校して離れ離れになったが、秀人に教わったヤンキー魂を胸に、

新たな場所で様々な仲間と出会い、真の漢を目指す物語

 ・感想 

知名度はそれなりに高いけどネタ漫画扱いされがちなこの作品です。

この漫画の大事なところは、狂気じみた性格と強さのヤンキーたちが

バチバチにぶつかり合うのですが、話がめちゃくちゃになってないんです。

ちゃんとそれぞれに背負うものがあり、

それを拓のお人よしが何とかしようともがいてスッキリ話がまとまってくれるので

読んでいて気持ちがいいです。

昔のマンガらしく、ご都合主義がつまっていますが

ギャグとシリアスの混ぜ具合が絶妙なので読んでると全然ご都合感は感じません。

(だからこそ、一部だけ切り取るとネタ感満載の作品なんです・・・)

ヤンキー漫画ってファンタジーよりよっぽどベタベタな「友情・努力・勝利」

やってると思うので子供にも受けがいいと思うんですよね

実写に似合うなと思ったところは、

・それなりに知名度が高いから見たくなる人は多そう

・ヤンキーのカッコよさは実写でこそ光る!

・ベタベタ友情物語ってドラマ見る層に受けがよさそう

 

以上です。

私は普段、ドラマもアニメもあまり見ない、映画ばっかり見ているのですが

この作品が実写化したらみたいなと思うものを挙げました。

(本当は、シオリエクスペリエンスみて感動しすぎて、

映像化して欲しいと思った衝動をぶつけただけです。。。)

萌え脳死日常ものは一番相性悪いと思うんです。

熱くて非ファンタジーな物こそ実写化して欲しいです!

【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貼り付けしてみたけど面倒くさすぎて泣けた・・・