第三部分:Input 输入系统、InputMap、八方向移动、动画状态切换

2026-05-19
401414 分钟
...

这一部分开始,你就会真正进入“角色能动起来”的阶段。

前面我们讲了:

Node 是对象
Scene 是节点组合
SceneTree 是运行时的总树
_process 是每帧更新
_physics_process 是物理更新
Signal 是节点通信

现在要接上游戏里最直接的东西:

玩家按键

Godot 识别输入

转换成移动方向

更新角色速度

move_and_slide() 移动

根据方向播放动画

1. Input 是什么

Input 是 Godot 内置的输入单例,用来处理键盘、鼠标、手柄、触摸等输入。它可以直接检查某个按键是否按下,也可以检查某个“输入动作”是否触发。Godot 官方文档也说明,Input 负责处理键盘、鼠标、手柄等输入,而输入动作可以在 Project Settings 的 Input Map 里配置。(Godot Engine documentation)

最简单的例子:

func _process(delta):
    if Input.is_key_pressed(KEY_SPACE):
        print("空格被按住了")

但在实际游戏里,不推荐大量直接写 KEY_SPACEKEY_WKEY_A

更推荐写成动作名:

func _process(delta):
    if Input.is_action_pressed("attack"):
        print("攻击键被按住了")

为什么?

因为你以后可以把 attack 绑定到:

键盘 J
鼠标左键
手柄 X
手机虚拟按钮

代码不用改。

这就是 InputMap 的价值。

2. InputMap 是什么

InputMap 可以理解成“按键配置表”。

前端类比的话,它有点像:

const keymap = {
  move_left: ['A', 'ArrowLeft'],
  move_right: ['D', 'ArrowRight'],
  attack: ['J', 'MouseLeft']
}

Godot 里则是在编辑器中配置:

Project
Project Settings
Input Map

然后添加动作:

move_up
move_down
move_left
move_right
interact
attack
dash
open_bag

Godot 官方文档里也说,InputMap 用来管理 InputEventAction,这些动作可以在 Project Settings > Input Map 里创建和修改,也可以通过代码创建。(Godot Engine documentation)

也就是说,你的代码不应该关心玩家到底按的是 W 还是方向键上。

代码只需要关心:

玩家是否触发了 move_up

3. 为什么不要直接写具体按键

不太推荐这样:

if Input.is_key_pressed(KEY_W):
    velocity.y = -speed

原因是:

以后想支持方向键要改代码
以后想支持手柄要改代码
以后想让玩家自定义按键也麻烦

推荐这样:

if Input.is_action_pressed("move_up"):
    velocity.y = -speed

这样你只需要在 InputMap 里给 move_up 添加多个输入:

W
方向键上
手柄左摇杆上

代码不用动。

这个思路非常重要。

做游戏时,代码最好写“行为”,不要写死“设备”。

差的写法 W 向上走
好的写法触发 move_up 向上走

4. 常用输入方法

Godot 里最常用的是这几个:

Input.is_action_pressed("xxx")
Input.is_action_just_pressed("xxx")
Input.is_action_just_released("xxx")

5. is_action_pressed()

表示某个动作正在被按住。

if Input.is_action_pressed("move_right"):
    print("正在向右")

适合:

移动
持续蓄力
持续奔跑
长按按钮
持续瞄准

比如角色移动:

if Input.is_action_pressed("move_right"):
    direction.x += 1

只要玩家一直按着右键,这个条件就一直成立。

6. is_action_just_pressed()

表示某个动作“刚刚按下的那一瞬间”。

if Input.is_action_just_pressed("attack"):
    print("攻击一次")

适合:

攻击
跳跃
打开背包
确认对话
拾取物品
交互

比如:

func _process(delta):
    if Input.is_action_just_pressed("open_bag"):
        open_bag()

为什么打开背包不能用 is_action_pressed()

因为如果你按住一小会儿,它可能会连续触发很多次。

