Wall Destruction / Block Break

This shaders creates a dynamic wall destruction effect by dividing a sprite into multiple blocks and making them fall down, simulating a mining or demolition effect.

The shader is highly customizable, allowing you to control:

  • Block size / divisions
  • Rotation amount
  • Fall speed
  • Fade out effect

This makes it suitable for destructible environments, mining systems, or any 2D destruction effects.

How to Use

In my game, I used this shader for wall demolition during mining, as shown in the GIF.

To use:

  1. Apply the shader to a Sprite2D (or TextureRect)
  2. Animate the shader parameters using an AnimationPlayer or Tween
  3. Adjust the parameters to achieve the desired effect

The shader also works with AtlasTexture.

Shader code
shader_type canvas_item;

uniform float progress_fall : hint_range(0.0, 1.0) = 0.0;     
uniform float progress_rotation : hint_range(0.0, 10.0) = 0.0; // Aumentei o range para rotações infinitas
uniform float progress_scatter : hint_range(0.0, 6.0) = 0.0; 
uniform float progress_fade : hint_range(0.0, 1.0) = 0.0;     

uniform vec2 grid_size = vec2(10.0, 10.0);

// Física
uniform float gravity = 6.0;
uniform float ground_level : hint_range(0.0,2.0) = 0.9;
uniform float stack_variation = 0.15;
uniform float slide_on_ground = 0.3;

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

const float scale = 2.1;

void vertex() {
    VERTEX *= 1. + scale;
}

void fragment() {
    float total_scale = 1.0 + scale;
    vec2 local_uv = (UV - REGION_RECT.xy) / REGION_RECT.zw;
    vec2 adjusted_local_uv = (local_uv - 0.5) * total_scale + 0.5;
    
    vec4 final_color = vec4(0.0);
    vec2 my_cell = floor(adjusted_local_uv * grid_size);
    bool found = false;

    for (int y = -17; y <= 5; y++) { // Aumentei a margem vertical para não sumir ao espalhar muito
        for (int x = -8; x <= 8; x++) { // Aumentei a margem horizontal para o scatter alto
            vec2 check_cell = my_cell + vec2(float(x), float(y));

            if (check_cell.x < 0.0 || check_cell.x >= grid_size.x || check_cell.y < 0.0 || check_cell.y >= grid_size.y) {
                continue;
            }

            float r_delay = rand(check_cell) * 0.2;
            float r_scatter = rand(check_cell + vec2(1.1, 0.0));
            float r_rotate = rand(check_cell + vec2(0.0, 1.1));
            float r_stack = rand(check_cell + vec2(1.1, 1.1));
            float r_slide = rand(check_cell + vec2(2.2, 0.0));

            // --- REMOVIDO O CLAMP DE 0-1 ---
            // Agora os valores podem crescer além de 1.0
            float p_fall = clamp((progress_fall - r_delay) / (0.8), 0.0, 1.0);
            float p_rot = max(progress_rotation - r_delay, 0.0); 
            float p_scat = max(progress_scatter - r_delay, 0.0);

            vec2 cell_center = (check_cell + vec2(0.5)) / grid_size;

            // --- Posição Vertical ---
            float fall_dist = gravity * p_fall * p_fall;
            float personal_ground = ground_level + (r_stack - 0.5) * stack_variation;
            
            float final_y = min(cell_center.y + fall_dist, personal_ground);
            bool is_on_ground = (cell_center.y + fall_dist) >= personal_ground;

            // --- Posição Horizontal (Scatter Destravado) ---
            // O scatter agora flui livremente conforme progress_scatter aumenta
            float lateral_move = (r_scatter - 0.5) * 0.4 * p_scat;
            float ground_slide_val = (r_slide - 0.5) * slide_on_ground * (is_on_ground ? p_scat : 0.0);
            float final_x = cell_center.x + lateral_move + ground_slide_val;

            vec2 offset = vec2(final_x - cell_center.x, final_y - cell_center.y);

            // --- Rotação (Destravada e Infinita) ---
            // p_rot agora não para em 1.0, permitindo múltiplas voltas
            float angle = (r_rotate - 0.5) * 6.0 * p_rot;
            float c = cos(angle); float s = sin(angle);

            // Mapeamento inverso
            vec2 rel_uv = adjusted_local_uv - cell_center - offset;
            vec2 rotated_rel_uv = vec2(rel_uv.x * c - rel_uv.y * s, rel_uv.x * s + rel_uv.y * c);
            vec2 orig_local_uv = cell_center + rotated_rel_uv;

            if (floor(orig_local_uv * grid_size) == check_cell) {
                vec2 atlas_uv = orig_local_uv * REGION_RECT.zw + REGION_RECT.xy;
                vec4 tex_color = texture(TEXTURE, atlas_uv);
                
                if (tex_color.a > 0.0) {
                    float local_fade = clamp((progress_fade - r_delay) / 0.8, 0.0, 1.0);
                    final_color = vec4(tex_color.rgb, tex_color.a * (1.0 - local_fade));
                    found = true; 
                    break;
                }
            }
        }
        if (found) break;
    }
    COLOR = final_color;
}
Live Preview
Tags
animation, block, Break, Destruction, fragment, Physics, pixel, sprite
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Purga

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments