Simplify creation of references to object members for animation purposes? - c#

I am trying to write animation component for my game engine. Animation component has to modify (animate) values of any member of any game object. Problem is that members are usually value types, but animation component needs some kind of reference to them, to be able to change it.
First I thought about using reflection, but reflection is too slow. I read about other techniques in C# that may be able to help (Pointers, Reflection.Emit, Expression trees, Dynamic Methods/Objects, delegates, lambda expressions, closures...) But i don't know these things good enough, to be able to solve the problem.
Animation component would have methods that would be able to take and store a reference to random object's member and animate its value over time. Like this: StartSomeAnimation(ref memberToAnimate)
There would be other parameters (like animation length), but problem is with passing members. Reference to memberToAnimate would need to be stored (even if it is value type) so it can be updated by animation component every frame.
The closest I was able to get to the solution on my own is with lambda expressions and Action<> Func<> delegates (see example below). It is about 4x slower than directly changing members + some more garbage allocation. But I still can't make such simple method signature like in example above.
class Program
{
static void Main(string[] args)
{
GameItem g = new GameItem();
Console.WriteLine("Initialized to:" + g.AnimatedField);
g.StartSomeAnimation();
// NOTE: in real application IntializeAnim method would create new animation object
// and add it to animation component that would call update method until
// animation is complete
Console.WriteLine("Animation started:" + g.AnimatedField);
Animation.Update();
Console.WriteLine("Animation update 1:" + g.AnimatedField);
Animation.Update();
Console.WriteLine("Animation update 2:" + g.AnimatedField);
Animation.Update();
Console.WriteLine("Animation update 3:" + g.AnimatedField);
Console.ReadLine();
}
}
class GameItem
{
public int AnimatedField;// Could be any member of any GameItem class
public void StartSomeAnimation()
{
// Question: can creation of getter and setter be moved inside the InitializeAnim method?
Animation.IntializeAnim(
() => AnimatedField, // return value of our member
(x) => this.AnimatedField = x); // set value of our member
}
}
class Animation // this is static dumb class just for simplicity's sake
{
static Action<int> setter;
static Func<int> getter;
// works fine, but we have to write getters and setters each time we start an animation
public static void IntializeAnim(Func<int> getter, Action<int> setter)
{
Animation.getter = getter;
Animation.setter = setter;
}
// Ideally we would need to pass only a member like this,
// but we get an ERROR: cannot use ref or out parameter inside an anonymous method lambda expression or query expression
public static void IntializeAnim(ref int memberToAnimate)
{
Animation.getter = () => memberToAnimate;
Animation.setter = (x) => memberToAnimate = x;
}
public static void Update()
{
// just some quick test code that queries and changes the value of a member that we animate
int currentValue = getter();
if (currentValue == 0)
{
currentValue = 5;
setter(currentValue);
}
else
setter(currentValue + currentValue);
}
}
EDIT: A more complete example added to hopefully make question a little clearer. Please focus on how closures are created with lambda expressions and not on game architecture. Currently for each member, we want to animate, two lambda expressions have to be written each time we start a new animation (IntializeAnim methods). Can starting an animation be simplified? Look at how IntializeAnim methods are called currently.
class Program
{
static bool GameRunning = true;
static void Main(string[] args)
{
// create game items
Lamp lamp = new Lamp();
GameWolrd.AddGameItem(lamp);
Enemy enemy1 = new Enemy();
Enemy enemy2 = new Enemy();
GameWolrd.AddGameItem(enemy1);
GameWolrd.AddGameItem(enemy2);
// simple game loop
while (GameRunning)
{
GameWolrd.Update();
AnimationComponent.Update();
}
}
}
static class GameWolrd
{
static List<IGameItem> gameItems;
public static void Update()
{
for (int i = 0; i < gameItems.Count; i++)
{
IGameItem gameItem = gameItems[i];
gameItem.Update();
}
}
public static void AddGameItem(IGameItem item)
{
gameItems.Add(item);
}
}
static class AnimationComponent
{
static List<IAnimation> animations;
public static void Update()
{
for (int i = 0; i < animations.Count; i++)
{
IAnimation animation = animations[i];
if (animation.Parent == null ||
animation.Parent.IsAlive == false ||
animation.IsFinished)
{// remove animations we don't need
animations.RemoveAt(i);
i--;
}
else // update animation
animation.Update();
}
}
public static void AddAnimation(IAnimation anim)
{
animations.Add(anim);
}
}
interface IAnimation
{
void Update();
bool IsFinished;
IGameItem Parent;
}
/// <summary>
/// Game items worry only about state changes.
/// Nice state transitions/animations logics reside inside IAnimation objects
/// </summary>
interface IGameItem
{
void Update();
bool IsAlive;
}
#region GameItems
class Lamp : IGameItem
{
public float Intensity;
public float ConeRadius;
public bool IsAlive;
public Lamp()
{
// Question: can be creation of getter and setter moved
// inside the InitializeAnim method?
SineOscillation.IntializeAnim(
() => Intensity, // getter
(x) => this.Intensity = x,// setter
parent: this,
max: 1,
min: 0.3f,
speed: 2);
// use same animation algorithm for different member
SineOscillation.IntializeAnim(
() => ConeRadius, // getter
(x) => this.ConeRadius = x,// setter
parent: this,
max: 50,
min: 20f,
speed: 15);
}
public void Update()
{}
}
class Enemy : IGameItem
{
public float EyesGlow;
public float Health;
public float Size;
public bool IsAlive;
public Enemy()
{
Health = 100f;
Size = 20;
// Question: can creation of getter and setter be moved
// inside the InitializeAnim method?
SineOscillation.IntializeAnim(
() => EyesGlow, // getter
(x) => this.EyesGlow = x,// setter
parent: this,
max: 1,
min: 0.5f,
speed: 0.5f);
}
public void Update()
{
if (GotHitbyPlayer)
{
DecreaseValueAnimation.IntializeAnim(
() => Health, // getter
(x) => this.Health = x,// setter
parent: this,
amount: 10,
speed: 1f);
DecreaseValueAnimation.IntializeAnim(
() => Size, // getter
(x) => this.Size = x,// setter
parent: this,
amount: 1.5f,
speed: 0.3f);
}
}
}
#endregion
#region Animations
public class SineOscillation : IAnimation
{
Action<float> setter;
Func<float> getter;
float max;
float min;
float speed;
bool IsFinished;
IGameItem Parent;
// works fine, but we have to write getters and setters each time we start an animation
public static void IntializeAnim(Func<float> getter, Action<float> setter, IGameItem parent, float max, float min, float speed)
{
SineOscillation anim = new SineOscillation();
anim.getter = getter;
anim.setter = setter;
anim.Parent = parent;
anim.max = max;
anim.min = min;
anim.speed = speed;
AnimationComponent.AddAnimation(anim);
}
public void Update()
{
float calcualtedValue = // calculate value using sine formula (use getter if necessary)
setter(calcualtedValue);
}
}
public class DecreaseValueAnimation : IAnimation
{
Action<float> setter;
Func<float> getter;
float startValue;
float amount;
float speed;
bool IsFinished;
IGameItem Parent;
// works fine, but we have to write getters and setters each time we start an animation
public static void IntializeAnim(Func<float> getter, Action<float> setter, IGameItem parent, float amount, float speed)
{
DecreaseValueAnimation anim = new DecreaseValueAnimation();
anim.getter = getter;
anim.setter = setter;
anim.Parent = parent;
anim.amount = amount;
anim.startValue = getter();
anim.speed = speed;
AnimationComponent.AddAnimation(anim);
}
public void Update()
{
float calcualtedValue = getter() - speed;
if (calcualtedValue <= startValue - amount)
{
calcualtedValue = startValue - amount;
this.IsFinished = true;
}
setter(calcualtedValue);
}
}
#endregion

