How do I make Material/Shader Instances (2D)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By rgrams
:warning: Old Version Published before Godot 3 was released.

I’ve created a simple shader to make my enemy sprites flash white when they get hit, but now they all flash when any one of them is hit. How do I create and assign dynamic instances of my material or shader to each instance of the enemy scene?

:bust_in_silhouette: Reply From: volzhs

you need to create material for each sprite.

const SHADER = preload("res://effect.shd")

var mat = CanvasItemMaterial.new()
mat.set_shader(SHADER)
sprite_instance.set_material(mat)

Ah, thanks! This works perfectly.

A variation on Puppetmaster’s snippet seems to be a more efficient way—just set the material to a duplicate the current one.

var mat = get_node("Sprite").get_material().duplicate()
get_node("Sprite").set_material(mat)

rgrams | 2016-04-11 11:49

:bust_in_silhouette: Reply From: puppetmaster-

There is an open issues Add “Make Resource Unique” flag (allow unique Materials on instanced nodes)

You can do something like this in _ready() of sprite

_ready()
    set_material(get_material().duplicate())

I have had the same problem with LightOccluder2D.

Thanks! Your line of code didn’t work for me, but duplicating is a good idea. i just did this:

var mat = get_node("Sprite").get_material().duplicate()
get_node("Sprite").set_material(mat)

rgrams | 2016-04-11 11:51

in Godot 3.0 you need to pass true to duplicate, so that all of the material’s children (including the shader) are duplicated.

var mat = get_node("Sprite").get_material().duplicate(true)
get_node("Sprite").set_material(mat)

zink | 2017-10-19 12:46

:bust_in_silhouette: Reply From: aggregate1166877

First, use the UI the set up the Material exactly as you want. In my case, I created fully functional shader materials all pointing to .shader files.

Then, save the material as a .tres file:
enter image description here

Now, you’re ready to import programmatically from anywhere. Below I dynamically create the target node, but this is optional can be done with nodes added via GUI as well.

# Contains a ViewportCollection with a Viewport and Node2D drawing.
const MyDynamicScene = preload('res://Scenes/MyDynamicScene.tscn')

# The shader material we just saved.
const MyShaderMaterial = preload('res://Materials/MyShaderMaterial.tres')

func _draw():
	# Use get_node or $NodeName if you'd rather use GUI-managed scenes.
	var dyn_scene = MyDynamicScene.instance()
	dyn_scene.name = 'DynamicScene'
	dyn_scene.material = MyShaderMaterial
	add_child(dyn_scene)

My shader material result:
enter image description here