382人加入学习
(0人评价)
GDScript初学者入门教程-基于Godot和AI教学

使用Godot4.6.2 制作完成于2026.5.13

价格 免费

### 12. Vector2 / Color(内置值类型)

Vector(中文:向量,),在godot中保存坐标或向量,在数学中,向量指具有大小和方向的量。在二维平面三维空间中,它可以形象化地表示为带箭头的线段。与向量对应的量叫做数量,只有大小,没有方向。

Color 是按照RGBA模式进行操作的:

R->RED,G->GREEN,B->BLUE,A->ALPHA

> 配套示例:`examples/12_vector_color.gd`

GDScript 内置了一批"几何 / 颜色"类型,写代码时离不开。

#### Vector2 向量

```gdscript
var pos := Vector2(100, 50) #坐标:100表示下,50表示y
var velocity := Vector2(3, 4)

# 向量加减乘除:跟数字一样自然,x和y坐标分别各自运算
pos + velocity              # Vector2(103, 54)
velocity * 2                # Vector2(6, 8)

# 方向常量(写移动代码每天都用)因为godot的坐标系设定为x轴向右为正,y轴向下为正
Vector2.ZERO                # (0, 0)
Vector2.UP                  # (0, -1)(y 轴向下,向上是负的)
Vector2.RIGHT               # (1, 0)

# 常用方法
velocity.length()           # 5.0 这里涉及到向量的运算,对角线长度^2 = 边长a^2 + 边长b^2 ± 2ab*cos (θ) 其中,a和b是平行四边形的相邻边长,θ是这两边之间的夹角。 根据平行四边形的不同类型,我们可以使用这个公式的不同形式。 例如,对于矩形,θ=90°,cos (θ)=0,所以公式简化为对角线长度^2 = a^2 + b^2。 对于菱形,a=b,且对角线互相垂直,因此可以直接使用勾股定理计算。
velocity.normalized()       # 单位向量(长度为 1)
pos.distance_to(target)     # 两点距离
pos.direction_to(target)    # 朝目标的单位向量
```

#### Color

```gdscript
var red := Color(1, 0, 0, 1)              # 浮点 RGBA,每个分量 0~1
var semi_blue := Color(0, 0, 1, 0.5)      # 半透明
var orange := Color("ff8000")             # 十六进制字符串
var green := Color.GREEN                  # 命名常量
```

> 还有 `Vector3` / `Vector4` / `Rect2` / `Transform2D` 等,按需查 Godot 文档。

## 第 12 节:Vector2 / Color(内置值类型)
##
## 演示常用的 Vector2 操作和 Color 表示。

extends Node


func _ready() -> void:
    demo_vector2()
    demo_color()


func demo_vector2() -> void:
    print("--- Vector2 ---")

    # 创建
    var pos := Vector2(100, 50)
    var velocity := Vector2(3, 4)
    print("位置:", pos)  #输出:位置:(100.0, 50.0)
    print("速度:", velocity)  #输出:速度:(3.0, 4.0)

    # 内置方向常量
    print("Vector2.ZERO  =", Vector2.ZERO)  #输出:Vector2.ZERO  =(0.0, 0.0)
    print("Vector2.UP    =", Vector2.UP) #输出:Vector2.UP    =(0.0, -1.0)
    print("Vector2.RIGHT =", Vector2.RIGHT)  #输出:Vector2.RIGHT =(1.0, 0.0)
    

    # 向量运算
    var new_pos := pos + velocity
    print("位置 + 速度 =", new_pos) #输出:位置 + 速度 =(103.0, 54.0)
    print("速度 * 2    =", velocity * 2)  #输出:速度 * 2    =(6.0, 8.0)

    # 长度与单位向量
    print("速度的长度:", velocity.length())            # 5.0
    print("速度归一化(单位向量):", velocity.normalized())

    # 距离与朝向
    var a := Vector2(0, 0)
    var b := Vector2(3, 4)
    print("a 到 b 的距离:", a.distance_to(b))          # 5
    print("a 指向 b 的单位向量:", a.direction_to(b))   # (0.6, 0.8)  b-a