You can create an interface:
interface IGameItem
{
int AnimatedField { get; set; }
}
class GameItem : IGameItem
{
public int AnimatedField { get; set; }
}
class Animation
{
public IGameItem Item { get; set; }
public void Update()
{
if (Item.AnimatedField == 0)
{
Item.AnimatedField = 5;
}
else
{
Item.AnimatedField = Item.AnimatedField + Item.AnimatedField;
}
}
}
And running of your super-animation engine will look like:
class Program
{
static void Main(string[] args)
{
GameItem g = new GameItem() { AnimatedField = 1 };
Animation a = new Animation() { Item = g };
a.Update();
Console.WriteLine(g.AnimatedField);
a.Update();
Console.WriteLine(g.AnimatedField);
a.Update();
Console.WriteLine(g.AnimatedField);
Console.ReadLine();
}
}
However, note that exposing public setters for everyone is not a good practice. Each class must be supplied with an interface which is fully used by it. Read about interface segregation principle and other SOLID principles.
UPD:
Another option is to make items to know how to animate themself:
interface IAnimatable
{
void Animate();
}
class IntegerItem : IAnimatable
{
int _n;
public IntegerItem(int n)
{
_n = n;
}
public void Animate()
{
Console.WriteLine(_n);
}
}
class AnimationSequencer
{
public void Update(IAnimatable item)
{
item.Animate();
}
}

