Find all children with different tags -Unity - unity3d

I'm trying to get a number of all children with a certain tag from the parent object.
For example if a parent object has 20 children and their tags can switch between two tags, I would want to find out how many have either tag.
I want to do it on a mouse click. I've tried using transform.childCount and FindGameObjectsWithTag() however I'm not having any luck.
Any suggestions or pointers?

public static class Extensions {
public static int ChildCountWithTag(this Transform tr, string tag, bool checkInactive = false) {
int count = 0;
Transform [] trs = tr.GetComponentsInChildren<Transform>(checkInactive);
foreach(Transform t in trs) {
if(t.gameObject.CompareTag(tag) == true) { count++; }
}
return count;
}
}
Then you call it as:
void Start() {
int result = this.transform.ChildCountWithTag("Tag");
int resultB = this.transform.ChildCountWithTag("OtherTag", true);
}
The first returns how many active with the given tag "Tag", the second how many children with the tag "OtherTag", including inactive ones.

Related

How do I increase the array and add a Gameobject to a new field?

I have a certain array that stores chunks of the world, and I need to increase the array through the script and place the chunk in a new field for the chunk. How can this be done?
I wrote this code:
public class WriteInWorld
{
[MenuItem("Assets/Add To World")]
public static void SetAsWmass()
{
GameObject[] selectedWmass = Selection.gameObjects;
Debug.Log(selectedWmass);
for (int i = 0; i < selectedWmass.Length; i++)
{
selectedWmass[i].GetComponent<Wmasss>().Chunk = selectedWmass[i];
Debug.Log(selectedWmass[i]);
}
//world = Find.FindWorldObjectAsBoolArgument(true);
//Debug.Log(world.GetComponent<World>().Chunk.ToList() + ": :" + world);
}
[MenuItem("Assets/Add To World", isValidateFunction:true)]
public static bool SetAsWmassValidator()
{
return Selection.activeObject is Wmasss;
}
}
Usually you have got 2 options:
If you know how many items you will need to store in a container, you can use an array.
If you don't know how many items you will need to store, a List might be what you're looking for.

What's actually happening when I iterate through Transform

Something that has been bothering me for a long time is why do the following lines of code have the same results.
Code 1:
Transform[] childs = gameObject.GetComponentsInChildren<Transform>();
foreach(Transform child in childs) { Debug.Log(child.name); }
Code 2:
foreach(Transform child in gameObject.transform) { Debug.Log(child.name); }
This is actuallly a pseudo-code, I didn't really test it but should be enough to explain.
My question is, what's happening on Code 2 ? Is gameObject.transform actually an array of Transform ? Why doesn't Code 2 print the name of the parent gameObject ?
Maybe this is something very simple and obvious I'm just overlooking but I can't make it out right now.
Transform implements the IEnumerable interface. This interface is what allows the use of the foreach keyword.
public partial class Transform : Component, IEnumerable
The IEnumerable interface requires implementation of the GetEnumerator() method. The enumerator is responsible for keeping track of the position in the underlying collection and indicating if there are more items to be iterated over.
This is implemented in Transform below
public IEnumerator GetEnumerator()
{
return new Transform.Enumerator(this);
}
private class Enumerator : IEnumerator
{
Transform outer;
int currentIndex = -1;
internal Enumerator(Transform outer)
{
this.outer = outer;
}
//*undocumented*
public object Current
{
get { return outer.GetChild(currentIndex); }
}
//*undocumented*
public bool MoveNext()
{
int childCount = outer.childCount;
return ++currentIndex < childCount;
}
//*undocumented*
public void Reset() { currentIndex = -1; }
}

Unity3D editor: Rendering nested elements in sidebar

