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 模型导入
支持的格式
| 格式 | 说明 | 推荐度 |
|---|---|---|
| glTF | Godot 推荐,支持动画 | ⭐⭐⭐ |
| 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_snap 的 snap 参数在跳跃时设为 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 资源控制全局视觉效果。