public static class Animation
{
public static void Initialize(object element)
{
//// initialize code
}
public static void Update(object element)
{
//// update code
}
}
public class GameItem : Animatable
{
public GameItem(object memberToAnimate)
{
this.MemberToAnimate = memberToAnimate;
}
}
public class Animatable
{
public object MemberToAnimate { get; set; }
public virtual void Initialize()
{
Animation.Initialize(this.MemberToAnimate);
}
public virtual void Update()
{
Animation.Update(this.MemberToAnimate);
}
}
Thus the code will be:
var gameItem = new GameItem(yourObjectToAnimate);
gameItem.Initialize();
gameItem.Update();

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.

Design Pattern - Decorator. Is correct used in my program?

I have a quesition.
Is in this example did i asue correctly design pattern - decorator?
I want to increase speed of plane after decorate.
Does decorator always has to add new methods? Can only overwrite existing methods?
This is IPlane interface:
interface IPlane
{
void MoveLeft();
void MoveRight();
Rectangle GetPlaneBody();
int GetHealthPoints();
int GetSpeed();
Bullet Shot();
void SetSpeed(int speed);
}
This is plane class:
public class Plane : IPlane
{
private Rectangle PlaneBody = new Rectangle();
private int HealthPoint;
private int x=200;
private int speed;
private Bullet Amunition = new Bullet();
private static Plane instance;
private Plane() { }
public static Plane Instance
{
get
{
if (instance == null)
{
instance = new Plane();
instance.PlaneBody.Height = 125;
instance.PlaneBody.Width = 115;
instance.PlaneBody.X = 200;
instance.PlaneBody.Y =600;
instance.HealthPoint = 1000;
instance.speed = 10;
}
return instance;
}
}
public void MoveLeft()
{
if(x>-10)
x -= speed;
PlaneBody.X = x;
}
public void MoveRight()
{
if(x<1165)
x += speed;
PlaneBody.X = x;
}
public Rectangle GetPlaneBody()
{
return PlaneBody;
}
public int GetHealthPoints()
{
return HealthPoint;
}
public int GetSpeed()
{
return speed;
}
public Bullet Shot()
{
Bullet bullet = new Bullet();
bullet.SetDamage(Amunition.GetDamage());
bullet.SetSpeed(Amunition.GetSpeed());
bullet.SetCoordinates(x+10, this.PlaneBody.Y - 30);
return bullet;
}
public void SetSpeed( int speed)
{
this.speed = speed;
}
}
This is abstract decorator:
class AbstractPlaneDecorator : IPlane
{
protected IPlane plane;
public AbstractPlaneDecorator(IPlane plane)
{
this.plane = plane;
}
public int GetHealthPoints()
{
return plane.GetHealthPoints();
}
public Rectangle GetPlaneBody()
{
return plane.GetPlaneBody();
}
public int GetSpeed()
{
return plane.GetSpeed();
}
public virtual void MoveLeft()
{
plane.MoveLeft();
}
public virtual void MoveRight()
{
plane.MoveRight();
}
public void SetSpeed(int speed)
{
plane.SetSpeed(speed);
}
public Bullet Shot()
{
return plane.Shot();
}
}
And concrete decorator:
class IncreaseSpeedDecorator : AbstractPlaneDecorator
{
public IncreaseSpeedDecorator(IPlane plane) : base(plane)
{
}
public override void MoveLeft()
{
plane.MoveLeft(); // move two times faster
plane.MoveLeft();
}
public override void MoveRight()
{
plane.MoveRight(); // move two times faster
plane.MoveRight();
}
}
This decorator makes that plane moves 2 times faster (He performs the same method twice). Does it meet the rules of decorator pattern?
I would say the implementation is fine.
I don't know the exact context of your code, but it might be a bit strange that if you ask your decorator for the plane's speed, it will return the plane's base speed, but the plane is moving twice as fast. I think a better name for the decorator to describe its funcionality would be DoubleMoveDecorator or something like that, since it doesn't actually do anything with the plane's speed.

Unity C# how to change bool in function

