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

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

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

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

Q&A

解決済

1回答

1390閲覧

Unityで他のクラスのフィールドが変更できない

kanoko_h

総合スコア0

Unity

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

0グッド

0クリップ

投稿2020/05/22 08:33

編集2020/05/25 03:14

前提・実現したいこと

Unity2Dで単純な橋渡しゲームを作っています.

1.キャラクター(Chara)が橋を渡り切ったとき,GameManagerクラス内のAddScoreメソッドを呼び出して_scoreに加算したいのですが,呼び出しても加算されません.

2.キャラクター(Chara)が橋から落ちたとき,GameManagerクラス内のDecreaseHPメソッドを呼び出して_hpを減算したいのですが,呼び出すと_hpの値が-3984とかになってしまいます.

3.プレイヤーのHPをハートの個数で表してHPManagerクラスで管理しているのですが,DecreaseHPメソッドからHPManagerクラス内のAddDamageメソッドを呼び出してもハート(オブジェクト)を削除できません.画面上ではハートが表示されているのにtransform.childCountが0になってしまいます.

エラーやワーニングは出ません.
プログラミングはかじってますが,UnityとC#は初心者です.
解決法や代替案など,ご教授頂けたら幸いです.

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Chara : MonoBehaviour 6{ 7 const float POWER = 50; 8 9 [SerializeField] 10 public RuntimeAnimatorController[] controller; 11 public GameObject gameManager; 12 13 GameManager _mgrScript; 14 Rigidbody2D _rigidbody; 15 Animator _animator; 16 float _speed = 2; 17 int _direction = 1; //右:1,左:-1 18 float _width; 19 float _height; 20 21 // Start is called before the first frame update 22 void Start() 23 { 24 _mgrScript = gameManager.GetComponent<GameManager>(); 25 _rigidbody = GetComponent<Rigidbody2D>(); 26 _animator = GetComponent<Animator>(); 27 //見た目(AnimatorController)をランダムで決める 28 _animator.runtimeAnimatorController 29 = RuntimeAnimatorController.Instantiate(controller[Random.Range(0,controller.Length)]); 30 //スプライトのサイズを取得 31 _width = GetComponent<SpriteRenderer>().bounds.size.x; 32 _height = GetComponent<SpriteRenderer>().bounds.size.y; 33 } 34 35 // Update is called once per frame 36 void Update() 37 { 38 //落下中・最高速度でないとき加速 39 if(_rigidbody.velocity.y > -0.1){ 40 if(Mathf.Abs(_rigidbody.velocity.x) < _speed){ 41 _rigidbody.AddForce(new Vector2(POWER*_direction, 0)); 42 } 43 } 44//----------------------問題の箇所--------------------------// 45 //橋を渡り切ったらオブジェクト削除,スコア加算 46 if(IsOutsideX()){ 47 _mgrScript.AddScore(100); 48 Destroy(gameObject); 49 } 50 //落っこちたらオブジェクト削除 51 if(IsFallen()){ 52 _mgrScript.DecreaseHP(); 53 Destroy(gameObject); 54 } 55//--------------------------------------------------------// 56 //アニメーター変数の更新 57 _animator.SetFloat("x", _rigidbody.velocity.x); 58 _animator.SetFloat("y", _rigidbody.velocity.y); 59 } 60 61 //渡り切ったかどうかの判定 62 bool IsOutsideX(){ 63 Vector2 position = transform.position; 64 if(_direction > 0){ 65 if(position.x > GetRight() + _width){ 66 return true; 67 } 68 } else { 69 if(position.x < GetLeft() - _width){ 70 return true; 71 } 72 } 73 return false; 74 } 75 76 //落下判定 77 bool IsFallen(){ 78 Vector2 position = transform.position; 79 if(position.y < GetBottom() - _height){ 80 return true; 81 } 82 return false; 83 } 84 85 //画面左端のX座標を取得 86 float GetLeft(){ 87 Vector2 min = Camera.main.ViewportToWorldPoint(Vector2.zero); 88 return min.x; 89 } 90 91 //画面右端のX座標を取得 92 float GetRight(){ 93 Vector2 max = Camera.main.ViewportToWorldPoint(Vector2.one); 94 return max.x; 95 } 96 97 //画面底辺のY座標を取得 98 float GetBottom(){ 99 Vector2 min = Camera.main.ViewportToWorldPoint(Vector2.zero); 100 return min.y; 101 } 102 103 public void SetVector(float speed, int direction){ 104 _speed = speed; 105 _direction = direction; 106 } 107}

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class GameManager : MonoBehaviour 6{ 7 int _score = 0; 8 float _timer = 0.0f; 9 int _hp = 4; 10 11 [SerializeField] 12 public GameObject hpManager; 13 14 HPManager _hpScript; 15 16 // Start is called before the first frame update 17 void Start() 18 { 19 _hpScript = hpManager.GetComponent<HPManager>(); 20 _hpScript.SetHP(_hp); 21 _hpScript.AddDamage(1); //ここはいける 22 Debug.Log(_hp); 23 } 24 25 // Update is called once per frame 26 void Update() 27 { 28 _timer -= Time.deltaTime; 29 if(_timer < 0){ 30 AddScore(1); 31 _timer += 0.5f; 32 } 33 } 34 35 void OnGUI() 36 { 37 _DrawScore(); 38 } 39 40//----------------------問題の箇所--------------------------// 41 public void AddScore(int num){ 42 _score += num; 43 } 44 45 public void DecreaseHP(){ 46 _hp = _hp - 1; 47 Debug.Log(_hp); 48 _hpScript = hpManager.GetComponent<HPManager>(); 49 _hpScript.AddDamage(1); 50 //_hpScript.SetDamageFlag(true); 51 } 52//--------------------------------------------------------// 53 54 void _DrawScore(){ 55 GUI.skin.label.fontSize = 20; 56 GUI.skin.label.alignment = TextAnchor.MiddleLeft; 57 Rect position = new Rect(3,0,300,30); 58 GUI.Label(position, string.Format("score:{0}", _score)); 59 } 60} 61

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class HPManager : MonoBehaviour 6{ 7 [SerializeField] 8 public GameObject HPobj; 9 10 bool _damageFlag = false; 11 12 // Start is called before the first frame update 13 void Start() 14 { 15 16 } 17 18 // Update is called once per frame 19 void Update() 20 { 21 Debug.Log(_damageFlag); 22 if(_damageFlag == true){ 23 Debug.Log("itai"); 24 AddDamage(1); 25 _damageFlag = false; 26 } 27 } 28 29 //HPオブジェクトをhp個セットする 30 public void SetHP(int hp){ 31 //一旦HPオブジェクトを全部削除 32 for(int i=0; i<transform.childCount; i++){ 33 Destroy(transform.GetChild(i).gameObject); 34 } 35 //hp個HPオブジェクトを生成 36 for(int i=0; i<hp; i++){ 37 Instantiate<GameObject>(HPobj, transform); 38 } 39 } 40 41//----------------------問題の箇所--------------------------// 42 //ダメージ分HPオブジェクトを削除 43 public void AddDamage(int damage){ 44 Debug.Log(transform.childCount); 45 if(damage > transform.childCount){ 46 damage = transform.childCount; 47 } 48 49 if(damage > 0){ 50 for(int i=0; i<damage; i++){ 51 Destroy(transform.GetChild(i).gameObject); 52 } 53 } 54 } 55//--------------------------------------------------------// 56 57 public void SetDamageFlag(bool flag){ 58 _damageFlag = flag; 59 Debug.Log(_damageFlag); 60 } 61} 62

試したこと

3についてはHPManagerクラス内に_damageFlagを作って,AddDamageメソッドをUpdate内から呼び出してみたのですが,_damageFlagが変更されなくて解決しませんでした.

追記:
1.AddScore()内にDebug.Log(_score)を置いて確認したところ,キャラが橋を渡り切ったタイミングでちゃんと100ずつ加算されていました.しかし画面に_scoreを表示すると,加算が行われていません.また,Unityでシーン再生すると,何故かスコアが初期化されずに前回の値から加算されています.
GameManagerクラス内で経過時間によるスコア加算もしているのですが,そちらは再生ごとに初期化されますし,画面にも正常に反映されています.

2.DecreaseHPの前にDebug.Logを置いてみましたが,キャラの落下ごとに表示されるログはちゃんと一回ずつでした.でも過剰に呼び出されている可能性はありそうです.この場合,フラグ管理して一回しか呼び出さないようにするべきなのでしょうか?

3.生成されたHPはちゃんとHPManagerの子オブジェクトになっていました.

感覚としてなのですが,別オブジェクトからメソッドを呼び出すと別のフィールド変数が用意されてしまう?感じなのでしょうか.解決方法がわかりません.初心者で申し訳ないです.

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

Unity2019.3.7f1

追記:
無事解決しました!
アドバイスをくださったsakura_hanaさんありがとうございました!

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

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

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

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

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

sakura_hana

2020/05/22 09:56

1.Debug.Logを用いるなどして、IsOutsideXが適切なタイミングでtrueになっているか確認してください。(そもそも_mgrScript.AddScore(100);が呼ばれていないかもしれない) 2.Updateは1フレーム毎に呼び出される為、過剰にDecreaseHPが呼び出されているのではないでしょうか。(Destroyも若干ラグがあった気がするのでそのせい?)こちらもDebug.Logを用いてメソッドの呼び出し回数等を確認してください。 3.オブジェクトの親子関係は正常ですか?
kanoko_h

2020/05/23 13:39 編集

アドバイスありがとうございます. 「試したこと」に追記させて頂きました.
sakura_hana

2020/05/25 01:31

謎挙動ですね……GameManagerが複数存在しているとかですかね……?(これはこれで描画が二重になる気もしますが) 「何故かスコアが初期化されずに前回の値から加算される」という点も不思議です。 動作の原理として、 「オブジェクトAに付けたGameManagerの変数やメソッドに、オブジェクトBの別スクリプトとオブジェクトCの別スクリプトからアクセス」した場合、いずれも同じ変数やメソッドが操作されます。 ですが「オブジェクトAに付けたGameManagerの変数やメソッドと、オブジェクトBに付けたGameManagerの変数やメソッド」は別物として扱われます。(※変数やメソッドがstaticでない場合)(オブジェクトAにGameManagerコンポーネントが複数付いている場合も同様) 一応この辺確認してみてください。
guest

回答1

0

自己解決

GameManagerがプレハブになっていたことが原因でした.
Chara内でGameManagerを扱う際に,ゲームオブジェクトをインスペクターから指定していて,その場合はAssets内のものしか指定できなかったため,プレハブ化していました.
CharaのStartに gameManager = GameObject.Find("GameManager"); を置き,オブジェクトのプレハブを解除することで解決しました.

原因がわかると納得の現象なのですが,Unity初心者は結構陥りやすいミスなのではないかなと思います.
気軽にプレハブ化してはいけないという戒めを得ました.

投稿2020/05/25 03:11

kanoko_h

総合スコア0

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

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

sakura_hana

2020/05/25 07:40

インスペクターからでもシーン上のオブジェクトを指定出来ますよ(ドラッグ&ドロップ)。 GameObject.Findは重めなのとうっかりオブジェクト名変えたり同じ名前のオブジェクト作ると危険なので極力使わない方がいいと思います。 推測するに失敗していた時は「シーン上にGameManager付きGameObjectを作成→それをプレハブ化→CharaのgameManagerにプレハブを指定(シーン上のGameManager付きGameObjectは維持)」という感じでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問