func demo_color() -> void:
    print("--- Color ---")

    # 浮点 RGBA(0~1)
    var red := Color(0.276, 0.565, 0.763, 1.0)
    var semi_blue := Color(0, 0, 1, 0.5)
    print("纯红:", red)   #输出:纯红:(1.0, 0.0, 0.0, 1.0)
    print("半透明蓝:", semi_blue)  #输出:半透明蓝:(0.0, 0.0, 1.0, 0.5)

    # 也可以用十六进制字符串
    var orange := Color("ff8000")
    print("橘色:", orange)  #输出:橘色:(0.0, 0.502, 0.0, 1.0)

    # 命名颜色(常量)
    print("Color.GREEN:", Color.GREEN)  #输出:Color.GREEN:(0.0, 1.0, 0.0, 1.0)
    

 

[展开全文]

### 11. 字符串 String

> 配套示例:`examples/11_string.gd`

String 在 GDScript 中本身就是一种内置类,方法非常丰富。

```gdscript
int float bool String 

注意String第一个字母是大写 

var s := "Hello, GDScript!"

# 可以是空字符串,即空格

s.length()                  # 16
s[0]                        # "H"
s.substr(0, 5)              # "Hello" 注意函数里面的参数不是索引号,而是从0号位到第5号位置的字符的个数

s.to_lower()                # "hello, gdscript!"
s.to_upper()                # "HELLO, GDSCRIPT!"

s.contains("GD")            # true
s.begins_with("Hello")      # true
s.ends_with("!")            # true

s.replace("GDScript", "Godot")

# 拆分与拼接
"apple,banana,cherry".split(",")        # ["apple","banana","cherry"] 按照,进行拆分
" | ".join(["a", "b", "c"])             # "a | b | c"

"   hello   ".strip_edges()             # "hello" 只去字符串前端和后端的空格,字符串中间的空格不处理,一般用于用户输入名称登录,系统自动处理名称使用

# 转数字
"42".to_int()               # 42
"3.14".to_float()           # 3.14
```

 

## 第 11 节:字符串 String
##
## 演示 String 的常用方法。

extends Node


func _ready() -> void:
    var s := "Hello, GDScript!"
    #var s :String = "Hello, GDScript!"

    # 长度与索引
    print("长度:", s.length())
    print("第 0 个字符:", s[0])      # H
    print("子串 [0:5]:", s.substr(0, 5))      # Hello

    # 大小写
    print("小写:", s.to_lower())
    print("大写:", s.to_upper())

    # 包含 / 前缀 / 后缀
    print("包含 GD?", s.contains("GD"))
    print("以 Hello 开头?", s.begins_with("Hello"))
    print("以 ! 结尾?", s.ends_with("!"))

    # 替换
    print("替换 GDScript:", s.replace("GDScript", "Godot"))

    # 拆分与拼接
    var csv := "apple,banana,cherry"
    var parts := csv.split(",")
    print("拆分:", parts)
    print("再拼接:", " | ".join(parts))

    # 去除首尾空白
    var msg := "   hello world   "
    print("strip_edges:[", msg.strip_edges(), "]")

    # 转换数字
    var num_str := "34"  #如果字符串里面包含文字字符,godot高版本会自动去除非数字字符,而保留数字字符再进行转换
    print("转 int:", num_str.to_int())
    var float_str := "3.14"
    print("转 float:", float_str.to_float())

    # 字符串与值类型混拼,要用 str()
    var score := 99  
    print("得分:" + str(score)) 
 

[展开全文]

### 4. 枚举 enum

> 配套示例:`examples/04_enum.gd`

枚举用来定义"一组命名常量",比写一堆 `const` 整齐得多。

```gdscript
enum State { IDLE, RUN, JUMP, ATTACK } 

 # 这里的状态 翻译成中文,对应为4个状态,空闲,跑,跳,攻击

var current := State.IDLE

func update_state(s):
    current = s
    match current:
        State.IDLE:    print("在发呆")
        State.RUN:     print("在跑")
        State.JUMP:    print("在跳")
        State.ATTACK:  print("在攻击")
```

也可以显式指定值:

```gdscript
enum Dir { LEFT = -1, RIGHT = 1 }

#枚举类型通常是自动按0,1...取值,但也可以指定数值
```

