//tips
//時間経過でのアイテム配布コード修正
うまく3つ目のスロットのみが読み込まれない問題が起きたので問題を確認していく。
問題の要因は、項目削除で発生するlistと配列のindexのズレにあるのではないかと考える。まずはデバッグで各要素の動きを終えるようにする。
Debug.Log("s1:"+slots[0].name); //Slot
Debug.Log("s2:" + slots[1].name);//Slot(1)
Debug.Log("s3:"+slots[2].name);//Slot(2)
Debug.Log("i1:" + inventorytest.items[0].name); //DIY
Debug.Log("i2:" + inventorytest.items[1].name);//Melon
Debug.Log("i3:" + inventorytest.items[2].name);//Sea
を挿入した。
最初に削除されたのがDIYで
i1:Melon
i2:Sea
その次にSeaが削除され、
i1:Melon
最後はMelonが削除されている。
3つ目だけButton button = slots[index].GetComponentInChildren<Button>();とbutton.interactable = true;のコードが反映されない要因を考える。
最後にinventorytest.items.RemoveAt(index);をされた時点でindexのリストそのものが一時消滅するためこのようなことが発生してしまうのではないかと考えるたが、slotの中身とリストの中身をもう少し照らし合わせてみる。
<リスト>
1.DIY
2.Melon
3.Sea
<Slot>
基本は slots[i].AddItem(inventoryTest.items[I]);なので
slot1.DIY
slot2.Melon
slot3.Sea
となる。
inventoryTest.items[I]でのslotへのitem割り当てはスタート時点しか呼ばれないので障害にはなっていない。最初にリストからslotに割り当てられ、その後のリストの変動をslot側では受けないためリスト項目が削除されても、slotのアイテム表示が消えることはない。
Debug.Log("index:" + index);を追加。
このindexが3回0となってしまった場合は、リストのアイテムはなくなるにもかかわらず、slotの表示はslot[0]の分のみしか消えない。これは、slotのindexが初期時点のリストのindexにしか対応していないことにより起こる。
なので、 Button button = slots[index].GetComponentInChildren<Button>();
の部分を修正する必要がある。
inventorytest.items[index].name =“”で分岐させれば良いので下記のメソッドを追加した。
void SlotManage(int index)
{
if (inventorytest.items[index].name == "Diy")
{
Button button = slots[0].GetComponentInChildren<Button>();
button.interactable = true;
}
if (inventorytest.items[index].name == "Melon")
{
Button button = slots[1].GetComponentInChildren<Button>();
button.interactable = true;
}
if (inventorytest.items[index].name == "Sea")
{
Button button = slots[2].GetComponentInChildren<Button>();
button.interactable = true;
}
}
2回目のメソッド試行時にSlotManage(int index)の読み込みが行われずエラー。
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
4秒後に実行されたitem2Sea (Item)
TimeController:Item2() (at Assets/TimeController.cs:62)
不要なデバッグの削除とSlotManage(int index)内に起動されているかのチェックのデバッグ設置。
またリストから削除した後に名前の参照を行なっているのが問題かと思い
inventorytest.items.RemoveAt(index);
も移動。
また、inventorytest.items.RemoveAt(index);をしたすぐ後にはリストの値も変更され、異なる値を返すようになってしまうため、return;を入れて、そこでSlotManage(int index)の処理を終了させる。
最終的にスクリプトは下記かたちになった。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimeController : MonoBehaviour
{
InventoryTest inventorytest;
Slot[] slots;
public Transform slotsParent;
void Start()
{
inventorytest = GetComponent<InventoryTest>();
slots = slotsParent.GetComponentsInChildren<Slot>();
Invoke("Item1", 3);
Invoke("Item2", 4);
Invoke("Item3", 5);
}
void Item1()
{
int index = Random.Range(0, inventorytest.items.Count); //int型なので最大値を含まない
Item item1 = inventorytest.items[index];
Debug.Log("index:" + index);
Debug.Log(inventorytest.items.Count);
Debug.Log("3秒後に実行されたitem1" + item1);
//inventorytest.items.RemoveAt(index);
//inventorytest.Remove(item1);
SlotManage(index);
//Button button = slots[index].GetComponentInChildren<Button>();
//button.interactable = true;
}
void Item2()
{
int index = Random.Range(0, inventorytest.items.Count);
Item item2 = inventorytest.items[index];
Debug.Log("index:" + index);
Debug.Log("4秒後に実行されたitem2" + item2);
//inventorytest.items.RemoveAt(index);
//inventorytest.Remove(item2);
SlotManage(index);
//Button button = slots[index].GetComponent<Button>();
//Button button = slots[index].GetComponentInChildren<Button>();
//button.interactable = true;
}
void Item3()
{
//int index = Random.Range(0, inventorytest.items.Count);
int index = 0;
Item item3 = inventorytest.items[index];
Debug.Log("index:" + index);
Debug.Log("5秒後に実行されたitem3" + item3);
//inventorytest.items.RemoveAt(index);
//inventorytest.Remove(item3);
SlotManage(index);
//Button button = slots[index].GetComponent<Button>();
//Button button = slots[index].GetComponentInChildren<Button>();
//Button button = slots[index].GetComponentInChildren<Button>();
//button.interactable = true;
/*
Debug.Log("i1:" + inventorytest.items[0].name);
Debug.Log("i2:" + inventorytest.items[1].name);
Debug.Log("i3:" + inventorytest.items[2].name);
Debug.Log("index:" + index);
*/
}
void SlotManage(int index)
{
Debug.Log("SlotManage");
Debug.Log(inventorytest.items[index].name);
if (inventorytest.items[index].name == "DIY")
{
Button button = slots[0].GetComponentInChildren<Button>();
button.interactable = true;
inventorytest.items.RemoveAt(index);
return;
}
if (inventorytest.items[index].name == "Melon")
{
Button button = slots[1].GetComponentInChildren<Button>();
button.interactable = true;
inventorytest.items.RemoveAt(index);
return;
}
if (inventorytest.items[index].name == "Sea")
{
Button button = slots[2].GetComponentInChildren<Button>();
button.interactable = true;
inventorytest.items.RemoveAt(index);
return;
}
}
ボタンのbutton.interactableを変化させる際にアイコンの表示色も明示的にしたい。
InventoryオブジェクトにアタッチしているTimeControllerスクリプト内に書くと孫オブジェクトの取得が非常に面倒になるので、slotに新たなスクリプトをアタッチし、そこでiconの色の表示変更を加えられるようにする。
一旦下記のスクリプトを作成し、slotオブジェクトにアタッチ。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ChangeColoricon : MonoBehaviour
{
[SerializeField]
GameObject icon;
Image image;
Color basecolor;
void Start()
{
Image image = icon.GetComponent<Image>();
basecolor = new Color(255.0f, 255.0f, 255.0f, 255.0f);
}
public void ChangeColor()
{
image.color = Color.red;
}
public void BackColor()
{
image.color = basecolor;
}
}
これを実行するとエラーが発生。
NullReferenceException: Object reference not set to an instance of an object
ChangeColoricon.ChangeColor () (at Assets/ChangeColoricon.cs:24)
image.color = Color.red;
TimeController.SlotManage (System.Int32 index) (at Assets/TimeController.cs:98)
changeColoricon.ChangeColor();
TimeController.Item2 () (at Assets/TimeController.cs:51)
Nullになっていないかデバッグで確認。
Debug.Log("changeColoricon"+changeColoricon);
public void ChangeColor()のimageが案の定nullになっていたので確認したら、Start()内のimageの前にImageがついており、image変数が二つあったのが問題になっていた。