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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Unity

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

Q&A

解決済

2回答

3760閲覧

ListまたはDictionaryの変化でイベントを発生させたい

tride

総合スコア68

Unity

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

0グッド

1クリップ

投稿2021/10/25 00:40

編集2021/10/26 03:01

List型またはDictionary型の変化でイベントを発生させたいです。

以下参考サイトを元に、int型やbool(以下サンプルコードのはbool)などの変数ではイベントの発生は上手くいっているのですが、List型またはDictionary型ではAdd時にNullReferenceExceptionが出て失敗してしまいます。

エラーがNullエラーなので、なにか単純な事を見落としている気がするのですが、そもそも値を追加するAddでなぜNullのエラーが出るのか分からず行き詰っています。
なんとなぁくアクセスしている先が違うのかなぁとは思っているのですが、対応方法も分からずです。

尚、参考サイトではシングルトンで実施していたのでそのままシングルトンを使用していますが、今回はイベント発生検知が主目的であるため、シングルトンである必要はありません。

環境

環境:Unity 2021.1.25f1(2D)
IDE:VisualStudio2019

参考サイト

1:シングルトンを使ってみよう
2:
値の変更を通知するクラス

ソースコード

イベントを検知して処理を実行するスクリプトファイル

C#

1public class SampleEvent : MonoBehaviour 2{ 3 4 private void Start() 5 { 6 //サンプル:単一値変化時のイベントハンドラ登録 7 CameraManager.Instance.TergetValue.Action += ChangeValue; 8 9 //Dictionaryの変化を検知するイベントを登録 10 CameraManager.Instance.DictionaryValue.Action += ChangeDictionary; 11 12 } 13 14 private void Update() 15 { 16 17 } 18 19 20 /// <summary> 21 /// サンプル:値の変更検知時の処理(bool) 22 /// </summary> 23 void ChangeValue(bool value) 24 { 25 Debug.Log("変更された(bool) / " + CameraManager.Instance.TergetValue.Value.ToString()); 26 } 27 28 /// <summary> 29 /// サンプル:値の変更検知時の処理(Dictionary) 30 /// </summary> 31 void ChangeDictionary(Dictionary<string,string> dictionaryValue) 32 { 33 Debug.Log("変更された(Dictionary) / " + CameraManager.Instance.DictionaryValue.Value.Count); 34 } 35 36}

コールする別のスクリプトファイル(値の追加)
※テストとしてボタンのClickイベントでコールしている

C#

1//こちらでは成功 2public void ValueChange() 3{ 4 //成功 5 //サンプル:値を変化(bool) 6 CameraManager.Instance.TergetValue.Value = true; 7} 8 9public void DictionaryChange() 10{ 11 //NullReferenceException: 12 //Object reference not set to an instance of an object 13 //サンプル:Listを変化 14 CameraManager.Instance.DictionaryValue.Value.Add("1","test"); 15 16}

GameManager(シングルトンクラス)

C#

1 2using UnityEngine; 3using System.Collections; 4 5public class GameManager : SingletonMonoBehaviour<GameManager> { 6 7 /// <summary> 8 /// サンプル:値の変化を検知する単一変数 9 /// </summary> 10 /// <remarks>型は要変更</remarks> 11 public NotificationObject<bool> TergetValue = new NotificationObject<bool>(); 12 //List型は省略 13 public NotificationObject<Dictionary<string,string>> DictionaryValue = new NotificationObject<Dictionary<string, string>>(); 14 15}

SingletonMonoBehaviour(参考サイト1をそのまま使用)

C#

1using UnityEngine; 2using System; 3 4public abstract class SingletonMonoBehaviour<T> : MonoBehaviour where T : MonoBehaviour{ 5 6 private static T instance; 7 public static T Instance 8 { 9 get{ 10 if (instance == null) { 11 Type t = typeof(T); 12 13 instance = (T)FindObjectOfType (t); 14 if (instance == null) { 15 Debug.LogError (t + " をアタッチしているGameObjectはありません"); 16 } 17 } 18 19 return instance; 20 } 21 } 22 23 virtual protected void Awake(){ 24 // 他のゲームオブジェクトにアタッチされているか調べる 25 // アタッチされている場合は破棄する。 26 CheckInstance(); 27 } 28 29 protected bool CheckInstance(){ 30 if (instance == null) { 31 instance = this as T; 32 return true; 33 } else if (Instance == this) { 34 return true; 35 } 36 Destroy (this); 37 return false; 38 } 39}

NotificationObject.cs(参考サイト2をそのまま使用)

C#

1using UnityEngine; 2using System.Collections; 3using System; 4 5[System.Serializable] 6public class NotificationObject<T> 7{ 8 public delegate void NotificationAction (T t); 9 10 private T data; 11 12 public NotificationObject (T t) 13 { 14 Value = t; 15 } 16 17 public NotificationObject () 18 { 19 } 20 21 ~NotificationObject () 22 { 23 Dispose (); 24 } 25 26 public UnityEngine.Events.UnityAction<T> action; 27 28 public T Value { 29 get { 30 return data; 31 } 32 set { 33 data = value; 34 Notice (); 35 } 36 } 37 38 private void Notice () 39 { 40 if (action != null) 41 action (data); 42 } 43 44 public void Dispose () 45 { 46 action = null; 47 } 48}

追記:
動的な増減には対応できなかった為、次回質問に続きます。
https://teratail.com/questions/366249?modal=q-comp

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

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

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

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

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

guest

回答2

0

ベストアンサー

この記述だとDictionaryValueの中身のDictionaryがNullのままなのでエラーが出ます。
下記の様な感じでDictionaryであるValueも初期化して下さい。

GameManager(シングルトンクラス)

C#

1using UnityEngine; 2using System.Collections; 3 4public class GameManager : SingletonMonoBehaviour<GameManager> { 5 6 /// <summary> 7 /// サンプル:値の変化を検知する単一変数 8 /// </summary> 9 /// <remarks>型は要変更</remarks> 10 public NotificationObject<bool> TergetValue = new NotificationObject<bool>(); 11 //List型は省略 12 public NotificationObject<Dictionary<string,string>> DictionaryValue = new NotificationObject<Dictionary<string, string>>(); 13 14 void Start() 15 { 16 DictionaryValue.Value = new Dictionary<string, string>(); 17 } 18}

・余談----------------------------------------
ここまででエラーは解決されるかと思いますが変更検知は反応しないかと思います。

反応しないパターン

C#

1CameraManager.Instance.DictionaryValue.Value.Add( "1", "test" );

下記の様に別Dictionaryを代入するような使い方であれば反応します。

反応するパターン

C#

1Dictionary<string, string> test = new Dictionary<string, string>() { { "1", "test"} }; 2CameraManager.Instance.DictionaryValue.Value = test;

投稿2021/10/25 09:34

編集2021/10/25 09:47
Hawn

総合スコア1222

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

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

tride

2021/10/25 10:10 編集

中のDictionaryの初期化をしていなかったのでNullが出ていたんですね。 うっかりしてました。 また、Dictionary型の中に格納されている値の変化だけではイベントが反応しない点については、少し使い方で悩みそうですが、一応newであれば同じキーであれば値の変更でイベントは反応してくれたので目的は達成していると言えそうです。 このままHawnさんの回答をベストアンサーにしたいと思います。
tride

2021/10/26 00:38

あー、ベストアンサーにしてから問題に気付きましたorz newしているから、Dictionaryは一つしか入らない、つまり値は一つまでしか反応してくれないんですね・・・。 思いっきり勘違いしてました。
tride

2021/10/26 01:34

一応、忘備録として。 以下の形であれば、各値で検知する事ができました。 public NotificationObject<Dictionary<string, NotificationObject<string>>> DictionaryValues = new NotificationObject<Dictionary<string, NotificationObject<string>>>(); //キーの追加 CameraManager.Instance.DictionaryValues.Value.Add("1", new NotificationObject<string>()); //値の上書き CameraManager.Instance.DictionaryValues.Value["1"].Value = "Sample8";
guest

0

下記はKey"1":Value"test"を追加しようとしてされているのでしょうか。

CameraManager.Instance.DictionaryValue.Value.Add("1","test");

そうであれば下記の記述が正しいかと思われます。

CameraManager.Instance.DictionaryValue.Add("1","test");

追記:
Hawnさんの回答を見て改めてコードを確認したところ、私の回答は見当違いのようです。
大変失礼致しました。

投稿2021/10/25 02:39

編集2021/10/25 09:50
YOshim

総合スコア1085

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

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

tride

2021/10/25 02:51

ご指摘の内容の場合は、VisualStudio上で以下のエラーが出ます。 CS1061 'NotificationObject<Dictionary<string, string>>' に 'Add' の定義が含まれておらず、型 'NotificationObject<Dictionary<string, string>>' の最初の引数を受け付けるアクセス可能な拡張メソッド 'Add' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足していないことを確認してください。
YOshim

2021/10/25 03:36

using System.Collections.Generic; を追加しても駄目ですかね
tride

2021/10/25 04:01

以下のように既に追加されております。 尚、System.Collections.Genericはグレーアウトになり、未使用状態となっています。 using System; using System.Collections; using System.Collections.Generic; using UnityEngine;
tride

2021/10/25 10:10

追記について> いえいえ。こちらこそ回答いただき、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問