**底层原理**:枚举值实际就是 int。`State.IDLE` 在内部是 `0`、`State.RUN` 是 `1`,依此类推。

 

## 第 4 节:枚举 enum
##
## 演示用 enum 表示"一组命名常量"。
## 配合 match 写状态分支非常顺手。

extends Node

#int  float  String   这里enum算是一个新的类型,但本质还是int类型
enum State { IDLE, RUN, JUMP, ATTACK }
enum Direction { LEFT = -1, RIGHT = 1 }

var current_state := State.IDLE
var facing := Direction.RIGHT


func _ready() -> void:
    print("当前状态:", current_state)
    print("State.IDLE 实际是数字:", State.IDLE)
    print("State.RUN 实际是数字:", State.RUN)

    current_state = State.RUN
    describe_state()

    current_state = State.ATTACK
    describe_state()

    print("朝向:", facing)


func describe_state() -> void:
    match current_state:
        State.IDLE:
            print("在发呆")
        State.RUN:
            print("在跑")
        State.JUMP:
            print("在跳")
        State.ATTACK:
            print("在攻击")
        _:
            print("未知状态")
 

## 第 4 节附加:为什么要用枚举?
##
## 同一个"角色状态机"分别用 3 种写法实现:
##   方案 A:直接用 int "魔法数字"
##   方案 B:用一堆 const 常量
##   方案 C:用 enum 枚举(推荐)
##
## 把脚本挂到任意 Node 上,F6 运行场景看输出。
## 然后建议你自己改两行代码,体会哪种写法最不容易出错。

extends Node


# =========================================================
# 方案 A:用裸数字(最差写法)
# =========================================================
# 约定:0 = 待机,1 = 跑步,2 = 跳跃,3 = 攻击
# 缺点:
#   1. 看到 "2" 完全不知道是啥
#   2. 写错数字(比如 5)编辑器不会报错
#   3. 以后想在中间插一个新状态,所有数字都要重排
var state_a: int = 0


# =========================================================
# 方案 B:用一堆 const 常量
# =========================================================
# 比 A 好多了,至少看得懂名字。
# 但还是有坑:
#   1. 类型还是 int,state_b = 999 不会报错
#   2. 没"组"的概念,常量列表会越来越乱
#   3. 不能一次性遍历"所有状态"
const STATE_IDLE := 0
const STATE_RUN := 1
const STATE_JUMP := 2
const STATE_ATTACK := 3

var state_b: int = STATE_IDLE


# =========================================================
# 方案 C:用 enum(推荐)
# =========================================================
# 好处:
#   1. State 是独立类型,编辑器有自动补全
#   2. State.RUNN 这种拼写错误会立刻飘红
#   3. 天然有"分组"概念,相关常量挤在一起
#   4. 可以用 State.keys() / State.values() 遍历
enum State { IDLE, RUN, JUMP, ATTACK }

var state_c: State = State.IDLE


func _ready() -> void:
    print("========== 方案 A:裸数字 ==========")
    state_a = 1
    describe_a(state_a)

    # 下面这一行是 bug,但编辑器不会拦你
    state_a = 999
    describe_a(state_a)

    print("\n========== 方案 B:const 常量 ==========")
    state_b = STATE_RUN
    describe_b(state_b)

    # const 也只是 int,这种错误还是逃不掉
    state_b = 999
    describe_b(state_b)

    print("\n========== 方案 C:enum(推荐)==========")
    state_c = State.RUN
    describe_c(state_c)

    state_c = State.ATTACK
    describe_c(state_c)

    # enum 自带遍历能力,A 和 B 都做不到
    print("\n所有状态名:", State.keys())
    print("所有状态值:", State.values())

    # 下面这一行如果取消注释,编辑器会直接飘红
    # state_c = State.RUNN     # ❌ 拼写错误,Godot 立刻报错
    # state_c = 999            # ❌ 类型不匹配,Godot 立刻报错


# ---------------------------------------------------------
# 方案 A 的处理函数:必须靠"魔法数字"判断
# ---------------------------------------------------------
func describe_a(s: int) -> void:
    # 看到这些数字,三个月后的你会一脸懵
    match s:
        0: print("[A] 待机")
        1: print("[A] 跑步")
        2: print("[A] 跳跃")
        3: print("[A] 攻击")
        _: print("[A] 未知状态(值=%d),但编辑器没发现错误" % s)


