質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

0回答

583閲覧

ScriptableObjectの値が外部参照で正しく取得できないことがある

decman

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2021/11/07 15:14

編集2021/11/08 10:46

前提・実現したいこと

テキストファイルからint型の2次元配列を読み取り、その数値に合わせて
Unity内でタイルを敷き詰めるようなプログラムを作成しています。

敷き詰めるタイルの種類をListで保存しており、これを別の箇所から参照したいのですが
参照できる箇所とできない箇所があり困っております。

具体的には、以下ソースのMapTileData.csのMapTileData.MapTileDataList.Countを別の箇所で参照したいのですが
MapInit.csとRouting.csのStart()からは正しい値を取得でき、Routing.cs内のCostMapRefresh()からは
正しい値を取得できませんでした。

Start()からだけでなく、CostMapRefresh()からも値を取得できるようにするにはどうしたらよいでしょうか。

ソースコードをすべて記載するのは不可能なため、必要な個所を抜き出して記載しています。
記載が足りない箇所などありましたら教えていただけると幸いです。

該当のソースコード

C#

1 2■MapTileData.cs 3 4using System.Collections; 5using System.Collections.Generic; 6using UnityEngine; 7using System; 8using UnityEngine.Tilemaps; 9 10[CreateAssetMenu] 11public class MapTileData : ScriptableObject 12{ 13 public List<MapTileDataInfo> MapTileDataList = new List<MapTileDataInfo>();     ←これを参照したい 14} 15[Serializable] 16public class MapTileDataInfo 17{ 18 public int id; 19 public int cost; 20 public bool canMove; 21 public Tile tile; 22}

■MapTileDataListの中身
MapTileDataList[0].id = 0
MapTileDataList[0].cost = 1
MapTileDataList[0].canMove = true
MapTileDataList[0].tile = 002-Woods_fall_001

MapTileDataList[1].id = 1
MapTileDataList[1].cost = 2
MapTileDataList[1].canMove = true
MapTileDataList[1].tile = BrightForest-A2_001

MapTileDataList[2].id = 2
MapTileDataList[2].cost = 0
MapTileDataList[2].canMove = false
MapTileDataList[2].tile = 002-Woods_fall_013

MapTileDataList[3].id = 3
MapTileDataList[3].cost = 0
MapTileDataList[3].canMove = true
MapTileDataList[3].tile = 002-Woods_fall_011

MapTileDataList[4].id = 4
MapTileDataList[4].cost = 0
MapTileDataList[4].canMove = true
MapTileDataList[4].tile = Market-D_128

C#

