feat(title): warm shader background, pulsing glow, drifting embers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
15
client/assets/theme/shaders/title_background.gdshader
Normal file
15
client/assets/theme/shaders/title_background.gdshader
Normal file
@@ -0,0 +1,15 @@
|
||||
shader_type canvas_item;
|
||||
// Warm radial for the Title screen (mock: radial-gradient warm centre -> near
|
||||
// black edge, centred at 70% 20%). Colours are Palette uniforms set by the title
|
||||
// script (the DarkBay precedent), so no hex lives in the shader.
|
||||
uniform vec4 warm : source_color;
|
||||
uniform vec4 mid : source_color;
|
||||
uniform vec4 edge : source_color;
|
||||
uniform vec2 center = vec2(0.7, 0.2);
|
||||
|
||||
void fragment() {
|
||||
float d = distance(UV, center);
|
||||
vec3 c = mix(warm.rgb, mid.rgb, clamp(d / 0.45, 0.0, 1.0));
|
||||
c = mix(c, edge.rgb, clamp((d - 0.45) / 0.55, 0.0, 1.0));
|
||||
COLOR = vec4(c, 1.0);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cfrs0nt60f2mb
|
||||
@@ -18,6 +18,21 @@ anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="Glow" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -420.0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Embers" type="GPUParticles2D" parent="."]
|
||||
position = Vector2(960, 1080)
|
||||
amount = 24
|
||||
lifetime = 9.0
|
||||
preprocess = 4.0
|
||||
|
||||
[node name="KeyArt" type="Label" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 6
|
||||
|
||||
@@ -19,6 +19,10 @@ const BORDER_3 := Color("4a4238")
|
||||
const BORDER_4 := Color("3a352e")
|
||||
const BORDER_5 := Color("1a1512")
|
||||
|
||||
# --- Title-screen ambiance (warm radial background) ---
|
||||
const TITLE_WARM := Color("2a2018") # radial centre
|
||||
const TITLE_MID := Color("160f0a") # radial midpoint (STAGE_1 is the edge)
|
||||
|
||||
# --- Parchment ---
|
||||
const SHEET_TOP := Color("ece2ca")
|
||||
const SHEET_BOTTOM := Color("e4d8bd")
|
||||
|
||||
@@ -3,7 +3,9 @@ extends Control
|
||||
## The entry-point screen (mock Title Screen). Editor-first (ADR 0001): the whole
|
||||
## composition lives in TitleScreen.tscn; this script drives selection, input, and
|
||||
## dispatches the menu. §2: presentation/state — no AI here (the DM enters at the
|
||||
## shell). The atmosphere (bg shader, glow, embers) is layered on in a later task.
|
||||
## shell). The atmosphere (warm bg shader, pulsing glow, drifting embers) is
|
||||
## configured in code from Palette (the DarkBay precedent) — see _apply_background,
|
||||
## _configure_embers, _pulse_glow.
|
||||
|
||||
signal menu_activated(id: StringName)
|
||||
|
||||
@@ -15,6 +17,8 @@ var _sel: int = 0
|
||||
@onready var _menu: VBoxContainer = $Content/Menu
|
||||
@onready var _footer_left: Label = $FooterLeft
|
||||
@onready var _background: ColorRect = $Background
|
||||
@onready var _glow: ColorRect = $Glow
|
||||
@onready var _embers: GPUParticles2D = $Embers
|
||||
@onready var _rule: ColorRect = $Content/Rule
|
||||
|
||||
|
||||
@@ -25,6 +29,10 @@ func _ready() -> void:
|
||||
|
||||
_background.color = Palette.STAGE_1
|
||||
_rule.color = Palette.GOLD
|
||||
_glow.color = Palette.BLOOD
|
||||
_apply_background()
|
||||
_configure_embers()
|
||||
_pulse_glow()
|
||||
|
||||
_rows = []
|
||||
for row in _menu.get_children():
|
||||
@@ -42,6 +50,48 @@ func _fit_to_viewport() -> void:
|
||||
size = get_viewport_rect().size
|
||||
|
||||
|
||||
func _apply_background() -> void:
|
||||
# Palette-driven shader material set in code (the DarkBay precedent) so no hex
|
||||
# lives in the shader/scene; the editor shows the ColorRect fallback.
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = load("res://assets/theme/shaders/title_background.gdshader")
|
||||
mat.set_shader_parameter("warm", Palette.TITLE_WARM)
|
||||
mat.set_shader_parameter("mid", Palette.TITLE_MID)
|
||||
mat.set_shader_parameter("edge", Palette.STAGE_1)
|
||||
_background.material = mat
|
||||
|
||||
|
||||
func _configure_embers() -> void:
|
||||
var pm := ParticleProcessMaterial.new()
|
||||
pm.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX
|
||||
pm.emission_box_extents = Vector3(760, 4, 0) # a wide strip along the bottom
|
||||
pm.direction = Vector3(0, -1, 0)
|
||||
pm.spread = 12.0
|
||||
pm.gravity = Vector3(0, -18, 0)
|
||||
pm.initial_velocity_min = 24.0
|
||||
pm.initial_velocity_max = 52.0
|
||||
pm.scale_min = 0.4
|
||||
pm.scale_max = 1.0
|
||||
pm.color = Palette.GOLD_BRIGHT
|
||||
# fade out over life
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(Palette.GOLD_BRIGHT, 0.0))
|
||||
ramp.set_color(1, Color(Palette.GOLD_BRIGHT, 0.0))
|
||||
ramp.add_point(0.15, Palette.GOLD_BRIGHT)
|
||||
var tex := GradientTexture1D.new()
|
||||
tex.gradient = ramp
|
||||
pm.color_ramp = tex
|
||||
_embers.process_material = pm
|
||||
|
||||
|
||||
func _pulse_glow() -> void:
|
||||
# A slow warm pulse (the off-screen fire). A code tween, not an AnimationPlayer:
|
||||
# a 2-keyframe modulate loop is trivial in code and motion is runtime-only anyway.
|
||||
var tween := create_tween().set_loops().set_trans(Tween.TRANS_SINE)
|
||||
tween.tween_property(_glow, "modulate:a", 0.8, 3.0)
|
||||
tween.tween_property(_glow, "modulate:a", 0.5, 3.0)
|
||||
|
||||
|
||||
func _wire_row_mouse() -> void:
|
||||
for i in range(_rows.size()):
|
||||
var idx := i # fresh per-iteration binding for the closures
|
||||
|
||||
Reference in New Issue
Block a user