I am currently creating a small plant caring game (Unity Engine 2022.3.40f1). It’s properties (genes) are passed onto it’s parts as a genome. These properties can be floats, integers or colors.
For that purpose I have created an abstract Gene<T>
class (see below) from which GeneFloat : Gene<float>
, GeneInt : Gene<int>
are created…
And I’ve run into a problem: this Gene class does not seem to be as generic as I would like it to be.
The Gene<T>
class
public abstract class Gene<T> {
public abstract T value { get; set; }
public abstract T randomRange { get; set; }
}
Example of an inherited Gene (here GeneFloat
) :
public class GeneFloat : Gene<float> {
public override float value { get; set; }
public override float randomRange { get; set; }
}
However, I can’t seem to use the Gene
class as a generic! That’s my problem.
The following code makes Unity log an error message into the console saying : “the generic type 'Gene<T>' requires 1 type arguments
“
public struct Genome {
public Gene[] genes; // <- a message for this line
public Gene GetGeneNyName(string name) { // <- and for this one
foreach(Gene gene in this.genes) { // <- but not for this one ?????
if (gene.GetName() == name) return gene;
}
Debug.LogError($"It seems that the gene '{name}' couldn't be found in the genome. Maybe it does not exist or the spelling is wrong.");
}
}
Could you help me figure out what is wrong?
Honestly I didn’t try everything as I’m just a casual programmer and it’s my first time using generics this thoroughly. I searched for some ideas on here, but nothing seems to pop out and helping me figure out.
I tried replacing Gene
by Gene<int, float, Color>
but, obviously, that didn’t get me far.
6
You need a base class which is not generic and which contains all the members having a concrete type:
public abstract class Gene
{
public string Name { get; set; }
public abstract void Mutate();
}
This class can be used for a gene collection.
Now, derive a generic class adding the values:
public abstract class Gene<T> : Gene
{
public virtual T Value { get; set; }
public virtual T RandomRange { get; set; }
}
Then create the concrete classes
public class GeneFloat : Gene<float>
{
// You can override the virtual members or leave them as is.
}
public class GeneColor : Gene<Color>
{
// You can override the virtual members or leave them as is.
}
...
Now, you can use a type pattern to do things with concrete types
public Gene[] genes;
...
var gene = genes.FirstOrDefault(g => g.Name == "leafColor");
if (gene is GeneColor c) {
Color color = c.Value;
...
}
I recently made something like this where the range was given by a Min
value plus a Range
. The random part was given by a Fraction
value (between 0 and 1) and the value itself by (for float
as an example):
public float Value => Min + Fraction * Range;
You can combine the ideas of having an array of genes for easier mutation and having properties for individual genes:
public class Genome
{
public Gene[] Genes { get; }
public GeneFloat LeafWidth => (GeneFloat)Genes[4];
// or, alternatively, return the value directly
public float LeafWidth => ((GeneFloat)Genes[4]).Value;
...
}
I also noted that you are using a method named GetName
. You might be used from Java to access fields through Get… and Set… methods for encapsulation. C# has a dedicated syntax for this purpose. I explain it in great detail in my answer to How can I find a specific element in a List?