This code is creating an inventory for an RPG game. I'm calling the item information from Items database that is a json file. For some reasons it is skipping this line. I marked it in the code to make it stand out a little more.
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
here is the entire code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
ItemDataBase database;
GameObject inventoryPanel;
GameObject slotPanel;
public GameObject inventorySlot;
public GameObject inventoryItem;
int slotAmount;
public List<Item> items = new List<Item>();
public List<GameObject> slots = new List<GameObject>();
private void Start()
{
database = GetComponent<ItemDataBase>();
slotAmount = 20;
inventoryPanel = GameObject.Find("Inventory Panel");
slotPanel = inventoryPanel.transform.FindChild("Slot Panel").gameObject;
for (int i = 0; i < slotAmount; i++)
{
items.Add(new Item());
// and empty slot
slots.Add(Instantiate(inventorySlot));
//set parent to slot panel
slots[i].transform.SetParent(slotPanel.transform);
}
for (int i = 0; i < items.Count; i++)
{
items[i].ID = -1;
}
AddItem(0);
AddItem(1);
AddItem(1);
Debug.Log(items[1].Title);
}
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
**data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();**
break;
}
}
}
else
{
for (int i = 0; i < items.Count; i++)
{
// in video had this set to -1
if (items[i].ID == -1)
{
items[i] = itemToAdd;
GameObject itemObj = Instantiate(inventoryItem);
itemObj.transform.SetParent(slots[i].transform);
itemObj.transform.position = Vector2.zero;
itemObj.GetComponent<Image>().sprite = itemToAdd.Sprite;
itemObj.name = itemToAdd.Title;
itemObj.name = itemToAdd.Title;
break;
}
}
}
}
bool checkIfItemIsInInventory(Item item)
{
for (int i = 0; i<items.Count; i++)
{
if (items[i].ID == item.ID)
{
return true;
}
}
return false;
}
}
Here is some debugging information. First here is the whole function it is messing up on
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
break;
}
}
}
so when I get to this line while debugging
data.amount++;
the watch says
data ---- null
data.amount ---- Could not find the member 'amount' for 'object'
amount---- the identifier 'amount'is not in the scope
so to break all of this down data is initialized here
ItemData data = slots[i].transform.GetChild(0).GetComponent();
amount is here (this is a different file called ItemData
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemData : MonoBehaviour {
public Item item;
public int amount;
}
thanks everyone for being helpful I really just want to learn how to code better.
Try placing a break point at the block above:
if (items[i].ID == id){
or even higher at:
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd)){
Step through (using F10) and see if it's hitting the line. It's probably jumping out when one of the conditions aren't met. That, or if you need to execute this line through the entirety of the for loop, it's jumping out of the loop when it hits the line the first time because of the break; following.
Either way, step through the lines after the break point, and check the values of everything. Make sure you're not getting null references or values.
Related
I'm creating a tetris-like game, now I'm trying to check if there are 10 blocks left on the line where the block fell (in this case) so I can remove them and add points.
My script runs with a weird delay except for the blocks that just fell (all the other ones that fell earlier counts).
It happens, however, that the sent RaycastAll will correctly catch all blocks in a given line, but it is quite rare and random.
using System.Collections.Generic;
using UnityEngine;
public class CheckBlocksRow : MonoBehaviour
{
public bool check;
public List<float> blocksHeight = new List<float>();
public int blocksInRow;
public GameObject[] caughtBlocks;
public LayerMask layerMask;
RaycastHit[] hittedBlocks;
void Update()
{
if (check)
{
blocksHeight.Sort();
for (int i = 0; i < blocksHeight.Count; i++)
{
if (SendLaser(blocksHeight[i])) CheckBlocks();
}
}
}
bool SendLaser(float heightToCheck)
{
RaycastHit[] hits = Physics.RaycastAll(new Vector2(-5.78f, heightToCheck), transform.TransformDirection(Vector3.right), Mathf.Infinity, layerMask);
blocksInRow = hits.Length;
if (blocksInRow >= 1)
{
hittedBlocks = hits;
return true;
}
else
{
check = false;
return false;
}
}
void CheckBlocks()
{
caughtBlocks = new GameObject[0];
caughtBlocks = new GameObject[hittedBlocks.Length];
for (int i = 0; i < caughtBlocks.Length; i++)
{
caughtBlocks[i] = hittedBlocks[i].transform.gameObject;
}
if (caughtBlocks.Length == 10) print("Full row: " + caughtBlocks[0].transform.position.y);
check = false;
}
}
The script is run as soon as the current block that is about to fall finally falls. Then the script that divides it into smaller parts is run, but it does not run the script of the earlier script.
void SetToCheck()
{
CheckBlocksRow _checkBlocksRow = GameObject.FindGameObjectWithTag("Board").transform.GetChild(1).GetComponent<CheckBlocksRow>();
_checkBlocksRow.blocksHeight.Clear();
for (int i = 0; i < transform.childCount; i++)
{
if(transform.GetChild(i).name != "Center") _checkBlocksRow.blocksHeight.Add(transform.GetChild(i).transform.position.y);
}
_checkBlocksRow.check = true;
}
In this picture, the script on the first line only caught 8 blocks out of the 10 that are there.
I managed to maybe not remove the error but make everything work with a slight delay.
private IEnumerator Delay()
{
print("before");
yield return new WaitForSeconds(0.1f);
print("after");
for (int i = 0; i < blocksHeight.Count; i++)
{
if (SendLaser(blocksHeight[i])) CheckBlocks();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PickupObjects))]
public class PickupObjectsEditor : Editor
{
private static List<GameObject> pickeditems = new List<GameObject>();
private static bool picked = false;
private SerializedProperty _serializedpickeditems;
[MenuItem("GameObject/Generate as Pickup Item", false, 30)]
public static void GeneratePickupItems()
{
if (Selection.gameObjects.Length > 0)
{
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
if (Selection.gameObjects[i].GetComponent<TestScript>() == null)
{
Selection.gameObjects[i].AddComponent<BoxCollider>();
Selection.gameObjects[i].AddComponent<TestScript>();
}
Selection.gameObjects[i].layer = 9;
Selection.gameObjects[i].tag = "Pickup Item";
pickeditems.Add(Selection.gameObjects[i]);
}
picked = true;
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
PickupObjects myTarget = (PickupObjects)target;
DrawDefaultInspector();
if (picked == true)
{
for (int i = 0; i < pickeditems.Count; i++)
{
myTarget.pickUpObjects.Add(pickeditems[i]);
var item = _serializedpickeditems.GetArrayElementAtIndex(i);
var serializedItem = new SerializedObject(item.objectReferenceValue);
serializedItem.Update();
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + item.name));
serializedItem.ApplyModifiedProperties();
}
pickeditems.Clear();
picked = false;
serializedObject.ApplyModifiedProperties();
}
}
private void OnEnable()
{
_serializedpickeditems = serializedObject.FindProperty("pickUpObjects");
}
}
And the mono script
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class PickupObjects : MonoBehaviour
{
public List<GameObject> pickUpObjects = new List<GameObject>();
}
I tried to use serialize and PropertyField but still it's showing the List with Element0,Element1,Element2.... And I want it to be :
Picked Item Box
Picked Item Can
Picked Item Cube
Picked Item Dock_Pod
Your
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + item.name));
sits within a code block that is only executed once.
What you see currently is actually only the list drawn by
DrawDefaultInspector();
since the rest is disappeared after 1 frame/draw call.
You would rather want to separate the pick "method" from the drawing like e.g.
public override void OnInspectorGUI()
{
serializedObject.Update();
if (picked)
{
for (var i = 0; i < pickeditems.Count; i++)
{
// NOTE: Never mix serializedProperties and direct access/modifications on the target!
// This messes up the marking dirty and saving these changes!
// Rather always go through the SerializedProperties so the editor handles everything automatically
_serializedpickeditems.arraySize++;
_serializedpickeditems.GetArrayElementAtIndex(i).objectReferenceValue = pickeditems[i];
}
picked = false;
pickeditems.Clear();
}
for (var i = 0; i < _serializedpickeditems.arraySize; i++)
{
var item = _serializedpickeditems.GetArrayElementAtIndex(i);
// little bonus from me: Color the field if the value is null ;)
var color = GUI.color;
if(!item.objectReferenceValue) GUI.color = Color.red;
{
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " " + (item.objectReferenceValue ? item.objectReferenceValue.name : "null")));
}
GUI.color = color;
// The only case you would need to go deeper here and use
// your new SerializedObject would be if you actually make changes
// to these objects/components like e.g. directly allow to edit their name
}
serializedObject.ApplyModifiedProperties();
}
Note you should also clear the pickeditems list before adding new items:
[MenuItem("GameObject/Generate as Pickup Item", false, 30)]
public static void GeneratePickupItems()
{
if (Selection.gameObjects.Length > 0)
{
pickeditems.Clear();
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
if (Selection.gameObjects[i].GetComponent<Whilefun.FPEKit.FPEInteractablePickupScript>() == null)
{
Selection.gameObjects[i].AddComponent<BoxCollider>();
Selection.gameObjects[i].AddComponent<Whilefun.FPEKit.FPEInteractablePickupScript>();
}
Selection.gameObjects[i].layer = 9;
Selection.gameObjects[i].tag = "Pickup Item";
pickeditems.Add(Selection.gameObjects[i]);
}
picked = true;
}
}
In general I always recommend to use a ReorderableList!
It's a bit tricky at first to get into it but as soon as you have set it up it is an amazing tool. even if you don't make it actually reorderable it is still a huge advantage to be e.g. able to dynamically remove an item from the middle ;)
I am currently unable to print all elements of my array. I am trying to use a nested for loop to print and number all elements but I can only print one element over and over again which is the last element that I click in the game.
The first 7 elements printed are all the same and the last element is left completely blank.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PickUpItem : MonoBehaviour, IInteractable
{
public string DisplaySprite;
public string DisplayImage;
public int counter;
public int j;
public string[] ChoosenItems = new string[7];
private GameObject InventorySlots;
public void Interact(DisplayImage currentDisplay)
{
ItemPickUp();
}
void Start()
{
}
void Update()
{
}
void ItemPickUp()
{
InventorySlots = GameObject.Find("Slots");
counter = 0;
foreach (Transform slot in InventorySlots.transform)
{
if (slot.transform.GetChild(0).GetComponent<Image>().sprite.name == "empty_item")
{
slot.transform.GetChild(0).GetComponent<Image>().sprite =
Resources.Load<Sprite>("Inventory Items/" + DisplaySprite);
Destroy(gameObject);
break;
}
if (counter <= 7)
{
ChoosenItems[counter] = (gameObject.name);
counter++;
if (counter >= 7)
{
Debug.Log("You have choosen 8 itmes, would you like to continue or retry?");
for (j = 0; j <= 7; j++)
{
Debug.LogFormat("Element[{0}] = {1}", j, ChoosenItems[j]);
}
}
}
}
}
}
//http://csharp.net-informations.com/collection/list.html
You are declaring your ChoosenItems = new string[7] with 7 element, ChoosenItems[0], through ChoosenItems[6]. As you can see, this is only 7 items. Thus, you will never have 8 items. Try declaring it as ChoosenItems = new string[8] to have it contain 8 items. See Mozilla Developer Docs for more information.
Hope this helps!
This question already has an answer here:
Unity Crashing on Play, most likely infinite While loop, but cannot locate issue
(1 answer)
Closed 5 years ago.
So I am very nooby with C#. I am currently following a tutorial to make a memory game (https://www.youtube.com/watch?v=prfzIpNhQMM).
I have followed it all and managed to fix all problems I ran into until now; every time I click play, Unity freezes. There a few comments on the videos of people having the same issues with the creator saying that its probably due to an infinite loop in the code. I do not have enough knowledge to enable me to recognise what one of those would be.
I know that the issue is with my GameManager script. If someone could take a look over it and see if they can find my issue, I would be most grateful:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;
using System.Collections.Generic;
public class GameManager : MonoBehaviour {
public Sprite[] cardFace;
public Sprite cardBack;
public GameObject[] cards;
public Text matchText;
private bool _init = false;
private int _matches = 6;
// Update is called once per frame
void Update () {
if (!_init)
initializeCards ();
if (Input.GetMouseButtonUp(0))
checkCards();
}
void initializeCards()
{
for(int id = 0; id < 2; id++)
{
for(int i = 1; i < 6; i++)
{
bool test = false;
int choice = 0;
while (!test) {
choice = Random.Range(0, cards.Length);
test = !(cards[choice].GetComponent<Card>().initialized);
}
cards[choice].GetComponent<Card>().cardValue = i;
cards[choice].GetComponent<Card>().initialized = true;
}
}
foreach (GameObject c in cards)
c.GetComponent<Card>().setupGraphics();
if (!_init)
_init = true;
}
public Sprite getCardBack()
{
return cardBack;
}
public Sprite getCardFace(int i)
{
return cardFace[i - 1];
}
void checkCards()
{
List<int> c = new List<int>();
for(int i = 0; i < cards.Length; i++)
{
if (cards[i].GetComponent<Card>().state == 1)
c.Add(i);
}
if (c.Count == 2)
cardComparison(c);
}
void cardComparison(List<int> c)
{
Card.DO_NOT = true;
int x = 0;
if(cards[c[0]].GetComponent<Card>().cardValue == cards[c[1]].GetComponent<Card> ().cardValue)
{
x = 2;
_matches--;
matchText.text = "Number of Matches: " + _matches;
if (_matches == 0)
SceneManager.LoadScene("VirusInfo3");
}
for(int i = 0; i < c.Count; i++)
{
cards[c[i]].GetComponent<Card>().state = x;
cards[c[i]].GetComponent<Card>().falseCheck();
}
}
}
Thanks!
The only code part where I think something could lead to an infinite loop in your code is the following:
while (!test) {
choice = Random.Range(0, cards.Length);
test = !(cards[choice].GetComponent<Card>().initialized);
}
The problem of this section is that, if all your cards are initialized (so initialized being equal to true), your test variable will always be equal to false. So you will end up with while(!test) being while(true) everytime, leading to the infinite loop.
Add a way to not enter this while section or to exit it if such a case happens, and you should be done.
as I know that to get the top most parent is transform.root .How about if I want to get the bottom most child and in the condition that I don't know the child name?I have try
for (int i=0; i<theParent.childCount; i++)
{
int bottommost=theParent.childCount-1;
Debug.Log(theParent.GetChild(bottommost).name);
}
But is not the result I expect,I just get the first child but I want the bottom most one.I mean the bottom most child in a hierarchy by the way.Is that any tips to get the most bottom child?Thanks.
lastChild = transform.GetChild(transform.childCount - 1);
First you would need to define what is the bottom child, since the depth of the children can depend also on the siblings.
for example:
Root
child_1
child_2
child_3
But then each of the children can have their own children
Child_1
Child_1A
Child_1B
Child_2
Child_2A
Child_2B
Child_2C
Child_2D
Child_2E
So in this case al of the childrens' children are at the same level.
Maybe you are refering to the bottom most as seen in the editor?
In that case maybe add something to the nae in order to search for it...
If you want the child object itself rather than only the transform:
int lastChildIndex = parentObject.transform.childCount - 1;
lastChildObject = parentObject.transform.GetChild(lastChildIndex).gameObject;
I recently had this issue, where I was making a Klondike solitaire game with stacks of cards. I knew that they would all only have 1 child and I wanted to grab the bottom most child of 1 column and move that child to another column. Unfortunately, all of the other answers here simply speculate on if this should be done rather than providing a method to solve the problem. So, I created a method to grab children. Please note that this will work best when used with a stack of single children.
/**
* returns parent if there are no children
*/
private GameObject GetLastChild(GameObject parent)
{
//the parent object
GameObject lastChild = parent;
/*
Initialize a checkChild element
while check child is not null, continue checking
assign the checkChild to its child
*/
for (GameObject checkChild = parent; checkChild != null; checkChild = checkChild.transform.GetChild(0).gameObject)
{
lastChild = checkChild;
if (checkChild.transform.childCount == 0)
{
break;
}
}
return lastChild;
}
Any child could have some children and it can continues interminably. #Caribenz mentioned this. So I propose the code below.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChildGetter : MonoBehaviour
{
private List<DepthLevelHandler> _allChildren = new List<DepthLevelHandler>();
private List<DepthLevelHandler> _allChildrenAlternative = new List<DepthLevelHandler>();
private DepthLevelHandler _deepest;
private GameObject _mostBottomDeepest;
private DepthLevelHandler _deepestMostBottom;
private IEnumerator GetDeepestChild(Action callBack = null)
{
for (int i = 0; i < transform.childCount; i++)
{
DepthLevelHandler dLH = new DepthLevelHandler
{
depthLevel = 1,
gameObject = transform.GetChild(i).gameObject
};
_allChildren.Add(dLH);
}
_allChildrenAlternative = _allChildren;
for (int i = 0; i < transform.childCount; i++)
{
StartCoroutine(AddAllChildrenToList(_allChildren[i]));
yield return new WaitForSeconds(1f);
}
_deepest = _allChildren[0];
foreach (var child in _allChildren)
{
if (child.depthLevel > _deepest.depthLevel)
{
_deepest = child;
}
}
Debug.Log("Name of deepest child : "+_deepest.gameObject.name);
callBack?.Invoke();
}
private IEnumerator AddAllChildrenToList(DepthLevelHandler parent)
{
if (parent.gameObject.transform.childCount == 0) yield break;
for (int i = 0; i < parent.gameObject.transform.childCount; i++)
{
GameObject newChild = parent.gameObject.transform.GetChild(i).gameObject;
int newDepthLevel = parent.depthLevel + 1;
DepthLevelHandler newDepthHandler = new DepthLevelHandler
{
depthLevel = newDepthLevel,
gameObject = newChild
};
if (!_allChildrenAlternative.Contains(newDepthHandler))
{
_allChildrenAlternative.Add(newDepthHandler);
if (newDepthHandler.gameObject.transform.childCount != 0)
{
yield return new WaitForSeconds(0.01f);
yield return AddAllChildrenToList(newDepthHandler);
}
}
}
}
private void GetDeepestMostBottomChild()
{
List<DepthLevelHandler> allChildrenWithSameDepthLevel = new List<DepthLevelHandler>();
allChildrenWithSameDepthLevel.Add(_deepest);
_deepestMostBottom = _deepest;
foreach (var child in _allChildrenAlternative)
{
if (child.depthLevel == _deepest.depthLevel)
{
allChildrenWithSameDepthLevel.Add(child);
}
}
foreach (var child in allChildrenWithSameDepthLevel)
{
if (child.gameObject.transform.GetSiblingIndex() > _deepestMostBottom.gameObject.transform.GetSiblingIndex())
{
_deepestMostBottom = child;
}
}
Debug.Log("name of deepest most bottom child : "+_deepestMostBottom.gameObject.name);
}
private IEnumerator GetMostBottomDeepestChild()
{
Transform mostBottom = transform;
while (mostBottom.childCount != 0)
{
yield return new WaitForSeconds(0.02f);
mostBottom = mostBottom.GetChild(mostBottom.childCount - 1);
}
_mostBottomDeepest = mostBottom.gameObject;
Debug.Log("name of most bottom deepest child : "+_mostBottomDeepest.gameObject.name);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.A)) StartCoroutine(GetDeepestChild(GetDeepestMostBottomChild));
if (Input.GetKeyDown(KeyCode.B)) StartCoroutine(GetDeepestChild());
if (Input.GetKeyDown(KeyCode.C)) StartCoroutine(GetMostBottomDeepestChild());
}
}
public class DepthLevelHandler
{
public int depthLevel = 0;
public GameObject gameObject = null;
}
Create a list of all the children under the parent's transform.
List<GameObject> list = new List<GameObject>();
foreach(Transform t in transform)
{
list.Add(t.gameObject);
if(t.childCount > 0)
foreach(Transform c in t)
list.Add(c);
}
The last element is the last child:
GameObject lastChild = list[list.Count-1];