强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Godot 3 GDScript 教程 / Godot 3 GDScript 教程(十六):3D 基础

3D 基础

Godot 3 拥有完整的 3D 游戏开发能力。本章讲解 Godot 3 的 3D 开发基础,包括节点系统、模型导入、光照、物理和角色控制。


Spatial 节点

Spatial 是所有 3D 节点的基类,类似于 2D 中的 Node2D

基本属性

属性说明
translation相对于父节点的位置(Vector3)
rotation_degrees旋转角度(度数,Vector3)
scale缩放(Vector3)
transform完整变换矩阵(Transform)
global_transform全局变换矩阵

坐标系

Godot 使用右手坐标系:
- X 轴:右    - Y 轴:上    - Z 轴:前方
extends Spatial

func _ready():
    translation = Vector3(1, 2, 3)
    rotation_degrees = Vector3(0, 45, 0)
    scale = Vector3(2, 2, 2)

# 朝向目标
func look_at_target(target: Vector3):
    look_at(target, Vector3.UP)

# 本地坐标系移动
func move_local(offset: Vector3):
    global_translate(transform.basis.xform(offset))

💡 提示transform.basis.xform(direction) 将本地方向转换为世界方向。


MeshInstance

MeshInstance 是 3D 网格的渲染节点。

内置网格类型

网格类型说明
CubeMesh立方体
SphereMesh球体
CylinderMesh圆柱体
PlaneMesh平面
CapsuleMesh胶囊体
# 代码创建红色立方体
func create_cube(pos: Vector3, color: Color) -> MeshInstance:
    var mi = MeshInstance.new()
    var cube = CubeMesh.new()
    cube.size = Vector3(1, 1, 1)
    mi.mesh = cube

    var mat = SpatialMaterial.new()
    mat.albedo_color = color
    mi.material_override = mat

    mi.translation = pos
    add_child(mi)
    return mi

# SpatialMaterial 常用设置
var mat = SpatialMaterial.new()
mat.albedo_color = Color(0.2, 0.5, 0.8)   # 基础颜色
mat.metallic = 0.8                          # 金属度
mat.roughness = 0.2                         # 粗糙度
mat.emission_enabled = true                 # 自发光
mat.emission = Color(1, 0.5, 0)
mat.emission_energy = 2.0
mat.flags_transparent = true                # 透明
mat.albedo_color.a = 0.5

3D 相机 Camera

# 透视相机(FPS/TPS)
$Camera.fov = 75.0
$Camera.near = 0.1
$Camera.far = 1000.0

# 正交相机(策略游戏)
$Camera.size = 10.0
$Camera.projection = Camera.PROJECTION_ORTHOGONAL

# 第三人称相机
extends Camera
export var target_path: NodePath
export var distance: float = 5.0
export var height: float = 3.0
var target: Spatial = null

func _ready():
    target = get_node(target_path)
    make_current()

func _process(delta):
    if target == null:
        return
    var desired = target.global_translation + Vector3(0, height, distance)
    global_translation = global_translation.linear_interpolate(desired, delta * 5.0)
    look_at(target.global_translation + Vector3(0, 1.5, 0), Vector3.UP)

# 第一人称相机(FPS)
extends Camera
export var sensitivity: float = 0.3
var yaw: float = 0.0
var pitch: float = 0.0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
    if event is InputEventMouseMotion:
        yaw -= event.relative.x * sensitivity
        pitch -= event.relative.y * sensitivity
        pitch = clamp(pitch, -80, 80)
        rotation_degrees = Vector3(pitch, yaw, 0)

3D 模型导入

支持的格式

格式说明推荐度
glTFGodot 推荐,支持动画⭐⭐⭐
FBX广泛使用⭐⭐
OBJ仅静态模型
Blender直接导入 .blend 文件⭐⭐⭐

⚠️ 注意:Blender 默认 Z 轴朝上,Godot 是 Y 轴朝上。从 Blender 导出时建议勾选 “-Y Forward” 选项。


光照系统

光源类型

光源类型说明典型用途
DirectionalLight平行光(模拟太阳光)室外场景主光源
OmniLight点光源(全向发光)灯泡、火焰
SpotLight聚光灯(锥形光束)手电筒、路灯
# 太阳光
$DirectionalLight.light_color = Color(1, 0.95, 0.9)
$DirectionalLight.shadow_enabled = true

# 点光源
var light = OmniLight.new()
light.light_color = Color(1, 0.8, 0.5)
light.light_energy = 2.0
light.omni_range = 10.0
add_child(light)

# 聚光灯(手电筒)
var flashlight = SpotLight.new()
flashlight.spot_range = 20.0
flashlight.spot_angle = 30.0

