agentleFS
MindsMasterMindsMaster/godot-claude-code-skills/skills/godot-2d-in-3d/SKILL.md

godot-2d-in-3d

Skill3 starsChanged 20 days agoMITView on GitHub

在 Godot 4 的 3D 场景里放 2D 角色时用,2.5D 纸片人 HD-2D 像素角色配 3D 场景。Sprite3D AnimatedSprite3D billboard 正对镜头、pixel_size、投影阴影 cast_shadow、透明排序、像素清晰不发糊、SubViewport 把 2D 贴进 3D、Label3D、景深移轴。纯 2D 游戏见 godot-2d。

The file

Files
SKILL.md

Godot 2D 角色放进 3D 场景

用 Sprite3D、SubViewport 把 2D 美术放进真 3D 场景,配 Environment 做 HD-2D 观感。

标了实测的条目来自 4.7.2 软件渲染读像素,Compatibility 与 Forward+ 两个渲染器,方法见 references/render-check.md。

范围

不含去哪
纯 2D 项目、TileMapLayer、Camera2Dgodot-2d
动画状态机、帧动画godot-animation
shader 细节godot-shaders-vfx
UI 主题与布局godot-ui
3D 光照、GI、导入godot-3d

先查内置

需求用这个别自己写
3D 里的 2D 角色Sprite3D、AnimatedSprite3D手写四边形网格加 shader
始终面向相机SpriteBase3D.billboard 取 BILLBOARD_FIXED_Y每帧 look_at 相机
朝向翻转flip_hscale.x = -1,billboard 下无效
角色阴影alpha_cut 取 ALPHA_CUT_DISCARD 或 ALPHA_CUT_OPAQUE_PREPASS额外画一个阴影网格
脚下软阴影Sprite3D 平放,或 Decal 限 Forward+ 与 Mobile每帧射线加手摆网格
2D 场景、2D 骨骼、Spine 贴进 3DSubViewport 加 ViewportTexture逐帧 get_image 拷贝
世界里的文字Label3D每个文字一个 SubViewport
屏幕固定 HUDCanvasLayer 加 Control3D 里摆 UI 面板
头顶血条跟随Camera3D.unproject_position 定位 Control,或 Sprite3D手算投影矩阵
泛光、环境光遮蔽、雾WorldEnvironment 的 Environment多个 SubViewport 叠 bloom
移轴景深CameraAttributesPractical 的近远模糊后处理 shader 手写模糊

做法

路线选择

维度2D 节点手算透视伪 3D真 3D 加 Sprite3D
摆放位置、缩放、排序全靠每帧自己算节点在 3D 空间,相机负责投影
相机自由度固定角度,旋转或俯仰要重写公式任意移动、旋转、改 fov
前后遮挡手改 z_index 或 y_sort_enabled深度缓冲,透明物要处理排序
光照阴影Light2D 平面光,阴影靠 LightOccluder2D真 3D 光源,精灵可接受并投射阴影
雾、景深、SSAO没有,只有 glow,要 HDR 2D 或 BG_CANVASEnvironment 与相机属性,按渲染器取舍
像素对齐项目设置一键取整,见 godot-2d要满足整数倍像素规则,见下
物理CharacterBody2D 现成用 3D 物理,或 2D 逻辑再映射 xz
地图工具TileMapLayerGridMap 或自建网格
渲染器限制HDR 2D 不支持 Compatibility效果随渲染器增减,见 references/hd2d-environment.md
适合横版、固定视角、深度仅作缩放层视角会转或俯仰、要真实光影与景深
  • 要旋转镜头、动态光影、雾、景深任一项,走真 3D
  • 只要前景大后景小,手算缩放更省事
  • 中途换路线要重做美术摆放与相机

Sprite3D 配置

extends Sprite3D

func _ready() -> void:
	pixel_size = 1.0 / 32.0
	billboard = BaseMaterial3D.BILLBOARD_FIXED_Y
	texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
	alpha_cut = SpriteBase3D.ALPHA_CUT_DISCARD
	shaded = true
	var used := texture.get_image().get_used_rect()
	offset.y = used.end.y - texture.get_height() * 0.5
