- Home /
Return a gameObject from LINQ .GroupBy()
I've got a list of gameObjects and I want to track the unique objects in the list as well as the count of each unique object. I'm trying to accomplish this by using LINQ and doing two different things: first, return the unique gameObjects as gameObjects and second I need to keep track of the count of each type of gameObject.
More specifically, I've got a list of troops and there are many different types of troops. In the UI I want to keep track of the type of troop and the current number of them on the battlefield like so:
For the image above the gameObject list would be 100 gameObjects where 25 are soldiers, 25 are archers, and 50 are cavalry troops.
My idea is to return the unique gameObjects from that list using .Distinct()
so I know how many different types of troops there are and can .GetComponent
and grab their sprites. Then I need to count how many of each unique game object there are for the troop totals.
Every time a troop is added or removed from the list, a function in the UI manager gets called to update the totals. I'm trying to use LINQ queries and I just can't figure out how to return a gameObject when I use .GroupBy()
.
// BattleManager.cs (singleton)
// Gets updated every time a player troop is spawned or despawned
public List<GameObject> playerTroops;
// UITroopCounter.cs
public GameObject troopCountPrefab;
private int _uniqueTroopCount = 0;
public void UpdateTroopCounts()
{
var _groupedTroops = BattleManager.Instance.playerTroops.Select(g => g.gameObject).GroupBy(g => g.name).ToList();
if (_uniqueTroopCount != _groupedTroops.Count())
{
//Clean slate
foreach (Transform child in transform)
Destroy(child.gameObject);
//Create a troopCounter object for each unique type of troop
foreach (var troop in _groupedTroops)
{
var _newTroopCounter = Instantiate(troopCountPrefab, transform).GetComponent<UITroopCount>();
_newTroopCounter.icon = troop.GetComponent<TroopCore>().icon;
_newTroopCounter.counter = troop.Count();
}
}
//Once these objects are created, I need some way to keep them attached to the count of the type of troop they are
//Troops can be revived and new types of troops can be summoned during the battle as well
}
}
Even though I'm doing .Select(g => g.gameObject)
it comes back in a format I can't use .GetComponent
on. Seriously, if there's a better way please let me know because this is feeling pretty squirrely at this point. Thanks!
Answer by yummy81 · Apr 25, 2020 at 12:17 PM
I modified your code a bit. At first I rewrote _groupTroops. I will do my best to explain it. I will stick to your assumptions: 25 x soldiers, 25 x archers, 50 x cavalry. So, _groupTroops is a List of anonymous objects. The count of the _groupTroops is 3 - one unique troop. Each anonymous object have three fields, it's just like class of this sort:
public class Troop{
public string name;
public int count;
public List<GameObject> gameObject;
}
To iterate through _groupTroops, you have to do something like that (there is string "s". It contains the result):
string s = string.Empty;
for(int i = 0; i < _groupedTroops.Count; i++){
string name = _groupedTroops[i].name;
int count = _groupedTroops[i].count;
s += name + ": " + count + "\n";
for (int j = 0; j < _groupedTroops[i].gameObject.Count; j++){
GameObject go = _groupedTroops[i].gameObject[j];
s += "\t" + go + "\n";
}
}
Ok. Here you have your UITroopCounter class code. I reproduced your project, and when it comes to me, I can guarantee that it works. Enjoy!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class UITroopCounter : MonoBehaviour
{
public GameObject troopCountPrefab;
private int _uniqueTroopCount = 0;
public void UpdateTroopCounts(){
var _groupedTroops =
BattleManager.Instance.playerTroops
.GroupBy(troop => troop.name)
.Select(troop =>
new {
name = troop.Key,
count = troop.Count(),
gameObject = troop
}
)
.Select(troop =>
new{
name = troop.name,
count = troop.count,
gameObject = troop.gameObject.ToList()
}
)
.ToList();
int _groupedTroopsCount = _groupedTroops.Count;
if (_uniqueTroopCount != _groupedTroopsCount){
foreach (Transform child in transform)
Destroy(child.gameObject);
foreach(var troop in _groupedTroops){
var _newTroopCounter = Instantiate<GameObject>(troopCountPrefab, transform).GetComponent<UITroopCount>();
_newTroopCounter.icon = troop.gameObject.First().GetComponent<TroopCore>().icon;
_newTroopCounter.counter = troop.count;
}
}
}
}
Thanks! Your solution totally works, but while working on it I changed it up a bit so the Troop
class contains more info like the transform.position of each troop. The only thing I didn't totally understand is why you needed to use .Select
twice when creating _groupedTroops
?
You are right! I overcomplicated it. I turned it into something cleaner. Works fine:
var _groupedTroops =
Battle$$anonymous$$anager.Instance.playerTroops
.GroupBy(troop => troop.name)
.Select(troop =>
new {
name = troop.Key,
count = troop.Count(),
gameObject = troop.ToList()
}
)
.ToList();
Your answer
Follow this Question
Related Questions
Keep list of GameObjects between scenes 2 Answers
Grab a specific item from a list 3 Answers
C# - How to get only Distinct values (with threshold) and a specific count? 0 Answers
List is created correctly in Start() but returns size 0 in play mode 3 Answers
How to add elements from a List of another class to another List (C#) 1 Answer