P2.1 — Functions¶
What you'll learn
- What a function is: a named block of steps you can run by name, as many times as you like.
- How to define a function and call it, and that defining is not the same as running it.
- How a function takes inputs (parameters) and gives back an output (return).
- Why breaking a program into functions is the core habit of thinking like a programmer.
How it applies
- Functions tame complexity. A big program is unmanageable as one long list of steps. Naming a
chunk of steps —
take_damage,save_game— lets you think about what it does without holding how in your head. This is the main tool for keeping a growing program understandable. - Write once, use many. A step you need in five places becomes one function called five times. Fix a bug in it once and all five callers are fixed — versus hunting down five copies.
- Defining is not running. A function's body does nothing until the function is called. Beginners often expect the code to run where it is written; it runs where it is called. Knowing this prevents "why didn't my function happen?" confusion.
- No return, no result to use. A function that computes something but does not
returnit leaves the caller with nothing to work with. Deciding what a function hands back is part of designing it.
Concepts¶
A function is named, reusable steps¶
A function is a block of instructions with a name. You define it once, then call it by name
whenever you want those steps to run. You have already been using one — _ready is a function Godot
calls for you. Now you write your own:
func starts the definition, greet is the name, the () will hold inputs (none yet), and the
indented lines are the body — the steps that run when the function is called. Defining it does
not run it. To run the body, you call the function by writing its name with parentheses:
func _ready():
greet() # this runs greet's body now
greet() # call it again — the body runs a second time
Calling greet() twice runs its two prints twice. The body lives in one place; the calls decide when
and how often it happens.
Inputs: parameters and arguments¶
A function is far more useful when it can work on values you give it. A parameter is a named input the function declares; an argument is the actual value you pass when you call it:
func greet(who): # `who` is a parameter — a placeholder for an input
print("Hello, " + who)
func _ready():
greet("Vex") # "Vex" is the argument — it fills in `who`
greet("Mara") # different argument, same function
Inside the body, who stands for whatever value the caller passed. So greet("Vex") prints
Hello, Vex and greet("Mara") prints Hello, Mara — one definition, different inputs each call.
(The next book adds types to parameters, like who: String; here, focus on the idea that a
parameter is a named slot the caller fills.)
Output: return¶
A function can hand a value back to whoever called it, using return:
func add(a, b):
return a + b # hand back the sum
func _ready():
var result := add(2, 3) # result becomes 5
print(result) # 5
print(add(10, 20)) # 30 — use the returned value directly
return a + b computes the sum and gives it back, so add(2, 3) is the value 5 at the call site —
you can store it, print it, or use it in a bigger expression. return also ends the function
immediately. A function with no return simply does its steps and hands nothing back (like greet,
which prints but returns nothing) — fine when you only want the effect, not a result.
Example
Inputs and output together, traced:
func price_with_tax(price, rate):
var tax := price * rate
return price + tax
func _ready():
var total := price_with_tax(100, 0.2) # price=100, rate=0.2
print(total) # 120
The call passes 100 and 0.2 into the parameters price and rate. Inside, tax becomes
100 * 0.2 = 20, and the function returns 100 + 20 = 120. Back at the call site, total receives
that 120. The function packaged a small computation under a name; the caller just supplies inputs
and uses the output, without caring how the tax is figured. Call it again with different numbers and
it computes a new result — the reuse that makes functions worth defining.
Walkthrough¶
Use your P1.1 script setup. (Define your functions at the top level of the script, alongside
_ready, not inside it; call them from inside _ready.)
- Define
func greet(): print("Hi"). From_ready, callgreet()twice. Run; confirmHiappears twice. Then remove the calls but keep the definition, run, and confirm nothing prints — defining is not running. - Add a parameter:
func greet(who): print("Hi, " + who). Call it with two different names and confirm each greeting. - Write
func add(a, b): return a + b. From_ready, storevar r := add(4, 5)and printr(9). Then printadd(2, 3) + add(10, 10)and predict the result (25) before running. - Write
price_with_tax(price, rate)from the example and call it with two different price/rate pairs, printing each total.
Optional sanity check
Put a print("defining now") as the last line of your script file, outside any function, and a
print("inside greet") inside greet — but do not call greet. Run. You will see that the
function's inner print never happens (it was defined, not called). Add the greet() call and it
appears. That is "defining is not running," demonstrated.
Self-check quiz¶
Q1 — What is the difference between a parameter and an argument?
A. They are the same thing. B. A parameter is the named input in the function's definition; an argument is the actual value passed when the function is called. C. A parameter is the output; an argument is the input. D. An argument is only for numbers.
Reveal answer
B. The definition declares parameters (named slots, like who); the call supplies arguments
(actual values, like "Vex") that fill those slots. A misses the distinction. C confuses input
with output (return is the output). D invents a restriction.
Q2 — You define a function but never call it. What runs?
A. The function body runs once where it is defined. B. Nothing from the body runs — a function's body only runs when it is called. C. It runs automatically at the end of the program. D. It causes an error.
Reveal answer
B. Defining a function only describes the steps; they execute only when the function is called by name. A is the common misconception (code runs where written). C and D invent behaviors — an uncalled function is simply never run, which is not an error.
Q3 — What does return do in func add(a, b): return a + b?
A. Prints the sum.
B. Hands the value a + b back to the caller, so add(2, 3) becomes 5 at the call site.
C. Stores the sum in a variable named return.
D. Nothing; return is optional decoration.
Reveal answer
B. return gives a value back to whoever called the function, so the call expression is
that value and can be stored or used. A confuses returning with printing (they are different —
a returned value is not shown unless you print it). C and D misread return entirely.
Integration question¶
Q4 — open
A program computes a player's final damage three different places, each time as
base * 2 + bonus. A teammate copy-pastes that expression into all three spots. Explain why turning
it into a function func final_damage(base, bonus): return base * 2 + bonus is better, covering
reuse, single-point-of-fix, and the parameter/return roles — and explain what would go wrong if the
function computed the value but forgot to return it.
Reveal expected answer
Wrapping the formula in a function replaces three copies with one definition called three times:
final_damage(base, bonus) at each site. Reuse: the logic is written once and used wherever
needed. Single point of fix: if the formula changes (say base * 3 + bonus), you edit the
one function and all three callers update at once — versus finding and fixing three copies and
likely missing one, which is how copies silently diverge. Parameter/return roles: base and
bonus are parameters (named inputs the callers fill with their own values), and return hands
the computed result back so each call is the damage value, usable in an assignment or a bigger
expression. If it forgot to return: the function would compute base * 2 + bonus
internally and then throw it away, handing nothing back; every caller writing var d :=
final_damage(...) would receive no usable value, so the damage would effectively be missing.
A function that produces a result the caller needs must return it — computing without
returning is the same as not computing at all, from the caller's point of view.