# ---------------------------------------------------------
# 方案 B 的处理函数:好一点,但还是 int
# ---------------------------------------------------------
func describe_b(s: int) -> void:
    match s:
        STATE_IDLE:   print("[B] 待机")
        STATE_RUN:    print("[B] 跑步")
        STATE_JUMP:   print("[B] 跳跃")
        STATE_ATTACK: print("[B] 攻击")
        _:            print("[B] 未知状态(值=%d)" % s)


# ---------------------------------------------------------
# 方案 C 的处理函数:参数类型就是 State,传错直接报错
# ---------------------------------------------------------
func describe_c(s: State) -> void:
    match s:
        State.IDLE:   print("[C] 待机")
        State.RUN:    print("[C] 跑步")
        State.JUMP:   print("[C] 跳跃")
        State.ATTACK: print("[C] 攻击")
        _:            print("[C] 未知状态")
 

[展开全文]

### 10. 字典 Dictionary

> 配套示例:`examples/10_dictionary.gd`

字典是键值对集合,相当于其他语言的 map / object。key-value

```gdscript
var player := {
    "name": "Alice",
    "level": 10,
    "hp": 100,
}

# 上述player是一个字典,两个数据用:来分辨键(key)和值(value),比如"name": "Alice" 键就是name,值就是Alice,每对键值用,分割

 

# 访问(两种语法都行) 访问=读取
player["name"]       # "Alice"
player.level         # 10(key 是合法标识符时才能这样)

# 增 / 改
player["mp"] = 50
player["hp"] = 80

# 删
player.erase("level")

# 查
player.has("hp")
player.size()

# 遍历
for key in player:
    print(key, " = ", player[key])
```

字典也是按引用传递。

---
 

## 第 10 节:字典 Dictionary
##
## 演示 Dictionary(键值对)的常用操作。

extends Node

enum STATE{IDLE,RUN}  #这里用枚举来举例使用数组


func _ready() -> void:
    # 创建
    var player := {
        "name": "Alice",
        "level": 10,
        "hp": 100,
    }
    #var player :Dictionary = {
        #"name": "Alice",
        #"level": 10,
        #"hp": 100,
    #}

    # 访问
    print("名字:", player["name"])
    print("等级:", player.level)         # 也可以用点语法(key 是合法标识符时)

    # 修改
    player["hp"] = 80
    print("修改后血量:", player["hp"])

    # 新增
    player["mp"] = 50
    print("新增魔法值后:", player)

    # 删除
    player.erase("level")
    print("删除等级后:", player)

    # 查询
    print("有 hp 吗:", player.has("hp"))
    print("一共有 ", player.size(), " 个字段")

    # 遍历 key
    print("--- 遍历 keys ---")
    for key in player.keys():
        print(key, " = ", player[key])

    # 遍历 value
    print("--- 遍历 values ---")
    for v in player.values():
        print("值:", v)

    # 简写:直接遍历 dict 等价于遍历 keys
    print("--- 直接 for in dict ---")
    for key in player:
        print(key, " -> ", player[key])
    
    var v1:Array = [1,2,3,4,5,2,3]
    print(v1)
    
    var v2:Dictionary ={
        "name":"siki",
        "name2":"siki"
    }
    
# 注意:字典中的键 不可以相同或者重复,否则报错,但数组可以有相同的或者重复的值

[展开全文]

## 第 9 节:数组 Array
##
## 演示 Array 的创建、增删查改、遍历。

数组用方括号创建,存放任意类型的元素。
var enemies: Array = ["slime", "Goblin", "orc"]


#索引访问(支持负数索引)

enemies [o]      #"Slime" 这里的0表示数组数据中的第一个

enemies[-1]     #“orc"(最后一个) ,-1在这里表示数组中倒数第一个数据

#问题:如果查询超出索引数据,会出现什么情况?


#增删
enemies.append ("Dragon")    #末尾追加

enemies.insert(1,"wolf")    #在指定位置1插入

enemies.erase("Slime")    #按值删除

enemies.pop_back()   #弹出最后一个,弹出即删除的意思

