Search Unity

Maintainable code?

Discussion in 'Scripting' started by shmomo, Apr 1, 2015.

  1. shmomo

    shmomo

    Joined:
    Oct 11, 2011
    Posts:
    127
    What techniques can help avoid these situations:

    1. multiple scripts / methods modifying a single field
    2. a change to the order two methods are executed resulting in different outcomes
    3. disabling a method breaking the program

    there is a lot of information about what programming practices to avoid but less practical solutions it seems. Im hoping you have some suggestions.
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Don't expose public fields. I know a lot of tutorials (and even Unity Tech themselves) does it, but it creates highly coupled code. Don't be afraid of using interfaces.

    Instead of:
    Code (csharp):
    1.  
    2. // add to GameObject:
    3. public class HealthComponent : MonoBehaviour { public float value; }
    4.  
    5. // then to deal damage:
    6. HealthComponent health = target.GetComponent<HealthComponent>();
    7. health.value -= 10;
    8.  
    Use an interface:
    Code (csharp):
    1.  
    2. public interface IDamageable { void TakeDamage(float amount); }
    3.  
    4. public class HealthComponent : MonoBehaviour, IDamageable
    5. {
    6. protected float value;
    7.  
    8. public void TakeDamage(float amount) { value -= amount; }
    9. }
    10.  
    11. // then to deal damage:
    12. IDamagable damageable = target.GetComponent<IDamageable>();
    13. damageable.TakeDamage(10);
    14.  
    Now if you want to add on say, damage types (physical, fire, ice, etc.) and resistances, you can just change the interface and fix the errors instead of finding every single GetComponent<HealthComponent>() and making the appropriate changes. Much more future proof and debug friendly.
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Make everything private. Then assume that anything public can be called or changed at random. You can further restrict this by using properties.

    You'll end up fighting Unity's serialisation system a bit with this approach. But its worth it. If you want something to turn up in the inspector use [SerialiseField] instead of public.
     
  4. shmomo

    shmomo

    Joined:
    Oct 11, 2011
    Posts:
    127
    thanks guys! i'm spending 90% of my time debugging. I will start using these things.
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Totally normal. You'll still do plenty of debugging. Its just easier with well designed code.
     
  6. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
  7. shmomo

    shmomo

    Joined:
    Oct 11, 2011
    Posts:
    127
    great!

    Is A better than B?

    A: call a method, sending field in parameters, and have it return a new value. Set field to returned value.
    B: method modifies the field directly.

    is it A because A can keep fields in local scope?
     
  8. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    That is completely dependent on what you want the function to do! BUT for value types like int,float etc, you can't modify that directly within a function anyway, unless you pass it explicitly by reference.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Depends. Strictly speaking in traditional programming the best practice is to keep scopes as small as possible. Class level fields should only be used for information that needs to be shared with other classes.

    In unity it's not that clear cut. You have the GC to worry about. Creating and abandoning a lot of data every frame can cause havoc in memory.
     
  10. shmomo

    shmomo

    Joined:
    Oct 11, 2011
    Posts:
    127
    thanks! Understood.

    I was just wondering if there is some reason that is better for maintainable code.. returning values to set local scope fields so that you can avoid modifying class scope fields and also avoiding 'ref' parameters.
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Ref parameters should be avoided. They are difficult to maintain. In some cases they are the best solution, but in most cases there are better ways. Ref parameters force your method and it's caller to know something about each other, thus breaking encapsulation.
     
  12. shmomo

    shmomo

    Joined:
    Oct 11, 2011
    Posts:
    127
    Ok one more bonus question..

    A needs to change depending on B's state.

    Is it better for A to check B instead of B inform A?
     
  13. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Depends.

    Polling, or checking for a value change at some regular frequency, can be useful in some circumstances, like input. But generally its a pretty expensive operation. Not that checking the value is expensive. But that checking a value that hasn't changed is just a waste of time.

    Informing on a change can be useful. Properties are a good way to do this. But requires your class to know about every class that is watching it. This sort of dependency is not good practice.

    The next solution is to use events. Events let classes subscribe and listen, without the class triggering the even knowing a thing about the class that is listening for it. Event based systems require the subscriber to know what classes its listening too.

    Then you can go into full blown design pattern messaging systems. There are a bunch of ways for classes to communicate value changes without any dependency at all. I'll let you explore this one later. Design patterns are very useful, but they can be overkill for small projects.