Add instance elements to an instantiated parent - c#

I'm working with four prefabs, "Elements_" is the parent, content_image, content_tittle and content_Options are the children. Inside a for I am adding a number at the end of your name to put them inside the father. Any idea how to do it?

If you want Content_image_1, Content_tittle_1 and Content_Options_1 to be children of elements_1, you can do so:
public GameObject[] Elements;
public GameObject aux;
public int numberofchildren=3;
//...
//instances yours objects
//...
public void makeChildren()
{
for (int i = 0; i < numberofchildren; i++)
{
aux = GameObject.FindGameObjectWithTag("Content_image_" + i);
aux.transform.parent = Elements[i].transform;
aux = GameObject.FindGameObjectWithTag("Content_tittle_" + i);
aux.transform.parent = Elements[i].transform;
aux = GameObject.FindGameObjectWithTag("Content_Options_" + i);
aux.transform.parent = Elements[i].transform;
}
}
}
If you want to undo this action you can do this:
public void undoChildren(){
for(int i=0;i<numberofchildren;i++){
Elements[i].transform.DetachChildren();
}
}
Remember to add the tags to each prefab
I hope it helps you.

Related

Changes made to Scriptable Object are lost even after calling EditorUtility.SetDirty()

I have a script called HandSkeleton for which I wrote a custom editor. HandSkeleton has a field of type HandSkeletonData which is a ScriptableObject. I wrote a custome inspector which can save some values from HandSkeleton to the HandSkeletonData it's referencing.
Here is the code:
[CustomEditor(typeof(HandSkeleton))]
public class HandSkeletonEditor : UnityEditor.Editor
{
private const string BasePath = "Assets/ScriptableObjects/HandSkeletonData";
public override void OnInspectorGUI()
{
DrawDefaultInspector();
var handSkeleton = (HandSkeleton)target;
if (GUILayout.Button("Save hand skeleton data"))
{
var handSkeletonTransform = handSkeleton.transform;
var rootName = handSkeletonTransform.root.name;
var parentName = handSkeletonTransform.parent.name;
var skeletonName = handSkeletonTransform.name;
if (handSkeleton.handSkeletonData == null)
{
var so = CreateInstance<HandSkeletonData>();
if (!AssetDatabase.IsValidFolder(BasePath + $"/{rootName}"))
AssetDatabase.CreateFolder(BasePath, rootName);
AssetDatabase.CreateAsset(so, $"{BasePath}/{rootName}/{parentName}_{skeletonName}.asset");
serializedObject.FindProperty(nameof(handSkeleton.handSkeletonData))
.objectReferenceValue = so;
serializedObject.ApplyModifiedProperties();
}
var rotations = handSkeleton.EditorInitialized.GetAllFingerSegmentRotations()
.Select(x => (Quaternion[])x.Clone())
.ToArray();
// here I change the Scriptable Object
handSkeleton.handSkeletonData.FingerSegmentRotations = rotations;
EditorUtility.SetDirty(handSkeleton.handSkeletonData);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
The issue is that the changes made to handSkeletonData are lost after I refresh the editor. I found many other posts where people had the same issue but it was mostly caused by not calling EditorUtility.SetDirty(). Am I calling it wrong? Or is there something else I'm doing wrong?
Turns out the issue wasn't the way I was saving the Scriptable Object, but rather what was saved inside it - a 2D array of Quaternions. Unity simply doesn't serialize 2D arrays. To fix this I changed the 2D array to a 1D array and am accessing it through a property. If anyone is interested here is the code for this:
[HideInInspector] [SerializeField] private Quaternion[] fingerSegmentRotations;
public Quaternion[][] FingerSegmentRotations
{
get
{
if (fingerSegmentRotations == null || fingerSegmentRotations.Length == 0) return null;
var rotations = new Quaternion[HandSkeleton.FingerCount][];
for (var i = 0; i < HandSkeleton.FingerCount; i++)
{
rotations[i] = new Quaternion[HandSkeleton.SegmentCount];
for (var j = 0; j < HandSkeleton.SegmentCount; j++)
rotations[i][j] = fingerSegmentRotations[i * HandSkeleton.SegmentCount + j];
}
return rotations;
}
set
{
fingerSegmentRotations = new Quaternion[HandSkeleton.FingerCount * HandSkeleton.SegmentCount];
for (var i = 0; i < HandSkeleton.FingerCount; i++)
for (var j = 0; j < HandSkeleton.SegmentCount; j++)
fingerSegmentRotations[i * HandSkeleton.SegmentCount + j] = value[i][j];
}
}

Destroy clone prefab

I have a problem. In a scene I have a list and when I click on it shows me with some clones prefab the contents of the database. The point is that once generated, no matter how much I select another item from the list, the prefabs do not update me and show the contents of the first selection. I've tried to destroy them. But the most I've managed to do is destroy the original prefab and don't believe me anymore. How can I fix this?
public Transform contentParent;
public List<TablePlayerTransctData> playersTransct;
public GameObject playerTransactItemPrefab;
private Models.Club _club;
public TextMeshProUGUI memberUuid;
public void Start()
{
GetTablePlayerTransactRequest();
}
private void GetTablePlayerTransactRequest()
{
_club = TablesManager.Instance.club;
new LogEventRequest().SetEventKey("Transactions")
.SetEventAttribute("clubId", _club.id)
.SetEventAttribute("playerId", memberUuid.text)
.Send(GetTablePlayerTransactResponse);
}
private void HandleResults()
{
var cont = 0f;
Vector3 posicion = new Vector3();
foreach (TablePlayerTransctData data in playersTransct)
{
GameObject item = Instantiate(playerTransactItemPrefab, contentParent);
PlayerTransact playerTableTransact = item.GetComponent<PlayerTransact>();
if (cont == 0)
{
posicion = playerTableTransact.transform.position;
}
else
{
posicion.Set(posicion.x, (posicion.y) - 0.5f, posicion.z);
playerTableTransact.transform.position = posicion;
}
playerTableTransact.Setup(data);
cont++;
}
}
private void GetTablePlayerTransactResponse(LogEventResponse response)
{
if (response.HasErrors)
{
return;
}
GSData scriptData = response.ScriptData;
var list = scriptData.GetGSDataList("list");
playersTransct = new List<TablePlayerTransctData>();
foreach (GSData entry in list)
{
TablePlayerTransctData data = new TablePlayerTransctData()
{
chips = GSUtil.GetInt(entry, "chips"),
date = GSUtil.GetDouble(entry, "date"),
};
playersTransct.Add(data);
}
HandleResults();
}
public class TablePlayerTransctData
{
public int chips;
public double date;
}
I think these prefabs should be reset or removed just before they are re-created. But I don't know how to do this or maybe there's another, more correct way to do it.
Edit:
I changed Start to Update and magically works almost as it should. I don't understand what this miracle is like. Since with starting every time you enter you should cool off on your own. Now all that remains is to remove the leftover prefabs which is what I don't finish doing properly.
Before instantiating the new items you could destroy them all like e.g.
// General hint: Make this of the correct type
// - Makes sure that the referenced prefab actually HAS that component
// - Gets rid of some GetComponent calls later since Instantiate already
// returns the same type as the given prefab has
public PlayerTransact playerTransactItemPrefab;
foreach(Transform child in contentParent)
{
Destroy(child.gameObject);
}
foreach (var data in playersTransct)
{
PlayerTransact item = Instantiate(playerTransactItemPrefab, contentParent);
...
}
Alternatively you could of course only create the amount of objects you need and destroy others and instead of destroying and recreating all of them reuse those that already exists like e.g.
// cache the counts
var childCount = contentParent.childCount;
var dataCount = playersTransct.Count;
// we want to iterate the count that is greater
var count = Mathf.Max(childCount, dataCount);
for (var i = 0; i < count; i++)
{
if (i < dataCount)
{
// there is data for this index
var data = playersTransct[i];
// so we need a child for it
PlayerTransact playerTableTransact;
if (i < childCount)
{
// We are still in the range of existing childs -> Re-use a child that already exists
playerTableTransact = contentParent.GetChild(i).GetComponent<PlayerTransact>();
posicion = playerTableTransact.transform.position;
}
else // i < dataCount && i >= childCount
{
// There is still data but no childs left -> create a new one now
playerTableTransact = Instantiate(playerTransactItemPrefab, contentParent);
if (i == 0)
{
posicion = playerTableTransact.transform.position;
}
else
{
posicion -= Vector3.up * 0.5f;
playerTableTransact.transform.position = posicion;
}
}
playerTableTransact.Setup(data);
}
else // i < childCount && i >= dataCount
{
// There are more childs than data -> destroy the unneeded childs
Destroy(contentParent.GetChild(i).gameObject);
}
}

I'm trying to draw list items in the Inspector but it's not working how can I replace the element0,element1....with my own string?

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 ;)

Get bottom most child

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];

Looping through Textboxes to retrieve data, c#

I have a class called "Player" with a constructor that takes 2 strings and an int.
These are declared in Textboxes on a form, with each team (Home H / Away A) having a different name, and each name type (Last L / First F) adding to the textbox's name. therefor giving a unique name such as txtFHome1.
In a foreach loop I want to create a new Player with the details of the textboxes on a page.
This is what I have got so far.
List <Player> HomeTeam = new List<Player>
private void btnAccept_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
for (int i = 0; i < 10; i++)
{
HomeTeam.Add (new Player(c.Name.EndsWith("FHome"+i),c.Name.EndsWith("LHome"+i),c.Name.EndsWith("upDownH"+i)));
}
}
}
any help?
From you post I understand that there are always 3 controls for 11 players, so there is no need to iterate trough all Controls in the form.
private void btnAccept_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var player = new Player(((TextBox)this.Controls.FindControl("FHome" + i)).Text, ((TextBox)this.Controls.FindControl("LHome" + i)).Text, ((NumericUpDown)this.Controls.FindControl("upDownH" + i)).Value);
HomeTeam.Add(player);
}
}
The first way is to use dictionaries with all controls. It is the fastest and easiest way.
Dictionary<int, TextBox> FHome;
Dictionary<int, TextBox> LHome;
Dictionary<int, TextBox> upDownH;
You should add the controls like this:
FHome.Add(0, textBoxLHome0);
FHome.Add(1, textBoxLHome1);
Then in your code you can use
for (int i = 0; i < 10; i++)
{
HomeTeam.Add (new Player(FHome[i].Text, LHome[i].Text, upDownH[i].Text));
}
try this:
Controls.OfType<TextBox>().ToList().ForEach((textbox) =>
{
for (int i = 0; i < 10; i++)
{
textbox.Text = "your text";
}
});
remember to include using System.Linq
I advice you to at lease try an encapsulate the three textboxes into a single UserControl like so:
public partial class ControlPlayerParams : UserControl {
public string Param1 { get { return this.textBox1.Text; } }
public string Param2 { get { return this.textBox2.Text; } }
public string Param3 { get { return this.textBox3.Text; } }
public ControlPlayerParams() {
this.InitializeComponent();
}
}
That way, you could at least do what you wanted to do more fluently and more safely (plus you get to modify just one UserControl in case you need something changed (Validation ??)):
foreach (ControlPlayerParams cpp in this.Controls.OfType<ControlPlayerParams>())
HomeTeam.Add(new Player(cpp.Param1, cpp.Param2, cpp.Param3));
BUT you should rethink the architecture of the app a bit if you ask me.

Categories

Resources