#这里使用方法的是数组名+"."+方法(或者叫系统给出的函数)来调用,来实现某个功能


#查询
enemies.size   #数组长度,即多少个数据

Oenemies.has("Goblin")   # true / false

enemies.find("Goblin")  #索引,找不到返回-1
 

#has 和find都是查询,has只是简单判断有没有这个数据,find除了判断有没有,还要给出具体的索引位置


#遍历
for e in enemies:print(e)


重要坑:数组在GDScript中是按引用传递的。
func modify(arr):arr.append("x")
var a := [1,2, 3]modify(a)print(a)
#这会改到外面的原数组!
#[1,2,3,"x"]
要传副本得显式调用 .duplicate()。

extends Node

enum State{IDLE,RUN}
    

func _ready() -> void:
    var enemies: Array = ["Slime", "Goblin", "Orc"]
    var enemies2: Array = [1,2,3,4,5]
    var enemies4: Array = [1,2,3,4,5,"sdfer"]
    
    var enemies3: Array = [State.IDLE,2,3,4,5]
    

    # 索引访问
    print("第一个敌人:", enemies[0])
    print("最后一个敌人:", enemies[-1])    # 支持负索引

    # 长度
    print("一共有 ", enemies.size(), " 个敌人")

    # 增加
    enemies.append("Dragon")
    print("追加 Dragon 后:", enemies)

    enemies.insert(1, "Wolf")
    print("在位置 1 插入 Wolf:", enemies)

    # 删除
    enemies.erase("Slime")
    print("删除 Slime 后:", enemies)

    enemies.pop_back()
    print("弹出最后一个后:", enemies)

    # 查找
    print("有 Goblin 吗:", enemies.has("Goblin"))
    print("Goblin 的索引:", enemies.find("Goblin"))

    # 遍历
    print("--- 遍历 ---")
    for e in enemies:
        print("敌人:", e)

    # 带索引遍历
    print("--- 带索引遍历 ---")
    for i in range(enemies.size()):
        print(i, ":", enemies[i])

    # 注意:数组是按引用传递的
    demo_reference(enemies)
    print("函数处理后的原数组:", enemies)


func demo_reference(arr: Array) -> void:
    # 这里修改会影响外部的原数组!
    arr.append("被函数加进来的")
 

[展开全文]

## 第 3 节附加:变量作用域(新手只要懂这两种)
##
## "作用域" = 一个变量"在哪一段代码里能用"。
## 新手只需要分清两种就够了:
##   1. 脚本级变量(成员变量):整个脚本都能用
##   2. 函数局部变量:只在它所在的那个函数里能用
##
## 把脚本挂到任意 Node 上,F6 运行,对照控制台输出和注释看。

extends Node


# =========================================================
# 这里是"脚本级变量"区域
# ---------------------------------------------------------
# 写在所有函数外面(注意没有缩进),整个脚本里的任何函数都能读和改它。
# 通常用来存"这个角色一直要用到的数据",比如血量、金币、等级。
# =========================================================
var player_hp: int = 100
var gold: int = 0


func _ready() -> void:
    demo_script_level()
    demo_local_variable()
    demo_function_cant_see_local()
    demo_parameter_is_local()
    demo_shadow()


# =========================================================
# 演示 1:脚本级变量在任何函数里都能访问
# =========================================================
func demo_script_level() -> void:
    print("--- 1. 脚本级变量 ---")
    print("初始血量:", player_hp)   # 能读
    player_hp -= 20                   # 能改
    print("受伤后血量:", player_hp)
    add_gold()                        # 调用另一个函数也能改 gold
    print("拾取金币后:gold = ", gold)


# 另一个函数:照样能看到 gold,因为 gold 是脚本级的
func add_gold() -> void:
    gold += 50


# =========================================================
# 演示 2:函数里声明的变量 = 局部变量
# ---------------------------------------------------------
# 写在函数缩进里的 var,只在这个函数内部能用。
# 函数一旦执行完,这个变量就"消失"了。
# 不同的函数可以在内部起相同的变量名称,互不影响,相当于1班有个小明,2班也有个小明,是两个不同的人,分属不同的班级,但是同一个函数内不能起两个相同名字的变量