In Unity, one of my MonoBehaviours has a field pointing to another object (a ScriptableObject). If I double-click that field, I can see the fields of that object. How do I render those fields into the top-level MonoBehaviour's property drawer?
In picture form
What I have
(double-click the element)
What I want
I have my own [CustomEditor] component, but I can't quite get it to work right; stuff like this:
SerializedProperty activityStack = serializedObject.FindProperty("activityStack");
EditorGUILayout.PropertyField(activityStack.GetArrayElementAtIndex(0));
just renders the "Element 0 (Idle Activity)" bit and not the actual contents of the reference.
Because the default PropertyField for a ScriptableObject is just the one you get: A UnityEngine.Object reference field like for GameObject and Components and other assets ;)
Of course you can implement what you want to achieve but that's a bit more complex and not really good for maintenance and I would not recommend it.
I don't know your ScriptableObject so here an example
public class ExampleSO : ScriptableObject
{
public int SomeInt;
[SerializeField] private string _someString;
}
and your MonoBehaviour e.g.
public class Example : MonoBehaviour
{
public List<ExampleSO> _SOList;
}
Then the editor could look like e.g.
using UnityEditor;
using UnityEngine;
// This is the namespace for the ReorderableList
using UnityEditorInternal;
[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
SerializedProperty _SOList;
Example _example;
MonoScript _script;
ReorderableList _list;
private void OnEnable()
{
// Link up the serializedProperty
_SOList = serializedObject.FindProperty("_SOList");
// get the casted target instance (only needed for drawing the script field)
_example = (Example) target;
// get the according script instance (only needed for drawing the script field)
_script = MonoScript.FromMonoBehaviour(_example);
// Set up the ReorderableList
_list = new ReorderableList(serializedObject, _SOList, true, true, true, true)
{
// What shall be displayed as header for the list?
drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, _SOList.displayName),
// How is each element displayed?
drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
// Get the element in the list (SerializedProperty)
var element = _SOList.GetArrayElementAtIndex(index);
// and draw the default object reference field
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, new GUIContent("Reference"));
// Check if an asset is referenced - if not we are done here
if (!element.objectReferenceValue) return;
// Otherwise get the SerializedObject for this asset
var elementSerializedObject = new SerializedObject(element.objectReferenceValue);
// and all the properties (SerializedProperty) of it you want to display
var someInt = elementSerializedObject.FindProperty("SomeInt");
var someString = elementSerializedObject.FindProperty("_someString");
// Similar to the OnInspectorGUI first load the current values into this serializedobject
elementSerializedObject.Update();
{
// Adding some indentation just to show that the following fields are actually belonging to the referenced asset
EditorGUI.indentLevel++;
{
rect = EditorGUI.IndentedRect(rect);
// shift down the rect by one line
rect.y += EditorGUIUtility.singleLineHeight;
// Draw the field for the Int
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), someInt);
// Shift down the rect another line
rect.y += EditorGUIUtility.singleLineHeight;
// Draw the string field
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), someString);
}
EditorGUI.indentLevel--;
}
// Write back the changed values and trigger the checks for logging dirty states and Undo/Redo
elementSerializedObject.ApplyModifiedProperties();
},
// How much vertical space should be reserved for each element?
elementHeightCallback = (int index) =>
{
// Get the elements serialized property
var element = _SOList.GetArrayElementAtIndex(index);
// by default we have only the asset reference -> single line
var lines = 1;
// if the asset is referenced adds space for the additional fields
if (element.objectReferenceValue) lines += 2; // or how many lines you'll need
return lines * EditorGUIUtility.singleLineHeight;
}
};
}
public override void OnInspectorGUI()
{
// draw th script field
DrawScriptField();
// Load the current values into the serializedObject
serializedObject.Update();
{
// let the ReorderableList do its magic
_list.DoLayoutList();
}
// Write back the changed values into the actual instance
serializedObject.ApplyModifiedProperties();
}
// Just draws the usual script field at the top of the Inspector
private void DrawScriptField()
{
EditorGUI.BeginDisabledGroup(true);
{
EditorGUILayout.ObjectField("Script", _script, typeof(Example), false);
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
}
}
Which results in the following Inspector. As you can see I opened the Isnpectors of the MonoBehaviour and two instances of the ExampleSO to show how the values are taken over to the actual instances

