- Home /
Can't get method using Generics to populate List
I have the following scenario:
public class Human_Behaviour : MonoBehaviour {
public static List<Human_Behaviour>[,] humanObject;
/*...
...
STUFF HAPPENS HERE THAT POPULATES humanObject
...
...*/
static List<T> CheckCollisionsAtPoint<T>(){
List<T> hitHuman = new List<T>();
if (typeof(T) == typeof(Human_Behaviour)){
hitHuman.Add(humanObject[0,0][0]);
}
else{
hitHuman.Add(humanObject[0,0][0].gameObject);
}
return hitHuman;
}
}
Of course, this gives me the following errors: "Argument #1' cannot convert
Human_Behaviour' expression to type T'" and "Argument
#1' cannot convert UnityEngine.GameObject' expression to type
T'"
I tried to add Constraints to the method declaration:
static List<T> CheckCollisionsAtPoint<T>() where T:GameObject, Human_Behaviour{
}
But was not allowed, since I read C# does not allow multiple inheritance.
I tried to throw a couple of Casts where I believed was reasonable, but wouldn't work either.
So, how would I get the hitHuman.Add lines to work?
WARNING: This code is an extremely simplified example, not code in use in a real application. I'm interested in whether this very particular case can be made to work.
What are you trying to do? As in, what is CheckCollisionsAtPoint supposed to do?
It seems to be that you're trying to use a generic list in a context where you need actual types. I'm suspecting that what you're trying to do should be solved by method overloading, but I'll need to see code where you're using CheckCollisionsAtPoint to be sure.
And is humanObject
supposed to be a list of arrays or an array of lists?
@Baste This is just an extremely simplified example, since I'm actually interested in this very particular form of solving the problem. The "real" code is already working, but using Generics would make it simpler to read and maintain.
@Baste @Nose$$anonymous$$ills: If you're curious, in the real case, humanObject is a matrix that divides a terrain in smaller squares (therefore the [,] need, i.e. the first 100x100 patch of terrain would be assigned [0,0]) and holds a list of all Human_Behaviour within that patch (and that's the 3rd index, and why it has to be a List)
What it does: This is a way to "virtualize" Human_Behaviour character GameObjects, where when they go offscreen, I convert their position to just numbers in a Vector3, and "move" those numbers ins$$anonymous$$d of moving the GameObject with its collider and other stuff attached. In a my mobile game with 200+ characters, this saves a cr*pload of performance.
The real "CheckCollisionsAtPoint(Vector3 point, float radius)" is used as a form of OverlapSphere that returns these "virtual" Humans (pointers to Human_Behaviour) as well as real collisions with Enabled GameObjects (containing Human_Behaviour as a Component) at that point.
So, in the "virtual" collision part of this collision check we get a Human_Behaviour, and in the "real" collision part we get a Collider (where it's easy to get a GameObject).
In several parts of the game, I need to get either Human_Behaviours or GameObjects returned from CheckCollisionsAtPoint depending on what I'll do with it next.
Since converting Human_Behaviour to GameObject wastes a little of performance (need to use, say, "human_BehaviourObject.gameObject") and converting GameObjects to Human_Behaviour wastes a lot more ("human_BehaviourObject.GetComponent"), I wanted to use Generics to avoid this conversion where it's unnecessary (since either the "virtual" or the "real" collision is certain to have the type I want).
Sorry the reply is so long... but you DID ask :) Thanks for your replies.
So, the reason I asked is because the method takes no arguments, so it doesn't make sense to have it generic unless it's in a generic class. A generic method does the same thing regardless of what class you send in, while you want two different behaviours at different points.
If you need two different behaviours from the method at different points, and you know what behaviour you need at any point, you could just make two different methods - checkVirtualCollisions and checkRealCollisions.
I might not be understanding what you're actually looking for - maybe you have a snippet of a real use case you could post?
Sorry, I should've mentioned this is just a test case from the go. I've since added a note at the end of the question.
As it is right now, the method is split into two -kind of like you mention-, the function call is overloaded, and it works. But I hate it, since using Generics should've given me the whole method, in one tidy single spot, and nicely readable at one glance.
Here's the "true" code I want to use: public static List CheckCollisionsAtPoint(Vector3 point, float radius = 0) where T:$$anonymous$$onoBehaviour{ // Since humans have no colliders sometimes, this function checks if a human character is hit at POSITION. Optionally checks for collision at a certain DISTANCE radius. List hitHuman = new List();
radius += 0.5F; // Add the character's width to the radius checked
/****** FIRST, CHEC$$anonymous$$ COLLISIONS WITH INVISIBLE (VIRTUAL) OBJECTS IN humanObject $$anonymous$$ATRIX ********/
// Based on the radius of the collision, see by how many positions in the humanObject matrix we have to extend the search
int matrixRadius = $$anonymous$$athf.CeilToInt(radius / humanObjectTerrainDivision);
// Find in which position of the humanObject matrix the center point of the collision check falls
int[] centerPoint = WorldToHumanObjectPosition(point);
// Check for hit humans in the humanObject, within matrixRadius distance from centerPoint
for (int x = (centerPoint[0] - matrixRadius); x <= centerPoint[0] + matrixRadius; ++x){
for (int y = (centerPoint[1] - matrixRadius); y <= centerPoint[1] + matrixRadius; ++y){
if ((x >= 0) && (x < humanObject.GetLength(0)) && (y >= 0) && (y < humanObject.GetLength(1))){
// This matrix check is within humanObject boundaries
if (humanObject[x,y] != null){
for (int i=0; i < humanObject[x,y].Count; ++i){
if (humanObject[x,y][i]){
if (Vector3.Distance(humanObject[x,y][i].transform.position, point) < radius){
// This human was hit
if (typeof(T)==typeof(Human_Behaviour)){
hitHuman.Add(humanObject[x,y][i]);
}
else{
hitHuman.Add(humanObject[x,y][i].gameObject);
}
}
}
}
}
}
}
}
/****** THEN, CHEC$$anonymous$$ REAL COLLISIONS WITH ACTUAL COLLIDERS IN VISIBLE OBJECTS ********/
int character$$anonymous$$ask = 1 << GlobalCharacterData.character$$anonymous$$askPosition;
Collider[] humanCollisions = Physics.OverlapSphere(point, radius, character$$anonymous$$ask);
if (humanCollisions.Length > 0){
for (int i = 0; i < humanCollisions.Length; ++i){
if (typeof(T)==typeof(Human_Behaviour)){
hitHuman.Add(humanCollisions[i].GetComponent<Human_Behaviour>());
}
else{
hitHuman.Add(humanCollisions[i]);
}
}
}
return hitHuman;
}
Answer by spiceboy9994 · Mar 20, 2015 at 11:43 PM
Can you paste the actual line that gives you the error?, you mention the error the error but not the line where the compiler or the console detects it.
Another thing:
hitHuman.Add(humanObject[0,0][0]);
that is defined as strongly typed
List<Human_Behaviour> hitHuman
My guess is you cannot add a strongly typed variable to a List of generic T. Have you tried this?
hitHuman.Add((T)humanObject[0,0][0]);
Hope this helps a bit
In lines... "hitHuman.Add(humanObject[0,0][0]);" and "hitHuman.Add(humanObject[0,0][0].gameObject);" I get the error "Argument #1' cannot convert
Human_Behaviour' expression to type T'**" and "**Argument
#1' cannot convert UnityEngine.GameObject' expression to type
T'" ... which is quite logical, since the compiler has no idea what T will be.
So, of course, the first thing I did was try to typecast with hitHuman.Add((T)humanObject[0,0][0]); like you said, but then I get "Cannot convert type Human_Behaviour' to
T'", which again, has its logic for the same reason.
In my brain, Constraints should've fixed both issues, but when I tried to add Constraints as mentioned in the Question, the error was: "The class type constraint `Human_Behaviour' must be listed before any other constraints. Consider moving type constraint to the beginning of the constraint list"
...and moving Human_Behaviour to the front of the Constraints enumeration, prompted the same message but with "'GameObject' must be listed before..." ins$$anonymous$$d.
I even tried to add $$anonymous$$onoBehaviour and object as Constraints to see what that would do, but with the same result.
Thanks for your help.
Another observation, why don't you try to get the Human_Behavior part working first before using the gameobject method. I know since the method is generic, it may work with any kind of types, but that may help you to narrow down the issue. Another thing is that I've realized your Human_Behavior class is inheriting from $$anonymous$$onoBehavior, have you changed your constraint to be
static List<T> CheckCollisionsAtPoint<T>() where T:$$anonymous$$onoBehavior
$$anonymous$$aybe that could help you out on the Human_Behavior Part.
Or a more generic one
static List<T> CheckCollisionsAtPoint<T>() where T:Component
That may help you with both $$anonymous$$onoBehavior and GameObject types.
Just ping me if that works...
The method already works for both Human_Behaviour and GameObject in both its "regular" form and when using Generics (by adding "where T:Human_Behaviour" or "where T:GameObject"). The problem comes when trying to accept both types.
I had already tried using $$anonymous$$onoBehaviour in the "where" clause, and on your suggestion I tried Component just for kicks, with the same result (the errors stated in my previous comments, "Cannot convert ...")
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Are generic constraints supported? 0 Answers
How to make sense of error message? 1 Answer
Implicit Downcast warning from "List.IndexOf" ...Bad? Unavoidable? 1 Answer