#=========================================================
func demo_local_variable() -> void:
    print("\n--- 2. 局部变量 ---")
    var damage: int = 30              # ← 局部变量,只在本函数里有效
    var crit: bool = true             # ← 同上
    print("本次伤害:", damage, "  暴击:", crit)
    # 函数结束后,damage 和 crit 就不存在了


# =========================================================
# 演示 3:别的函数看不到这里的局部变量
# ---------------------------------------------------------
# 这是新手最常踩的坑:以为在 A 函数里定义的变量,B 函数也能直接用。
# =========================================================
func demo_function_cant_see_local() -> void:
    print("\n--- 3. 局部变量出不了函数 ---")
    var secret: String = "我只在这个函数里存在"
    print("本函数内可以读:", secret)
    
    try_read_secret()
    # 如果你在 try_read_secret 里写 print(secret),会直接报错。


func try_read_secret() -> void:
    # 这里看不到 demo_function_cant_see_local 里的 secret
    # 下面这行如果取消注释,编辑器会立刻飘红:
    # print(secret)   # ❌ Identifier "secret" not declared
    print("另一个函数读不到上面的 secret")


# =========================================================
# 演示 4:函数的"参数"本质上也是局部变量
# ---------------------------------------------------------
# 参数和函数体里 var 出来的变量一样,只在本函数内有效。
# =========================================================
func demo_parameter_is_local() -> void:
    print("\n--- 4. 参数也是局部变量 ---")
    greet("小明")
    greet("小红")
    # 这里看不到上面 greet 函数里的 name 参数


func greet(name: String) -> void:
    # name 在 greet 内部有效,函数一结束就消失
    print("你好,", name)


# =========================================================
# 演示 5:同名时谁优先?("局部变量遮住脚本级变量")
# ---------------------------------------------------------
# 如果脚本级和局部变量起了相同的名字,函数里用到的会是"局部那个"。
# 新手建议:别故意起重名,纯粹找麻烦。
# =========================================================
func demo_shadow() -> void:
    print("\n--- 5. 同名时局部优先(建议避免)---")
    print("脚本级 player_hp 现在是:", player_hp)

    var player_hp: int = 9999         # 同名的局部变量
    print("函数里再声明同名的 player_hp = ", player_hp)
    print("这里用到的是【局部那个】,脚本级的没被改")

    # 函数结束后,下面那个 player_hp 消失,脚本级的还在
 

[展开全文]

# while
var i := 0
# 死循环,需要避免


while i < 3: #执行判断,当前是否<3,是的,执行循环
    print(i)   #实际输出0,1,2,当i=3时循环结束,不再执行当前print语句
    i += 1   # i=i+1, 

# for + range 

#range(n)->0...n-1

for i in range(5):           # 0, 1, 2, 3, 4  因为前面定义了i=0,所以数数是从零开始数5个数,所以只到4
    print(i)

#range(start,end) -> start...end-1

for i in range(2, 5):        # 2, 3, 4 注意5并不包括在内
    print(i)

# 遍历数组
for fruit in ["apple", "banana", "cherry"]:
    print(fruit)

#输出结果:水果:apple 水果:banana  水果:cherry

# break / continue
for i in range(10):
    if i == 3:
        continue            # 跳过 3  continue只终止本次循环,意为着后面没有输出print(i),即没有输出3
    if i == 7:
        break               # 到 7 停止  break会跳出这个循环,直接执行下面的语句
    print(i)

#上面程序输出的结果为i=0,i=1,i=2,i=4,i=5,i=6
```

`range()` 是 GDScript 内置的"按需生成数字序列"工具,最常用的就是 `for i in range(n):`。

在while语句中,i变量是提前定义好的

而for语句中,i变量可以提不用前定义,但是注意i变量只能用于当前for语句

[展开全文]

if 语句:只能有1个if和1个else,中间可以有多个分支:

1.

if

else

2.

if

elif

elif

...

else

match语句:(可以用if语句替代)

提高代码的可读性,分支简单,易观察的情况下用match

末尾:

_: 通配符 兜底

 

[展开全文]

授课教师

问问题加入A计划,有专门负责答疑的老师哦!!!

课程特色

下载资料(1)
视频(35)