Maintainable code?

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.

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:

// add to GameObject:
public class HealthComponent : MonoBehaviour { public float value; }

// then to deal damage:
HealthComponent health = target.GetComponent<HealthComponent>();
health.value -= 10;

Use an interface:

public interface IDamageable { void TakeDamage(float amount); }

public class HealthComponent : MonoBehaviour, IDamageable
{
protected float value;

public void TakeDamage(float amount) { value -= amount; }
}

// then to deal damage:
IDamagable damageable = target.GetComponent<IDamageable>();
damageable.TakeDamage(10);

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() and making the appropriate changes. Much more future proof and debug friendly.

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.

thanks guys! i’m spending 90% of my time debugging. I will start using these things.

Totally normal. You’ll still do plenty of debugging. Its just easier with well designed code.

I’m a big fan of this: Model-View-Controller | Microsoft Learn

also this looks like a good link: Introduction to Model View Control (MVC) Pattern using C#

Makes maintaining systems a lot easier, if you want to change the logic, modify the “model” script. If you want to change how something behaves on screen, modify the “view” script, if you want to modify how a user interacts with the system, modify the “controller” script.

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?

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.

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.

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.

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.

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?

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.