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_right = 1.0
|
||||||
anchor_bottom = 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="."]
|
[node name="KeyArt" type="Label" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 6
|
anchors_preset = 6
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ const BORDER_3 := Color("4a4238")
|
|||||||
const BORDER_4 := Color("3a352e")
|
const BORDER_4 := Color("3a352e")
|
||||||
const BORDER_5 := Color("1a1512")
|
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 ---
|
# --- Parchment ---
|
||||||
const SHEET_TOP := Color("ece2ca")
|
const SHEET_TOP := Color("ece2ca")
|
||||||
const SHEET_BOTTOM := Color("e4d8bd")
|
const SHEET_BOTTOM := Color("e4d8bd")
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ extends Control
|
|||||||
## The entry-point screen (mock Title Screen). Editor-first (ADR 0001): the whole
|
## The entry-point screen (mock Title Screen). Editor-first (ADR 0001): the whole
|
||||||
## composition lives in TitleScreen.tscn; this script drives selection, input, and
|
## 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
|
## 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)
|
signal menu_activated(id: StringName)
|
||||||
|
|
||||||
@@ -15,6 +17,8 @@ var _sel: int = 0
|
|||||||
@onready var _menu: VBoxContainer = $Content/Menu
|
@onready var _menu: VBoxContainer = $Content/Menu
|
||||||
@onready var _footer_left: Label = $FooterLeft
|
@onready var _footer_left: Label = $FooterLeft
|
||||||
@onready var _background: ColorRect = $Background
|
@onready var _background: ColorRect = $Background
|
||||||
|
@onready var _glow: ColorRect = $Glow
|
||||||
|
@onready var _embers: GPUParticles2D = $Embers
|
||||||
@onready var _rule: ColorRect = $Content/Rule
|
@onready var _rule: ColorRect = $Content/Rule
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +29,10 @@ func _ready() -> void:
|
|||||||
|
|
||||||
_background.color = Palette.STAGE_1
|
_background.color = Palette.STAGE_1
|
||||||
_rule.color = Palette.GOLD
|
_rule.color = Palette.GOLD
|
||||||
|
_glow.color = Palette.BLOOD
|
||||||
|
_apply_background()
|
||||||
|
_configure_embers()
|
||||||
|
_pulse_glow()
|
||||||
|
|
||||||
_rows = []
|
_rows = []
|
||||||
for row in _menu.get_children():
|
for row in _menu.get_children():
|
||||||
@@ -42,6 +50,48 @@ func _fit_to_viewport() -> void:
|
|||||||
size = get_viewport_rect().size
|
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:
|
func _wire_row_mouse() -> void:
|
||||||
for i in range(_rows.size()):
|
for i in range(_rows.size()):
|
||||||
var idx := i # fresh per-iteration binding for the closures
|
var idx := i # fresh per-iteration binding for the closures
|
||||||
|
|||||||
Reference in New Issue
Block a user