ToggleableTileMapLayer: Apply shader transition - #2699
Conversation
|
Play this branch at https://play.threadbare.game/branches/endlessm/toggleable-transition/. (This launches the game from the start, not directly at the change(s) in this pull request.) |
|
As part of #2675 I'm trying to animate the ToggleableTileMapLayers that toggle with quest changes: toggleable-transition.webmAlso works with any ToggleableTileMapLayer of course: hookable-transition.webmrepellable-transition.webm |
|
It took me some time (and back and forth with the help of Claude) to make it work both for CanvasGroups (as before in cutscenes) and for a single CanvasItem, which may be affected by other shaders: like the Light2D nodes and the CanvasModulate inside TimeAndWeather. This works for the existing broken bridges, with a bit of setup. In the future we may want to have more complex "old world / new world" to the actual levels than a single bridge tile. But they are enough for now! The setup is:
It uses a Tween internally so an AnimationPlayer is not needed. Because the bounds now have to be specified, the cutscenes are also reliable to edit now in the editor. The problem I commented in this video (the effect moving as the tilemap crops in the editor) is now gone! Grabacion.de.pantalla.desde.2026-08-12.18-24-48.mp4I also improved the shader to make it fully transparent at progress = 0 regardless of the mask texture, and fully visible at progress = 1. |
98cb626 to
046a7b9
Compare
|
I went to aplly the transition to the Void barrier in Linenville.. uh oh it has as shader already (the Void chromakey). Fine, I can add it to a child of a CanvasGroup and apply the transition effect to it.. but now I need to make the toggleable script smarter and lookup in the SceneTree the first node that is a CanvasGroup. This is getting a bit complex. Grabacion.de.pantalla.desde.2026-08-13.13-08-19.mp4 |
6dd001c to
591856b
Compare
Bridges and Void barriers made with `ToggleableTileMapLayer` appeared and disappeared when toggled. This change give them an optional fade transition, reusing the world-rewoven effect. The shader require several changes to be extended to work on a `TileMapLayer` (or any other `CanvasItem`), in addition to keep working on a `CanvasGroup`. And now it works better for the prior `CanvasGroup` case too. Toggleable transition Add `transition_duration` to `ToggleableTileMapLayer` . When a usable material is present it tweens the shader's `progress` parameter instead of flipping the layer instantly. - The state is committed halfway through the fade, so the change is *felt* mid-transition rather than at the end. Only `collision_enabled`, `navigation_enabled` and `occlusion_enabled` flip at the midpoint. The prior `enabled` is left until the very end, because it also controls rendering and turning it off early would hide the second half of the animation. - Interrupting a transition doesn't reset it. Each half of the tween lasts in proportion to the distance it covers, so `progress` moves at a constant rate and a toggle that interrupts another one is proportionally shorter. - The material is looked up once, in `target` first and then in the first ancestor `CanvasGroup`, since the material may be wrapped in one. This is the case for Void barriers because they already have a shader, the Void chromakey. It is only accepted if it is a `ShaderMaterial` with a `progress` parameter. Otherwise the toggle stays instant. In the `Toggleable` base class, `set_toggled()` now has an `immediate` parameter, and `initialize_with_value()` passes `true`. This is to keep the initialisation immediate on scene entry. World rewoven shader changes Before the effect was for a `CanvasGroup` to create cutscenes. Now it's extended to serve both cases. - Add `target_canvas_group` toggle for the two cases. - The mask rect is now explicit, sampled from world by new `mask_origin` / `mask_size` uniforms, instead of by `UV`. Because on a `TileMapLayer` the UV is the coordinate into the tileset *atlas* for each tile, so the mask was repeating on every single tile. The `mask_texture` also gained `repeat_disable` so the region clamps rather than wrapping. - Drop `render_mode unshaded`. The engine only applies a `CanvasModulate`'s color and 2D lights to items that are not unshaded, so the layer stayed untinted while the rest of the scene darkened. - Fix `progress` extremes with a threshold, so 0 hides every pixel and 1 shows every pixel regardless of what the mask contains. In between, the mask still shapes the wipe. Previously, pixels at the mask minimum value stayed invisible at `progress = 1`. - Make burn look a bit more as light. The new `canvas_modulate` uniform (which unfortunately has to be passed, matching the scene) divides out the scene tint so the burn keeps full brightness, and the default `burn_color` is now brighter than white. This is a detail: when not passed the effect still looks ok. Check custom_hookable_objects_test.tscn for a scene that uses it. New TileMapMaskBounds helper node Because the mask is now positioned in world units, something has to supply the rect. This behavior node computes the mask rect from `get_used_rect()` of the TileMapLayers in its target (itself and any descendants) and writes `mask_origin` / `mask_size` into the shader material. It has an "Update bounds" button to apply it after tile edits. Note: only TileMapLayer nodes are considered (wouldn't work for WorldNew / WorldOld in cutscenes). Sprite2Ds and other CanvasItems are ignored because Godot doesn't have a general method to obtain bounds on them. This is surfaced as a configuration warning when no tile layer is found. Add BaseCanvasItemBehavior and refactor ModulateAsSkyBehavior to use it, plus this new behavior above. Scene changes Transitions applied to existing toggleable layers, each paired with a TileMapMaskBounds: - `frays_end.tscn`: Dev Archipelago bridge fixed / broken. Also updated waiters in the townie dialogue lines to out-last the new bridge transition. - `frays_end.tscn`: Void barrier (same style as in linenville below). - `song_sanctuary_path.tscn`: The side-quest bridge fixed / broken. - `grapple_gym_2.tscn`: The bridge that appears (I had to initialize it in the scene script). - `linenville_path.tscn`: The Void barrier. For this one I used a noise texture and a different burn color, because it's not actually a rewoven, it's a "void cleared" effect. - `custom_hookable_objects_test.tscn` and `custom_repellable_objects_test.tscn`: The fence doors. Also the two `world_rewoven_cutscene_*.tscn` scenes were updated for the new uniforms (`target_canvas_group`, `mask_origin`, `mask_size`, `canvas_modulate`, new `burn_color`).
591856b to
ca4f65c
Compare
So yeah I ended up doing this (making the script lookup the CanvasGroup ancestor). |
Bridges and Void barriers made with
ToggleableTileMapLayerappearedand disappeared when toggled. This change give them an optional fade
transition, reusing the world-rewoven effect.
The shader require several changes to be extended to work on a
TileMapLayer(or any otherCanvasItem), in addition to keepworking on a
CanvasGroup. And now it works better for the priorCanvasGroupcase too.Toggleable transition
Add
transition_durationtoToggleableTileMapLayer. When a usablematerial is present it tweens the shader's
progressparameterinstead of flipping the layer instantly.
felt mid-transition rather than at the end. Only
collision_enabled,navigation_enabledandocclusion_enabledflip at the midpoint. The prior
enabledis left until the veryend, because it also controls rendering and turning it off early
would hide the second half of the animation.
lasts in proportion to the distance it covers, so
progressmovesat a constant rate and a toggle that interrupts another one is
proportionally shorter.
targetfirst and then in thefirst ancestor
CanvasGroup, since the material may be wrapped inone. This is the case for Void barriers because they already have a
shader, the Void chromakey. It is only accepted if it is a
ShaderMaterialwith aprogressparameter. Otherwise the togglestays instant.
In the
Toggleablebase class,set_toggled()now has animmediateparameter, and
initialize_with_value()passestrue. This is tokeep the initialization immediate on scene entry.
World rewoven shader changes
Before the effect was for a
CanvasGroupto create cutscenes. Nowit's extended to serve both cases.
target_canvas_grouptoggle for the two cases.mask_origin/mask_sizeuniforms, instead of byUV. Because ona
TileMapLayerthe UV is the coordinate into the tileset atlasfor each tile, so the mask was repeating on every single tile. The
mask_texturealso gainedrepeat_disableso the region clampsrather than wrapping.
render_mode unshaded. The engine only applies aCanvasModulate's color and 2D lights to items that are notunshaded, so the layer stayed untinted while the rest of the scene
darkened.
progressextremes with a threshold, so 0 hides every pixel and1 shows every pixel regardless of what the mask contains. In
between, the mask still shapes the wipe. Previously, pixels at the
mask minimum value stayed invisible at
progress = 1.canvas_modulateuniform (which unfortunately has to be passed, matching the scene)
divides out the scene tint so the burn keeps full brightness, and
the default
burn_coloris now brighter than white. This is adetail: when not passed the effect still looks ok. Check
custom_hookable_objects_test.tscn for a scene that uses it.
New TileMapMaskBounds helper node
Because the mask is now positioned in world units, something has to
supply the rect. This behavior node computes the mask rect from
get_used_rect()of the TileMapLayers in its target (itself and anydescendants) and writes
mask_origin/mask_sizeinto the shadermaterial. It has an "Update bounds" button to apply it after tile
edits.
Note: only TileMapLayer nodes are considered (wouldn't work for
WorldNew / WorldOld in cutscenes). Sprite2Ds and other CanvasItems are
ignored because Godot doesn't have a general method to obtain bounds
on them. This is surfaced as a configuration warning when no tile
layer is found.
Add BaseCanvasItemBehavior and refactor ModulateAsSkyBehavior to use
it, plus this new behavior above.
Scene changes
Transitions applied to existing toggleable layers, each paired with a
TileMapMaskBounds:
frays_end.tscn: Dev Archipelago bridge fixed / broken. Also updated waiters in the townie dialogue lines to out-last the new bridge transition.frays_end.tscn: Void barrier (same style as in linenville below).song_sanctuary_path.tscn: The side-quest bridge fixed / broken.grapple_gym_2.tscn: The bridge that appears (I had to initialize it in the scene script).linenville_path.tscn: The Void barrier. For this one I used a noise texture and a different burn color, because it's not actually a rewoven, it's a "void cleared" effect.custom_hookable_objects_test.tscnandcustom_repellable_objects_test.tscn: The fence doors.Also the two
world_rewoven_cutscene_*.tscnscenes were updated forthe new uniforms (
target_canvas_group,mask_origin,mask_size,canvas_modulate, newburn_color).Helps #2675