dynamically select a checkbox for siblings treenode in Smart GWT

I have a selectable Tree with checkbox appearance. I need to select all sibling TreeNode on selection of a specific TreeNode.
I could get all the sibling tree nodes, but I don't know what is the attribute name of TreeNode to make that checkbox selected.
Can anybody help me giving some way to select those nodes.
compareGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
#Override
public void onSelectionChanged(SelectionEvent event) {
TreeNode node = (TreeNode) event.getSelectedRecord();
TreeNode parent = tree.getParent(node);//tree is Tree object
treeGrid.selectRecord(parent);
TreeNode[] nodes = tree.getAllNodes(parent);
for(int i=0; i< nodes.length; i++){
if(!nodes[i].getAttributeAsBoolean("isSelected"))
treeGrid.selectRecord(nodes[i]);
}
}
}
});
You can use any of the following:
treeGrid.selectAllRecords();
treeGrid.selectRecord(record);
treeGrid.selectRecords(records);
The first method will select all the TreeNodes of the tree.
The 2nd one will select only one specified TreeNodes of the tree.
And the 3rd one will select multiple specified TreeNodes of the tree.
There are multiple overloaded methods for the last 2 methods, which allows you to specify Nodes in terms of, TreeNode(s) itself, or index of the TreeNode(s).
Here's a solution quite close (without checkboxes) to what you need.
employeeTreeGrid.addNodeClickHandler(new NodeClickHandler() {
public void onNodeClick(NodeClickEvent event) {
if (event.getNode() != null) {
TreeNode node = event.getNode();
TreeNode parent = employeeTree.getParent(node);
if (employeeTreeGrid.isSelected(node)) {
List<TreeNode> nodesToSelect = new ArrayList<TreeNode>();
// omit parent (root) if on first level
if (!"1".equals(node.getAttribute("ReportsTo"))) {
nodesToSelect.add(parent);
}
TreeNode[] siblings = employeeTree.getChildren(parent);
nodesToSelect.addAll(Arrays.asList(siblings));
RecordList recordList = employeeTreeGrid.getOriginalRecordList();
for (TreeNode treeNode : nodesToSelect) {
Record record = recordList.find("EmployeeId", treeNode.getAttribute("EmployeeId"));
if (record != null) {
employeeTreeGrid.selectRecord(record);
}
}
}
}
}
});
Have to use the RecordList and first find required records in order to use ListGrid.selectRecord() methods.
Using SelectionAppearance.CHECKBOX and SelectionChangedHandler can be tricky as programmatic selections are going to trigger further selection events.
This is based on Checkbox tree sample with below changes.
// employeeTreeGrid.setSelectionAppearance(SelectionAppearance.CHECKBOX);
// employeeTreeGrid.setShowSelectedStyle(false);
employeeTreeGrid.setShowPartialSelection(false);
// employeeTreeGrid.setCascadeSelection(true);
employeeTreeGrid.setSelectionType(SelectionStyle.SIMPLE);
To get the value of selected checkbox from tree grid in smart gwt I have following solution ListGridRecord[] arrRec = event.getSelection(); sample code is below.
employeeTreeGrid.setSelectionAppearance(SelectionAppearance.CHECKBOX);
employeeTreeGrid.setSelectionType(SelectionStyle.SIMPLE);
employeeTreeGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
#Override
public void onSelectionChanged(SelectionEvent event)
//selectedCounties Set to add selected checkbox or deslected checkbox names/title
if (selectedCounties == null || selectedCounties.size() == 0)
selectedCounties = new TreeSet<String>();
selectedCounties.clear();
ListGridRecord[] arrRec = event.getSelection();
for (ListGridRecord listGridRecord : arrRec) {
selectedCounties.add(listGridRecord.getAttribute("Name"));
}
// You can do iteration over it if needed
selectedCounties.remove("All Counties");
Iterator<String> it = selectedCounties.iterator();
while (it.hasNext()) {
if (it.next().contains("Zone")) {
it.remove();
}
}
}
});

