Generics in Unity C# - c#

I am having a lot of trouble with the syntax and the rules for using Generics. I am trying to make a structure, where different classes, can use the WaitAction class to disable input while a couroutine is running, an re-enable it once the coroutine is finished.
This example is a simplified version, and in reality I will not be using a count float to define the length of the coroutine, but the length will based on animations and translation.
Is what I am trying to do at all possible?
"Somehow use "T _ready" to change the "bool ready" in "Main Class" back to "true""
public class Main : Monobehaviour {
WaitAction _waitAction = new WaitAction();
public bool ready;
float delay = 5f;
void Update()
{
if(Input.GetMouseButton(0) && ready)
{
ready = false;
StartCoroutine(_waitAction.SomeCoroutine((delay, this));
}
}
public class WaitAction {
public IEnumerator SomeCoroutine<T>(float count, T _ready)
{
float time = Time.time;
while(Time.time < time + count)
{
yield return null;
}
// Somehow use "T _ready" to change the "bool ready" in "Main Class" back to "true"
}
}

The solution is to constrain the generic type, such that the generic method knows how to set the ready flag. This is easily done using an interface:
public interface IReady
{
bool ready { get; set; }
}
public class Main : Monobehaviour, IReady {
...
public bool bool ready { get; set; }
...
}
public class WaitAction {
public IEnumerator SomeCoroutine<T>(float count, T _ready) where T : IReady
{
...
_ready.Ready = true;
}
}

Related

How do I store the same types of classes that have a different generic inside of a list?

I've been tinkering with this and I have a 'RespawnManager' that I want to use to manage my multiple 'SpawnPoint' classes with different generics but it ended up forcing me to use generics for my 'RespawnManager' which I don't want.
Let's say I had a SpawnPoint<T> class and I made a SpawnPoint<Enemy1>, SpawnPoint<Enemy2>, and SpawnPoint<Enemy3>. Is there any way I can make a list that can just manage multiple 'SpawnPoint's of any generic?
Base class:
public abstract class SpawnPoint<T> : MonoBehaviour
{
//how big the range of the spawn protection is
public int spawnProtectionRadius = 20;
public bool Occupied { get; set; }
public bool IsInSpawn(Transform target)
{
Debug.Log((target.position - transform.position).magnitude);
if ((target.position - transform.position).magnitude <= spawnProtectionRadius)
{
return true;
}
return false;
}
public abstract T Get();
}
Class that Inherits this
public class SeaMineSpawnPoint : SpawnPoint<Seamine>
{
public override Seamine Get()
{
return SeaMineObjectPool.PoolInstance.Get();
}
private void Start()
{
RespawnManager<Seamine>.respawnManager.AddSpawn(this);
}
}
Respawn manager:
public class RespawnManager<T> : MonoBehaviour where T : Component
{
public static RespawnManager<T> respawnManager;
[SerializeField]
private List<Transform> playerList;
[SerializeField]
private List<SpawnPoint<T>> spawnpoints;
private float respawnCounter;
private void Awake()
{
respawnManager = this;
}
private void Start()
{
foreach (SpawnPoint<T> sp in spawnpoints)
{
Debug.Log(sp.transform.position);
}
}
public void AddSpawn(SpawnPoint<T> spawnPoint)
{
spawnpoints.Add(spawnPoint);
}
public void RespawnSeaMines()
{
if (respawnCounter > 5)
{
respawnCounter = 0;
foreach (SpawnPoint<T> sp in spawnpoints)
{
foreach (Transform playerT in playerList)
{
if (sp.Occupied == false && !sp.IsInSpawn(playerT))
{
Component ourGameObj = sp.Get();
ourGameObj.transform.position = sp.transform.position;
ourGameObj.gameObject.SetActive(true);
sp.Occupied = true;
return;
}
}
}
}
}
private void Update()
{
respawnCounter += Time.deltaTime;
Debug.Log(respawnCounter);
RespawnSeaMines();
}
}
ObjectPool
//Class that's used for object pooling of different types.
//'T' must be a Unity component or it will error.
public abstract class ObjectPool<T> : MonoBehaviour where T : Component
{
//An object with this specific component that we use to copy.
[SerializeField]
private T prefab;
//Makes sure that only 1 coroutine runs at a time
private bool coroutineIsRunning;
//The singleton instance to our object pool.
public static ObjectPool<T> PoolInstance { get; private set; }
//A queue is used to organize plus activate and deactivate objects which
//have this component.
protected Queue<T> objects = new Queue<T>();
private void Awake()
{
//Set the instance of this pool to this class instance. Only one of these can be set.
if (PoolInstance != null)
{
throw new System.Exception("Singleton already exists. Cannot make another copy of this");
}
PoolInstance = this;
}
public T Get()
{
//If the queue happens to be empty, then add a brand new component.
if (objects.Count == 0) AddObjects(1);
//Returns the generic component and removes it from the queue.
return objects.Dequeue();
}
public void ReturnToPool(T objectToReturn)
{
//Disables the game object that the T component is attached to.
objectToReturn.gameObject.SetActive(false);
//Stores the T component in the queue.
objects.Enqueue(objectToReturn);
}
public void AddObjects(int count)
{
for (int i = 0; i < count; i++)
{
//Create a new copy of the prefab.
//The prefab is a game object with the T component attached to it.
T newObject = Instantiate(prefab);
//Disable the game object.
newObject.gameObject.SetActive(false);
//Add the T component to the queue.
//The T component is attached to the game object we created earlier.
objects.Enqueue(newObject);
}
}
public T GetWithDelay(int time)
{
T genericToReturn = null;
if (!coroutineIsRunning)
{
coroutineIsRunning = true;
StartCoroutine(GetCoroutine(time, genericToReturn));
}
return genericToReturn;
}
private IEnumerator GetCoroutine(int time, T generic)
{
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
yield return null;
}
generic = Get();
generic.gameObject.SetActive(true);
coroutineIsRunning = false;
}
}
You should be able to declare your spawnpoints property in RespawnManager as a List<SpawnPoint<Component>> instead of List<SpawnPoint<T>>. That will allow you to get rid of the <T> type parameter entirely from RespawnManager and make it non-generic.

Custom static variables for child classes

I have something like this :
class powerup {
public static int cooldown = 1;
}
class freeze : powerup {
//some unrelated code
}
class burn : powerup {
//unrelated
}
and i'd like to have a different value for the cooldown of the freeze and burn powerups, and in a static way since i can't instantiate them where i set the cooldown, and it also makes more sense to have them static since they are unique. So i feel like i'm needing to override the cooldown with "new" , but it doesnt feel right. Is there any solution i'm not aware of ?
Thanks in advance
There is no combination of overridability and staticness in C#; those are in a sense opposites.
The better technique would be to make instances; those instances could be singletons if that makes sense. I'd be inclined to do something like:
abstract class Powerup
{
public virtual int Cooldown => 1
}
sealed class Freeze : Powerup
{
}
sealed class Burn : Powerup
{
public override int Cooldown => 2;
}
But a technique I particularly like when these are singletons is:
abstract class Powerup
{
private Powerup() {} // Prevent instantiation
public virtual int Cooldown => 1
public static readonly Powerup Freeze = new FreezePower();
private sealed class FreezePower : Powerup
{
}
public static readonly Powerup Burn = new BurnPower();
private sealed class BurnPower : Powerup
{
public override int Cooldown => 2;
}
}
Now look at the use site:
Console.WriteLine(Powerup.Freeze.Cooldown); // 2
That looks really nice at the use site I think.
You can use the new modifier to hide the parent property on child classes, such as:
class powerup
{
public static int cooldown = 1;
}
class freeze : powerup
{
public new static int cooldown = 3;
//some unrelated code
}
class burn : powerup
{
public new static int cooldown = 2;
//unrelated
}
This provides the following results:
Console.WriteLine($"powerup: {powerup.cooldown}");
Console.WriteLine($"freeze: {freeze.cooldown}");
Console.WriteLine($"burn: {burn.cooldown}");
I believe you are wanting to update the cooldown value of all instances of a specific powerup. In that case, I would use something like this:
interface IPowerup {
int Cooldown { get; set; }
}
class Freeze : IPowerup {
private static int _cooldown;
public int Cooldown { get { return _cooldown } set { _cooldown = value; }
public Freeze() { Cooldown = 1; }
}
class Burn : IPowerup {
private static int _cooldown;
public int Cooldown { get { return _cooldown } set { _cooldown = value; }
public Burn() { Cooldown = 2; }
}
So now, if you set the cooldown for one powerup, you set the value for all.
You can also do away with the constructors and instantiate the powerups and set the cooldown period like this:
var burnPowerup = new Burn { Cooldown = 2 };

Better way to count all existing citizens?

I've started making a simple city builder game, in the spirit of Zeus/Poseidon, but much simpler. I have the grid system ready and ability to add houses and roads. Yesterday I began to add citizens, in a simple way, that is, whenever a house is created, 5 people are created and move directly from one edge of the map to that particular house. Once they reach that particular house, I consider they became citizens, and add them to the list of residents of the house, and also to the list of citizens of the city.
For that, each house instance has a List of Human, and my Game class which contains all the information of the game also has one List of human.
To simplify it looks like this:
Game.cs
public class Game {
private static Game instance; // this is a singleton
private int currentAmount; //this is the value I'm using to display the number of citizens on screen
private List<Human> humen;
public List<Human> Humen
{
get { return humen; }
set
{
humen = value;
currentAmount = humen != null ? humen.Count : 0;
}
}
public void AddHuman(Human human)
{
humen.Add(human);
currentAmount = humen.Count;
}
/// <summary>
/// Private constructor to ensure it's only called when we want it
/// </summary>
private Game()
{
humen = new List<Human>();
}
public static void setGame(Game game)
{
instance = game;
}
/// <summary>
/// Returns the instance, creates it first if it does not exist
/// </summary>
/// <returns></returns>
public static Game getInstance() {
if (instance == null)
instance = new Game();
return instance;
}
}
House.cs
public class House : Building {
public static int CAPACITY = 5;
private List<Human> habitants;
public List<Human> Habitants
{
get { return habitants; }
set { habitants = value; }
}
public House() {
habitants = new List<Human>();
}
}
HumanEntity.cs
public class HumanEntity : MonoBehaviour {
private Human human;
private float speed;
public Human Human
{
get { return human; }
set { human = value; }
}
// Use this for initialization
void Start () {
speed = Random.Range(5.0f, 10.0f);
}
// Update is called once per frame
void Update () {
if (human != null)
{
Vector3 targetPosition = human.Target.GameObject.transform.position;
if (transform.position.Equals(targetPosition)) {
if (!human.HasAHouse)
{
human.HasAHouse = true;
Game.getInstance().AddHuman(human); // here I'm adding the human to the list of citizens
((House)human.Target).Habitants.Add(human); // here I'm adding it to the house list of habitants
}
}
else {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
}
}
}
}
And this is working as expected, but I'm wondering if having one list of human by house in addition with a global list in the game class is not a little overkill, and if there was maybe a more elegant way to achieve that count on the Game class, maybe something more "Unity friendly" if I may say so, as I don't really know a lot about the capacities of Unity. Do you have any advice on what to do, is that okay to keep it this way or is there a more elegant way?
Fast and appropriate way to know how many human would be to have a static counter on HumanEntity class:
public class HumanEntity : MonoBehaviour
{
public static int HousedHuman { get; private set; }
public static int HumanCount { get; private set; }
void Awake() { HumanCount++; }
void OnDestroy()
{
HumanCount--;
if(human.HasAHouse == true){ HousedHuman--; }
}
public static void ResetCounter() { HouseHuman = HumanCount = 0; }
void Update () {
if (human != null)
{
Vector3 targetPosition = human.Target.GameObject.transform.position;
if (transform.position.Equals(targetPosition)) {
if (!human.HasAHouse)
{
HouseHuman++; // Added
human.HasAHouse = true;
// Rest of code
}
}
// Rest of code
}
}
}
When a new instance is added, the counter is increased, when the instance is destroyed, the counter is decreased.
You can access via HumanEntity.HumanCount. You won't be able to set it elsewhere than in the HumanEntity class.
Make sure to reset the counter when you start/leave the scene.
EDIT: based on comment, I added a second static counter for HousedHuman. This is increased when the entity reaches the house. It gets decreased when the entity is destroyed if the entity was housed. It also gets reset when needed along with the overall counter.
Building on Everts's idea...
Game:
public class Game {
private static Game instance; // this is a singleton
public static int currentAmount { get; set; }
//rest of class
}
House:
public class House : Building {
public static int CAPACITY = 5;
private List<Human> habitants;
public List<Human> Habitants
{
get { return habitants; }
set { habitants = value; }
}
public House() {
habitants = new List<Human>();
}
public void AddHuman(Human human)
{
human.HasAHouse = true;
habitants.Add(human);
Game.currentAmount++;
}
}
UpdateLoop:
// Update is called once per frame
void Update () {
if (human != null)
{
Vector3 targetPosition = human.Target.GameObject.transform.position;
if (transform.position.Equals(targetPosition)) {
if (!human.HasAHouse)
((House)human.Target).AddHuman(human);
}
else {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, step);
}
}
}
If checking house capacity is required, you can change the AddHuman method to a bool return type, do a capacity check inside and return whether or not it was successfully added.
You can also add a RemoveHuman method that would count humans down via Game.currentAmount--;
As for the list in Game, it really depends on the context. The List in your Game class could be useful to differentiate between wandering humans, and humans who are housed, if this behavior is required. (Wandering humans in the Game list, housed in the housed list)

How to distinguish between different objects using an interface

I'm creating a platform system using a raycast controller that uses an interface to perform different tasks based on the type of platform with which my player is currently colliding. Some of the platform types include ice, passable blocks and muddy ground.
I want to know how to better optimize my code, as I currently call Unity's somewhat expensive "GetComponent()" function every frame, even if I never change between blocks. What I'd like to do is only call GetComponent() when I change from one type of platform to a different type of platform (i.e. muddy ground --> ice), but don't know how to do this using an interface.
I thought I would be able to compare types using enums, but you're not allowed to declare types in an interface.
if (hit)
{
//I'd only like to run this block of code if the type of platform changes
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
State.IsCollidingWithPassablePlatform = platform.IsPassable;
State.IsJumpBoosted = platform.IsJumpForce;
State.IsBoosted = platform.IsForce;
xForce = platform.XForce;
yForce = platform.YForce;
zForce = platform.ZForce;
defaultParameters.accelerationTimeGrounded = platform.AccelerationTimeGrounded;
defaultParameters.accelerationTimeAirborne = platform.AccelerationTimeAirborne;
Interface example:
interface IPlatform {
float AccelerationTimeGrounded { get; }
float AccelerationTimeAirborne { get; }
float XForce { get; }
float YForce { get; }
float ZForce { get; }
bool IsPassable { get; }
bool IsForce { get; }
bool IsJumpForce { get; }
Ice platform:
public class PlatformIce : MonoBehaviour, IPlatform {
public float AccelerationTimeGrounded { get { return accelerationTimeGrounded; } }
public float AccelerationTimeAirborne { get { return accelerationTimeAirborne; } }
public float XForce { get { return xForce; } }
public float YForce { get { return yForce; } }
public float ZForce { get { return zForce; } }
public virtual bool IsPassable { get { return false; } }
public bool IsForce { get { return false; } }
public bool IsJumpForce { get { return false; } }
[SerializeField]
private float accelerationTimeGrounded = 1.0f;
[SerializeField]
private float accelerationTimeAirborne = 3.0f;
private float xForce = 0;
private float yForce = 0;
private float zForce = 0;
}
Remember your last GameObject and check if this one has changed
private lastGameObj;
[...]
if(lastGameObj!= hit.collider.gameObject) {
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
// [...] your magic here
lastGameObj= hit.collider.gameObject;
}
You will get an additional condition, but you won't run your code 60 times/sec inclusive that GetComponent();.
You CAN use enums inside an Interface, you just have to declare the enum type outside the Interface.
I.E.:
public enum PlatformType {Normal, Ice, Fire, etc}; //Use public only if needed, of course
interface IPlatform {
PlatformType platformType { get; }
//Your other stuff here
}
This will break encapsulation, clearly, but if you really want to use an enum in an interface, there's no way around it.

Best way to read object from another class without code coupling

for example we have class Foo
class Foo {
public class Physics {
float gravity = 9.8f;
float seperateDistance = 10f;
}
public Physics physics;
void Start(){
physics = new Physics();
Bar baz = AddComponent<Bar>();
}
}
Meanwhile, in Bar component, we are trying to get the gravity value inside Foo's physics.
Currently this is the only way I can think of. which i somehow feel not good about. If class Foo is removed then Bar will not working anymore, which i guess is not a good practice.
Inside the baz we created in Foo:
void Start(){
//...
float gravity = getComponent<Foo>().physics.gravity;
//...
}
Is there any better ways to do this?
If I'm reading you correctly, the issue that you're worried about is class coupling. As Georg's answer said, you should be using properties, not fields. But you can avoid class coupling by injecting an interface into Bar that Foo implements. This means that you don't need a Foo to code Bar:
public interface IGravitySource
{
double Gravity { get; }
}
public sealed class Foo : IGravitySource
{
private readonly Physics myPrivatePhysics = new Physics();
private sealed class Physics
{
public double Gravity { get; } = 9.81;
public double Smoothness { get; } = 0.05;
}
public double Gravity => myPrivatePhysics.Gravity;
}
public sealed class Bar
{
private readonly IGravitySource gravitySource;
public Bar(IGravitySource gravitySource)
{
this.gravitySource = gravitySource;
}
public void Start()
{
//...
var gravity = gravitySource.Gravity;
gravity += 1;
//...
}
}
EDIT:
Technically, you might be introducing debt by effectively re-implementing the properties of IGravitySource in multiple places. One solution to this is to have the IGravitySource only have a single member, IGravity. This way, if you decide to extend IGravity with direction, you don't need to change the implementation of Foo:
public interface IGravitySource
{
IGravity Gravity { get; }
}
internal interface IGravity
{
double AccelerationDueToGravity { get; }
}
public sealed class Foo : IGravitySource
{
private readonly Physics myPrivatePhysics = new Physics();
private sealed class Physics : IGravity
{
public double AccelerationDueToGravity { get; } = 9.81;
public double Smoothness { get; } = 0.05;
}
public IGravity Gravity => myPrivatePhysics;
}
public sealed class Bar
{
private readonly IGravitySource gravitySource;
public Bar(IGravitySource gravitySource)
{
this.gravitySource = gravitySource;
}
public void Start()
{
//...
var gravity = gravitySource.Gravity.AccelerationDueToGravity;
gravity += 1;
//...
}
}
This is usually done through properties which are basically pairs of two methods, a getter and setter method.
To use them, just exchange public Physics physics; by public Physics physics { get; set; }. There is no need to change the consuming code since properties are accessed like fields in C#. However, properties also allow you to customize the getter and setter. The good thing is that such a modification is transparent to client code, so you can change the access to that property without having to change code that consumes the property. Further, properties can be virtual or abstract and are allowed to be part of an interface.
You can check if there's a Foo component attached
void Start() {
//Check if there's Foo, if no then give them default to 3;
float gravity = GetComponent<Foo>() ? GetComponent<Foo>().physics.gravity : 3;
//...
}
Or you can also add RequireComponentAttribute to the class Bar, so that Foo will always tied to Bar RequireComponentAttribute
But if you mean 'what if Foo Class(not component) doesn't exist anymore.
I suggest you to do Method Injection like Adam Brown answers.
But there's another solution when class is no longer exist.
here's my thought.
Create a special Attribute to detemine which field is shareable. and then get the field wh use that Attribute.
Example :
of course this isn't tested yet, but by theory it will work.
//Some special attribute
[AttributeUsage(AttributeTargets.Field)]
public class ShareFieldAttribute : Attribute {
}
class Foo {
public class Physics {
float gravity = 9.8f;
float seperateDistance = 10f;
}
//mark ShareField attribute to this field
[ShareField]
public Physics physics;
void Start(){
physics = new Physics();
Bar baz = AddComponent<Bar>();
}
}
class Bar {
void Start() {
//Get the field 'public Physics physics' by name and Type
Physics physics = GetShareField<Physics>("physics", null);
float gravity = physics ? physics.gravity : 3;
//...
}
//Method Helper
public T GetShareField<T>(string fieldName, T defaultValue)
{
foreach(var c in GetComponents<MonoBehaviour>())
{
var fields = c.GetType().GetFields().Where(field => field.FieldType == T && field.Name == fieldName && field.IsDefined(typeof(ShareFieldAttribute), false));
return (T)fields[0].GetValue(c);
}
return defaultValue;
}
}

Categories