# UnityやC#に関するコードの書き方や小ネタ ここでは、Unityまたは、C#に関するコードの書き方や小ネタを書きます。 コードの書き方や便利な関数など、調べたことをどんどん書いていきましょう! [Unityのリファレンス](https://docs.unity3d.com/ja/2023.2/ScriptReference/MonoBehaviour.html)はここです。 ## MonoBehaviourについて Unityで、新しくC#スクリプトを作成すると ```csharp! using UnityEngine; //ほかにもあります public static NewScript : MonoBehaviour{ //":"をつけることで継承できる! //e.g. NewScript : MonoBehaviour } ``` クラスといくつかの名前空間が書かれた状態で、 新しいファイルが作成されるはずです。 C#ではJavaとは違い、 継承するときは : (コロン)を使用して継承します。 また、多重継承はできません。 加えて、インターフェイスを継承するときも : を用います。 インターフェイスの書き方についてはJavaと同じです。 Monobehaviourには、Unityが自動で実行する便利な関数がたくさん入っています。 (例えば、Start(), Update(), Coroutineなど) 逆に、Unityに自動で実行させる必要がないものは、 Monobehaviourを継承させる必要はないです。 ```csharp! using UnityEngine; public class Player{ private int hp = 0; private int mp = 0; private int speed; public Player(){ this.hp = 100; this.mp = 100; } public void move(){ this.speed = 10; } } ``` ```csharp! using UnityEngine; public class Stage : MonoBehaviour{ private Start(){ Player player1 = new Player(); } private Update(){ if(Input.GetKey(KeyCode.LeftArrow)){ player1.move(); } } ``` ### [SerializeField]の使い方 [詳細はここ](https://wizardia.hateblo.jp/entry/2023/10/26/100000) ## シーン間のデータの保持の仕方 ### シングルトン ### public static classについて class名の後ろにstatic修飾子をつけると、 そのクラスはすべてのシーンからアクセス可能になります。 (グローバル変数みたいな感じです。) static修飾子をつけたクラスの メンバ変数とメンバ関数にもstatic修飾子をつける必要があります。 メリット * シーンを移動しても変数の中身が破棄されない デメリット * どこからでもアクセスできるため、アクセス制限などが必要 -> 慎重な運用が必須 なので、自作の数学関数や定数を作成するときとかに便利かもです。 ```csharp! using UnityEngine; using UnityEngine.SceneManagement; public static class SceneController{ //Variable public static int currentSceneID; //Method public static int ChangeSceneToTitle(){ SceneManager.LoadScene("Title"); } } ``` ## 自動実装プロパティ 要約すると、Javaの授業で実装したsetX(), getX()などのメソッド(アクセサーと呼ぶ)を簡略化して書ける [詳しくはこちら](https://ufcpp.net/study/csharp/oo_property.html) ## 非同期処理 Unityで待つ・並列処理をするときに使用する。 [詳しくはこちら](https://qiita.com/YutaGameMusic/items/645c05d6246f117ac577) ### Task C#の機能 ```csharp! using System.Threading.Tasks; using UnityEngine; public class SampleProgram : MonoBehaviour { void Start() { BetweenTwoSceneDelay(); } private async Task AssetLoading() { await Task.Delay(1000); Debug.Log("1秒待機"); } } ``` * 非同期処理はasyncをつけた1つの関数の中に書き、その関数をTask.Runに指定する。 * 非同期処理を行うだけならawaitは不要で、プログラムの実行を待機させたいなら、awaitを使用する ### Coroutine Unityの機能 ```csharp! using UnityEngine; using System.Collection; public class SampleProgram : MonoBehaviour { void Start() { var coroutine = WaitFor1Frames(); StartCoroutine(coroutine); } public IEnumerator WaitFor1Frames() { for(int frameCount = 0 ; frameCount < 1 ; frameCount++) { yield return new WaitForEndOfFrame(); } Debug.Log("1フレーム待機"); yield break; } } ``` IEnumeratorとは、イテレータのこと。 ## Linqを使う コレクションの操作をオブジェクト指向のように記述できる便利機能です コードが見やすくなります。 ### コレクション C#においてコレクションとは、オブジェクト管理用の便利な拡張配列クラスのこと 以下の配列がある * ArrayList ```csharp! using System; using System.Collection.Generics; //Collection, Generic"s" ArrayList list = new ArrayList(); list.Add("hoge"); list.Remove("hoge"); list[0] = "hoge"; int num = list.Count; ``` :::info ・ArrayList 普通の配列と同じように複数の要素を一つにまとめられる 配列との違いの変更が容易なこと ArrayListの要素の方はオブジェクト型でどんなものでも格納できる ただし、要素を取り出すときはキャストが必要 e.g. int num = (int)ArrayList; ::: * List ```csharp! using System; using System.Collections.Generic; // Collection"s", Generic List<string> list = new List<string>(); list.Add("One"); foreach(String str in list) { Debug.Log(str); } ``` :::info ・List データ型を指定するため、異なるデータ型を格納できない 決まった種類のオブジェクトだけを保管しておくような場合に用いる ArrayListと同じメソッドが用意されてる キャストは不要 ::: * HashTable ```csharp! using System; using System.Collection.Generics; //Collection, Generic"s" Hashtable table = new Hashtable(); table.Add("tom","tom@hogehoge.com"); table["tom"] = "tom@hogehoge.com"; table.Remove("tom"); ``` :::info ・HashTable 簡単に説明するとインデックス(array[0]など)を使わず、 キーと呼ばれるもので配列内の要素を指定する 詳しくは[ここを参照](https://wa3.i-3-i.info/word11947.html)←URLです! ::: * Dictionary ```csharp! using System; using System.Collections.Generic; Dictionary<string,string> dict = new Dictionary<string, string>(); dict.Add("tom","tom@hogehoge.com"); dict["tom"] = "tom@hogehoge.com"; dict.Remove("tom"); ``` :::info ・Dictionary HashTableをArrayListのような操作感にしたもの 使い方はHashTableとArrayListと同じ ::: * Queue * Stack :::info ・QueueとStack StackとQueueの解説は省きます。 ::: ### Linq実践 Linq実践してみる 普通にコードを書けば、 ```csharp! List<int> numTwiceList; //int型の要素を格納するList foreach(int num in _listTest){ //拡張for文でListである _listTest内の要素を順にみていく numTwiceList.Add(num*2); //numTwiceListに_listTestの各要素を2倍にした数を追加する } ``` このようになる。 Linqを使うと、 ```csharp! List<int> numTwiceList = _listTest.Select(num => num*2).ToList<int>(); ``` 一行で済むようになる! [Linqチートシートはこちらから](https://qiita.com/Marimoiro/items/0e119b47d65bf138789a) ## Delegate(デリゲート) C#におけるデリゲートとは「メソッドの参照を保持するための型」を意味しています。 簡単にすると、メソッドを格納するための機能 ### デリゲートの紹介 以下にデリゲートの例を示してみる ```csharp! using UnityEngine; delegate void NewDelegate(); //デリゲートの型を新しく作る public class SampleDelegate : MonoBehaviour { void start() { int i = 10; Debug.Log(i); i = 3; Debug.Log(i); //NewDelegate型のインスタンスを作成してsayHelloメソッドで初期化する NewDelegate dg = sayHello; //デリゲートインスタンス名()でデリゲート呼び出し(簡易バージョン) dg(); //デリゲートを介してsayHelloメソッドを呼び出す df = sayByeBye; //違うメソッドを代入 dg(); //デリゲートを介してsayByeByeメソッドを呼び出す } void sayHello() { Debug.Log("Hello"); } void sayByeBye() { Debug.Log("ByeBye"); } } ``` 実行結果は以下の通り :::info 10 3 Hello ByeBye ::: ### デリゲートの特徴 また、デリゲートは複数のメソッドを格納できる ```csharp! using UnityEngine; delegate void NewDelegate(); public class SampleDelegate : MonoBehaviour { void Start() { NewDelegate md = sayHello; md += sayByeBye; md(); md -= sayByeBye; md(); } void sayHello() { Debug.Log("Hello"); } void sayByeBye() { Debug.Log("ByeBye"); } } ``` 実行結果は以下の通り :::info Hello ByeBye Hello ::: ### デリゲートのNull対策 デリゲートは参照型なので、デリゲートに何も格納されていない場合nullになる。 以下のように対策する ```csharp! using UnityEngine; delegate void NewDelegate(); public class SampleDelegate : MonoBehaviour { NewDelegate md = () => { }; //これでデリゲートを初期化 void Start() { md += sayHello; md += sayByeBye; md(); } void sayHello() { Debug.Log("Hello"); } void sayByeBye() { Debug.Log("ByeBye"); } } また、以下のコードでnullかどうかチェックできる using UnityEngine; delegate void NewDelegate(); public class SampleDelegate : MonoBehaviour { NewDelegate md; void Start() { md += sayHello; md += sayByeBye; //デリゲートを呼び出すための本当のメソッド md?.Invoke(); //?.はnull条件演算子 // md?.Invoke(); の部分はこれと同じ if (md != null) { md(); } } void sayHello() { Debug.Log("Hello"); } void sayByeBye() { Debug.Log("ByeBye"); } } ``` ### 便利なデリゲート群 C#には、よく使われるデリゲート群が事前に用意されている。 ここでは、Action/Func群を解説する ```csharp! // 引数がないメソッド用のActionデリゲート public delegate void Action(); // 引数が1つのメソッド用のAction<T>デリゲート public delegate void Action<in T>(T obj); // 引数が2つのメソッド用のAction<T1,T2>デリゲート public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2); // 引数が3つのメソッド用のAction<T1,T2,T3>デリゲート public delegate void Action<in T1,in T2,in T3>(T1 arg1, T2 arg2, T3 arg3); // 引数がなく、戻り値があるメソッド用のFunc<TResult>デリゲート public delegate TResult Func<out TResult>(); // 引数が1つあり、戻り値があるメソッド用のFunc<T,TResult>デリゲート public delegate TResult Func<in T,out TResult>(T arg); // 引数が2つあり、戻り値があるメソッド用のFunc<T1,T2,TResult>デリゲート public delegate TResult Func<in T1,in T2,out TResult>(T1 arg1, T2 arg2); 以下のようにして使用する using UnityEngine; public class SampleDelegate : MonoBehaviour { void Start() { //Actionは引数なし、戻り値なしのメソッドを格納するためのデリゲート型 System.Action action = Hello; action(); //Action<string>はstring型の引数を1つ受け取る戻り値がないメソッドを格納するためのデリゲート型 System.Action<string> action2 = Say; action2("Hello"); } void Hello() { Debug.Log("world!"); } void Say(string message) { Debug.Log(message); } } using UnityEngine; public class SampleDelegate : MonoBehaviour { void Start() { //Func<int, int, float>はint型の引数を2つ受け取り //float型の値を戻すメソッドを格納するためのデリゲート型 System.Func<int, int, float> func = Plus; Debug.Log(func(5, 2)); func = Minus; Debug.Log(func(5,2)); } float Plus(int a, int b) { return a + b; } float Minus(int a, int b) { return a - b; } } ``` ### デリゲート活用例 ここで、使用例を見ていく #### Topic1 : 述語の一般化 ```csharp! using System.Collections.Generic; using UnityEngine; public class SamplePredicate { public void Process () { var list = new List<int>() {1, 40, 20, 0, 30}; Debug.Log(Count(list)); } public int Count (List<int> list) { var count = 0; foreach (var value in list) { /*この部分で if(value > 10){ ++count; } //10より大きい数をカウントする if(value % 2 == 0){ ++count; } //偶数をカウントする 上記2つの処理を実行したい!*/ } return count; } } 以下のようにデリゲートを使う using System.Collections.Generic; using UnityEngine; using System; public class SamplePredicate : MonoBehaviour { public void Start() { var list = new List<int>() { 1, 40, 20, 0, 30 }; //10より大きい数をカウント。 Debug.Log(Count(list, IsLagerThanTen)); //偶数をカウント。 Debug.Log(Count(list, IsEven)); } /*デリゲートに入れる関数*/ bool IsLagerThanTen(int x) { return x > 10; } bool IsEven(int x) { return x % 2 == 0; } public int Count(List<int> list, Predicate<int> predicate) { var count = 0; foreach (var value in list) { //カウントする if (predicate(value)) { ++count; } //ここでデリゲートを実行 } return count; } } ``` :::info PredicateデリゲートはActionなどと同じくSystem名前空間に属している、述語のために使われることが想定されたデリゲート型 ::: #### Topic2 : イベントハンドラ イベントハンドラとは、 > イベント(ユーザーによるボタンのクリックや、 > メッセージキューによるメッセージ受信など)が > 発生したときに特定の処理を与えるための記述。 イベントについて勉強する前に、事前知識としてプロパティを見ていく Javaの授業でカプセル化を勉強したが、実際メソッドから取得するのはめんどくさい そこで、C#の機能の1つであるプロパティを使う プロパティとは、 > 内部からはメンバ変数のように、外部からはメソッドのようにふるまう機能のこと 以下、プロパティの例を挙げる ```csharp! using System; public class Person { private int age; public int Age //プロパティ { get //読み出し部分 カプセル化で言うところのgetAge()みたいなもの { return age; } set //書き込み部分 カプセル化で言うところのsetAge()みたいなもの { if (value < 0) //年齢が負でないかチェック { //例外をぶんなげる throw new InvalidOperationException("Age is below zero!"); } age = value; } } } public class MyClass { public void Process() { var p = new Person(); p.Age = 10; p.Age = -1; } } ``` 他にも、 * 読み出しのみ * 複数のフィールドを組みにした値を戻す 等ができる では、話を戻して ゲームでは「プレイヤーがゴールした」「プレイヤーがジャンプした」などのアクションを起こします その時の出来事をイベントと呼び、イベントが発生したときに行う処理のことをイベントハンドラという イベントとイベントハンドラで動くプログラムのことをイベント駆動型プログラムという 以下、イベント駆動型プログラムの例です ```csharp! using UnityEngine; using System; /*イベントの待ち受け側*/ public class KeyboardEvent : MonoBehaviour { public Action OnSpaceKeyPressed = () => { }; //デリゲートを初期化 private void Update() { //スペースキーを押したら if(Input.GetKeyDown(KeyCode.Space)) { OnSpaceKeyPressed(); //デリゲートを実行 } } } using UnityEngine; /*イベントハンドラ側*/ public class SampleEventHandler : MonoBehaviour { private KeyboardEvent keyboardEvent; private void Start() { keyboardEvent = GetComponent<KeyboardEvent>(); keyboardEvent.OnSpaceKeyPressed += IgniteWhenSpaceKeyPressed; } //イベントハンドラ private void IgniteWhenSpaceKeyPressed() { Debug.Log("スペースキーが押された!"); } } ``` このプログラムには欠陥があります イベント待ち受け側のアクセスレベルがpublicです(他のクラスからもデリゲートを実行できるようになる) privateにするのではだめそうです(他のクラスがデリゲートに登録できなくなる) プロパティも使えません(イベントハンドラにするための条件がある) そこで、C#ではその問題点を解決するための機能が用意されています! ```csharp! using UnityEngine; using System; public class KeyboardEvent : MonoBehaviour { //イベント public event Action OnSpaceKeyPressed = () => { }; private void Update() { if(Input.GetKeyDown(KeyCode.Space)) { OnSpaceKeyPressed(); } } } ``` 使い方は、デリゲートの前に==event==と置くだけです これにより、そのデリゲートはクラスの内部からの実行可能となります また、クラス外部からはメソッドの追加と削除のみできます ## ラムダ式 デリゲートにメソッドを登録するために、別の場所でその関数の中身を変えるのは少々面倒です ```csharp! using UnityEngine; delegate void NewDelegate(); public class SampleDelegate : MonoBehaviour { NewDelegate md = () => { }; //これでデリゲートを初期化 void Start() { md += sayHello; md += sayByeBye; md(); } /*ここで2つメソッドを定義するのめんどくさいよ~*/ void sayHello() { Debug.Log("Hello"); } void sayByeBye() { Debug.Log("ByeBye"); } } ``` そこで、匿名メソッド・ラムダ式を使います :::info NewDelegate md = () => { }; これが匿名メソッド・ラムダ式です 名前がないメソッドだから匿名メソッドなんですね~ ::: 以下、匿名メソッド・ラムダ式の例です ```csharp! using UnityEngine; using System; public class LambdaExpressionSample : MonoBehaviour { void Start() { Action<string> action = (string message) => { Debug.Log(message); }; action("こんにちは"); //変数の型が左辺値や関数の引数から推論できる場合には引数の型を省略できる action = (message) => Debug.Log(message); action("しーしゃーぷ"); //ラムダ式の中身がreturn文1つだけの場合には、returnキーワードも省略できる Func<int, int, int> func = (a, b) => a + b; Debug.Log(func(3, 5)); } } また、プロパティ・メソッドなどにもラムダ式が使える private int _age; //読み取り専用プロパティ public int Age { get => _age; } //関数をラムダ式で記述 public int Max(int a, int b) => a > b ? a : b; ``` ## ファイル操作 ここでは、C#におけるファイル操作を紹介する。 なお、ファイル構造は以下の通り、 D:\ ├─ sampleProgram.cs └─ files ├─ sampleRead.txt └─ subFiles └─ sampleWrite.txt ### ファイルの存在確認 ```csharp! using System; using System.IO; //必須 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { /*文字列の先頭に@(アットマーク)をつけると制御文字をただの文字列として使える*/ string path = @"D:\files\sampleRead.txt"; //sample.txtへのパス if(File.Exists(path)) //File.Exists(ファイルへのパス)の形 { Debug.Log("存在します"); } else { Debug.Log("存在しません"); } } } ``` ### フォルダの存在確認 ```csharp! using System; using System.IO; //必須 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files"; //sample.txtへのパス if(Directry.Exists(path)) //Directry.Exists(ディレクトリへのパス)の形 { Debug.Log("存在します"); } else { Debug.Log("存在しません"); } } } ``` ### ファイルの中を確認 ```csharp! using System; using System.IO; using System.Linq; //EnumerateFilesを使用するのに必要 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files"; /*varは型類推->コンパイラが適切な型に直してくれる*/ //SearchOption.TopDirectoryOnlyで現在のディレクトリのファイルの名前だけを表示 var files = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly); foreach (string file in files){ Debug.Log(file); } //SearchOption.AllDirectoriesでこのディレクトリが持つフォルダを含めてすべてのファイル名を表示 files = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories); foreach (string file in files){ Debug.Log(file); } } } ``` ### ファイルの数を数える ```csharp! using System; using System.IO; using System.Linq; //EnumerateFilesを使用するのに必要 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files"; int filenum = 0; //count()で数え上げる //*はワイルドカード, テキストファイルを数え上げている //*(任意の文字).txtの条件に含まれるものはすべて数え上げられる filenum = Directory.EnumerateFiles(path, "*.txt", SearchOption.TopDirectoryOnly).Count(); Debug.Log("Exclue SubDir: " + filenum); //count()で数え上げる //*はワイルドカード, テキストファイルを数え上げている //*(任意の文字).txtの条件に含まれるものはすべて数え上げられる filenum = Directory.EnumerateFiles(path, "*.txt", SearchOption.AllDirectories).Count(); Debug.Log("Ixclue SubDir: " + filenum); } } ``` ### ファイルの読み込み 以下の文を読み込ませる 1.なんでここにchat-GPT産のコードがあんだよ 2.教えはどうなってんだ教えは 3.お前ら禁じられたchat-GPTを平気で使ってんじゃねえか 4.分かってんのか!? 5.「コード丸パクリ」が生まれたのは人間がchat-GPTに甘えたせいだろうが 6.金取んのかよ!? 7.くそったれ! ```csharp! using System; using System.IO; using System.Text; //Encoding.GetEncodingを使用するのに必要 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\sampleRead.txt"; /*File.ReadAllTextは開いたファイルを処理が終了したときに自動で閉じてくれる*/ //ファイル読み込み //文字化け対策のためshift-jisを指定 var text = File.ReadAllText(path, Encoding.GetEncoding("shift-jis")); //1文字ずつ表示 foreach (var ch in text) { Debug.Log(ch); } //一度で全部表示 Debug.Log(text); } } using System; using System.IO; using System.Text; //Encoding.GetEncodingを使用するのに必要 using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\sampleRead.txt"; /*StramReader.ReadToEndはファイルを開いたら閉じる処理を書かなくてはならない*/ //ファイルを開く //文字化け対策のためshift-jisを指定 StreamReader file = new StreamReader(path, Encoding.GetEncoding("shift-jis")); if (file == null) { Denug.Log("ファイルを開けませんでした。"); return; } //ファイルを読み込む var text = file.ReadToEnd(); //ファイルを閉じる file.Close(); //ファイルを閉じてから読み込んだ文字を表示 Debug.Log(text); } } ファイルを閉じるのが面倒なとき、 using System; using System.IO; using Syste.Text; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:files\sampleRead.txt"; //usingステートメント, 使い終わったリソースを自動的に開放する using(StreamReader file = new StreamReader(path, Encoding.GetEncoding("shift-jis"))) { var text = file.ReadToEnd(); Debug.Log(text); } } } using System; using System.IO; using System.Text; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:files\sampleRead.txt"; /*File.ReadAllLinesで一行ずつ読み込み*/ var lines = File.ReadAllLines(path, Encoding.GetEncoding("shift-jis")); foreach(var line in lines){ Debug.Log(line); } } } using System; using System.IO; using System.Text; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\sampleRead.txt"; //ファイルを開く StreamReader file = new StreamReader(path, Encoding.GetEncoding("shift-jis")); if(file == null){ Debug.Log("ファイルの読み込みに失敗しました"); return; } //一行ずつ読み込む while(file.Peek() != -1){ Debug.Log(file.ReadLine()); } //ファイルを閉じる file.Close(); } } using System; using System.IO; using System.Text; using UnityEnigne; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\sampleRead.txt"; using (StreamReader file = new StreamReader(path, Encoding.GetEncoding("shift-jis"))) { while(file.Peek() != -1){ Debug.Log(file.ReadLine()); } } } } ``` ### ファイルの書き込み ```csharp! using System; using System.IO; using System.Text; using System.Collections.Generic; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\subFiles\sampleWrite.txt"; //書き込む内容 List<string> text = new List<string>{ "C#ができないのか?", "俺はできるが" }; /*一行ずつファイルに書きこみ*/ File.AllLines(path, text, Encoding.GetEncoding("shift-jis")); Debug.Log(File.ReadAllText(path, Encoding.GetEncoding("shift-jis"))); } } using System; using System.IO; using System.Text; using System.Collections.Generic; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\subFiles\sampleWrite.txt"; //書き込む内容 List<string> text = new List<string>{ "ハァ… 困ったなァ", "まさか提出期限に間に合わないなんてェ…", "救済処置を出して待ってくれてるみたいだから", "急いで進捗を出したいけど", "もう疲れちゃって 全然筆が進まなくてェ…" }; //trueなら、追加の書き込みを許可する //falseなら、追加書き込みはせず、上書きして書き込む StreamWriter file = new StreamWriter(path, true, Encoding.GetEncoding("shift-jis")); foreach(var line in text){ file.WriteLine(line); } //ファイルを閉じる file.close(); Debug.Log(File.ReadAllText(path, Encoding.GetEncoding("shift-jis"))); } } using System; using System.IO; using System.Text; using System.Collections.Generic; public class sampleProgram : MonoBehaviour { void Start(){ string path = @"D:\files\subFiles\sampleRead.txt"; List<string> text = new List<string>{ "ファイルを開きっぱなしにすると", "メモリなどのリソースを食ってしまったり、", "第3者に不正に書き込まれてしまったりしてしまいます。", "だから、ちゃんとファイルを閉じる必要があったんですね。" }; using (StreamWriter file = new StreamWriter(path, false, Encoding.GetEncoding("shift-jis"))) { foreach(var line in text) { file.WriteLine(line); } } Debug.Log(File.ReadAllText(path, Encoding.GetEncoding("shift-jis"))); } } ``` ### ファイル作成 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { //新しく作るファイルのパス string path = @"D:\files\subFiles\new.txt"; //新しいファイルを作成する File.Create(path); } } ``` ### ファイルを削除 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\subFiles\new.txt"; } //ファイルを削除 File.Delete(path); } ``` ### フォルダ作成 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { //新しく作るフォルダのパス string path = @"D:\files\newFolder"; //フォルダを作成 Directry.CreateDirectry(path); } } ``` ### フォルダ削除 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\newFolder"; //フォルダを削除 Directory.Delete(path); } } ``` ### 中身だけ削除 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\subFiles\sampleWrite.txt"; //既に存在しているファイル名で新規作成する File.Create(path); } } using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string path = @"D:\files\subFiles\sampleWrite.txt"; using (var fs = new FileStream(path, FileMode.Open)) { fs.SetLength(0); } } } ``` ### 移動 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string filePath = @"D:\files\sampleRead.txt"; string targetFilePath = @"D:\files\subFiles\sampleRead.txt"; //ファイルの移動 File.Move(filePath, targetFilePath); } } ``` ### ファイル名の変更 ```csharp! using System; using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string filePath = @"D:\files\sampleRead.txt"; string rename = @"D:\files\小泉構文集.txt"; File.Move(filePath, rename); } } ``` ### ファイルのコピー ```csharp! using System using System.IO; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { string originalPath = @"D:\files\sampleRead.txt"; string clonePath = @"D:\files\subFiles\sampleRead.txt"; //ファイルのコピー File.Copy(originalPath, clonePath); } } ``` ## JSON形式へのシリアル化 [詳細はこちら](https://blog.yucchiy.com/2022/06/jsonutility-serialize-deserialize-unity-object/) ### JSON形式のシリアル化 JsonUtilityクラスを使用して、 UnityObject -> JSON形式 JSON形式 -> UnityObject に変換できる。 これにより、データをテキストベースの形式に簡単に詰め込んだり、取り出したり出来る。 JSON形式へのシリアル化では、構造化されたJSONという概念を使用する。 クラスや構造体を作成してJSON形式で保存したい変数を記述する。 ```csharp! using System; using UnityEngine; [Serializable] public class SampleProgram { [SerializeField] private int level; //[serializeField]でprivateもシリアライズ可能になる public float timeElapesd; public string playerName; } ``` これは、3つの変数を含む簡単なC#のクラスを定義して、 JSONシリアライザーで動作するようにSerializable属性でマークしている。 クラスのインスタンスの作成は、以下のように行う。 ```csharp! using System; using UnityEngine; public class SampleProgram : MonoBehaviour { void Start() { SampleProgram sampleProgram = new SampleProgram(); sampleProgram.level = 1; sampleProgram.timeElapesd = 47.5f; sampleProgram.playerNmae = "Ikemoto"; } } ``` その後、JsonUtility.ToJsonメソッドを使用して、JSON形式にシリアライズ(変換)する。 ```csharp! using System; using UnityEngine; public class SampleProgram : MonoBehaviour { void Start() { SampleProgram sampleProgram = new SampleProgram(); sampleProgram.level = 1; sampleProgram.timeElapesd = 47.5f; sampleProgram.playerNmae = "Ikemoto"; //tureで読みやすい形に変換 string json = JsonUtility.ToJson(sampleProgram, true); } } ``` JSON形式のデータをオブジェクトに戻すには、JsonUtility.FromJsonを使用する。 ```csharp! using System; using UnityEngine; public class sampleProgram : MonoBehaviour { void Start() { SampleProgram sampleProgram; sampleProgram = jsonUtility.FromJson<SampleProgram>(json); } } ``` これにより、SampleProgramの新しいインスタンスが作られ、 JSON形式のデータを使用して値が初期化されます。 JSON形式のデータのSampleProgramのフィールドにマップされていない値が格納されていない場合、 シリアライザーはその変数を無視する。 JSON形式のデータのSampleProgramのフィールドに値が格納されていない場合、 シリアライザーは、フィールドの構築された値をそのままオブジェクトに残す。 JSONシリアライザーは、木構造として走査と編集することができる。 ### JSONを使用したオブジェクトの上書き 既存のオブジェクトにJSONデータをデシリアライズできる。 その場合、データは上書きされる。 ```csharp! using System; using UnityEngine; public class SampleProgram : MonoBehaviour { void Start() { SampleProgram sampleProgram = new SampleProgram(); sampleProgram.level = 1; sampleProgram.timeElapesd = 47.5f; sampleProgram.playerNmae = "Ikemoto"; //デシリアライズ JsonUtility.FromJsonOverwrite(json, sampleProgram); } } ```