Transform[] drawns = GetComponentsInChildren<Transform>()
This include also the parent but i want to get only the children of the transform the script is connected.
The problem is that it's destroying in the loop also the parent.
The first item in the array of drawns is the parent :
case DrawStates.DrawOnGizmosRuntime:
drawOnce = true;
if (line != null && drawOnGizmos == false)
{
Transform[] drawns = GetComponentsInChildren<Transform>();
if (drawns.Length > 0)
{
foreach (Transform drawn in drawns)
{
Destroy(drawn.gameObject);
}
}
}
if (boxCollider == null)
{
boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>();
}
drawOnGizmos = true;
break;
There are actually several ways to find children without parent.
foreach (var child in children) Debug.Log(child);
Use Where extension:
After using system.linq, you can separate children that are not the original transform, as shown below.
var children = transform.GetComponentsInChildren<Transform>().Where(t => t != transform);
Removing index 0:
Since index 0 is always the main transform, you can delete it after converting children to list.
var children = transform.GetComponentsInChildren<Transform>().ToList();
children.RemoveAt(0);
Using Skip(1)
Thanks to dear #Enigmativity, Another solution is to use Skip(1), which actually avoids the main transform member.
var children = transform.GetComponentsInChildren<Transform>().Skip(1);
Related
I have been running into an interesting error while building a "search optimized" BST (each node has an access count identifier). I can successfully move nodes up the tree however, some nodes do not move up the tree as many times as expected.
For example:
When I call my find method on value 1 the tree successfully adjusts and 1 becomes the root. Subsequently when I call the method on 4 the node moves up to be the right child of the root 1.
However, when I call on 5 or 6 the node only rotates upwards once, then the optimization ends.
Here is my code that optimizes the tree, it is called on the root of the tree after a node is found using the find method:
private void RecOptimize(TreeNode<T> currentNode, TreeNode<T> prevNode)
{
if (currentNode == null) return;
RecOptimize(currentNode.left, currentNode);
RecOptimize(currentNode.right, currentNode);
var left = currentNode.left;
var right = currentNode.right;
var oldNode = currentNode;
if(right != null)
{
if (right.iAccessCount > currentNode.iAccessCount)
{
currentNode = MakeRightRoot(currentNode);
}
}
if(left != null)
{
if (left.iAccessCount > currentNode.iAccessCount)
{
currentNode = MakeLeftRoot(currentNode);
}
}
if(prevNode != null)
{
if(prevNode.left != null && prevNode.left.Equals(oldNode))
{
prevNode.left = currentNode;
}
if(prevNode.right != null && prevNode.right.Equals(oldNode))
{
prevNode.right = currentNode;
}
}
else
{
this.rootNode = currentNode;
}
}
Edit Thought I would clear something up.
MakeLeftRoot(node) and MakeRightRoot(node) simply perform a rotation about the node, MakeLeftRoot rotates the nodes left node and returns it. While MakeRightRoot does the same but to the right side.
Edit 2 Heres the code for the rotation methods
protected TreeNode<T> MakeRightRoot(TreeNode<T> oldRoot)
{
var newRoot = oldRoot.right;
oldRoot.right = newRoot.left;
newRoot.left = oldRoot;
return newRoot;
}
protected TreeNode<T> MakeLeftRoot(TreeNode<T> oldRoot)
{
var newRoot = oldRoot.left;
oldRoot.left = newRoot.right;
newRoot.right = oldRoot;
return newRoot;
}
I will assume all your other code is correct, including the functions that perform rotations. Then there is still this issue in RecOptimize:
If currentNode changes by the call of MakeRightRoot(currentNode), then the left and right variables no longer represent the left and right nodes of currentNode and so the next if block will not be doing what is intended, as at that moment left is a grandchild of currentNode.
I understand why you also pass prevNode to this function, and how the if(prevNode != null) block is dealing with setting the link with prevNode and the potentially changed reference for currentNode, but it will be much easier if you use the same coding pattern as for your rotation functions: let the function return the potentially changed reference for currentNode, and let the caller be responsible for attaching that node back to its parent node.
Also, you should determine whether it is possible that a higher access count occurs in both subtrees of a node or not. If it cannot occur (because after each access you call this function), then you may want to use an if ...else if construct for the two first blocks. If however it could occur, then think how you want the tree to get rotated. I believe it is better to then to deal with each subtree separately: first finish the work for one subtree completely (recursive call + potential rotation), and then only deal with the other subtree (recursive call + potential rotation).
Here is the suggested code:
private TreeNode<T> RecOptimize(TreeNode<T> node) {
if (node == null) return null;
if (node.right != null) {
node = RecOptimize(node.right);
if (node.right.iAccessCount > node.iAccessCount) {
node = MakeRightRoot(node);
}
}
if (node.left != null) {
node = RecOptimize(node.left);
if (node.left.iAccessCount > node.iAccessCount) {
node = MakeLeftRoot(node);
}
}
return node;
}
Main call:
this.rootNode = RecOptimize(this.rootNode);
One more comment: RecOptimize is going to visit every node in the tree. This is not optimal. If you access a node through binary search, then you should only have to check for rotations along the path to that found node, as a "bubble up" phase.
This is a game based on certain elemental combos, I'm trying to find a way to have two elements compare to each other in order to reduce hard coding. I do have a working solution provided at the end, but I'm trying to learn any ways that would be more simple, straight forward, and readable.
//Wizard is a class
Wizard player1;
Wizard player2;
player1.health = 3;
player2.health = 3;
//Elements is an enum that allows fire/water/air to be selected
player1.elementSelected = Elements.fire;
player2.elementSelected = Elements.water;
Wizard[] bothPlayers = { player1, player2 };
I want to search for the active element in bothPlayers so I can effect the Player's health. I know this doesn't work, but I was wondering if I could do something like:
if (bothPlayers.Contains(Wizard.elementSelected.Elements.fire) && bothPlayers.Contains(Wizard.elementSelected.Elements.water))
Alternatively I was thinking of just setting it to a new array but that wont let me call back to effect player health unless I set them up to a new variable like:
Elements[] bothSelectedElements = { player1.elementSelected, player2.elementSelected };
if (bothSelectedElements.Contains(Elements.fire) && bothSelectedElements.Contains(Elements.water))
{
Wizard playerWithFire; // = player who selected fire. Can't set without hardcoding
Wizard playerWithWater; // = player who selected water. Can't set without hardcoding
playerWithFire.health--;
playerWithWater.waterStrength++;
}
//CURRENT WORKING SOLUTION
//set each Wizard container to null at the start of each check
Wizard fire = null;
Wizard water = null;
Wizard air = null;
//add check to make sure same elements aren't selected. Then assign the players to the containers
foreach (Wizard player in bothPlayers)
{
if (player.elementSelected == Elements.fire)
{
fire = player;
}
if (player.elementSelected == Elements.water)
{
water = player;
}
if (player.elementSelected == Elements.air)
{
air = player;
}
}
//then do the actual check
if (fire != null && water != null)
{
fire.health--;
water.waterStrength++;
}
//repeat with other if statement comparisons
This is a fiddly problem, but thankfully you are working with a language that has all the tools you need to hide the complexity away.
var elementWizards = wizards.GroupBy(w => w.elementSelected).ToDictionary(g => g.Key);
var elements = elementWizards.Keys.ToHashSet(); // gives us access to SetEquals
if (elements.SetEquals(new[] { Elements.Fire, Elements.Water }))
{
foreach (var wizard in elementWizards[Elements.Fire]) wizard.health--;
foreach (var wizard in elementWizards[Elements.Water]) wizard.waterStrength++;
}
else if (elements.SetEquals(new[] { Elements.Earth, Elements.Fire }))
{
// more effects...
}
Note that SetEquals doesn't care about the order of items, so you don't need to worry about handling water/fire instead of fire/water.
Footnote: in the real world I would define some static HashSet<Element> objects and call if(foo.SetEquals(elementWizards.Keys)), but I kept things simple for this answer.
Solution with Linq:
Check if there are water and fire wizard (with Linq)
Foreach to search and add health etc...
if (bothPlayers.Count(wizard => wizard.elementSelected == Elements.fire) > 0 &&
bothPlayers.Count(wizard => wizard.elementSelected == Elements.water) > 0)
{
bothPlayers.ForEach(wizard =>
{
var _ = wizard.elementSelected == Elements.water ? wizard.waterStrength++ :
wizard.elementSelected == Elements.fire ? wizard.health-- : 1;
});
}
you can change the wizards you are searching for and the properties that depends on it
There are multiple ways of finding item in a collection. For example :
// using Array.Find method
Wizard fire = Array.Find(bothPlayers, player => player.elementSelected == Elements.fire);
// or using System.Linq FirstOrDefault extension
Wizard water = bothPlayers.FirstOrDefault(player => player.elementSelected == Elements.water);
if (fire != null && water != null)
{
fire.health--;
water.waterStrength++;
}
For more than two players, a Lookup can be used to separate the players into groups :
var lookup = bothPlayers.ToLookup(player => player.elementSelected);
if (lookup.Contains(Elements.fire) && lookup.Contains(Elements.water))
{
foreach (Wizard fire in lookup[Elements.fire] ) fire.health--;
foreach (Wizard water in lookup[Elements.water]) water.waterStrength++;
}
I'm using a Linked List for turn management in my game. I have Players which I iterate through them, however when one player completes the game he needs to be skipped over, which is where I'm failing at.
How can I do this? here is what I have right now:
public Player GetNextPlayer() {
var current = linkedPlayerList.Find(currentPlayer);
Player nextPlayer = current.Next == null ? linkedPlayerList.First.Value : current.Next.Value;
SetCurrentPlayer(nextPlayer);
return nextPlayer;
}
I tried the following but it doesn't work.
Player nextPlayer = current.Next == null
? linkedPlayerList.First(f => !f.RoundCompleted)
: current.Next.List.Where(w=>!w.RoundCompleted).ElementAt(linkedPlayerList.ToList().IndexOf(currentPlayer));
I would use a loop to check for your condition. Comments in code to explain.
LinkedList<Player> linkedPlayerList = ......;
Player currentPlayer = .......;
public Player GetNextPlayer()
{
// Find the current node
var curNode = linkedPlayerList.Find(currentPlayer);
// Point to the next
LinkedListNode<Player> nextNode = curNode.Next;
// Check if at the end of the list
nextNode = nextNode == null ? linkedPlayerList.First : nextNode;
// Loop stops when we find the condition true or we reach the starting point
while (curNode != nextNode)
{
// Exit if found....
if (!nextNode.Value.RoundCompleted)
break;
// Manage the next node to check for....
nextNode = nextNode?.Next == null ? linkedPlayerList.First : nextNode.Next;
}
SetCurrentPlayer(nextNode.Value);
return nextNode.Value;
}
You can do something like this (add an an extra null check on next):
Player nextPlayer = current.Next.List.FirstOrDefault (x => !.RoundCompleted);
I have two trees like these:
I want to insert the second tree into the first tree in a node that has the same name of its root and attach the children of this node to the left most child of the second tree.
I tried:
PTree attachPoint = chain.Find(x => x.Val == RTree.Val);
if (attachPoint != null)
{
foreach (var c in attachPoint.Childs)
{
RTree.Left.Childs.Add(c);
}
attachPoint = RTree;
}
else
{
RTree.Left.Childs.Add(root);
root = RTree;
}
Here, RTree is the second tree and root points to the root of first tree and chain holds the branch from root "A" to "D". But it seems the desired tree is not built. Did i do it correctly?
It would have been easier to help if you have included the essential parts of your PTree class. Here is what I could suggest you based on the posted code:
PTree attachPoint = chain.Find(x => x.Val == RTree.Val);
if (attachPoint != null)
{
foreach (var c in attachPoint.Childs)
{
RTree.Left.Childs.Add(c);
}
// Here you either have to find the attachPoint parent and
// replace attachPoint with RTree in parent.Childs,
// or make attachPoint.Childs to be RTree.Childs as below
attachPoint.Childs.Clear();
foreach (var c in RTree.Childs)
{
attachPoint.Childs.Add(c);
}
}
else
{
RTree.Left.Childs.Add(root);
root = RTree;
}
attachPoint (and root?) are just local variables so attachPoint = RTree; will not affect the structure of the tree. You need to search the left tree to find the parent node of the insert point and then modify the parent node so that parent.Right = attachPoint;
I have this list of 2000+ categories that need to be organized in a tree before being sent to the controller and the View so the javascript plugin can render them correctly.
I am already doing this but the performance is terrible. It is taking like 30 seconds to assemble the tree.
I can't see what is dropping performance here. Can you guys help me to improve this code?
var allCategories = dal.Listar();
List<Model.Entity.CategoriaCursoEADVO> nestedCategories = new List<Model.Entity.CategoriaCursoEADVO>();
foreach (Model.Entity.CategoriaCursoEAD item in allCategories)
{
if (item.IdCategoriaPai == null)
{
CategoriaCursoEADVO child = new CategoriaCursoEADVO();
child.id = item.Id;
child.text = item.Descricao;
nestedCategories.Add(child);
FillChild(allCategories, child, item.Id);
}
}
And here is the FillChild method:
public int FillChild(IEnumerable<CategoriaCursoEAD> categorias, CategoriaCursoEADVO parent, int IID)
{
var childCategories = categorias.Where(w => w.IdCategoriaPai.Equals(IID));
parent.children = new List<CategoriaCursoEADVO>();
if (childCategories.Count() > 0)
{
foreach (CategoriaCursoEAD cat in childCategories)
{
CategoriaCursoEADVO child = new CategoriaCursoEADVO();
child.id = cat.Id;
child.text = cat.Descricao;
parent.children.Add(child);
FillChild(categorias, child, cat.Id);
}
return 0;
}
else
{
return 0;
}
}
I think the problem is with the new instances and tried using Parallel loops with no satisfatory level of improvement.
This is a pretty good time to use a HashTable (Dictionary). Something like the below code should help.
// Convert the flat list into a hash table with the ID
// of the element as the key
var dict = allCategories.ToDictionary (i => i.Id);
// Group categories by the parent id
var parentGrouping = allCategories.Where(c => c.IdCategoriaPai != null).GroupBy(c => c.ParentId);
// Since we group the items by parent id, we can find
// the parent by id in the dictionary and add the children
// that have that particular id.
foreach(var groupItem in parentGrouping)
if(groupItem.Key != null)
dict[(int)groupItem.Key].children.AddRange(groupItem);
// Get the root elements.
var hierarchicalCategories = allCategories.Where(item => item.IdCategoriaPai == null);
// Do what you need to do here.
This code will create a tree of categories. hierarchicalCategories will contain direct references to the root elements (categories that do not have a parent), assuming that your data is structured that way.