第五章:GDScript基础语法

第五章: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的异同

相似之处:

  • 缩进定义代码块
  • 动态类型(可选静态类型)
  • 简洁的语法风格
  • 列表和字典语法相似

主要区别:

特性PythonGDScript
变量声明x = 10var x = 10
常量声明X = 10(约定)const X = 10
函数定义def func():func func():
空值Nonenull
布尔值True/Falsetrue/false
自引用selfself(可省略)
类型注解x: int = 10var 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
└── ...

创建脚本的方式:

  1. 右键节点 → 附加脚本
  2. 文件系统面板 → 右键 → 新建脚本
  3. 快捷键:选中节点后按 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_caseplayer_health
常量UPPER_SNAKEMAX_SPEED
函数snake_caseget_health()
类名PascalCasePlayerController
信号snake_casehealth_changed
枚举PascalCaseState.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< > <= >= == !=
12in is
13not !
14and &&
15or `\\`
16if 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
%.2f2位小数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的基础语法:

  1. GDScript简介:专为Godot设计,类Python语法,深度集成引擎
  2. 脚本结构:extends继承、class_name命名、组织规范
  3. 注释文档:单行注释#、文档注释##
  4. 语法规则:缩进、标识符、命名约定
  5. 关键字:声明、控制流、操作符、特殊关键字
  6. 运算符:算术、比较、逻辑、位运算、赋值
  7. 输入输出:print函数、格式化输出
  8. 代码风格:官方风格指南

掌握这些基础后,下一章我们将深入学习GDScript的变量和数据类型系统。


上一章:第一个项目

下一章:变量与数据类型

← 返回目录