I am currently writing an application in C# with Unity, and I've hit a small stumbling block. I'm trying to change the value of a bool in the buyBusinessCourse() method of a class, but the value is unaffected. The initial value of the boolean is set to false, then I want the update method to run an if statement if it returns true.
public Text healthText;
public Text hungerText;
public Text moneyText;
public Text ageText;
public GameObject youDied;
public GameObject notEnoughMoney;
public GameObject mainScreenPanel;
public GameObject healthPanel;
public GameObject hungerPanel;
public GameObject storePanel;
public GameObject moneyPanel;
public GameObject bicycleImage;
public GameObject businessButton;
public GameObject boughtBusiness;
public int health;
public int hunger;
public int money;
public int age;
private bool business;
public void buyBusinessCourse()
{
if (money >= 3000)
{
money -= 3000;
moneyText.text = "Money: " + money;
Debug.Log("1");
businessButton.SetActive(false);
boughtBusiness.SetActive(true);
bool business = true;
Debug.Log(business);
return;
}
else
{
StartCoroutine(notEnoughMoneyCaroutine());
Debug.Log("2");
}
}
private void Start()
{
bool business = false;
Debug.Log(business);
}
//Update
private void Update()
{
if (health<0 | hunger<0 )
{
youDied.SetActive(true);
mainScreenPanel.SetActive(false);
healthPanel.SetActive(false);
hungerPanel.SetActive(false);
storePanel.SetActive(false);
moneyPanel.SetActive(false);
}
if (business == true)
{
Debug.Log("update"+business);
}
}
}
You don't need to respecify the type, just do business = true. However you are creating a local variable.
You've redefined business in if statement.
public void buyBusinessCourse()
{
if (money >= 3000)
{
...
bool business = true;
...
If you want to change the value of the business bool you need to write this.business=true or business=true.
public void buyBusinessCourse()
{
if (money >= 3000)
{
...
this.business = true;
...
Example
class Rect {
private int width;
private int height;
Rect(int width, int height) {
this.width=width; //Change the value of 'width member of Rect class'
this.height=height; //Change the value of height to given parameter.
}
changeWidthToTen() {
this.width=10;
}
}

Unity5 : Is there any idea to write code FadeIn/Out without Coroutine?

I'm thinking to make FadeIn/Out effect by C#.
// so, If there's a code like this :
float targetAlpha = 0.7f;
Color targetColor = new Color();
public void FadeIn() {
if(color.a < targetAlpha) { color.a += Time.deltaTime; }
sprite.color = targetColor;
}
1) I don't want to put FadeIn() in Update() because I don't use this FadeIn() function often.
2) I don't want to use Coroutine because StartCoroutine() makes garbage. I will set active on/off this object very often.
3) Animator... There's no way, right?
So I'm going to make 1 event, which will always work on Update(), and then I will put everything in that event. (add when OnEnable(), remove when OnDisable())
Is there a better solution?
If you don't want to put it in update because it won't be used often, maybe you can consider a state machine approach. I would only consider this if performance really is more important than readability. Because this approach will add a lot of additional code.
For simplicity the code below is more verbose than it has to be.
public interface IState
{
void Update();
}
public class FadeInState : IState
{
private readonly float targetAlpha;
private readonly Sprite sprite;
private readonly Action onComplete;
public FadeInState(Sprite sprite, float targetAlpha, Action onComplete)
{
this.targetAlpha = targetAlpha;
this.sprite = sprite;
this.onComplete = onComplete;
}
public void Update()
{
// Your fade-in code
if (sprite.color.a < targetAlpha)
{
Color tmp = sprite.color;
tmp.a += Time.deltaTime;
sprite.color = tmp;
}
else
{
this.onComplete.Invoke();
}
}
}
public class FadeOutState : IState
{
private readonly float targetAlpha;
private readonly Sprite sprite;
private readonly Action onComplete;
public FadeOutState(Sprite sprite, float targetAlpha, Action onComplete)
{
this.targetAlpha = targetAlpha;
this.sprite = sprite;
this.onComplete = onComplete;
}
public void Update()
{
// Your fade-out code
if (sprite.color.a > targetAlpha)
{
Color tmp = sprite.color;
tmp.a -= Time.deltaTime;
sprite.color = tmp;
}
else
{
this.onComplete.Invoke();
}
}
}
public class DoNothingState : IState
{
public void Update()
{
// Do nothing
}
}
public class YourClass : MonoBehaviour
{
private IState currentState;
void Awake()
{
this.currentState = new DoNothingState();
}
void Update()
{
this.currentState.Update();
}
public void FadeIn(Sprite sprite, float targetAlpha)
{
this.currentState = new FadeInState(sprite, targetAlpha,
() =>
{
this.currentState = new DoNothingState();
});
}
public void FadeOut(Sprite sprite, float targetAlpha)
{
this.currentState = new FadeOutState(sprite, targetAlpha,
() =>
{
this.currentState = new DoNothingState();
});
}
}
Initially your class is in the DoNothing state. So update will effectively do nothing.
If someone calls FadeIn, your FadeInState will do your fading logic as if it were in the MonoBehaviour.Update().
The state takes an Action in the constructor that is executed when it is finished. This way you can control what happens after the animation completes from within YourClass. In the example I just set the state to DoNothing but you can probably disable the gameObject.
If you go with this approach and you start using it for other things, you should just look into some better StateMachine implementations. Otherwise you will eventually end up with tons of state classes. This one is decent.

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)

Categories