feat: animate P1 tent hover with curtain and garland lights

This commit is contained in:
enne2
2026-09-10 22:39:27 +02:00
parent 882302c6d2
commit 717a702fb2
33 changed files with 756 additions and 24 deletions
+1 -11
View File
@@ -36,6 +36,7 @@ func _build() -> void:
back_button = _make_back_button()
add_child(back_button)
MenuUI.set_normalized_rect(back_button, MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE)
score_label = _make_label("", Vector2(112 * x_scale, 520), Vector2(220 * x_scale, 45), 25, ink)
score_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
@@ -77,17 +78,6 @@ func _make_back_button() -> TextureButton:
button.ignore_texture_size = true
button.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
button.tooltip_text = "Torna al menu Juggle"
# Margine sicuro relativo, uguale al bordo utile dei controlli nei menu.
# Ancore coincidenti: gli offset definiscono soltanto la dimensione del tasto.
button.set_anchors_preset(Control.PRESET_TOP_LEFT)
button.anchor_left = 0.03
button.anchor_top = 0.06
button.anchor_right = 0.03
button.anchor_bottom = 0.06
button.offset_left = 0.0
button.offset_top = 0.0
button.offset_right = 76.0
button.offset_bottom = 64.0
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
button.mouse_entered.connect(func(): button.modulate = Color(1.10, 1.10, 1.10, 1.0))
button.mouse_exited.connect(func(): button.modulate = Color.WHITE)
@@ -40,7 +40,7 @@ func _build() -> void:
artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(artwork)
var back := MenuUI.add_hotspot(self, Vector2(0.09, 0.14), Vector2(0.10, 0.14))
var back := MenuUI.add_hotspot(self, MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE)
back.tooltip_text = "Torna alla palestra"
back.pressed.connect(func(): return_requested.emit())
back.z_index = 20
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bn2t4knffd3n"
path="res://.godot/imported/curtain_00.png-9f0d1112d7c078353d46dc0cd923f1a1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_00.png"
dest_files=["res://.godot/imported/curtain_00.png-9f0d1112d7c078353d46dc0cd923f1a1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dh0gj5aetn5us"
path="res://.godot/imported/curtain_01.png-2aa80e3cba63bc6d921dcf29d97198bd.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_01.png"
dest_files=["res://.godot/imported/curtain_01.png-2aa80e3cba63bc6d921dcf29d97198bd.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cscj22s168j1c"
path="res://.godot/imported/curtain_02.png-9e1d1f7ce0266d1da830c1e4a58992f8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_02.png"
dest_files=["res://.godot/imported/curtain_02.png-9e1d1f7ce0266d1da830c1e4a58992f8.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://iw6xfqb882wr"
path="res://.godot/imported/curtain_03.png-f3f04b160b8ae4275d2c1b60da78ec9c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_03.png"
dest_files=["res://.godot/imported/curtain_03.png-f3f04b160b8ae4275d2c1b60da78ec9c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bj0564i0rxa6x"
path="res://.godot/imported/curtain_04.png-e21854186643d232305e198528d76b35.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_04.png"
dest_files=["res://.godot/imported/curtain_04.png-e21854186643d232305e198528d76b35.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dfkhpaccg22k7"
path="res://.godot/imported/curtain_05.png-0fab7af970e77da94fba7e98267b384e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_05.png"
dest_files=["res://.godot/imported/curtain_05.png-0fab7af970e77da94fba7e98267b384e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://csvmjinsa8ct5"
path="res://.godot/imported/curtain_06.png-4beeda4b533be6cefd4aab87117936ed.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/curtain_anim/curtain_06.png"
dest_files=["res://.godot/imported/curtain_06.png-4beeda4b533be6cefd4aab87117936ed.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqyga2dtnatrw"
path="res://.godot/imported/lights_00.png-9c87aa8587b4ae63db312b0b1c331079.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_00.png"
dest_files=["res://.godot/imported/lights_00.png-9c87aa8587b4ae63db312b0b1c331079.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dfkqds6xp0tju"
path="res://.godot/imported/lights_01.png-015d6f38bb58d82ed9b8078ea32b269e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_01.png"
dest_files=["res://.godot/imported/lights_01.png-015d6f38bb58d82ed9b8078ea32b269e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c8670kiyy0k83"
path="res://.godot/imported/lights_02.png-2fa8069910e0537d302506600eeca44d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_02.png"
dest_files=["res://.godot/imported/lights_02.png-2fa8069910e0537d302506600eeca44d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dgr3wno4bmimg"
path="res://.godot/imported/lights_03.png-b14f87015201a2decbafe2bec7276a61.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_03.png"
dest_files=["res://.godot/imported/lights_03.png-b14f87015201a2decbafe2bec7276a61.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cu38618jhaxb"
path="res://.godot/imported/lights_04.png-1f84cc03a726eb56bb479508bfc88cb4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_04.png"
dest_files=["res://.godot/imported/lights_04.png-1f84cc03a726eb56bb479508bfc88cb4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://diqlap0wh55rh"
path="res://.godot/imported/lights_05.png-953bc7308a81f09c6fa8d9579b06b985.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://menus/assets/p1/garland_anim/lights_05.png"
dest_files=["res://.godot/imported/lights_05.png-953bc7308a81f09c6fa8d9579b06b985.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
+103
View File
@@ -0,0 +1,103 @@
shader_type canvas_item;
// Tendone del menu: chase delle lucine delle garlande, attivo solo in hover.
// Il chase replica l'animazione sorgente (animazione_tendone.gif, 12,5 fps):
// periodo 6 frame, 3 fasi da 2 frame; ogni lampadina fa rampa per 2 frame,
// brilla a pieno intensità per 2 frame e resta spenta per 2 frame.
// Lato sinistro: l'onda sale dal fondo verso il picco (fasi [4,2,0] per i mod 3).
// Lato destro: l'onda scende dal picco al fondo (fasi [0,2,4] per i mod 3);
// al picco L0 (fase 0) precede R0 (fase 1) di 2 frame, come nella GIF.
uniform float hover_strength : hint_range(0.0, 1.0) = 0.0;
uniform vec2 star_center = vec2(0.50, 0.13);
uniform float drift_strength : hint_range(0.0, 4.0) = 0.65;
uniform float drift_speed : hint_range(0.0, 2.0) = 0.20;
// Intensità del chase lucine: 0 = spento a riposo, 1.0 = attivo in hover.
uniform float garland_strength : hint_range(0.0, 1.0) = 0.0;
// FPS sorgente dell'animazione (GIF a 8 centesimi di secondo per frame).
uniform float garland_fps : hint_range(1.0, 60.0) = 12.5;
// Frame assoluto con cui congelare il chase (debug/catture); -1 = tempo reale.
uniform float garland_freeze_frame = -1.0;
// Nucleo della lampadina accesa e colore dell'alone, campionati dalla GIF.
const vec3 GOLD_CORE = vec3(0.945, 0.80, 0.385);
const vec3 GOLD_HALO = vec3(1.0, 0.78, 0.35);
// Posizioni lampadina in UV texture (canvas 880x750) + offset di fase in frame
// nel ciclo di 6: z = frame di inizio rampa. L0..L9 dal picco al fondo sinistro,
// R0..R9 dal picco al fondo destro.
const vec3 bulbs[20] = vec3[20](
// garlanda sinistra: onda che sale
vec3(0.4500, 0.2893, 4.0), vec3(0.4330, 0.3187, 2.0), vec3(0.4125, 0.3453, 0.0),
vec3(0.3875, 0.3747, 4.0), vec3(0.3591, 0.4080, 2.0), vec3(0.3341, 0.4360, 0.0),
vec3(0.3000, 0.4693, 4.0), vec3(0.2670, 0.5013, 2.0), vec3(0.2295, 0.5307, 0.0),
vec3(0.1920, 0.5587, 4.0),
// garlanda destra: onda che scende
vec3(0.5307, 0.3000, 0.0), vec3(0.5443, 0.3333, 2.0), vec3(0.5648, 0.3573, 4.0),
vec3(0.5852, 0.3840, 0.0), vec3(0.6205, 0.4253, 2.0), vec3(0.6420, 0.4573, 4.0),
vec3(0.6761, 0.4880, 0.0), vec3(0.7114, 0.5160, 2.0), vec3(0.7466, 0.5427, 4.0),
vec3(0.7773, 0.5613, 0.0)
);
// Intensità nel ciclo di 6 frame della singola lampadina (d = frame dalla rampa),
// ricavata dalle aree misurate sui frame della GIF: ~25%, ~50%, pieno, pieno, 0, 0.
float chase_intensity(float d) {
if (d < 1.0) {
return 0.3;
}
if (d < 2.0) {
return 0.6;
}
if (d < 3.0) {
return 1.0;
}
if (d < 4.0) {
return 0.95;
}
return 0.0;
}
void vertex() {
float wave = sin(TIME * drift_speed + VERTEX.y * 0.012) * drift_strength;
VERTEX.x += wave * (UV.y * 0.7 + 0.3);
}
void fragment() {
vec4 color = texture(TEXTURE, UV);
// Maschera morbida limitata alla stella in cima al tendone, non al tendone.
vec2 aspect_uv = (UV - star_center) * vec2(1.0, 1.35);
float star_area = 1.0 - smoothstep(0.105, 0.185, length(aspect_uv));
float pulse = 0.88 + 0.12 * sin(TIME * 5.0);
vec3 gold_glow = vec3(1.0, 0.70, 0.14) * star_area * hover_strength * pulse;
color.rgb += gold_glow * color.a;
// Chase lucine: sincronizzato a 12,5 fps come la GIF sorgente.
if (garland_strength > 0.001) {
float frame_time = TIME * garland_fps;
if (garland_freeze_frame >= 0.0) {
frame_time = garland_freeze_frame;
}
float cycle = mod(floor(frame_time), 6.0);
vec2 px = UV * vec2(880.0, 750.0);
for (int i = 0; i < 20; i++) {
float d = mod(cycle - bulbs[i].z + 6.0, 6.0);
float intensity = chase_intensity(d);
if (intensity <= 0.0) {
continue;
}
float r = length(px - bulbs[i].xy * vec2(880.0, 750.0));
// Nucleo pieno ~6px + alone ampio e diffuso fino a ~25px (come nella GIF).
float core = 1.0 - smoothstep(3.0, 8.0, r);
float halo = exp(-r / 12.0);
float ga = clamp(intensity * garland_strength, 0.0, 1.0);
// Il nucleo porta la lampadina all'oro pieno della GIF.
color.rgb = mix(color.rgb, GOLD_CORE, core * ga);
// Alone caldo additivo solo attorno al nucleo (senza sforare sul nucleo,
// altrimenti il clamp sbiadisce l'oro verso il bianco).
color.rgb += GOLD_HALO * (halo * ga * 0.7 * (1.0 - core));
color.a = max(color.a, halo * ga * 0.5);
}
}
color.rgb = min(color.rgb, vec3(1.0));
COLOR = color;
}
+12
View File
@@ -5,6 +5,11 @@ extends RefCounted
const DISPLAY_FONT := preload("res://shared/assets/fonts/NotoSerifDisplay-CondensedExtraBold.ttf")
const MENU_SHADER := preload("res://menus/assets/shaders/ambient_drift.gdshader")
const TENT_STAR_SHADER := preload("res://menus/assets/shaders/tent_star_glow.gdshader")
const TENT_LIGHTS_SHADER := preload("res://menus/assets/shaders/tent_lights.gdshader")
# Posizione e ingombro condivisi della freccia indietro in tutte le schermate.
const BACK_BUTTON_CENTER := Vector2(0.12, 0.14)
const BACK_BUTTON_SIZE := Vector2(0.09, 0.14)
static func set_normalized_rect(control: Control, center: Vector2, normalized_size: Vector2) -> void:
@@ -40,6 +45,13 @@ static func tent_star_material() -> ShaderMaterial:
return material
static func tent_material(garland_strength := 0.0) -> ShaderMaterial:
var material := ShaderMaterial.new()
material.shader = TENT_LIGHTS_SHADER
material.set_shader_parameter("garland_strength", garland_strength)
return material
static func add_image(parent: Control, texture: Texture2D, center: Vector2, normalized_size: Vector2, material: Material = null) -> TextureRect:
var image := TextureRect.new()
image.texture = texture
+117 -10
View File
@@ -18,6 +18,25 @@ const SETTINGS_BUTTON := preload("res://menus/assets/common/pulsante_rotellina.p
const CIRCUS_SIGN_PANEL := preload("res://menus/assets/p1/upendi_circus_sign_panel.png")
const CIRCUS_SIGN_ROPES := preload("res://menus/assets/p1/upendi_circus_sign_ropes.png")
const CURTAIN_FRAMES := [
preload("res://menus/assets/p1/curtain_anim/curtain_00.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_01.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_02.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_03.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_04.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_05.png"),
preload("res://menus/assets/p1/curtain_anim/curtain_06.png"),
]
const GARLAND_FRAMES := [
preload("res://menus/assets/p1/garland_anim/lights_00.png"),
preload("res://menus/assets/p1/garland_anim/lights_01.png"),
preload("res://menus/assets/p1/garland_anim/lights_02.png"),
preload("res://menus/assets/p1/garland_anim/lights_03.png"),
preload("res://menus/assets/p1/garland_anim/lights_04.png"),
preload("res://menus/assets/p1/garland_anim/lights_05.png"),
]
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
@@ -29,30 +48,118 @@ func _build() -> void:
MenuUI.add_full_texture(self, CLOUDS, MenuUI.ambient_material(1.0, 0.16))
_add_sign()
var blue := _add_tent_button(BLUE_TENT, Vector2(0.29, 0.46))
# Solo il tendone blu di riferimento attiva lo shader delle lucine in hover.
var blue := _add_tent_button(BLUE_TENT, Vector2(0.29, 0.46), true)
blue.pressed.connect(func(): juggle_requested.emit(blue))
var red := _add_tent_button(RED_TENT, Vector2(0.71, 0.46))
var red := _add_tent_button(RED_TENT, Vector2(0.71, 0.46), false)
red.pressed.connect(func(): gym_requested.emit(red))
_add_bottom_bar()
MenuUI.add_image(self, SETTINGS_BUTTON, Vector2(0.91, 0.13), Vector2(0.09, 0.14))
if not OS.has_feature("web"):
var exit_button := MenuUI.add_image_button(self, BACK_BUTTON, Vector2(0.075, 0.13), Vector2(0.09, 0.14))
var exit_button := MenuUI.add_image_button(self, BACK_BUTTON, MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE)
exit_button.pressed.connect(func(): exit_requested.emit())
func _add_tent_button(texture: Texture2D, center: Vector2) -> TextureButton:
func _add_tent_button(texture: Texture2D, center: Vector2, has_lights := false) -> TextureButton:
var button := MenuUI.add_image_button(self, texture, center, Vector2(0.36, 0.59))
var star_material := MenuUI.tent_star_material()
button.material = star_material
var star_mat := MenuUI.tent_star_material()
button.material = star_mat
button.tooltip_text = "Apri questo percorso"
# L'hover non altera più geometria e hitbox del tendone: mette in risalto
# esclusivamente la stella, tramite il parametro dello shader.
button.mouse_entered.connect(func(): star_material.set_shader_parameter("hover_strength", 1.0))
button.mouse_exited.connect(func(): star_material.set_shader_parameter("hover_strength", 0.0))
# Glow stella su entrambi i tendoni al passaggio del mouse
button.mouse_entered.connect(func(): star_mat.set_shader_parameter("hover_strength", 1.0))
button.mouse_exited.connect(func(): star_mat.set_shader_parameter("hover_strength", 0.0))
# Animazione apertura sipario e lucine in hover
if has_lights:
_setup_hover_curtain_and_garland(button, center)
return button
func _setup_hover_curtain_and_garland(parent_button: TextureButton, center: Vector2) -> void:
# Sipario: parte chiuso (frame 0). All'hover-in si apre (0 -> 6).
# All'hover-out si richiude al contrario (6 -> 0).
var curtain := TextureRect.new()
curtain.name = "Curtain"
curtain.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
curtain.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
curtain.mouse_filter = Control.MOUSE_FILTER_IGNORE
curtain.texture = CURTAIN_FRAMES[0]
add_child(curtain)
MenuUI.set_normalized_rect(curtain, center, Vector2(0.36, 0.59))
var curtain_idx := [0]
var curtain_dir := [0] # 0 = fermo, 1 = apre, -1 = chiude
var curtain_timer := Timer.new()
curtain_timer.wait_time = 1.0 / 12.5
curtain_timer.process_mode = Node.PROCESS_MODE_ALWAYS
curtain_timer.timeout.connect(func():
if curtain_dir[0] == 1:
if curtain_idx[0] < CURTAIN_FRAMES.size() - 1:
curtain_idx[0] += 1
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
curtain.visible = true
else:
# Completamente aperto: frame 06 è vuoto/trasparente
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
curtain_dir[0] = 0
curtain_timer.stop()
elif curtain_dir[0] == -1:
if curtain_idx[0] > 0:
curtain_idx[0] -= 1
curtain.texture = CURTAIN_FRAMES[curtain_idx[0]]
curtain.visible = true
else:
# Completamente chiuso: resta fermo su frame 00
curtain.texture = CURTAIN_FRAMES[0]
curtain_dir[0] = 0
curtain_timer.stop()
)
add_child(curtain_timer)
# Lucine a inseguimento
var lights := TextureRect.new()
lights.name = "HoverGarland"
lights.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
lights.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
lights.mouse_filter = Control.MOUSE_FILTER_IGNORE
lights.texture = GARLAND_FRAMES[0]
lights.visible = false
add_child(lights)
MenuUI.set_normalized_rect(lights, center, Vector2(0.36, 0.59))
var lights_idx := [0]
var lights_timer := Timer.new()
lights_timer.wait_time = 1.0 / 12.5
lights_timer.process_mode = Node.PROCESS_MODE_ALWAYS
lights_timer.timeout.connect(func():
lights_idx[0] = (lights_idx[0] + 1) % GARLAND_FRAMES.size()
if lights.visible:
lights.texture = GARLAND_FRAMES[lights_idx[0]]
)
add_child(lights_timer)
lights_timer.start()
parent_button.mouse_entered.connect(func():
# Avvia apertura sipario (hover in)
curtain_dir[0] = 1
curtain_timer.start()
# Avvia lucine
lights_idx[0] = 0
lights.texture = GARLAND_FRAMES[0]
lights.visible = true
)
parent_button.mouse_exited.connect(func():
# Avvia chiusura sipario al contrario (hover out)
curtain_dir[0] = -1
curtain_timer.start()
# Spegne lucine
lights.visible = false
)
func _add_bottom_bar() -> void:
# Il piedone entra dal basso come un unico blocco; la scritta resta
# invisibile fino a quando il rimbalzo della barra non è terminato.
@@ -30,7 +30,7 @@ func _build() -> void:
artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(artwork)
_add_hotspot(Vector2(0.12, 0.14), Vector2(0.10, 0.14), func(): back_requested.emit())
_add_hotspot(MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE, func(): back_requested.emit())
_add_hotspot(Vector2(0.775, 0.64), Vector2(0.07, 0.10), func(): _change_balls(-1))
_add_hotspot(Vector2(0.895, 0.64), Vector2(0.07, 0.10), func(): _change_balls(1))
_add_hotspot(Vector2(0.79, 0.88), Vector2(0.30, 0.14), _start_game)
+1 -1
View File
@@ -27,7 +27,7 @@ func _build() -> void:
artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(artwork)
_add_hotspot(Vector2(0.12, 0.14), Vector2(0.10, 0.14), func(): back_requested.emit())
_add_hotspot(MenuUI.BACK_BUTTON_CENTER, MenuUI.BACK_BUTTON_SIZE, func(): back_requested.emit())
_add_hotspot(Vector2(0.775, 0.64), Vector2(0.07, 0.10), func(): _change_players(-1))
_add_hotspot(Vector2(0.895, 0.64), Vector2(0.07, 0.10), func(): _change_players(1))
_add_hotspot(Vector2(0.79, 0.88), Vector2(0.30, 0.14), _start_game)