Setters and getters are functions associated with variables. They allow you to run code when you assign a value to a variable (setter) or when you read its value (getter). They are also tools to filter and validate data coming in or out of variables.In GDScript, there are two ways to define setters and getters. The first one is to write them directly under a script-wide variable:Here's one example:
var health :=10:set(new_health): health =max(0, new_health) get:return health
The line set(new_health): defines a setter function, and get: defines a getter function. It's a short syntax designed to define setters and getters directly under the variable.In your script, when you write something like health = -5, the setter function will run and assign 0 to the health variable (because the new health value is lower than 0 and the setter function filters that). When you write print(health), the getter function will run and return the current value of health variable.The second way to define setters and getters is to use the set and get keywords and point to a specific function. Here's the same example as above, but using the set and get keywords. You can define the associated setter and getter functions anywhere in the script:
var health :=10: set = set_health, get = get_health
funcset_health(new_health:int)->void: health =max(0, new_health)funcget_health()->int:return health
As with many things in code, neither syntax is really better. It's a matter of taste and what you find more readable.The first syntax keeps the setter and getter functions close to the variable, and you can fold the code in the editor to hide them. The second syntax keeps the variable declaration concise, and the setter and getter functions can be placed anywhere in the script. This is the one we favor in our courses.With code style, I recommend sticking to one syntax in your project for consistency. Consistency makes the code easier for you and your teammates to read and understand.
When to use setters
You may use a setter function to filter unwanted values. In this example, nothing will happen if you try to set age to 0, a negative value, or a value higher than 100.
var age :=15: set = set_age
funcset_age(new_age:int)->void:if new_age >0and new_age <100: age = new_age
Setters are also useful for triggering events. For example, you may want to save a value to a file every time it changes:
var current_profile_name :="Player 1":set(new_profile_name) current_profile_name = new_profile_name
save()
Why would I use getters?
A little disclaimer first: we rarely use getter at GDQuest. But here are examples of how you could use getters.Getters can be useful when calculating a value based on other values. For example, you may want to convert a temperature from Celsius to Fahrenheit:
var celsius :=37var farenheit:float: get = get_farenheit
funcget_farenheit()->float:return(celsius *9/5)+32.0
With this code, when you write print(farenheit), the formula will always apply and give you the correct value, even if you change the celsius variable.You may also use getters to load something only on demand. For example, you may want to load high scores from a file only when needed. In this case, you may use a getter to load the high scores.
var high_scores:Resource=null: get = get_high_scores
funcget_high_scores()->Resource:returnload("user://high_scores.tres")funcdisplay_scores()->void:for score in high_scores.scores:var score_node := Label.new() score_node.text = score
$ScoresList.add_child(score_node)
Finally, you may use a getter to make a read-only property:
var account_authentication_key ="": set = set_account_authentication_key, get = get_account_authentication_key
funcset_account_authentication_key(new_key:String)->void:push_error("you cannot change this property")funcget_account_authentication_key()->String:returnload("user://auth.tres").key
Setters and Getters together
Setters are more often used than getters, but there are cases where you want to use both, for example, when you want to keep two properties in sync.Here are two properties representing time in seconds and milliseconds. You can set the time in seconds, and it will automatically update the milliseconds value. You can also get the time in seconds, and it will return the correct value from the milliseconds value.
var milliseconds :=0.0var seconds:float: get = get_seconds, set = set_seconds
funcget_seconds()->float:return milliseconds /1000funcset_seconds(new_seconds:float)->void: milliseconds = new_seconds *1000
Yes! That's a good observation. Indeed, you could use functions for all the above instead of variables with setters and getters. Setters and getters kind of hide how a variable works. This is a conscious choice in some programming styles, where we try to hide the inner workings of code. Other developers may prefer to be more explicit and expose how all the code works.I prefer code to be explicit, so I'm not fond of setters and getters. I like to see the code that runs when I assign a value to a variable. Still, setters and getters do have some benefits:
Refactoring: If you already have a property used in a codebase and you now want to change its behavior, adding a setter to it does that without changing the code that uses the property. This is a good way to refactor code without breaking it.
Standardization: using setters and getters limits you. You can only use one parameter for setters and zero for getters (as opposed to functions, which are more flexible). Setters have to use the type of your variable. Constraints like these force you to write code that meets some expectations.
Consistency: if setters and getters are already used in a codebase, it's always good to stick to the same style. Consistency makes the code easier for everyone involved to read and understand. It's less cognitive load compared to having to process different styles.
Consistency is largely why you'll find setters and getters in GDQuest code: Godot already uses them, and lots of people in the community do, so we do too, to make our code more accessible to others.