Copying ScriptableObjects

Is there a way of mimicking MonoBehaviour copy semantics in ScriptableObjects?
Say I have a MonoBehaviour like so:
public class DummyClassBehaviour : MonoBehaviour {
public DummyClass DummyClassTest; //ScriptableObject
public DummyClassBehaviour DummyBehaviourTest; //Another DummyClassBehaviour
}
And a ScriptableObject:
public class DummyClass : ScriptableObject {
public string Text = "";
}
When I duplicate(CTRL+D) a GameObject w/ DummyClassBehaviour attached, 'DummyBehaviourTest' copies as you would expect: If it references a MonoBehaviour in the GameObject I'm copying, the copy mechanism updates the reference to the same MonoBehaviour type in the new GameObject. If it references a MonoBehaviour in another GameObject, that reference remains unchanged.
The ScriptableObject, on the other hand, always references the original. So I end up with N GameObject's all sharing the same ScriptableObject (DummyClass) from the original GameObject. I'm using ScriptableObjects to allow serialization of non-Monobehaviour data classes.
As far as I can tell, and please someone correct me if I'm wrong, you cannot modify the serialization behavior of a ScriptableObject to match that of a MonoBehaviour. Namely that it should update references if a duplicate is made.
Instead I opted for a less than optimal solution, but it works. My class is assigned a unique identifier that gets serialized like everything else. I use this ID in DummyBehaviour.Awake() to create a lookup table that I can then use to reassign my DummyClass.
I'm not going to accept my own answer because I don't feel it answers my original question fully, but it's related:
[System.Serializable]
public class DummyClass {
// Unique id is assigned by DummyBehaviour and is unique to the game object
// that DummyBehaviour is attached to.
public int UniqueID = -1;
public string Text = "";
// Override GetHashCode so Dictionary lookups
public override int GetHashCode(){
int hash = 17;
hash = hash * 31 + UniqueID;
return hash;
}
// override equality function, allows dictionary to do comparisons.
public override bool Equals(object obj)
{
if (object.ReferenceEquals(obj, null))return false;
DummyClass item = obj as DummyClass;
return item.UniqueID == this.UniqueID;
}
// Allow checks of the form 'if(dummyClass)'
public static implicit operator bool(DummyClass a)
{
if (object.ReferenceEquals(a, null)) return false;
return (a.UniqueID==-1)?false:true;
}
public static bool operator ==(DummyClass a, DummyClass b)
{
if (object.ReferenceEquals(a, null))
{
return object.ReferenceEquals(b, null);
}
return a.Equals(b);
}
public static bool operator !=(DummyClass a, DummyClass b)
{
if (object.ReferenceEquals(a, null))
{
return object.ReferenceEquals(b, null);
}
return !a.Equals(b);
}
}
And my MonoBehaviour:
[ExecuteInEditMode]
public class DummyBehaviour : MonoBehaviour {
public List<DummyClass> DummyClasses = new List<DummyClass>();
// reassign references based on uniqueid.
void Awake(){
Dictionary<DummyClass,DummyClass> dmap = new Dictionary<DummyClass,DummyClass>();
// iterate over all dummyclasses, reassign references.
for(int i = 0; i < DummyClasses.Count; i++){
DummyClass2 d = DummyClasses[i];
if(dmap.ContainsKey(d)){
DummyClasses[i] = dmap[d];
} else {
dmap[d] = d;
}
}
DummyClasses[0].Text = "All items same";
}
// helper function, for inspector contextmenu, to add more classes from Editor
[ContextMenu ("AddDummy")]
void AddDummy(){
if(DummyClasses.Count==0)DummyClasses.Add(new DummyClass{UniqueID = 1});
else {
// Every item after 0 points to zero, serialization will remove refs during deep copy.
DummyClasses.Add(DummyClasses[0]);
}
UnityEditor.EditorUtility.SetDirty(this);
}
}