I have it narrowed down to the shader I use on the scene.
I use a color swap shader that has palette swap functionality by changing the output palette.
I use an enum and an array to figure out which palette to load(in the Game manager).I swap colors in the func _ready of the scene. I simplified the following to just the issue. So I put all the code I use to replicate the issue.
Shader
:
shader_type canvas_item;
const uint max_palette_array_size = uint(8);
uniform uint palette_array_size = uint(0);
uniform sampler2D input_palette_texture: filter_nearest;
uniform sampler2D output_palette_texture: filter_nearest;
uniform vec4 input_palette_array[max_palette_array_size]: source_color;
uniform vec4 output_palette_array[max_palette_array_size]: source_color;
int compare_floats(float a, float b) {
float difference = a - b;
if(abs(difference) < 0.0001) {
return 0;
} else if(difference > 0.0) {
return 1;
} else {
return -1;
}
}
bool color_is_approx_equal(vec4 a, vec4 b) {
if (
a.r >= b.r - 0.001 && a.r <= b.r + 0.001 &&
a.g >= b.g - 0.001 && a.g <= b.g + 0.001 &&
a.b >= b.b - 0.001 && a.b <= b.b + 0.001 &&
a.a >= b.a - 0.001 && a.a <= b.a + 0.001
) {
return true;
}
return false;
}
bool color_is_transparent(vec4 color) {
return compare_floats(color.a, 0.0) == 0;
}
vec2 find_color_in_palette_texture(vec4 color, sampler2D palette) {
ivec2 palette_size = textureSize(palette, 0);
for(uint y = uint(0); y < uint(palette_size.y); y++) {
for(uint x = uint(0); x < uint(palette_size.x); x++) {
vec2 palette_coord = vec2(
float(x) / float(palette_size.x),
float(y) / float(palette_size.y)
);
vec4 palette_color = texture(palette, palette_coord);
if(color == palette_color) {
return palette_coord;
}
if(
color_is_transparent(color) &&
color_is_transparent(palette_color)
) {
return palette_coord;
}
}
}
return vec2(-1.0);
}
vec4 get_color_from_palette_texture(vec2 coord, sampler2D palette) {
return texture(palette, coord);
}
void swap_colors_from_palette_textures(
vec4 input_color,
inout vec4 output_color
) {
vec2 palette_coord = find_color_in_palette_texture(
input_color, input_palette_texture
);
if(palette_coord == vec2(-1.0)) {
return;
}
output_color = get_color_from_palette_texture(
palette_coord,
output_palette_texture
);
}
int find_color_in_palette_array(vec4 color, vec4[max_palette_array_size] palette) {
int safe_palette_array_size = int(clamp(
palette_array_size,
0,
max_palette_array_size
));
for(int i = 0; i < safe_palette_array_size; i++) {
vec4 palette_color = palette[i];
if(color_is_approx_equal(color, palette_color)) {
return i;
}
if(color_is_transparent(color) && color_is_transparent(palette_color)) {
return i;
}
}
return -1;
}
vec4 get_color_from_palette_array(
uint coord,
vec4[max_palette_array_size] palette
) {
return palette[coord];
}
void swap_colors_from_palette_arrays(
vec4 input_color,
inout vec4 output_color
) {
int palette_coord = find_color_in_palette_array(
input_color,
input_palette_array
);
if(palette_coord == -1) {
return;
}
output_color = get_color_from_palette_array(
uint(palette_coord),
output_palette_array
);
}
void fragment() {
vec4 input_color = texture(TEXTURE, UV);
vec4 output_color = texture(TEXTURE, UV);
swap_colors_from_palette_textures(input_color, output_color);
swap_colors_from_palette_arrays(input_color, output_color);
COLOR = output_color;
}
Game Manager
extends Node2D
class_name GameManager
enum StateColor {YELLOW, RED, BLUE, GREEN}
const StateColorValue:=["YELLOW", "RED", "BLUE", "GREEN"]
const ShaderColorPalette:=[preload("res://shader/yellow_palette.png") ,preload("res://shader/red_palette.png") ,preload("res://shader/blue_palette.png"),preload("res://shader/green_palette.png")]
THE ACTUAL SCENE:
extends Area2D
@onready var animatedsprite2d= $AnimatedSprite2D
@export var PlayerColorState:GameManager.StateColor=GameManager.StateColor.YELLOW
func _ready():
self.animatedsprite2d.material.set("shader_parameter/output_palette_texture",GameManager.ShaderColorPalette[PlayerColorState])
Is the shader shared between all instances of that scene I use in my level?
I’m guessing this is the reason?
Possible solutions I see are:
- Create a new scene with an animation sprite 2d that has different animations for different colors (ok-ish solution?)
- Each color has a separate scene (not a good solution)
- Whatever someone else says on here?