第五章:GDScript基础语法
"GDScript是专为游戏开发而生的语言,简洁而强大。"
GDScript是Godot引擎的原生脚本语言,专为游戏开发优化。它的语法类似Python,学习曲线平缓,但又深度集成了Godot的节点系统。本章将带你全面掌握GDScript的基础语法。
5.1 GDScript简介
5.1.1 为什么选择GDScript
Godot支持多种脚本语言,但GDScript是最推荐的选择:
| 语言 | 特点 | 适用场景 |
|---|---|---|
| GDScript | 简单易学,深度集成,性能好 | 大多数项目(推荐) |
| C# | 静态类型,生态丰富 | 大型项目,Unity迁移 |
| C++ (GDExtension) | 最高性能 | 性能关键模块 |
GDScript的优势:
- 与引擎深度集成:自动补全、即时文档、调试工具
- 类Python语法:简洁优雅,易于学习
- 无需编译:修改即生效,快速迭代
- 游戏优化:内置Vector、Color等游戏常用类型
- 热重载:运行时修改代码,无需重启
5.1.2 GDScript与Python的异同
相似之处:
- 缩进定义代码块
- 动态类型(可选静态类型)
- 简洁的语法风格
- 列表和字典语法相似
主要区别:
| 特性 | Python | GDScript |
|---|---|---|
| 变量声明 | x = 10 | var x = 10 |
| 常量声明 | X = 10(约定) | const X = 10 |
| 函数定义 | def func(): | func func(): |
| 空值 | None | null |
| 布尔值 | True/False | true/false |
| 自引用 | self | self(可省略) |
| 类型注解 | x: int = 10 | var x: int = 10 |
| 打印输出 | print() | print() |
5.1.3 第一个GDScript脚本
# 这是一个简单的GDScript示例
extends Node
# 成员变量
var player_name: String = "Hero"
var health: int = 100
# 常量
const MAX_HEALTH: int = 100
# 当节点准备好时调用
func _ready() -> void:
print("游戏开始!")
print("玩家名称:", player_name)
greet()
# 自定义函数
func greet() -> void:
print("欢迎来到Godot的世界!")
# 每帧调用
func _process(delta: float) -> void:
# delta是上一帧到这一帧的时间间隔
pass # 暂时不做任何事
5.2 脚本结构
5.2.1 脚本文件
GDScript脚本文件以 .gd 为扩展名:
project/
├── scripts/
│ ├── player.gd
│ ├── enemy.gd
│ └── game_manager.gd
└── ...
创建脚本的方式:
- 右键节点 → 附加脚本
- 文件系统面板 → 右键 → 新建脚本
- 快捷键:选中节点后按 Ctrl+Shift+A
5.2.2 脚本基本结构
一个典型的GDScript脚本结构:
# 1. 类名(可选)
class_name Player
# 2. 继承声明(必须)
extends CharacterBody2D
# 3. 文档注释(可选)
## 玩家控制器类
## 处理玩家的移动、跳跃和动画
# 4. 信号声明
signal health_changed(new_health: int)
signal died
# 5. 枚举定义
enum State { IDLE, RUNNING, JUMPING, FALLING }
# 6. 常量
const MAX_SPEED: float = 300.0
const JUMP_FORCE: float = -400.0
# 7. 导出变量(在检查器中可见)
@export var speed: float = 200.0
@export var jump_height: float = 300.0
# 8. 公共变量
var current_state: State = State.IDLE
var is_alive: bool = true
# 9. 私有变量(约定以_开头)
var _health: int = 100
var _velocity: Vector2 = Vector2.ZERO
# 10. @onready变量
@onready var sprite: Sprite2D = $Sprite2D
@onready var animation_player: AnimationPlayer = $AnimationPlayer
# 11. 内置回调函数
func _ready() -> void:
pass
func _process(delta: float) -> void:
pass
func _physics_process(delta: float) -> void:
pass
func _input(event: InputEvent) -> void:
pass
# 12. 公共方法
func take_damage(amount: int) -> void:
_health -= amount
health_changed.emit(_health)
if _health <= 0:
die()
# 13. 私有方法
func _die() -> void:
is_alive = false
died.emit()
5.2.3 extends关键字
extends 声明脚本继承的父类:
# 继承内置节点类
extends Node2D
extends CharacterBody2D
extends Control
# 继承自定义类
extends "res://scripts/base_enemy.gd"
# 继承命名类
extends BaseEnemy # 如果BaseEnemy有class_name声明
不写extends时:
- 默认继承
RefCounted - 脚本可以作为独立资源使用
- 不能附加到节点上
5.2.4 class_name关键字
class_name 为脚本定义全局可访问的类名:
# player.gd
class_name Player
extends CharacterBody2D
# 现在可以在其他地方直接使用Player类型
使用场景:
# 其他脚本中
var player: Player # 类型提示
var new_player = Player.new() # 创建实例(仅适用于不依赖场景的类)
# 类型检查
if node is Player:
print("这是玩家")
5.3 注释与文档
5.3.1 单行注释
使用 # 开始单行注释:
# 这是一个单行注释
var health = 100 # 行末注释
# 可以用于临时禁用代码
# var debug_mode = true
5.3.2 多行注释
GDScript没有专门的多行注释语法,但可以使用连续的单行注释:
# 这是一段
# 多行注释
# 说明这个函数的用途
func complex_function():
pass
或使用多行字符串(但会占用内存):
"""
这是一段多行字符串
可以用作长注释
但实际上会被当作字符串处理
"""
5.3.3 文档注释
使用 ## 创建文档注释,会显示在编辑器的帮助系统中:
## 玩家角色类
##
## 这个类负责处理玩家的所有行为,包括:
## - 移动和跳跃
## - 生命值管理
## - 动画控制
class_name Player
extends CharacterBody2D
## 玩家的移动速度
@export var speed: float = 200.0
## 玩家的最大生命值
const MAX_HEALTH: int = 100
## 对玩家造成伤害
## [br][br]
## [param amount]: 伤害数值
## [br]
## [return]: 剩余生命值
func take_damage(amount: int) -> int:
health -= amount
return health
文档标签:
| 标签 | 用途 |
|---|---|
[br] | 换行 |
[param name] | 参数说明 |
[return] | 返回值说明 |
[code]...[/code] | 代码块 |
[b]...[/b] | 粗体 |
[i]...[/i] | 斜体 |
5.4 基本语法规则
5.4.1 缩进规则
GDScript使用缩进定义代码块,必须使用Tab或空格(一致):
func example():
if true:
print("缩进一级")
if true:
print("缩进两级")
print("回到一级缩进")
注意事项:
- 推荐使用Tab缩进
- 不要混用Tab和空格
- 编辑器默认使用Tab
5.4.2 语句和表达式
语句(Statement):执行操作
var x = 10 # 变量声明语句
print("Hello") # 函数调用语句
if x > 5: # 条件语句
pass
表达式(Expression):产生值
10 + 20 # 算术表达式
x > 5 # 比较表达式
func_call() # 函数调用表达式
"Hello" + " World" # 字符串连接表达式
5.4.3 行继续
长行可以使用 `` 继续到下一行:
var long_calculation = 100 + 200 + 300 + \
400 + 500 + 600
# 括号内自动继续
var array = [
1, 2, 3,
4, 5, 6
]
var dict = {
"name": "Player",
"health": 100,
"position": Vector2(0, 0)
}
5.4.4 标识符命名规则
合法的标识符:
- 以字母或下划线开头
- 可包含字母、数字、下划线
- 区分大小写
- 不能是关键字
# 合法
var player_name
var _private_var
var playerHealth
var player2
# 非法
var 2player # 不能以数字开头
var player-name # 不能包含连字符
var var # 不能使用关键字
命名约定:
| 类型 | 约定 | 示例 |
|---|---|---|
| 变量 | snake_case | player_health |
| 常量 | UPPER_SNAKE | MAX_SPEED |
| 函数 | snake_case | get_health() |
| 类名 | PascalCase | PlayerController |
| 信号 | snake_case | health_changed |
| 枚举 | PascalCase | State.IDLE |
| 私有 | 前缀_ | internalvalue |
5.5 关键字一览
5.5.1 声明关键字
var # 变量声明
const # 常量声明
enum # 枚举声明
class # 内部类声明
class_name # 类名声明
extends # 继承声明
signal # 信号声明
func # 函数声明
static # 静态成员
5.5.2 控制流关键字
if # 条件判断
elif # 否则如果
else # 否则
for # 循环
while # 条件循环
match # 模式匹配
break # 跳出循环
continue # 继续下一次循环
pass # 空语句
return # 返回值
5.5.3 操作符关键字
and # 逻辑与(等同于 &&)
or # 逻辑或(等同于 ||)
not # 逻辑非(等同于 !)
in # 包含检测
is # 类型检测
as # 类型转换
5.5.4 特殊关键字
self # 当前实例引用
super # 父类引用
null # 空值
true # 布尔真
false # 布尔假
await # 等待协程/信号
preload # 预加载资源
load # 运行时加载资源
5.5.5 注解(Annotations)
@export # 导出到检查器
@export_range(0, 100) # 带范围的导出
@export_enum("A", "B") # 枚举导出
@onready # 节点准备后初始化
@tool # 编辑器中运行
@icon # 自定义节点图标
@warning_ignore # 忽略特定警告
5.6 运算符详解
5.6.1 算术运算符
var a = 10
var b = 3
print(a + b) # 加法: 13
print(a - b) # 减法: 7
print(a * b) # 乘法: 30
print(a / b) # 除法: 3.333...
print(a % b) # 取模: 1
print(a ** b) # 幂运算: 1000
# 整数除法
print(int(a / b)) # 3
5.6.2 比较运算符
var x = 10
var y = 20
print(x == y) # 等于: false
print(x != y) # 不等于: true
print(x < y) # 小于: true
print(x > y) # 大于: false
print(x <= y) # 小于等于: true
print(x >= y) # 大于等于: false
5.6.3 逻辑运算符
var a = true
var b = false
print(a and b) # 逻辑与: false
print(a or b) # 逻辑或: true
print(not a) # 逻辑非: false
# 也可以使用符号形式
print(a && b) # false
print(a || b) # true
print(!a) # false
5.6.4 位运算符
var a = 0b1010 # 10
var b = 0b1100 # 12
print(a & b) # 位与: 0b1000 = 8
print(a | b) # 位或: 0b1110 = 14
print(a ^ b) # 位异或: 0b0110 = 6
print(~a) # 位非: -11
print(a << 1) # 左移: 0b10100 = 20
print(a >> 1) # 右移: 0b0101 = 5
5.6.5 赋值运算符
var x = 10
x += 5 # x = x + 5 → 15
x -= 3 # x = x - 3 → 12
x *= 2 # x = x * 2 → 24
x /= 4 # x = x / 4 → 6
x %= 4 # x = x % 4 → 2
x **= 3 # x = x ** 3 → 8
# 位运算赋值
x &= 0b111
x |= 0b001
x ^= 0b010
x <<= 1
x >>= 1
5.6.6 特殊运算符
三元运算符:
var max_value = a if a > b else b
var status = "alive" if health > 0 else "dead"
in运算符:
# 检查元素是否在容器中
print("a" in "abc") # true
print(1 in [1, 2, 3]) # true
print("key" in {"key": 1}) # true
# 检查范围
for i in range(5):
print(i) # 0, 1, 2, 3, 4
is运算符:
# 类型检查
var node = Node2D.new()
print(node is Node) # true
print(node is Node2D) # true
print(node is Node3D) # false
# 空值检查
print(node is Node2D) # true
print(null is Node2D) # false
as运算符:
# 类型转换(失败返回null)
var sprite = node as Sprite2D
if sprite:
sprite.texture = some_texture
5.6.7 运算符优先级
从高到低:
| 优先级 | 运算符 | ||
|---|---|---|---|
| 1 | () 括号 | ||
| 2 | ** 幂 | ||
| 3 | ~ 位非 | ||
| 4 | - 负号(一元) | ||
| 5 | * / % | ||
| 6 | + - | ||
| 7 | << >> | ||
| 8 | & | ||
| 9 | ^ | ||
| 10 | `\ | ` | |
| 11 | < > <= >= == != | ||
| 12 | in is | ||
| 13 | not ! | ||
| 14 | and && | ||
| 15 | or `\ | \ | ` |
| 16 | if else(三元) | ||
| 17 | = 赋值运算符 |
5.7 输入输出
5.7.1 print函数
# 基本打印
print("Hello, Godot!")
# 多参数打印
var name = "Player"
var score = 100
print("Name:", name, "Score:", score)
# 打印变量
print(Vector2(10, 20)) # (10, 20)
print([1, 2, 3]) # [1, 2, 3]
print({"a": 1}) # {a:1}
5.7.2 格式化输出
# 字符串格式化
var name = "Player"
var health = 100
var position = Vector2(10, 20)
# 方法1:字符串连接
print("Name: " + name + ", Health: " + str(health))
# 方法2:格式化字符串
print("Name: %s, Health: %d" % [name, health])
# 方法3:模板字符串(推荐)
print("Name: {name}, Health: {health}".format({"name": name, "health": health}))
# 方法4:数组格式化
print("Position: %s" % [position])
格式化占位符:
| 占位符 | 类型 | 示例 |
|---|---|---|
%s | 字符串 | "Hello" |
%d | 整数 | 42 |
%f | 浮点数 | 3.14 |
%.2f | 2位小数 | 3.14 |
%x | 十六进制 | ff |
%o | 八进制 | 77 |
5.7.3 其他打印函数
# 打印到输出面板
print("普通输出")
# 打印错误(红色)
printerr("这是一个错误!")
# 打印警告(黄色)
push_warning("这是一个警告")
# 打印错误(会显示调用栈)
push_error("这是一个严重错误")
# 打印原始数据(不换行)
printt("Tab", "分隔") # Tab分隔
prints("空格", "分隔") # 空格分隔
printraw("无换行") # 不添加换行
5.8 代码风格指南
5.8.1 官方风格指南
Godot官方提供了GDScript风格指南,建议遵循:
# 1. 使用snake_case命名变量和函数
var player_health: int = 100
func get_player_position() -> Vector2:
pass
# 2. 使用PascalCase命名类
class_name PlayerController
# 3. 使用UPPER_SNAKE_CASE命名常量
const MAX_SPEED: float = 300.0
# 4. 私有成员前缀_
var _internal_state: int = 0
func _private_method() -> void:
pass
# 5. 类型注解增加可读性
var speed: float = 100.0
func move(direction: Vector2) -> void:
pass
# 6. 信号使用过去时或现在进行时
signal health_changed
signal player_died
signal item_collecting
5.8.2 代码组织
# 推荐的代码组织顺序
# 1. 类定义
class_name MyClass
extends Node
# 2. 信号
signal value_changed
# 3. 枚举
enum State { IDLE, ACTIVE }
# 4. 常量
const MAX_VALUE = 100
# 5. 导出变量
@export var exported_value: int = 0
# 6. 公共变量
var public_value: int = 0
# 7. 私有变量
var _private_value: int = 0
# 8. @onready变量
@onready var child_node = $ChildNode
# 9. 内置虚函数
func _ready() -> void:
pass
func _process(delta: float) -> void:
pass
# 10. 公共方法
func public_method() -> void:
pass
# 11. 私有方法
func _private_method() -> void:
pass
本章小结
本章我们学习了GDScript的基础语法:
- GDScript简介:专为Godot设计,类Python语法,深度集成引擎
- 脚本结构:extends继承、class_name命名、组织规范
- 注释文档:单行注释
#、文档注释## - 语法规则:缩进、标识符、命名约定
- 关键字:声明、控制流、操作符、特殊关键字
- 运算符:算术、比较、逻辑、位运算、赋值
- 输入输出:print函数、格式化输出
- 代码风格:官方风格指南
掌握这些基础后,下一章我们将深入学习GDScript的变量和数据类型系统。
上一章:第一个项目
下一章:变量与数据类型