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

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

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

Nullとは、プログラミング言語やデータベースにおけるデータ表現の一種です。コンテキストによって"空"もしくは"長さ0の文字列"、”未知・不明”を意味します。

Unity

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

Q&A

解決済

3回答

1361閲覧

あるスクリプトのメソッドを他のスクリプトから呼び出すと、そのメソッドでだけ変数の値がnullや0になる

Errors

総合スコア1

Null

Nullとは、プログラミング言語やデータベースにおけるデータ表現の一種です。コンテキストによって"空"もしくは"長さ0の文字列"、”未知・不明”を意味します。

Unity

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

0グッド

0クリップ

投稿2021/08/07 14:58

編集2021/08/08 02:56

他のスクリプトからWeaponActiveメソッドを実行すると、その時だけあらゆる変数が0やnullなどになる。

キャラにInstantiateで武器を持たせて(ここではSwordというオブジェクト)キャラにAddComponentでSwordスクリプトをアタッチし、持っている武器をSetActiveで表示非表示を切り替えたいと思い、別のスクリプトからinvokeで呼び出されるWeaponActiveメソッドを作りました。

SwordクラスのStart()から呼び出したWeaponActive(false)は、ちゃんと武器は消えるし、それ以外の変数も問題なく値を保持していました。

しかしTechniquePerformerと言う同じオブジェクトにアタッチした別のスクリプトでボタン入力を検知し、Invokeを利用してWeaponActiveを実行しました。

するとなぜかNullReferenceとなり、武器が表示されませんでした。
調べてみると、swordObというGameObject型で武器を参照している場所がnullでした

public class Sword : MonoBehaviour { CharaData charaData; SwordCol swordCol; GameObject swordOb; public void Start() { charaData = this.GetComponent<CharaData>(); swordOb = this.gameObject.transform.Find("StickHuman/body/R_upperarm/R_forearm/Sword").gameObject; swordCol = swordOb.transform.Find("grip").GetComponent<SwordCol>(); WeaponActive(false); } public void WeaponActive(bool a) { swordOb.SetActive(a); } int count; void FixedUpdate() { Debug.Log(swordOb); count++;//強制的にswordObを表示する if (count == 300) { WeaponActive(true); } } }

試しにFixedUpdateでDebug.Log(swordOb)を実行してみましたが、Startで呼び出された後もオブジェクトの名前を出力し続け、ErrorPauseを無効にしてRullReference後の状況も見てみましたが、FixedUpdateからswordObがnullだと表示されたことは一度もありませんでした。

試しに同じスクリプトからゲームスタート5秒後にWeaponActiveを呼び出させてみましたが、そうすると正常にswordは実行されました。
しかし第三者のスクリプトからinvokeで実行すると、今度はTechniqueの方から呼んだのと同じようにエラーが発生しました。

そしてdebug.logを使っていくうちに分かったことが、他のスクリプトからWeaponActiveを実行した時のみあらゆる変数、更にthis.gameObjectまでもがnullや0となってエラーが発生していると分かりました。

ただしnullなどにならない変数もあって、たとえば宣言と同時に初期化したint型の変数は問題なし。
staticなクラスにjsonで保存していた変数とかも問題なし。しかしstartなどで値を入力したものはnullなどになる。

ちなみに宣言と同時に初期化して、その後startで別の値を入力してみましたが、weaponActiveでは変数の中が宣言時に初期化した値でした。

変数はprivateなので他のスクリプトからは変更できないはずで、Swordスクリプト内でも代入してるのはStart内のみで、他から参照先は変更されていません。

試したこと

Invokeでは無くGetComponentからメソッド実行、しかし同じ結果。

WeaponActiveの引数をなくして返り値からSetActive、返り値もnullだった。

補足情報(FW/ツールのバージョンなど)

WeaponActiveメソッドを使わずに武器を非アクティブ化したりするなど、別の方法を使えばいいような気もしますが、なぜこれでエラーが出るのか分からないので、できればこの方法を使う方針でよろしくお願いします。

unity 2020.3.15f2

NullReferenceException: Object reference not set to an instance of an object Sword.WeaponActive (System.Boolean a) (at Assets/Sclipt/Technique/Sword.cs:23) System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <695d1cc93cca45069c528c15c9fdd749>:0) Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <695d1cc93cca45069c528c15c9fdd749>:0) System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <695d1cc93cca45069c528c15c9fdd749>:0) TechniquePerformer.CallMethod (System.String className, System.String methodName, System.Object value) (at Assets/Sclipt/CharaCommon/TechniquePerformer.cs:247) TechniquePerformer.Equiped () (at Assets/Sclipt/CharaCommon/TechniquePerformer.cs:119) CharaDataという第三者のスクリプトからWeaponActiveを実行したとき NullReferenceException: Object reference not set to an instance of an object Sword.WeaponActive (System.Boolean a) (at Assets/Sclipt/Technique/Sword.cs:23) System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <695d1cc93cca45069c528c15c9fdd749>:0) Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <695d1cc93cca45069c528c15c9fdd749>:0) System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <695d1cc93cca45069c528c15c9fdd749>:0) CharaData.CallMethod (System.String className, System.String methodName, System.Object value) (at Assets/Sclipt/CharaCommon/CharaData.cs:254) CharaData.FixedUpdate () (at Assets/Sclipt/CharaCommon/CharaData.cs:238)

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/08/08 07:17

