Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Damage manager practicality vs performance

Discussion in 'Scripting' started by tawdry, Mar 9, 2015.

  1. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Hiho folks
    So i'm exploring the idea of having a damage manager but seems to me it will really hurt performance.
    If i have a damage manager that handles all the calculations won't it have to run a find GO everytime to get a reference to the damage been dealt and another for health on the GO been damaged.Not to mention the getcomponent calls Might not be a huge impact for a small fight but if u have dozens going on it seems unworkable?

    Is there some kind of a streamlined way it can be done short of attaching a damage script to every ai in the game?
     
  2. Void24

    Void24

    Joined:
    Oct 15, 2013
    Posts:
    50
    This doesn't seem excessive at all! You don't necessarily need to run a find either.

    When the script(s) that apply damage wish to do so, instead of applying it directly to a game object, call the damage manager's damage method, and pass the desired game object to be damaged into the function as a parameter. This gives you a free reference, and should not cause any significant slowdown. Even with hundreds of objects being damaged.
     
  3. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    I don't understand what you mean by pass the GO as a parameter. Could you supply some code to illustrate only been coding for the last couple months
     
  4. Void24

    Void24

    Joined:
    Oct 15, 2013
    Posts:
    50
    I'll do my best,

    You have probably already run into parameters, they work like this:

    public void myMethod(int myNumber) {
    Debug.Log("I know that my number is "+myNumber);
    }

    Well you can pass any class type as a parameter, including Game Objects!

    public void doDamage(GameObject myObject) {
    Debug.Log("I know about a game object with public variable HP = "+myObject.HP);
    myObject.HP--; // Ouch
    }

    Now your bullet (Or whatever does damage) can call this when it wants to damage something:

    DamageManager.doDamage(objectThatIHit);

    No Object find required.

    If this is too rough of an example, look into object oriented code tutorials. Passing parameters is an important tool in your future game coding.
     
  5. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Hmm i have not had dealings with parameters before unless triggers count. But in the damage manager would still have to get a number of variables like armor resists etc . Ill play around with your example see if i can get it to work and go from there . Thx for the input.
     
  6. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    You are preoptimizing. Even though yes, you should probably be passing Game objects as parameters, even a Gameobject.Find and a GetComponent call whenever you take damage won't really cause you that many issues. Running them many times each frame on a ton of gameobjects will start to cause issues, but what you are describing (dozens of units, make the calls only when one damages another) isn't going to give you too many issues.

    I would focus on learning how to pass the gameobjects as parameters, because its something you should know, but then worry about getting things working. Then, once things work, IF you are noticing a framerate issue, and the profiler (now free with Unity 5) points to your damage processes, THEN you look at optimizing it.
     
    Kiwasi likes this.
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Take a step back and learn about scope and parameters first. You'll need them, and I'm surprised you got as far as conceptualising a damage manager without them.

    Code everything the way that seems simplest and most straight forward to you. Putting a damage script on every single entity is not that big a deal, you can just use the same script and change a few variables around. If you are worried about coupling then you can use SendMessage or ExecuteEvents to actually deal your damage.

    I find managers most useful in the following situations
    • Major decoupling is required.
    • You need to keep track of every instance of a script. Say you have an attack that can damage every entity in the world.
    • You need access to some common pool of data.
    You should almost never have to use GameObject.Find. There is always a way around it. The only time I use it is where I'm lazy in setting up my initial links in the inspector, or if I need decoupling for some reason. Even then there are better ways to do it.

    As to performance, its not likely to make a difference. Unless you are doing a loop with 10,000 iterations you typically won't see the effect. If frame rate is a problem, use profiler. Preoptimisation is of the devil, (or whatever ultimate bad thing fits your frame of reference).
     
    tawdry likes this.
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    C2309 HP is not a member of GameObject. :)

    You probably want

    Code (CSharp):
    1. public void doDamage(GameObject myObject) {
    2.   MyDamageScript myDamageScript = myObject.GetComponent<MyDamageScript>();
    3.   Debug.Log("I know about a game object with public variable HP = "+myDamageScript.HP);
    4.   myDamageScript.HP--;
    5. }
    Where MyDamageScript is attached to the GameObject of concern, and has a public member called HP.

    You can of course pass in the MyDamageScript instead. You can't pass in the HP value directly, as ints and floats are a value type.
     
    Void24 likes this.
  9. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Hey bored like that you say its ok to put damagescripts on all combatants I was told once if u copypasting a lot of stuff u doing something wrong. For me if u don't know a shortcut(less typing) take the long route, long as you arrive at the destination at the same time doesn't matter how ugly the behind the scenes goings on are.

    Ok i admit to been a coding noob and i was terrible at maths:p.So I might not be understanding some very fundamental truth here but I'm still struggling to grasp the ideas put forward here.
    I am imagining that this is working in a way similar to how a trigger event works? where u can get a ref to the object by its collider(always thought the trigger routine did the. Find object routine in the background.).

    So boredmormon && void in your example myobject is the injured? but How does the computer know who the injured is what is telling the myobject what it is?Where does the actual damage been done come into this? all i see is the victim hp script there are no damages been passed in at any stage.Did the injured trigger the function if so does the script that calls the function automatically assign the myobject to the GO that hosts the script?

    What happens if u have 10 damages been sent at a similar time how does a central script handle that when the scripts are all passing the same named variables would it not need some sore of a list iteration to stop the numbers getting jumbled up then another iteration to assign the numbers to the correct recipients. Coding that makes my head hurt just thinking about it but I guess a good coder would not have much trouble?.

    So basically where is the damage been fed in how does the function know what myobject is when the attacker is calling this function as there seems no discernible link to the actual injured party is the attacker shomehow sending the name of the GO to be hurt.
     
    Last edited: Mar 9, 2015
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Ugh. Remember you are dealing with coders here. We are guys that can spot a misplaced ; or a missed letter case from a mile away. Missing capital letters and shorthand hurt my head. Its a general good tip for getting the best out of the eccentrics that hang around on the forums giving away knowledge.

    Copy paste should be avoided in all cases. As soon as you write the same line of code twice then you need to split it off into its own method. DRY is a coding mantra.

    Here is an example damage system at its simplest.

    The damage script gets attached to everything that can take damage.

    Code (CSharp):
    1. public class Damagable : MonoBehaviour {
    2.     public float life;
    3.  
    4.     public void DoDamage (float damage){
    5.         life = life - damage;
    6.         if(life < 0){
    7.             Destroy(gameObject);
    8.         }
    9.     }
    10. }
    This script goes on the damager. I'm thinking a projectile or something. I've done it with OnCollisionEnter, but there are other ways to determine what you are damaging.

    Code (CSharp):
    1. public class Damager : MonoBehaviour {
    2.     public float damage;
    3.     private void OnCollisionEnter(Collision collision){
    4.         collision.gameObject.SendMessage("DoDamage", damage, SendMessageOptions.DontRequireReciever);
    5.         Destroy(gameObject);
    6.     }
    7. }
    There are plenty of other ways to do this. And I've taken some shortcuts. But you get the idea.
     
    Void24 likes this.
  11. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Assuming I get an Idea is a very dangerous and in this case wildly incorrect assumption haha.

    Ok this goes on the victim. Has the victims health and a float (damage)? damage is passed in when the function is called with SendMessage?
    Code (CSharp):
    1. public class Damagable : MonoBehaviour {
    2.     public float life;
    3.     public void DoDamage (float damage){//
    4.         life = life - damage;
    5.         if(life < 0){
    6.             Destroy(gameObject);
    7.         }
    8.     }
    9. }
    This script is on the projectile it has the damage

    Code (CSharp):
    1. public class Damager : MonoBehaviour {
    2.     public float damage;
    3.     private void OnCollisionEnter(Collision collision){
    4.         collision.gameObject.SendMessage("DoDamage", damage, SendMessageOptions.DontRequireReciever);//why is there a damage here and a damage on the other script?"Dodamage" already applies damage?
    5.         Destroy(gameObject);//why is there destroy gameObject here it is already on the other script?
    6.     }
    7. }
    This is roughly how I am doing it at the moment except for the SentMessage part.

    I kinda follow except for the need for damage twice and destroy twice but this isn't really a combat manager in the sense I meant it.. As these scripts need to be on every combatant. Whereas the combat manager I was envisioning would do all of this. Taking in the values of damage sent by the attacker the health of the victim and any modifiers that may need to be applied therefore all the maths is done by this third party. Ps Do not know anything about SendMessage, just looked it up now.
    Ps I went back and applied Capitals and removed shorthand where I used it might have missed a few though.
     
    Void24 and Kiwasi like this.
  12. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    SendMessage is actually old, there is a better way to do it now, ExecuteEvents. But it's syntax is more complicated.

    The destroy on the Damager script was for a projectile system. After a bullet hits and damages a single target it gets destroyed.

    Doing the same thing through a manager might look something like this.

    Code (CSharp):
    1. public class Damagable : MonoBehaviour {
    2.     public float life;
    3.     public void DoDamage (float damage){//
    4.         life = life - damage;
    5.         if(life < 0){
    6.             Destroy(gameObject);
    7.         }
    8.     }
    9. }
    Code (CSharp):
    1. public class Damager : MonoBehaviour {
    2.     public float damage;
    3.     public CombatManager combatManager;
    4.     private void OnCollisionEnter (Collision collision){
    5.         combatManager.RegisterHit (collision.gameObject, damage);
    6.         Destroy(gameObject); // Still thinking bullets, remove if not applicable
    7.     }
    8. }
    Code (CSharp):
    1. public class CombatManager : MonoBehaviour {
    2.     public void RegisterHit (GameObject target, float damage){
    3.         // Complicated logic here
    4.         target.GetComponent<Damagable>().DoDamage(damage);
    5.     }
    6. }
     
    Last edited: Mar 10, 2015
    Void24 likes this.
  13. tawdry

    tawdry

    Joined:
    Sep 3, 2014
    Posts:
    1,356
    Register hit doesn't seem to have a meaning in Unity? or at least in that segment of text in the Damager class it doesn't
     
  14. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Whoops, typo. Problem with coding on the phone. Have fixed now.