比如:

if Input.is_action_pressed("open_bag"):
    open_bag()

这就可能一秒打开关闭几十次,场面非常鬼畜。

所以这种“一次性行为”用:

is_action_just_pressed()

7. is_action_just_released()

表示某个动作“刚刚松开”。

if Input.is_action_just_released("charge"):
    release_charge_attack()

适合:

松开蓄力攻击
松开鼠标释放技能
松开弓箭射击

比如:

if Input.is_action_pressed("charge"):
    charge_time += delta

if Input.is_action_just_released("charge"):
    shoot_arrow(charge_time)
    charge_time = 0

8. 给 RPG 角色配置 InputMap

对于你的 RPG 游戏,建议先配置这些:

move_up
move_down
move_left
move_right
interact
attack
open_bag
dash

先不要贪多。

第一阶段只做:

移动
交互
攻击
打开背包

对应按键可以这样:

move_upW方向键上
move_downS方向键下
move_leftA方向键左
move_rightD方向键右
interactEEnter
attackJ鼠标左键
open_bagBI
dashShift

现在你还不需要真的做攻击和背包,只要先把动作名设计好。

这样后面加功能时会舒服很多。

9. 八方向移动的基础写法

最基础的移动代码是这样:

extends CharacterBody2D

@export var speed: float = 120.0

func _physics_process(delta):
    var direction = Vector2.ZERO

    if Input.is_action_pressed("move_right"):
        direction.x += 1

    if Input.is_action_pressed("move_left"):
        direction.x -= 1

    if Input.is_action_pressed("move_down"):
        direction.y += 1

    if Input.is_action_pressed("move_up"):
        direction.y -= 1

    direction = direction.normalized()
    velocity = direction * speed
    move_and_slide()

这段代码是 RPG 移动的最小骨架。

重点是:

var direction = Vector2.ZERO

表示当前没有方向。

然后根据按键修改方向:

direction.x += 1
direction.x -= 1
direction.y += 1
direction.y -= 1

最后:

velocity = direction * speed
move_and_slide()

让角色移动。

Godot 官方文档提醒,移动 CharacterBody2D 时通常不应该直接改 position,而是使用 move_and_collide()move_and_slide() 这类方法,并且物理身体的移动逻辑应该放在 _physics_process() 中处理。(Godot Engine documentation)

10. Vector2 是什么

Vector2 就是二维向量。

你可以先简单理解成:

Vector2(x, y)

比如:

Vector2(1, 0)

表示向右。

Vector2(-1, 0)

表示向左。

Vector2(0, -1)

表示向上。

Vector2(0, 1)

表示向下。

Godot 的 Vector2 是一个包含两个数值的结构,常用来表示 2D 坐标、方向、速度等二维数据。(Godot Engine documentation)

对于 2D 游戏来说,Vector2 基本天天见。

position = Vector2(100, 200)
velocity = Vector2(120, 0)
direction = Vector2(-1, 0)

分别可以理解成:

位置
速度
方向

11. 为什么要 normalized()

如果玩家只按右:

direction = Vector2(1, 0)

长度是 1。

如果玩家同时按右和下:

direction = Vector2(1, 1)

这个向量的长度大约是 1.414。

也就是说,如果不处理,角色斜着走会比横着走更快。

所以要:

direction = direction.normalized()

它会把方向向量变成长度为 1 的单位向量。

这样不管是上下左右,还是斜方向,最终速度都一致。

你可以把它理解成:

我只要方向不要让方向本身偷偷加速

12. 更简洁的写法:Input.get_vector()

Godot 提供了一个非常适合移动的写法:

var direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")

完整写法:

extends CharacterBody2D

@export var speed: float = 120.0

func _physics_process(delta):
    var direction = Input.get_vector(
        "move_left",
        "move_right",
        "move_up",
        "move_down"
    )

    velocity = direction * speed
    move_and_slide()

