变量
变量就像一个盒子,是用于存储数据的容器,它可以存储不同类型的数据,如数字、字符串、布尔值等。
给数据起个名字,我们就可以通过这个名字来获取这个数据,这就是变量。
变量的声明
变量的声明语法如下:
var 变量名
var 变量名 = 初始值
var 变量名: 数据类型 = 初始值
示例:
var name
var foo = 100
var age: int = 18除此之外,对于变量名(标识符)的命名也有如下要求:
可以包含字母字符(
a-z和A-Z)可以包含数字(
0-9)可以包含下划线(
_)不能以数字开头
不能包含空格
不能是语言内保留关键字(比如
true、false、if、func等)不能与当前作用域已有变量重名
大小写敏感(
foo和Foo被认为是两个不同的变量)可以使用部分Unicode字符(也就是说,可以使用非英文字符作为标识符使用)
易与ASCII字符混淆的字符以及颜文字无法作为标识符使用
如上规则也同样适用于函数、信号、类等标识符的命名
在名字前添加下划线,表示是私有 (private) 的变量,或丢弃(不使用)
在GDScript中,变量的标识符一般使用蛇形命名(snake_case),即全小写字母并以下划线分割单词
变量的赋值
GDScript的变量赋值,使用两套规则
1. 非静态类型的变体赋值
GDScript是一门动态语言,其变量的类型可以不是固定的,这意味着你可以反复给一个变量赋不同类型的值
来看个例子:
var foo
func _ready() -> void:
foo = 10
foo = "Foo"
foo = Node.new()
foo = 3.14上面的代码中,我们声明了一个变量 foo,然后在 _ready() 函数中不断更改它的值:
foo = 10:将整数 10 赋给变量foofoo = "Foo":再把字符串"Foo"赋给它foo = Node.new():把一个新建的 Node 实例赋给foofoo = 3.14:最后又变成了一个浮点数
可以看出,变量 foo 的类型是动态变化的,这就是变体类型的特性
那么问题来了~如果我们声明变量时不给它赋值,它的类型会是什么呢?
我们可以使用print()函数来试试:
var foo
func _ready() -> void:
print(foo) # 会输出<null>运行后,就会发现控制台输入了如下内容:
Variant(变体)
像上面这样,声明变量但不既不赋予其初始值,也不指定其类型,那么这个变量的类型就会是Variant变体类型
Variant 是一种特殊的万能类型,它可以被赋为任意类型的值,也能表示为任意类型的值。
可以说它是 GDScript 中所有类型的「祖宗」
但要注意,Variant 并不是一个确切的类型,它更像是 “我暂时还不知道你是什么,但在我使用你之前先允许你可以是任何东西” 的状态
这在某些场景下可能会引发问题,比如写错类型调用了不存在的方法,就会报错!
Null 和 NIL
当 Variant 类型的变量没有被赋予任何值时,其值为null,其类型为NIL
null 的字面意思是「空值」或「未知值」~大多数情况下它表示某种错误或“还没初始化”,所以我们不能对它执行任何操作,因为它除了代表“没有”,啥都没有
2. 静态类型的赋值
什么叫静态类型呢?
区别于上文提及的动态变体类型,静态类型在声明变量时,其值的类型就是固定的,也就是说,在你声明变量时就明确指定其类型时,这个变量之后就只能保存这种类型的值,不能再像Variant那样变来变去
var integer: int
func _ready() -> void:
integer = 10
print(integer)
integer = 3.14 # 浮点数可以直接转换成整数,所以不会报错,但是有副作用
print(integer) # 可以看到输出的是3,而不是3.14
integer = Node.new() # 解析错误,不能将`Node`类型赋值给整数类型
integer = "Hello" # 解析错误,不能将字符串类型赋值给整数类型上面的示例代码中,我们使用var integert: int声明了一个整数类型的变量
又在_ready()函数中尝试将不同类型的值赋值给它
在编写过程中我们就能看到,第8行以及第9行的内容,会因类型错误而无法解析脚本,阻止你运行游戏
而浮点数赋值不会导致报错,是因为可以直接转换为整数(但是有副作用,会导致小数位丢失,比如将3.14变为3)
关于类型的知识,我们在后面的章节再讲,本文不过多赘述
所以,当我们使用var 变量名: 数据类型的语法声明变量时,其类型就是固定的,不能将不兼容的值赋值给它
相比于动态类型,使用静态类型可以让编辑器提供更好的代码补全和检查能力,还能减少运行时错误
变量的作用域
作用域决定了变量可以在程序的哪些地方使用
变量是当前代码块中声明的,就只能在这个代码块中使用
即便类的成员是属于同一个家庭的,但也总有些老死不相往来的家庭成员,这辈子都不会见一面,也不会去不想去不能去的地方
举个例子:
var father # 类成员变量
var mother# 类成员变量
func mather_room():
mother.give_gift(father) # 不会产生错误
func father_room():
father.talk_to(brother) # 解析错误,标识符`brother`未在此作用域内声明
brother.hug(sister) # 解析错误,标识符`brother`, `sister`未在此作用域内声明
func sister_room():
var sister # 局部变量
func brother_room():
var brother # 局部变量上面的例子中,你可以想象这是一个关系不合的家庭
父母夫妻关系很好
父母与子女之间关系不合
子女之间关系不合
类成员变量:定义在函数外部,可以被类内的所有函数访问,像经常在客厅里活动的父母,谁都能看见他们
局部变量:定义在函数内部,只能在函数内部当前代码块使用,像躲在房间里的小孩子,其他人根本找不到,ta也不想出去
遮蔽:当局部变量命名与成员变量相同时,会遮蔽成员变量,导致在当前作用域访问的该名称的变量始终是局部变量
小练习
- 下面这几行代码中,哪些变量的命名是非法的?为什么?
var 1apple = "fruit"
var my variable = 123
var _score = 99
var func = 3.14
var isReady = true
var True = false- 不运行代码,尝试直接说出下面代码的控制台输出结果
var a
func _ready() -> void:
print(a)
a = 3
print(a)- 下面这段代码会报错吗?如果会,是为什么?
var count: int
func _ready() -> void:
count = "five"
var index = 0
func method():
index = -1
print(index)- 这段代码中有哪些变量是局部变量,哪些是成员变量?
var outer = 100
func example():
var inner = 50
print(outer + inner)
var val = "value"
func example_2():
var inner_2 = 100
print(outer + inner_2)- 如何理解下面这段话?
“
Variant是一种可以容纳任何类型的变量,但它本身没有固定的类型。”