函数
显然,我们将所有的游戏逻辑都写 _ready _process 并不现实,并且我们也总不能都使用复制粘贴来复用之前代码。
在编码时也总会遇到很多重复逻辑的代码,这时,函数就派上了用场。
func is_upper(s: String) -> bool:
if s.length() != 1:
return false
if not s.is_valid_ascii_identifier():
return false
if s.is_valid_int() or s == "_":
return false
return s.to_upper() == s语法
func 函数名(参数1: 参数类型, 参数2: 参数类型) -> 返回类型:
pass函数名和参数名应为合法标识符
函数名不能和已有变量和函数重名
函数可以有零或多个参数,参数之间不能够重名
空实现函数应该使用pass,来确保语法正确
若函数体只含一行语句,则可以将函数及其函数体缩在同一行语句内编写
func hello_world(): print("Hello World")参数
参数一般是函数逻辑中所必须要或可能要用到的数据
相当于委托工厂生产产品,必须要交付工厂所需的原材料
函数可以没有参数,也可以有多个参数,多个参数间用逗号分割
参数可以同变量一样在函数内部直接使用
可以为参数指定类型也可以不指定类型,不指定类型时如变量一样其类型是Variant,调用函数传参时也会缺少类型检查
若想要接受任意类型的参数,可以不显式指定参数类型,或指定类型为Variant
参数始终按值传递,修改参数不会影响原本的值
引用类型参数传递的是引用的副本,为其赋予一个新的实例不会修改原本的对象
func _ready():
var node = Node.new()
node.name = "a"
print(node) # 输出 a:<Node#实例id>
print(node.name) # 输出 a
foo(node)
print(node) # 输出同第四行
print(node.name) # 输出 a
var b = 100
print(b) # 输出 100
foo_1(b)
print(b) # 输出 100
func foo(a: Node):
a = Node.new() # 为参数赋予一个新的实例
a.name = "b" # 不再是同一个实例,属性的修改不会共享
func foo_1(a: int):
a = 10可选参数
可以为函数的参数提供默认值,使之成为可选参数
相当于委托工厂生产产品,必须要交付其原材料,但是有些原材料工厂它自己有
可选参数只能在所有必选参数之后
可以有多个可选参数
可选参数同样可以指定类型,使用自动类型推断
不能指定向某个可选参数传参,必须从左到右依次传入
func foo(a, b, c = 3.14, d: int = 10, e := true):
pass可变参数
可变参数不限制可传入的参数数量,每个参数之间使用逗号间隔
在GDScript中称做剩余参数(Rest Parameters)
在Godot 4.5中,新增了对GDScript函数可变参数的支持
在参数名前加上三个点,声明参数为可变参数
参数类型必须为一个数组
参数必须位于整个参数列表最末尾
每个函数只能声明一个可变参数列表
之后你可以在函数内遍历该数组以获取调用方传入的剩余参数列表
func f(a: int, b: int = 0, ...args: Array):
prints(a, b, args)
func _ready() -> void:
f(1) # 1 0 []
f(1, 2) # 1 2 []
f(1, 2, 3) # 1 2 [3]
f(1, 2, 3, 4) # 1 2 [3, 4]
f(1, 2, 3, 4, 5) # 1 2 [3, 4, 5]返回值
可以为函数指定返回类型,用以给调用方提供信息,到时候对面就能使用这个值
相当于委托工厂生产产品,工厂要把产出的成品交给你
当不指定返回类型时,函数的返回值取决于具体实现,如果实现中没有返回任何值,那么函数的返回值为null
不指定返回类型时,函数可以返回任何值也可以不返回值
显式指定返回类型时,所有代码路径必须返回一个值,但void除外
void代表无返回值,可以使用 return 关键字提前返回结束函数的执行,但不能返回任何值。
func foo() -> void:
return
print("Hello World!") # 不会被执行返回使用return关键字,其后跟上一个表达式,表示返回的值,有返回类型的函数必须返回与返回值类型相匹配的值。
如果不返回值,后面什么都不需要加上
return将会结束函数的执行,其后的代码都不会被执行
下面的代码示例中并非所有的代码路径都返回一个值,因此会触发报错
要解决这个问题,需要在if语句外返回一个值
func foo(s: String) -> bool:
if s.length() != 1:
return false非
void函数 必须 返回一个值,如果你的代码具有分支语句(例如if/else构造),则所有可能的路径都必须有返回值。例如,如果在if块内有一个return,但在其后没有,则编辑器将抛出一个错误,因为如果该代码块未执行,那么该函数将没有值进行有效返回。
想要函数返回任何类型的值,可以不显式指定返回类型
也可以指定返回类型为Variant,但显式指定返回类型后必须所有代码路径都返回一个Variant
函数只能返回一个值,如果想要返回多个值,可以使用Array或者Dictionary,或嵌套起来
最常见的方法是使用一个Dictionary,一个键值对表示一个返回值,调用方只需要查询对应的键值对即可,详见后续的集合章节
调用
func add(a:int, b:int) -> int: return a + b
func hello_world() -> void: print("Hello World")
func print_message(msg := "Hello World") -> void: print(msg)函数的逻辑只有被调用才会被执行,函数会依据调用顺序由上到下依次执行,后面的函数会等待前面的函数执行完之后才会执行
调用函数时必须传入所有的必选参数,使用逗号分割传入的参数,参数的类型必须与函数需求的类型相匹配
func _ready() -> void:
hello_world()
print_message()
print(add(1, 2))对于具有返回值的函数,调用该函数后,你可以使用一个对应类型的变量来接收该函数的返回值,储存其结果。
当然,你也可以不储存函数的返回结果,直接使用其返回值:
直接将返回
bool类型的函数调用作为if等语句的条件表达式;将一个返回
int类型的函数调用,传入需求int类型参数的方法;使用返回
float类型的函数调用参与数学运算
func _ready() -> void:
var result: int = add(1, 2)
print(result) # 3if is_upper("S"):
print("is upper!")递归
函数可以在自身中调用自身,这种用法叫做递归(Recursion),适合解决一些分解性问题,比如阶乘或斐波那契数列
但是必须要为递归函数设定一个结束条件,也就是需要在合适的地方return
否则就会陷入无限递归导致堆栈溢出
Stack overflow (stack size: 1024). Check for infinite recursion in your script.
func get_all_children(node: Node) -> Array[Node]:
var nodes: Array[Node] = []
for child in node.get_children():
nodes.append(child)
nodes.append_array(get_all_children(child))
return nodesfunc get_scene_root(node: Node) -> Node:
if node.get_parent() == null or node.get_parent() == get_tree().get_root():
return node
return get_scene_root(node.get_parent())func copy_array(arr: Array) -> Array:
var new_arr = []
for i in arr:
if i is Array:
new_arr.append(copy_array(i))
continue
new_arr.append(i)
return new_arr调用函数时,会将函数压入调用栈,函数返回时会将函数弹出调用栈
以get_scene_root(Level3)和这个场景树结构为例
Root (Viewport)
└── Level1
└── Level2
└── Level3 (传入的 node)
练习
实现函数
is_even(n: int) -> bool,判断一个整数是否为偶数,如果是偶数返回true,否则返回false实现函数
first_char(s: String) -> String,返回传入字符串的第一个字符。如果字符串为空,返回""手动实现函数
max(a: int, b: int) -> int,返回两个整数中的较大值,不能内置的max函数手动实现函数
abs_val(n: int) -> int,返回这个整数的绝对值,不能使用内置的abs函数实现函数
is_capital_letter(s: String) -> bool,如果字符串是单个大写英文字母,则返回true实现函数
is_leap_year(year: int) -> bool,判断某年份是否是闰年(4年一闰,百年不闰,四百年再闰)实现函数
count_char(s: String, target: String) -> int,返回目标字符target在字符串s中出现的次数实现函数
find_char(s: String, target: String) -> int,返回目标字符target在字符串s中第一次出现的位置,如果不存在则返回-1实现函数
strlen(s: String) -> int,返回字符串的长度,不能使用.length()实现函数
count_to(n: int) -> void,递归打印从 1 到 n 的所有整数(每行一个)gdscriptcount_to(3) # 输出: # 1 # 2 # 3实现函数
factorial(n: int) -> int,递归计算 n 的阶乘(n! = n * (n-1)!,特别地, 0! = 1, 1! = 1)gdscriptfactorial(5) # 返回 120实现函数
countdown(n: int) -> void,递归打印从 n 到 1gdscriptcountdown(3) # 输出: # 3 # 2 # 1实现函数
fib(n: int) -> int,返回斐波那契数列的第 n 项(从 0 开始)gdscriptfib(0) # 返回 0 fib(1) # 返回 1 fib(2) # 返回 1 fib(3) # 返回 2 fib(4) # 返回 3fib(n) = fib(n-1) + fib(n-2),要注意考虑 n=0 和 n=1 的终止条件
实现函数
reverse(s: String) -> String,返回传入字符串的逆序结果gdscriptreverse("abcd") # 返回 "dcba"实现函数
strlen_rec(s: String) -> int,使用递归来计算字符串的长度,不用.length()和for实现函数
digit_sum(n: int) -> int,返回一个整数的所有位的和gdscriptdigit_sum(1234) # 返回 10 (1+2+3+4)实现函数
is_palindrome(s: String) -> bool,递归判断字符串是否是回文(正着反着一样)gdscriptis_palindrome("level") # true is_palindrome("hello") # false