这个比手动判断四个方向更干净。

Godot 的 2D movement 文档中也使用了 Input.get_vector() 来读取上下左右四个动作,然后把得到的方向向量乘以速度,最后调用 MoveAndSlide() / move_and_slide()。(Godot Engine documentation)

注意参数顺序:

Input.get_vector(
    negative_x,
    positive_x,
    negative_y,
    positive_y
)

也就是:

Input.get_vector(
    "move_left",
    "move_right",
    "move_up",
    "move_down"
)

别写反。

13. 推荐你现在就用 get_vector()

你现在做 RPG 项目,推荐直接用这个:

func get_input_direction() -> Vector2:
    return Input.get_vector(
        "move_left",
        "move_right",
        "move_up",
        "move_down"
    )

然后移动逻辑写成:

func _physics_process(delta):
    var direction = get_input_direction()
    velocity = direction * speed
    move_and_slide()

这样代码结构更清楚。

14. velocity 是什么

对于 CharacterBody2D 来说,velocity 是它的移动速度。

velocity = Vector2(100, 0)

可以理解成:

每秒向右移动 100 像素
velocity = Vector2(0, -100)

表示:

每秒向上移动 100 像素

所以:

velocity = direction * speed

意思是:

用方向乘以速度得到最终移动速度

例如:

direction = Vector2(1, 0)
speed = 120
velocity = Vector2(120, 0)

角色向右移动。

15. 不要手动修改 position 做角色移动

初学者很容易写:

position += direction * speed * delta

这个写法对普通 Node2D 可以,但对需要碰撞的角色不适合。

因为你绕过了 CharacterBody2D 的移动和碰撞逻辑。

如果角色需要撞墙、挡住、滑动、检测碰撞,就应该用:

velocity = direction * speed
move_and_slide()

简单记:

只是飘着动的东西可以改 position
有碰撞的角色 CharacterBody2D + velocity + move_and_slide()

16. 角色节点应该怎么搭

建议你的玩家场景先这样:

Player CharacterBody2D
├── AnimatedSprite2D
├── CollisionShape2D
└── Camera2D

其中:

CharacterBody2D负责移动和碰撞
AnimatedSprite2D负责角色动画
CollisionShape2D负责碰撞形状
Camera2D让镜头跟随角色

AnimatedSprite2D 类似 Sprite2D,但它可以用 SpriteFrames 资源管理多帧动画,所以很适合角色 idle、walk、attack 等帧动画。(Godot Engine documentation)

如果你暂时没有角色动画素材,也可以先用:

Player CharacterBody2D
├── Sprite2D
└── CollisionShape2D

Sprite2D 就是一个通用 2D 图片显示节点,可以显示一张纹理,也可以显示图集中的一块区域。(Godot Engine documentation)

17. AnimatedSprite2D 的动画命名建议

对于 RPG 俯视角角色,建议动画名一开始就设计清楚。

最简单版本:

idle_down
idle_up
idle_left
idle_right

walk_down
walk_up
walk_left
walk_right

如果你有四方向动画,这样最清楚。

如果你只有左右动画,也可以先这样:

idle
walk

然后用 flip_h 翻转左右。

但对于中世纪 RPG,后面你大概率会需要上下左右动画,所以我更建议你从一开始就按四方向规划。

18. 保存角色朝向

角色停止移动后,应该保持最后朝向。

比如玩家向下走,松开键后,应该播放:

idle_down

玩家向左走,松开键后,应该播放:

idle_left

所以我们需要一个变量保存最后方向:

var last_direction: Vector2 = Vector2.DOWN

默认面朝下。

当玩家移动时,更新它:

if direction != Vector2.ZERO:
    last_direction = direction

不过这里有个小问题。

如果玩家斜着走:

Vector2(1, 1)

那最后方向到底算右,还是下?

RPG 里通常会选择一个主方向。

比如:

