Search Unity

How to damage different types of objects with least code vs least overhead.

Discussion in 'Getting Started' started by Tset_Tsyung, Mar 5, 2016.

  1. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    How guys,

    I'm trying to set up a simple explosion that not only stuns and throws the player about (have simple 3D animation as my head has no arms - so can't ragdoll, lol). However I want the same explosion to be able to blow out lights, blow doors in and the like.

    FYI it's a simple 2.5D game, so for placeholder stuff I'm just removed doors/windows renderers and playing a particle system to simulate debris.

    My question is this...

    I haven't used SendMessage yet, but from what I'm read on these forums it seems to be rather overhead intensive. My other option is to check each object, then check it's tag, type and find the relevant script for the right types of object. This will be quite code intensive (the amount of code that is, imagine it's just a lot of the same with different references in if's).

    Which would be better to use? I will using a raycast to make sure that nothing it comes into contact with isn't on another floor (don't want that, lol).

    Also would I have to have an receiving Function in each objects script? For Example


    Code (CSharp):
    1. // this object does not actually respond to 'Boom'
    2. public void Boom()
    3. {
    4. return;
    5. }
     
  2. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    you use enum and statemachine to avoid overhead

    then you use GetComponent on interaction to access that object.. or SendMsg if you dont have well structured code.
     
    Last edited: Mar 5, 2016
    Tset_Tsyung likes this.
  3. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    Hi @Ironmax,
    Thanks for the reply. I haven't gotten round to using enum or statemachine - will look into those in a few mins.

    As for getcomponent, that's what I was meaning by "check each object, then check it's tag, type and find the relevant script" (wasn't clear, I know... :( ).

    Would it be better to have a 'boom' script on all objects and then have the relevant 'what object am I attached to' code in that to find the right componenet/script, or have all that in the explosives code? I'm still learning about well structured code - keep coming back to bits I wrote 2 days ago and thinking "now that's just a mess" lol.
     
  4. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    No problem. You use tags on target/objects type, but you use enum on "damage different types".

    Example:
    Code (CSharp):
    1.     public enum DamageType
    2.     {      
    3.         Fire,
    4.         Ice,
    5.         Magical,
    6.         Melee
    7.     }
    8.  
    9. DamageType damageType; // make an instance of that in this code..
    10. damageType = DamageType.Fire; // just example
    11.  
    12. if (damageType == DamageType.Fire)
    13. {
    14.      // on trigger event
    15. if (hit.gameObject.tag == "Monster")
    16. {
    17. // Do fire damage
    18. }
    19.  
    20.  
    21. }
    22.  
    There is also another way of doing it if you dont want to use tags. Tags uses string and string will allocate in memory..
     
    Tset_Tsyung likes this.
  5. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    Okay, that makes sense - but how do I communicate this to the other object in question? (sorry I'm being dense today, lol)

    Again, thank you for the help! XD
     
  6. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    one way is to use this:
    Code (CSharp):
    1. void  OnTriggerEnter(Collider  hit)
    2. {
    3.  
    4.     // Interaction
    5. // Example1:
    6. hit.gameobject.GetComponent<GameLogic>().GetHit(damagetype);
    7. // Example2:
    8. // Other is to use SendMessege to access a object with different script name..
    9.  
    10. }
     
    Tset_Tsyung likes this.
  7. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    Cool, that's what I was thinking.

    Essentially I'm looking for different scripts components depending on what the object is tagged as, right?

    So I could have a light bulb with a tag as "ExplodableBulb" and a door as "ExplodableDoor" and then after checking the tag just use something like GetComponent<RelevantScriptForDifferentObject>().Boom();

    Thank you for helping me to clear this up! Nice to know I'm on the right path still, lol.
     
  8. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I recommend using your enums in a global singletone script seperated. you can do statement check on enum when ever you want. its cheap and fast code..
     
    Tset_Tsyung likes this.
  9. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Only your mind is the limit ;) but that is correct

    you also can use the enum to access something else if damagetype == DamageType.Ice (example freezing)
     
    Tset_Tsyung likes this.
  10. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    AWESOME!

    Still not sure on the use of Enum (or singleton, for that matter)... but you've got me on a path now - I'll go do some research on using using them both... after lunch, lol.

    Again, many thanks for the Help @Ironmax - it's been really helpful to bounce this off of someone! XD

    P.S. you can probably tell that I'm teaching myself all this as I go, lol.
     
    Ironmax likes this.
  11. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Just happy i could help :) and that i put you on the right track

    // also hint.. if you make the enum public, there be a rollup menu in the inspector so that you can set things up without touching any code.
     
    Tset_Tsyung likes this.
  12. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    OK, here's the key bit I think you're missing: MonoBehaviours can implement an interface, and you can use that interface with GetComponent to get and invoke a method on all components that implement it.

    So for example, you could define your interface as:

    Code (CSharp):
    1. interface IExplodable {
    2.     void Explode(Vector3 sourcePosition);
    3. }
    Now, any object that should react to an explosion should implement IExplodable, and do whatever it wants to do in response to that.

    Code (CSharp):
    1. public class Bulb : MonoBehaviour, IExplodable {
    2.     void Explode(Vector3 sourcePosition) {
    3.         // here's what bulbs do when an explosion goes off nearby...
    4.     }
    5.     void Update() {
    6.         // here's what bulbs do every frame...
    7.     }
    8. }
    Now when you set off an explosion, you just find all objects within the blast radius (or on the same floor or whatever), and for each of those, do:
    Code (CSharp):
    1. foreach (IExplodable exp in obj.GetComponentsInChildren<IExplodable>()) {
    2.     exp.Explode(transform.position);
    3. }
    and presto, you now have things responding to explosions in their own way. Adding a new explosion response only requires defining a new MonoBehaviour that implements IExplodable; you won't have to touch the invoking code at all.

    Any time you find yourself checking a type and doing a switch or a big pile of if's based on that type, this is a bad smell, and you should look for a better way (as you have done here!). So, good job recognizing that and asking for help!

    Oh, and one more thing: you shouldn't be worrying too much about efficiency at this stage. You should be focused on writing code that is clear and correct, and gets your game finished as quickly as possible. Bonus points if it's also easy to maintain. Efficiency is the last thing you need to worry about, and only then after you've found an actual problem, and used a profiler to track it down to some inefficient thing you're doing. We avoid SendMessage not because it has "overhead," but because it is unsafe and fragile (using a string literal to look up a method is another bad smell).
     
    MikeTeavee, Kiwasi and Tset_Tsyung like this.
  13. Tset_Tsyung

    Tset_Tsyung

    Joined:
    Jan 12, 2016
    Posts:
    411
    COOL! Thanks guys.

    @JoeStrout Me? Write easy to maintain and easily understandable. Steady on bud, one thing at a time! lol.

    Yeah currently I'm using lot's of If's and, you're right, it reeks to high heaven (even with my limited knowledge!).

    Cool, well at least I have a few new skill sets to add to my trip bag. Will have to play around with them in my test project first to get to grips with them. Again, thanks guys!

    P.S. When I'm ready to release a 'Demo' level of my game would you be interested in a link to it? Gonna be a while as I'm adding core features (okay... and exposions, lol) and bug squashing at mo with placeholder models... but it's coming along, lol.
     
  14. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    GetComponent does also allocate memory + i don't really recommend interface at all (its a design/structure pattern). Let me clear up something. Overhead is not the same as allocation. You really cant avoid allocation like when you do collision or doing a raycast or even working with strings. Overheads happens when to much allocation happens at ones and the Garbage Collections kicks inn and draw your CPU resource to handle that.

    "The key is to make sure things happen only, ones..". Example OnCollisionTrigger is a much better practice then "OnCollisionEnter".

    I would really avoid doing things like this:

    Code (CSharp):
    1.     foreach (IExplodable exp in obj.GetComponentsInChildren<IExplodable>()) {
    2.         exp.Explode(transform.position);
    3.     }
    4.  
    The reference to a game Compented should be allocated before using for loops, also read about pool-managing,
    how C# works with mermory, stack/Heap boxing/unboxing etc before going in to interface pattern.

    Singleton is also a design pattern just like interface, and is much easier to understand for a beginner. Even what Joe here says is correct, i think interface is something you go learn "next" or when your game project is huge and you need good structure. Interface takes some time to implement but, makes things easy to change in a huge project.

    Here is a very good documentation about it:
    http://wiki.unity3d.com/index.php/Toolbox
     
    Last edited: Mar 5, 2016
    Tset_Tsyung likes this.
  15. Deleted User

    Deleted User

    Guest

    I was fully with you until you got to the last part:

    Code (CSharp):
    1. foreach (IExplodable exp in obj.GetComponentsInChildren<IExplodable>()) {
    2.     exp.Explode(transform.position);
    3. }
    Where would this go? You've got an interface that declares an Explode function, making that a requirement for any class inheriting IExplodable. Then you've got member classes defining Explode. I'm that far.

    It's been a while since I saw foreach (thing in array) - so that's saying "for each (type IExplodable Component) in list of all children gameObjects inheriting IExplodable."

    So you're suggesting all the IExplodable objects are children of some manager class in this example, correct? - I'm sure there are many other possible arrangements as well.
     
    Tset_Tsyung likes this.
  16. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I am sure Joe was just using it as an example in good fate :) I really like Joyes stuff on the forum. Interface is like the mafia, ones your a member, your with the "gang" all the time even if you like it or not :p Your right there are many possible arrangements.. Have a nice weekend every one.
     
    Last edited: Mar 5, 2016
    Tset_Tsyung likes this.
  17. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Interfaces are definitely the key to this. Interfaces are basically a contract saying what methods a type exposes. Tons of different types can implement the same interface. That way everything can respond to the explosion, without the explosion knowing about any other types.

    Here is another code example to consider, working on top of what @JoeStrout has given you.

    Code (CSharp):
    1. void DoExplosion (){
    2.     Collider[] hits = Physics.OverlapSphere (transform.position, 1);
    3.     foreach (Collider hit in hits){
    4.         IExplodable explodable = hit.GetComponent<IExplodable>();
    5.         if(explodable){
    6.             explodable.Explode(transform.position);
    7.         }
    8.     }
    9. }
     
    Tset_Tsyung and JoeStrout like this.
  18. Deleted User

    Deleted User

    Guest

    I can understand that example and I'm pretty sure I'm going to use it in my game of life simulator I'm building. I'm making a 3D critter box. So far I've just been working on movement and orientation (five iterations, three different approaches- all saved, much learned) but soon I'll want to implement more complex conditional behavior. I already use an interface to control my game's states and switch scenes.
     
  19. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I disagree, interface is a design method not a key-feature, there is no one type fits all situation. Secondly he didn't ask for vector3 explosion simulation,a simple partial effect would do it (and much better performance) and add some addforce physics or scaling physical objects. Arrays are also heavy on allocation. (the OP worried about overhead)...

    Recommending interface in the starter forum section is maybe not the best. :p

    Use enums, they are smart cheap and fast..ofc every one is free to explode there own memory..
     
    Tset_Tsyung likes this.
  20. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    This would go in the code for whatever's exploding. That has the job of finding all the game objects within range (and if you need help with that, start another thread about it — there are lots of ways to go about it), and then just run the above code for each one.

    Not at all. You run the above code on ALL objects within range (on that floor of your level or whatever). If the object has no IExplodable components on it, then the above code does nothing. No harm, no foul.

    And note that I strongly disagree with @Ironmax about the relative difficulty of interfaces vs. enums-and-ifs. Interfaces are not hard; I posted above everything you need to know about them. Doing a bunch of if-tests may seem easier until you've tried the other approach, but try it once in a real game and you'll see why it's so awesome.

    And yes, if you finish your game and notice performance problems and run the profiler and find that explosions are causing a glitch due to this GetComponentsInChildren call, then you can refactor it to improve performance in a variety of ways. But framerate glitches are not the greatest risk by far; the greatest risk is that you will never finish the project at all, due to drowning in complexity, or it taking so long that you lose interest. Thousands of games die such an ignoble death for every one that ever gets finished. So, be ruthless about using whatever code seems simplest, clearest, and easiest to maintain to you, and ignore any grim mutterings you may hear about allocations and garbage collection.[/code][/QUOTE]
     
    Tset_Tsyung, MikeTeavee and Kiwasi like this.
  21. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    First of all, allot of "real" games have been made without the use of interface. Its total nonsense to say that if you dont use interface you will never Finnish your game, or it will be so hard do maintain your game. Is total nonsense. Interface is a design choice to structure some code, you don't use it every where, else your structure will be a total mess and then it will be very hard to understand what is going on. In a well structured game you need managers to take care of things, not to have lots of messy codes hanging around just because you neeeed to use interface..

    Interface, is just one of many structure design pattern, you have : Builder, Prototype, Singleton/Toolbox, Composite, Decorator, Facade, Module Based, Proxy/
    abstract class etc..

    Another thing to mention if you want to teach people about interface, is that is based on 2 parts.
    1. The creation(Abstract Factory) , and 2. The structure (Adapter or Bridge)
    Interface is not even a class and can not have properties + allot of other things you can not do.

    Builder is maybe better suited for this situation where you can have "products". Its up to the designer to make the right choice. Where interface can be great, it can also be horrible, where singleton is great it can also be horrible.

    Secondly i never said that you use enum instead of interface, i think you missed what the OP was asking, i haven't seen a single interface example here that implements a type logic with enums. Nobody said anything about doing a bunch of "if tests", you need a statemachine for different purpose. Statmachine is also perfect with enum and makes your code clean and informative. Interface can not replace statments or bools, its main purpose is to build a structure without specifying classes. Interface is not even close to be the ultimate pattern to use everywhere.
     
    Last edited: Mar 6, 2016
  22. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    As usual you have no idea what you are talking about. I'm not going to argue. Just point out to the OP that most of @Ironmax's posts are junk and should be ignored.
     
  23. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    I just searched some of your trolling post on other forums. where you tell people to use Find method to access gameobjects. Stop width that crap please, your not contributing to the people on this forum or being generally nice..

    BoredMormon here cant argue because he can't, and simply loves his design pattern to much, or the only pattern he knows, then come here only to troll (clearly). Going personal is pretty immature and not very constructive.
     
    Last edited: Mar 6, 2016