属性默认改成
pixel_size0.01,32 像素图只有 0.32 米每米贴图像素的倒数,如 1.0 / 32.0
shadedfalse,不受光不接收阴影true
texture_filterTEXTURE_FILTER_LINEAR_WITH_MIPMAPS,像素图糊TEXTURE_FILTER_NEAREST
double_sidedtrue保持,单面时背光不投影
  • offset 单位是贴图像素,脚底要对准不透明脚底行
  • 贴图底部常留透明行,取半个高度会让脚悬空
  • Image.get_used_rect() 的 end.y 是最后不透明行的下沿
  • offset.y 取它减半个高度,上例只适用单帧贴图
  • 精灵表按帧区域量;写 .tscn 时用脚本或 PIL 读像素算出常数
  • TextureAtlas 导入类型只支持 2D,不能给 Sprite3D 用
导入陷阱做法
贴图首次被 3D 使用时自动改成 VRAM Compressed 加 mipmap,像素图出块Import 面板 Detect 3D > Compress To 设 Disabled,Compress > Mode 保持 Lossless,重导入

billboard

模式行为用在
BILLBOARD_DISABLED按节点自身朝向,法线固定地面贴花、告示牌
BILLBOARD_ENABLED平面始终正对相机,相机俯仰时跟着后仰特效、光点、图标
BILLBOARD_FIXED_Y保持竖直,只绕世界 Y 转向相机HD-2D 角色,站立物
  • 俯仰角 p 下 FIXED_Y 屏幕高度乘 cos p,ENABLED 不缩短
  • 保持原高:scale.y 设为 1 除以 cos p
  • billboard 下绕 Y 的朝向由相机决定,父节点转 180 度无效,实测
  • ENABLED 绕原点后仰,原点在中心时下沿抬离地面
  • 角色用 FIXED_Y,原点放脚底

朝向翻转

只用 flip_h,AnimatedSprite3D 同样有。

extends AnimatedSprite3D

func face(direction_x: float) -> void:
	if direction_x != 0.0:
		flip_h = direction_x < 0.0
  • billboard 下 scale.x = -1 不翻转,父节点转向或负缩放也无效,实测
  • 负缩放加 double_sided = false 时,ENABLED 下精灵被背面剔除消失
  • 非 billboard 下负缩放可以翻转

alpha_cut 与阴影

alpha_cut阴影边缘排序
ALPHA_CUT_DISABLED不投影,实测 0 像素平滑半透明按原点距离从后往前,会错
ALPHA_CUT_DISCARD只投不透明部分硬边写深度,无排序问题
ALPHA_CUT_OPAQUE_PREPASS轮廓正确边缘平滑较稳,更贵
ALPHA_CUT_HASHForward+ 与 DISCARD 一致抖动点阵写深度
  • 像素风角色选 DISCARD,半透明羽化边才选 OPAQUE_PREPASS
  • ALPHA_CUT_HASH 在 Compatibility 实测不裁剪,阴影整块,先别用
  • 硬边抗锯齿:alpha_antialiasing_mode 设 ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,或开 FXAA、TAA
  • 混合透明物不写深度,SSAO 等不作用在它上面
  • billboard 加阴影时按场景相机朝向渲染,多相机结果未定义
  • 相机绕角色转时影子随相机变形,稳定影子见 references/shadows.md

透明排序

只在 ALPHA_CUT_DISABLED 时调,实测两张重叠精灵,A 在前 0.5 米:

设置重叠处看到
默认离相机近的 A
B 的 sorting_offset 设 2.0B
B 的 render_priority 设 1B
B 的 no_depth_test 设 trueA,B 先画,后画的 A 盖在上面
两者 DISCARD,B 的优先级与偏移都调高A,深度决定,两项都无效
  • sorting_offset 在 VisualInstance3D 上,单位米
  • render_priority 只在透明物之间生效
  • 不靠 no_depth_test 排序,它让精灵穿墙可见