func get_main_direction(direction: Vector2) -> String:
    if abs(direction.x) > abs(direction.y):
        if direction.x > 0:
            return "right"
        else:
            return "left"
    else:
        if direction.y > 0:
            return "down"
        else:
            return "up"

意思是:

横向分量更大算左右
纵向分量更大算上下

19. 播放移动动画

假设你的 AnimatedSprite2D 叫:

AnimatedSprite2D

脚本里先拿到它:

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

播放动画:

animated_sprite.play("walk_down")

停止时播放 idle:

animated_sprite.play("idle_down")

完整动画函数可以这样写:

func update_animation(direction: Vector2):
    if direction == Vector2.ZERO:
        var idle_animation = "idle_" + get_direction_name(last_direction)
        animated_sprite.play(idle_animation)
        return

    last_direction = direction

    var walk_animation = "walk_" + get_direction_name(direction)
    animated_sprite.play(walk_animation)

不过这里的 get_direction_name() 要写得稍微严谨一点。

20. 方向转动画名

可以这样写:

func get_direction_name(direction: Vector2) -> String:
    if abs(direction.x) > abs(direction.y):
        if direction.x > 0:
            return "right"
        else:
            return "left"
    else:
        if direction.y > 0:
            return "down"
        else:
            return "up"

然后:

"walk_" + get_direction_name(direction)

就会得到:

walk_right
walk_left
walk_down
walk_up

比如:

direction = Vector2(1, 0)

结果是:

walk_right
direction = Vector2(0, -1)

结果是:

walk_up

21. 完整 Player.gd 示例

这是目前最推荐你保存下来的版本。

extends CharacterBody2D

@export var speed: float = 120.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

var last_direction: Vector2 = Vector2.DOWN

func _physics_process(delta):
    var direction = get_input_direction()

    velocity = direction * speed
    move_and_slide()

    update_animation(direction)

func get_input_direction() -> Vector2:
    return Input.get_vector(
        "move_left",
        "move_right",
        "move_up",
        "move_down"
    )

func update_animation(direction: Vector2):
    if direction == Vector2.ZERO:
        var idle_animation = "idle_" + get_direction_name(last_direction)
        animated_sprite.play(idle_animation)
        return

    last_direction = direction

    var walk_animation = "walk_" + get_direction_name(direction)
    animated_sprite.play(walk_animation)

func get_direction_name(direction: Vector2) -> String:
    if abs(direction.x) > abs(direction.y):
        if direction.x > 0:
            return "right"
        else:
            return "left"
    else:
        if direction.y > 0:
            return "down"
        else:
            return "up"

这个脚本已经包含:

输入读取
八方向移动
速度控制
碰撞移动
方向判断
站立动画
行走动画

你后面加攻击、交互、奔跑,都是在这个基础上扩展。

22. 如果暂时没有四方向动画怎么办

可以先用一个简单版本。

假设你只有:

idle
walk

那代码可以简化成:

extends CharacterBody2D

@export var speed: float = 120.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(delta):
    var direction = Input.get_vector(
        "move_left",
        "move_right",
        "move_up",
        "move_down"
    )

    velocity = direction * speed
    move_and_slide()

    if direction == Vector2.ZERO:
        animated_sprite.play("idle")
    else:
        animated_sprite.play("walk")

    if direction.x != 0:
        animated_sprite.flip_h = direction.x < 0

这适合临时测试。

但它只能比较好地表现左右移动。

上下移动就没那么自然。

23. AnimationPlayer 和 AnimatedSprite2D 的区别

这里你会经常迷糊,我直接讲清楚。

AnimatedSprite2D 主要适合播放一组帧图。

比如:

walk_down_0.png
walk_down_1.png
walk_down_2.png
walk_down_3.png

它就是不断切换图片,形成动画。

AnimationPlayer 更强,它可以动画化很多属性,比如位置、透明度、颜色、缩放、旋转、声音、调用方法等。Godot 官方文档也说明,AnimationPlayer 可以创建从简单到复杂的动画,并且可以给节点属性打关键帧。(Godot Engine documentation)