环境 Environment

var env = Environment.new()

env.background_mode = Environment.BG_COLOR
env.background_color = Color(0.1, 0.1, 0.2)
env.ambient_light_color = Color(0.2, 0.2, 0.3)
env.ambient_light_energy = 0.5

# 泛光(Bloom)
env.glow_enabled = true
env.glow_intensity = 0.8

# 雾效
env.fog_enabled = true
env.fog_color = Color(0.5, 0.6, 0.7)
env.fog_depth_begin = 10.0
env.fog_depth_end = 100.0

# 色调映射
env.tonemap_mode = Environment.TONEMAP_FILMIC

$Camera.environment = env

常用渲染效果表

效果说明性能影响
Bloom发光物体的光晕效果
SSAO角落阴影深度感
雾效远处物体逐渐被覆盖
景深远/近处模糊
SSR屏幕空间反射

3D 物理

# KinematicBody(玩家角色)
extends KinematicBody

var velocity = Vector3.ZERO
var gravity = -20.0
var speed = 10.0

func _physics_process(delta):
    velocity.y += gravity * delta

    var input = Vector3.ZERO
    input.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    input.z = Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
    input = input.normalized()

    velocity.x = input.x * speed
    velocity.z = input.z * speed
    velocity = move_and_slide(velocity, Vector3.UP)

    if is_on_floor() and Input.is_action_just_pressed("jump"):
        velocity.y = 10.0

碰撞层

Layer(层):我是什么
Mask(掩码):我与谁碰撞

Layer 1: 地形    Layer 2: 玩家
Layer 3: 敌人    Layer 4: 子弹

角色移动

完整 3D 角色控制器

extends KinematicBody

export var walk_speed: float = 5.0
export var run_speed: float = 10.0
export var jump_force: float = 8.0
export var gravity: float = -20.0

var velocity: Vector3 = Vector3.ZERO
var snap_vector: Vector3 = Vector3.DOWN

onready var camera = $CameraPivot/Camera
onready var model = $Model

func _physics_process(delta):
    var input = Vector2(
        Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
        Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
    ).normalized()

    var cam_basis = camera.global_transform.basis
    var forward = -cam_basis.z
    var right = cam_basis.x
    forward.y = 0; right.y = 0
    var move_dir = (forward * -input.y + right * input.x).normalized()

    var target_speed = run_speed if Input.is_action_pressed("run") else walk_speed
    velocity.x = move_dir.x * target_speed
    velocity.z = move_dir.z * target_speed
    velocity.y += gravity * delta

    if move_dir.length() > 0.1:
        model.look_at(translation - move_dir, Vector3.UP)

    if is_on_floor():
        if Input.is_action_just_pressed("jump"):
            velocity.y = jump_force
            snap_vector = Vector3.ZERO
        else:
            snap_vector = Vector3.DOWN

    velocity = move_and_slide_with_snap(velocity, snap_vector, Vector3.UP, true)

⚠️ 注意move_and_slide_with_snapsnap 参数在跳跃时设为 Vector3.ZERO,否则无法跳离地面。


游戏开发场景

场景一:3D 物品拾取

extends Area

export var item_name: String = ""

func _on_Item_body_entered(body):
    if body.is_in_group("player"):
        body.pickup_item(item_name)
        $PickupEffect.emitting = true
        $AudioStreamPlayer3D.play()
        visible = false
        yield($AudioStreamPlayer3D, "finished")
        queue_free()

场景二:FPS 控制器

extends KinematicBody

onready var head = $Head
onready var camera = $Head/Camera
var velocity = Vector3.ZERO
var speed = 8.0

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
    if event is InputEventMouseMotion:
        rotate_y(deg2rad(-event.relative.x * 0.2))
        camera.rotate_x(deg2rad(-event.relative.y * 0.2))
        camera.rotation.x = clamp(camera.rotation.x, deg2rad(-80), deg2rad(80))

func _physics_process(delta):
    var dir = Vector3.ZERO
    dir.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
    dir.z = Input.get_action_strength("move_back") - Input.get_action_strength("move_forward")
    dir = global_transform.basis.xform(dir.normalized())

    velocity.x = dir.x * speed
    velocity.z = dir.z * speed
    velocity.y -= 15 * delta
    velocity = move_and_slide(velocity, Vector3.UP)

扩展阅读

💡 总结:3D 开发的核心是理解 Spatial 节点的 Transform 系统。推荐使用 glTF 格式导入模型。KinematicBody 配合 move_and_slide 实现角色控制。Environment 资源控制全局视觉效果。