関数の呼び出し元のTechniquePerformerの、Swordクラスを取得している場所と呼び出している部分のコードを記載することは可能でしょうか?
Errors

2021/08/08 08:28

TechniquePerformerに下記のメソッドを用意して、これで呼び出しています。 public void Equiped() { CallMethod("Sword", "WeaponActive", true); //"Sword"は本来変数。変数の中身がSwordなのはDebug.Logで確認済み } public void CallMethod(string className, string methodName, dynamic value = null) { System.Type types = System.Type.GetType(className); object weaponClass = System.Activator.CreateInstance(types); MethodInfo methodinfo = types.GetMethod(methodName); object[] ob = new object[] { value }; methodinfo.Invoke(weaponClass, ob); }
Errors

2021/08/08 08:35 編集

NullReferenceのエラー前に、黄色三角のこんな警告が発生しています You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all UnityEngine.MonoBehaviour:.ctor () Sword:.ctor () System.Activator:CreateInstance (System.Type) TechniquePerformer:CallMethod (string,string,object) (at Assets/Sclipt/CharaCommon/TechniquePerformer.cs:243) TechniquePerformer:Equiped () (at Assets/Sclipt/CharaCommon/TechniquePerformer.cs:121)
Errors

2021/08/08 08:37

ちなみにTechniquePerformerの121行目はCallMethod("Sword", "WeaponActive", true) 243行目は object weaponClass = System.Activator.CreateInstance(types); という処理となります
Errors

2021/08/08 09:29

汎用性を捨ててEquipedメソッド内のCallMethodを、下記の処理に入れ替えると普通に動きました。 this.gameObject.GetComponent<Sword>().WeaponActive(true); どうやら原因はCallMethodの処理のようです。 しかし習得したいスクリプトは10個以上になる可能性もあり、それをif文とかで分岐しまくるのは嫌なので、出来ればCallMethodを使って文字列でメソッド実行したいです。
退会済みユーザー

退会済みユーザー

2021/08/08 11:20

そうですね…… 最悪出来そうになければ、効率は悪いですがswitch文で分岐させる方が良さそうかもしれないです。 わざわざ追記依頼をしておいて申し訳ないですが、私ではお役に立てそうにないですね。 お役に立てず申し訳ありません。
退会済みユーザー

退会済みユーザー

2021/08/10 06:41

全く分かりませんがMonoBehaviourを継承していると無理っぽい? 警告の通り、MonoBehaciourを継承しているクラスをnewして作る事は許可しません、ってことらしいので、て事はMonoBehaviourを継承しないなら出来るっぽい? object weaponClass = System.Activator.CreateInstance(types); Debug.Log(weaponClass); // MonoVehaviourを継承しているとnullになる
guest

回答3

0

ベストアンサー

異常な挙動に関しては、どうやら武器コンポーネントを取得するのではなく新規に生成していたためのようですね。
私からも、ご質問者さんがおっしゃる...

汎用性を捨ててEquipedメソッド内のCallMethodを、下記の処理に入れ替えると普通に動きました。
this.gameObject.GetComponent<Sword>().WeaponActive(true);

どうやら原因はCallMethodの処理のようです。
しかし習得したいスクリプトは10個以上になる可能性もあり、それをif文とかで分岐しまくるのは嫌なので、出来ればCallMethodを使って文字列でメソッド実行したいです。

という件について意見を申し上げさせていただきますと、代替案としてこんな手はどうでしょうかね?
まず、プロジェクト内に下記のようなスクリプトを作り...

lang

1public interface IWeapon 2{ 3 void WeaponActive(bool a); 4}

Swordなどの各種武器の宣言部分にIWeaponを追加し...

lang

1public class Sword : MonoBehaviour, IWeapon 2{ 3 // 以降は変更なし

そしてEquipedは下記のようにしてみてはいかがでしょう。

lang

1public void Equiped() 2{ 3 this.gameObject.GetComponent<IWeapon>().WeaponActive(true); 4}

こういった方式なら具体的なSword型だとかを登場させることなく、抽象的なIWeapon型として全武器に共通する操作を行うことができるんじゃないかと思います。

投稿2021/08/12 11:21

Bongo

総合スコア10807

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

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

Errors

2021/08/15 08:15

必要ないと判断して何も言わず省略してしまったのですが、武器によってはつかみやダッシュ攻撃、あるいは自傷ダメージなど、かなり特殊な処理が必要となり、数十個の武器すべての処理をIWeaponに収めると、キャラごとに使わない処理が出てきます。 検証したわけでもないのですが、これだとゲームの容量は減ってもメモリ使用量が増えるような気がします。シンプルな2dゲームですし、こんな事気にすることでは無いとは思いますが。 今の所はdynamic型を使用しています。私が回答者様の意図を履き違えてしまっているのかもしれません。その場合はすみません。
Bongo

2021/08/15 12:43

インターフェースをもっと細分化する手があるかもしれませんね。 たとえば、つかみ攻撃に関する機能を定義したIGrabAttackable、ダッシュ攻撃に関する機能を定義したIDashAttackable、自傷ダメージに関する機能を定義したIDoubleSided...みたいな感じで機能を分類してはいかがでしょう。 インターフェースは複数実装可能なので、たとえばSwordは武器としての基本機能に加えて自傷ダメージを受けるものとする場合... public class Sword : MonoBehaviour, IWeapon, IDoubleSided { // Swordの実装 } みたいな形にすることができるかと思います。 そして武器に関する処理を行う際には... var weapon = this.gameObject.GetComponent<IWeapon>(); if (weapon != null) { // ここに武器共通の処理...たとえば敵にダメージを与える、などを記述 var doubleSided = weapon as IDoubleSided; if (doubleSided != null) { // ここに自傷武器特有の処理...たとえばプレイヤーにダメージを与える、などを記述 } } みたいにできるかもしれません。といっても、ご質問者さんが制作中のゲームの構造を知らないまま思いつきを開陳しているだけですので、はたしてこういった手が適用可能かどうかは不確かですね。参考程度に受け取っていただけるとありがたいです。
Errors

2021/08/16 02:08

インターフェースというものを適当に流していたのですが、おかげで武器の処理だけでなく、特殊な敵へのダメージ処理もより効率よく作れそうです。 早速やってみます。
guest

0

csharp

1public void CallMethod(string className, string methodName, dynamic value = null) 2{ 3 // ... (略) 4 5 object weaponClass = System.Activator.CreateInstance(types); 6 7 // ... (略) 8 9}

 上記のコードの System.Activator.CreateInstance(types) のところで、新しいオブジェクト(weaponClass)を作成して、その新しく作成したオブジェクト(weaponClass)のメソッドを呼び出しているせいです。
新しく作成したオブジェクトは変数の初期化などしていないので、当然いろんな変数がnullになっています。

「TechniquePerformerと言う同じオブジェクトにアタッチした別のスクリプト」から呼び出すのであれば、 TechniquePerformerの中で GetComponent<Sword>() を使い、Swordクラスのオブジェクトを取得して使うのが良いでしょう。

投稿2021/08/12 02:28

JunSuzukiJapan

総合スコア310

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

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

Errors

2021/08/15 08:15

ご回答ありがとうございます。少し変えてみて、GetComponent(string)を使うことでif文を使うこと無く表示非表示を問題なくこなすことができるようになりました。
guest

0

今更ですが自己解決方法を。

CallMethodでクラスを習得するために、クラスの名前からタイプを生成し、それを使ってSystem.Activatorで新しいクラスを生成していたそうです。

恐らくやりたいのは今回のように既存のクラスのメソッドを実行することなので、上の方法では無くGetComponentでクラスを参照すると上手くいきました。

public void CallMethod(string className, string methodName, dynamic value = null)
{
dynamic wClass = GetComponent(weaponClass);

System.Type types = System.Type.GetType(weaponClass);
MethodInfo methodinfo = types.GetMethod(methodName);

methodinfo.Invoke(weaponClass, value);
}

どの程度の負荷なのかはまだ検証していませんが、これを使えば文字列でクラスとメソッドを指定して実行することが出来ます。

ほぼ使ったことはありませんが、返り値も習得出来たはずです。

投稿2021/09/22 00:13

Errors

総合スコア1

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問