简单记:

AnimatedSprite2D适合角色帧动画
AnimationPlayer适合复杂属性动画

比如:

角色走路AnimatedSprite2D
门慢慢打开AnimationPlayer
宝箱弹一下AnimationPlayer
UI 淡入淡出AnimationPlayer
火把火焰帧动画AnimatedSprite2D
技能特效两者都可能用

24. 移动逻辑和动画逻辑最好分开

不要把所有东西都挤在 _physics_process() 里。

不推荐一直写成这样:

func _physics_process(delta):
    var direction = Input.get_vector(...)
    velocity = direction * speed
    move_and_slide()

    if direction == Vector2.ZERO:
        ...
    else:
        ...

短期能用,但后面代码会膨胀。

更推荐:

func _physics_process(delta):
    handle_movement()
    update_animation()

或者:

func _physics_process(delta):
    var direction = get_input_direction()
    move_player(direction)
    update_animation(direction)

这样以后你加功能会更舒服:

func _physics_process(delta):
    var direction = get_input_direction()

    if can_move:
        move_player(direction)

    update_animation(direction)

代码结构清楚,后面不容易变成一锅粥。

25. 加一个奔跑功能

InputMap 里添加:

dash

绑定:

Shift

然后代码:

@export var walk_speed: float = 120.0
@export var run_speed: float = 180.0

func get_current_speed() -> float:
    if Input.is_action_pressed("dash"):
        return run_speed

    return walk_speed

移动时:

velocity = direction * get_current_speed()

完整一点:

extends CharacterBody2D

@export var walk_speed: float = 120.0
@export var run_speed: float = 180.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

var last_direction: Vector2 = Vector2.DOWN

func _physics_process(delta):
    var direction = get_input_direction()
    var speed = get_current_speed()

    velocity = direction * speed
    move_and_slide()

    update_animation(direction)

func get_input_direction() -> Vector2:
    return Input.get_vector(
        "move_left",
        "move_right",
        "move_up",
        "move_down"
    )

func get_current_speed() -> float:
    if Input.is_action_pressed("dash"):
        return run_speed

    return walk_speed

func update_animation(direction: Vector2):
    if direction == Vector2.ZERO:
        animated_sprite.play("idle_" + get_direction_name(last_direction))
        return

    last_direction = direction
    animated_sprite.play("walk_" + get_direction_name(direction))

func get_direction_name(direction: Vector2) -> String:
    if abs(direction.x) > abs(direction.y):
        return "right" if direction.x > 0 else "left"

    return "down" if direction.y > 0 else "up"

这已经是一个很像样的 RPG 玩家移动脚本了。

26. 加一个交互键

InputMap 里添加:

interact

绑定:

E
Enter

先写最简单的测试:

func _process(delta):
    if Input.is_action_just_pressed("interact"):
        print("尝试交互")

为什么这里用 _process() 也可以?

因为交互不是物理移动,不需要固定物理帧。

不过如果你的交互强依赖碰撞检测,也可以放在 _physics_process() 里。初学阶段不用纠结,先能跑起来。

27. _input、_unhandled_input、_process 的区别

Godot 输入处理有好几种方式,初学者很容易懵。

先记这个:

持续移动_physics_process + Input.get_vector()
一次性操作_input _unhandled_input _process + just_pressed
UI 按钮用按钮自己的 pressed 信号

Input 是全局输入状态,可以在 _process()_physics_process() 里检查。

_input(event) 是事件进来时调用。

例如:

func _input(event):
    if event.is_action_pressed("interact"):
        print("交互")

_unhandled_input(event) 通常用于“没有被 UI 消费掉的输入”。

比如打开菜单时,UI 按钮可能先处理了回车键。

如果你不想 UI 操作同时触发角色攻击,就会用到 _unhandled_input()