1■MapInit.cs 2 3using System.Collections; 4using System.Collections.Generic; 5using UnityEngine; 6using UnityEngine.Tilemaps; 7 8public class MapInit : MonoBehaviour 9{ 10 //最初に読み込むテキストファイル 11 [SerializeField] 12 private TextAsset GroundText; 13 [SerializeField] 14 private TextAsset ObjectText; 15 [SerializeField] 16 private MapTileData MapTileData; 17 [SerializeField] 18 private Tilemap LayerGround; 19 [SerializeField] 20 private Tilemap LayerObject; 21 22 23 private string[] lineSplittedGroundTextData; 24 private string[] lineSplittedObjectTextData; 25 26 // Start is called before the first frame update 27 void Start() 28 { 29 MapData mapData = new MapData(); 30 31 //テキスト代入 32 string groundTextAll = GroundText.text; 33 string objectTextAll = ObjectText.text; 34 35 //行ごとに分割 36 lineSplittedGroundTextData = groundTextAll.Split('\n'); 37 lineSplittedObjectTextData = objectTextAll.Split('\n'); 38 39 //1行を , で分割した長さを代入 40 mapData.SetWidthNum(lineSplittedGroundTextData[0].Split(',').Length); 41 mapData.SetHeightNum(lineSplittedGroundTextData.Length); 42 43 //groundレイヤーマップデータ定義 44 mapData.InitGroundMapData(); 45 46 //objectレイヤーマップデータ定義 47 mapData.InitObjectMapData(); 48 49 //作成した配列に1つずつ文字を入れていく 50 for (int i = 0; i < mapData.GetHeightNum(); i++) 51 { 52 //1文字ずつ区切って配列にする 53 string[] tempSplittedGroundText = lineSplittedGroundTextData[i].Split(','); 54 string[] tempSplittedObjectText = lineSplittedObjectTextData[i].Split(','); 55 56 for (int j = 0; j < mapData.GetWidthNum(); j++) 57 { 58 mapData.SetGroundMapData(i, j, tempSplittedGroundText[j]); 59 mapData.SetObjectMapData(i, j, tempSplittedObjectText[j]); 60 61 //MapMapTileDataListの配列の数だけforを回し、IDに合うかチェックする 62 for (int k = 0; k < MapTileData.MapTileDataList.Count; k++) ←ここでは正しく値を取得できる 63 { 64 65 if (mapData.GetGroundMapData()[i, j].Trim() == k.ToString()) 66 { 67 68 //座標x,yにListに登録されているタイルが敷かれる 69 LayerGround.SetTile(new Vector3Int(j, i, 0), MapTileData.MapTileDataList[k].tile); 70 71 } 72 if (mapData.GetObjectMapData()[i, j].Trim() == k.ToString()) 73 { 74 //座標x,yにListに登録されているタイルが敷かれる 75 LayerObject.SetTile(new Vector3Int(j, i, 0), MapTileData.MapTileDataList[k].tile); 76 77 } 78 } 79 80 } 81 } 82 } 83}

C#

1■Routing.cs 2using System.Collections; 3using System.Collections.Generic; 4using System; 5using UnityEngine; 6using UnityEngine.Tilemaps; 7 8public struct Node 9{ 10 public int PosX; 11 public int PosY; 12 public int NodeStatus; 13 //0:None 1:Open 2:Close 14 15 public int RealCost; 16 public int EstimatedCost; 17 public int TotalCost; 18 //public Node ParentNode; 19} 20 21public class Routing : MonoBehaviour 22{ 23 [SerializeField] 24 private MapTileData MapTileData; 25 26 private int[,] CostMap; 27 28 MapData mapData; 29 30 public List<Node> NodeOpenList; 31 32 public List<Node> NodeCloseList; 33 34 Node[,] NodeAll; 35 36 int[,] test; 37 38 public int GoalPosX; 39 public int GoalPosY; 40 41 42 public void RoutingInit() 43 { 44 mapData = new MapData(); 45 46 CostMap = new int[mapData.GetHeightNum(), mapData.GetWidthNum()]; 47 48 //予め、マップデータ分のノードを作成しておく。 49 NodeAll = new Node[mapData.GetHeightNum(), mapData.GetWidthNum()]; 50 51 52 for (int i = 0;i < mapData.GetHeightNum(); i++) 53 { 54 for (int j = 0; j < mapData.GetWidthNum(); j++) 55 { 56 57 //初期化(ノードの座標だけ先に入れておく) 58 NodeAll[i, j].PosX = j; 59 NodeAll[i, j].PosY = i; 60 NodeAll[i, j].NodeStatus = 0; 61 NodeAll[i, j].RealCost = -1; 62 NodeAll[i, j].EstimatedCost = -1; 63 NodeAll[i, j].TotalCost = 0; 64 //NodeAll[i, j].ParentNode = null; 65 66 } 67 } 68 69 NodeOpenList = new List<Node>(); 70 71 NodeCloseList = new List<Node>(); 72 73 CostMapRefresh(); 74 } 75 76  //テスト用に作成 77 private void Start() 78 { 79 Debug.Log(MapTileData.MapTileDataList.Count); ←ここでも正しい値を取得できる 80 } 81 82 void CostMapRefresh() 83 { 84 //MapTileData = new MapTileData(); 85 86 Debug.Log(MapTileData.MapTileDataList.Count);      ←ここでは正しい値が取得できない 87 88 //課題:タイルマップリストの数が0になっている 89 //List.Countで取得したいが、Staticじゃないので読めない 90 //Staticにすると、うまく通らない 91 92 for (int i = 0; i < mapData.GetHeightNum(); i++) 93 { 94 for (int j = 0; j < mapData.GetWidthNum(); j++) 95 { 96 //MapTileDataListの配列の数だけforを回し、IDに合うかチェックする 97 for (int k = 0; k < MapTileData.MapTileDataList.Count; k++) 98 { 99 if (mapData.GetGroundMapData()[i, j].Trim() == k.ToString()) 100 { 101 CostMap[i, j] += MapTileData.MapTileDataList[k].cost; 102 } 103 104 if (mapData.GetObjectMapData()[i, j].Trim() == k.ToString()) 105 { 106 CostMap[i, j] += MapTileData.MapTileDataList[k].cost; 107 } 108 } 109 } 110 } 111 } 112}

C#

1■MapData.cs 2public class MapData : MonoBehaviour 3{ 4 //マップデータを持つクラス 5 6 private static int WidthNum; 7 private static int HeightNum; 8 private static string[,] GroundMapData; 9 private static string[,] ObjectMapData; 10 11  public int GetWidthNum() 12 { 13 return WidthNum; 14 } 15  public int GetHeightNum() 16 { 17 return HeightNum; 18 } 19  //文字数制限のため最低限の部分のみ記載しました。 20}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BluOxy

2021/11/07 20:45

RoutingInit はどこから呼び出されているのでしょうか
decman

2021/11/08 03:23

反応ありがとうございます。 RoutingInit()は、別クラスにてキーボードのキーを押すと呼ばれるようになっております。 記載が抜けていました、申し訳ありません。
BluOxy

2021/11/08 04:13 編集

問題が漠然としているため、下記のような観点で事象を具体的に確認していただけますか ・Unity のエディタ上で MapTileData に何をセットしたか ・Routing を別々のゲームオブジェクトにそれぞれアタッチしていないか ・Start() を実行したときと CostMapRefresh() を実行したときで、MapTileData 以外のメンバーの状態も違っていないか
U_U_Jenkins

2021/11/08 08:18

タイトル的には 「他クラスで宣言したListの値を取得する方法について」ではなく 「ScriptableObjectのプロパティを外部参照で正しく呼び出せないことがある」の方が 正しいような気がするのですが、いかがでしょうか。 Start()で実行すると問題ないようですね。 自分はあまりScriptableObjectは使用しないので断定はできませんが、 ScritptableObjectの値はマスタデータ(完全読取専用) として扱うので、”データを後入れ”というのは間違った使い方なような気もします。 自分の認識するScriptableObjectの特性なのですが、 これはデータの雛形として作成して、インスペクター上で必要な数分データを入力して それをアセット化し、それぞれを呼び出す形で扱うものと思っています。 何か理由がなければ単純にMapTileDataはScriptableObjectを 継承するのではなく、シングルトン(デザインパターン)にしたり、 もっと簡潔にするならMapInit.csの役割をMonobehaviourを継承したMapTileDataクラスに入れてあげて、 MapTileDataListをMapTileDataクラスから呼び出せるようにしてあげても良いのではないでしょうか。
decman

2021/11/08 10:49 編集

BluOxy様 反応ありがとうございます。 以下、返信させてください。 ・Unity のエディタ上で MapTileData に何をセットしたか  →セット内容を質問文に追記しました。 ・Routing を別々のゲームオブジェクトにそれぞれアタッチしていないか  →まだソースの動作確認段階のため、Routing.csは   1つのゲームオブジェクトにのみアタッチしております。 ・Start() を実行したときと CostMapRefresh() を実行したときで、MapTileData 以外のメンバーの状態も違っていないか  →先ほど質問文に追記したMapData.csのGetHeightNum()のコール結果を確認しました。   Routing.cs Start()内:    NullReference: Object referernce not set to an instance of an object.    のエラーが出力されました。   Routing.cs CostMapRefresh()内:    結果が正しく出力されました。 長文になってしまい申し訳ありません、よろしくお願いします。
decman

2021/11/08 10:43

U_U_Jenkins様 反応ありがとうございます。 以下返信させてください。 >タイトル的には >「他クラスで宣言したListの値を取得する方法について」ではなく >「ScriptableObjectのプロパティを外部参照で正しく呼び出せないことがある」の方が >正しいような気がするのですが、いかがでしょうか。 タイトルがズレている件についてはおっしゃる通りです。修正します。 >自分はあまりScriptableObjectは使用しないので断定はできませんが、 >ScritptableObjectの値はマスタデータ(完全読取専用) >として扱うので、”データを後入れ”というのは間違った使い方なような気もします。 今回行いたい動作としては、既にインスペクタ上で入力したデータについてList.Countで リストの数を取得したいだけなのでセーフな認識です。 >何か理由がなければ単純にMapTileDataはScriptableObjectを >継承するのではなく、シングルトン(デザインパターン)にしたり、 >もっと簡潔にするならMapInit.csの役割をMonobehaviourを継承したMapTileDataクラスに >入れてあげて、MapTileDataListをMapTileDataクラスから呼び出せるようにしてあげても >良いのではないでしょうか。 自分の無学さによりScriptableObjectでしか実装方法を知らなかっただけで、 拘りがあるわけではありません。。 MapInit.csの役割をMapTileDataクラスに持たせる案については試してみます。 シングルトンについても初めて知りました。1度だけインスタンスを作成し、それ以降はGetInstanceを することでその中の変数を実質グローバル変数のような扱いができるようなイメージでしょうか。 こちらについても試しに実装してみます。 助言ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問