光照

  • shaded = true 后接收 Light3D
  • billboard 的法线随相机转,光从相机一侧来才亮,背后来为 0
  • FIXED_Y 法线水平,正上方的光照不亮,实测俯仰 40 度时亮度 0
  • 主光要有水平分量并来自相机一侧,背光面靠环境光补
  • 环境光:Environment.ambient_light_source 设成颜色或天空
  • 精灵整面一个法线,要法线贴图用 QuadMesh 加 StandardMaterial3D
  • billboard_keep_scale 默认 false,QuadMesh billboard 会丢缩放

相机

extends Camera3D

func _ready() -> void:
	projection = Camera3D.PROJECTION_ORTHOGONAL
	size = 360.0 * (1.0 / 32.0)
  • 正交 keep_aspect 默认 KEEP_HEIGHT,size 是视口高度对应的米数
  • 一贴图像素对一屏幕像素:size = 视口高度像素 × pixel_size,再除以整数倍
  • 透视下该关系只在距离 视口高度 × pixel_size ÷ (2 × tan(fov ÷ 2)) 的平面成立
  • 正交无近大远小,视差靠相机移动与多层场景
  • 透视像素风贴图像素大小随距离变,粗细不均
  • 竖直物按 cos 俯仰缩短,地面纵深按 sin 缩短
  • 经验起点:俯仰 25 到 45 度,透视 fov 20 到 35 度,非官方数值
  • fov 小透视变形小,相机要退得更远
  • display/window/stretch/mode 取 canvas_items 时 3D 不受拉伸,UI 按比例缩放
  • 取 viewport 时 3D 也低分辨率渲染再放大

像素对齐

  • 每个贴图像素占整数个屏幕像素,否则列宽交替不均,实测
  • 缩放用整数倍:调相机 size 或视口整数缩放
  • 整数倍下相机平移亚像素,列宽不变,实测
  • TEXTURE_FILTER_NEAREST 不采样 mipmap,远处颗粒感
  • TEXTURE_FILTER_NEAREST_WITH_MIPMAPS 远处平滑,需导入时生成 mipmap
  • 透视相机做不到全局整数倍,接受远处不均,或改正交

地面阴影

Decal 只在 Forward+ 与 Mobile 可用,通用写法是平放的 Sprite3D:

extends Sprite3D

func _ready() -> void:
	var gradient := Gradient.new()
	gradient.set_color(0, Color(0.0, 0.0, 0.0, 0.8))
	gradient.set_color(1, Color(0.0, 0.0, 0.0, 0.0))
	var tex := GradientTexture2D.new()
	tex.gradient = gradient
	tex.fill = GradientTexture2D.FILL_RADIAL
	tex.fill_from = Vector2(0.5, 0.5)
	tex.fill_to = Vector2(1.0, 0.5)
	tex.width = 64
	tex.height = 64
	texture = tex
	axis = Vector3.AXIS_Y
	pixel_size = 0.02
	position.y = 0.01
  • axis 取 Y 让精灵平放朝上,抬高 0.01 米避免深度闪烁
  • 用 Decal 时 cull_mask 排除角色所在层,否则投到角色身上
  • 贴花不能改变半透明材质的透明度
  • Decal.texture_albedo 不能直接用 ViewportTexture,先 get_image 再建 ImageTexture

SubViewport 与文字

  • 2D 场景、2D 骨骼贴进 3D 用 SubViewport 加 ViewportTexture
  • SubViewport 不继承项目的默认纹理过滤与像素取整
  • 设置、Label3D、世界内 UI、屏幕叠加见 references/subviewport-ui.md
  • 雾、景深、移轴见 references/hd2d-environment.md

陷阱

现象做法
billboard 精灵翻转无效,单面时消失用 flip_h,不用负缩放
半透明精灵互相遮挡错序角色用不透明系的 alpha_cut
调 render_priority、sorting_offset 对 DISCARD 无效不透明系由深度决定,调位置
2D 内容进了 SubViewport 又变糊SubViewport 单独设过滤,见 subviewport-ui.md
Compatibility 下 Decal 不显示用平放 Sprite3D
HDR 2D 下 SubViewport 色彩异常两个渲染器都未验证,自己读像素确认
Mobile 或真显卡上表现与本页不同本页未在 Mobile 渲染验证,以实机为准

参考

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.