现在你可以先这样记:

角色移动_physics_process
角色交互_unhandled_input _process + just_pressed
UI 点击pressed 信号

28. 常见错误 1:InputMap 没配置

代码没错,但角色不动,最常见原因就是 InputMap 没配置。

比如代码里写:

Input.get_vector("move_left", "move_right", "move_up", "move_down")

但是项目设置里没有这些动作。

那方向永远是:

Vector2.ZERO

解决:

Project
Project Settings
Input Map
添加 move_left / move_right / move_up / move_down
给它们绑定按键

29. 常见错误 2:节点类型用错

如果你写了:

extends CharacterBody2D

那脚本应该挂在 CharacterBody2D 节点上。

不要挂到 Node2DSprite2DAnimatedSprite2D 上。

正确结构:

Player CharacterBody2D
├── AnimatedSprite2D
└── CollisionShape2D

脚本挂在:

Player

不是挂在:

AnimatedSprite2D

这个坑很常见。

30. 常见错误 3:AnimatedSprite2D 名字不匹配

代码写:

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

那节点名字必须真的叫:

AnimatedSprite2D

如果你改名成了:

PlayerSprite

那代码也要改:

@onready var animated_sprite: AnimatedSprite2D = $PlayerSprite

Godot 的 $节点名 是按节点名字找的。

名字不一致就找不到。

31. 常见错误 4:动画名不匹配

代码里播放:

animated_sprite.play("walk_down")

那 AnimatedSprite2D 的 SpriteFrames 里必须真的有:

walk_down

如果你动画叫:

walk_front

那代码就播放不了。

所以建议动画名一开始统一成:

idle_down
idle_up
idle_left
idle_right
walk_down
walk_up
walk_left
walk_right

别一会儿 front,一会儿 down,一会儿 bottom

命名混乱,后面自己会被自己创飞。

32. 常见错误 5:CollisionShape2D 没有形状

角色不碰撞,可能是:

CollisionShape2D 节点有了
但是 Shape 没设置

要选中 CollisionShape2D,在 Inspector 里设置 Shape。

比如:

RectangleShape2D
CapsuleShape2D
CircleShape2D

RPG 角色常用:

CapsuleShape2D
RectangleShape2D

如果是像素风俯视角,碰撞范围不要覆盖整个角色图片。

更建议只覆盖脚底附近。

比如角色图片 32x48,碰撞可以只放下半部分:

不是整个身体都碰撞
而是脚底区域碰撞

这样角色靠近墙、桌子、树的时候会自然很多。

33. 这一部分你要记住的核心

这一部分最重要的东西是:

InputMap 是按键映射表
Input 是读取输入的入口
is_action_pressed 适合持续按住
is_action_just_pressed 适合单次触发
Input.get_vector 很适合角色移动
CharacterBody2D 移动用 velocity + move_and_slide()
角色移动逻辑放 _physics_process()
动画播放根据 direction 判断
last_direction 用来保存最后朝向

你现在要优先吃透这条链路:

InputMap

Input.get_vector()

direction

velocity

move_and_slide()

update_animation()

这就是 RPG 角色移动的基本闭环。

34. 建议你现在做的小练习

创建:

Player CharacterBody2D
├── AnimatedSprite2D
├── CollisionShape2D
└── Camera2D

配置 InputMap:

move_up
move_down
move_left
move_right
dash
interact

实现:

WASD 移动
Shift 奔跑
E 打印交互
移动时播放 walk 动画
停止时播放 idle 动画
角色停止后保持最后朝向

这一关过了之后,你的 RPG 项目就可以进入下一步:

第四部分预告:TileMapLayer、地图碰撞、角色和地图的关系

下一部分会讲:

TileMapLayer 是什么
TileSet 是什么
地形图块怎么理解
地图碰撞怎么设置
角色为什么能被墙挡住
Y-sort 是什么
角色走到树后面为什么应该被遮住
地图层级应该怎么拆

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
godot2026-07-27
Godot 4 常用 UI 节点详解
Godot 4 常用 UI 节点详解 在 Godot 4 中,UI 系统基于 Control(控件) 节点构建。所有 UI 节点都继承自 Control,形成一棵完整的 UI 树。与游戏引擎中常见的"Canvas + DOM"模式不同,Godot 的 UI 系统是声明式的——你在场景中搭好节点树,引擎自动完成布局计算。 一、布局系统:Container 家族 Container 是 Godot UI 的 骨架 。它决定了子节点的大小和位...
学习
godot2026-07-09
Godot 4 自动地形系统(AutoTileSet / Terrains)完全指南
前言 在 2D 游戏开发中,地形瓦片(tile)的拼接是一个绕不开的问题。想象一下:你有一片草地、一条河流、一段平台——如果每一块边缘、角落、过渡区域都要手动选择对应的瓦片图,工作量将是巨大的。 自动地形系统 就是为了解决这个问题而生的。 一、什么是自动地形(Autotiling)? 自动地形的核心思想很简单: 你只管画,引擎帮你选对瓦片 。 当你在 TileMap 上绘制地形时,引擎会自动检测每个瓦片的上下左右邻居,然后根据预设的规则...
学习
godot2026-06-25
Godot 中 zindex 和 ysort 的区别总结
在 Godot 2D 游戏开发中,角色、树木、怪物、地面、技能特效、UI 都需要正确的显示顺序。比如角色走到树前面时,角色应该挡住树;角色走到树后面时,树又应该挡住角色。 这种显示顺序主要和两个概念有关: 和 。 其中 用来手动控制图层顺序, 用来根据物体的 Y 坐标自动排序。 一句话理解 是手动分层。 是根据 Y 坐标自动排序。 简单来说: | 属性 | 作用 | 适合场景 | | | | | | | 数值越大,显示越靠前 | 地面、...
学习
godot2026-06-11
Tileset 资源图的标准和规范
一、什么是 Tileset 资源图 Tileset,中文通常叫“图块资源图”或“瓦片图”,是 2D 游戏中非常常见的一种地图资源组织方式。 简单来说,Tileset 就是把很多小图块按照固定尺寸排列在一张图片里。游戏引擎会按照固定的格子大小去切割这张图片,然后把每个小格子当成一个独立的地图块使用。 比如一个 32×32 像素的 Tileset 中,每一个 tile 都是 32×32 像素。地图编辑器或游戏引擎会按照 32×32 的网格,...
学习
godot2026-05-29
用户角色精灵图制作角色的完整流程
在 2D RPG 游戏里,角色通常不是用一张单独图片完成的,而是用一张“角色精灵图”来做。 所谓角色精灵图,通常是一张包含多个动作帧的大图。比如角色向下走有 4 帧,向左走有 4 帧,向右走有 4 帧,向上走有 4 帧。Godot 会根据这些帧不断切换图片,看起来角色就动起来了。 这篇文章主要介绍:拿到一张角色精灵图之后,如何在 Godot 中把它做成一个可以正常移动、播放动画、和地图产生遮挡关系的角色。 一、先理解角色精灵图是什么 角...
学习
godot2026-05-26
Godot 节点系统详细介绍
Godot 里最核心的东西不是“类”,也不是“组件”,而是 节点 Node 。 你可以把 Godot 的节点理解成: 在前端里,一个页面是由很多 DOM 元素组成的; 在 Godot 里,一个游戏场景是由很多 Node 节点组成的。 比如一个玩家角色,可能不是一个单独对象,而是这样的结构: 这里的 是根节点,下面挂着显示图片、播放动画、碰撞检测、摄像机、音效等子节点。 Godot 官方文档也把节点和场景放在一起讲:多个节点组成树状结构后...
学习

评论

请登录后发表评论

去登录
加载评论中...