Godot Version
4.6.2
Question
I’ve been researching this question for a while now and I am genuinely stumped and confused. The way I want to implement this is having a subviewport that never clears render the blood splats (i.e through a sprite 2d node that is deleted after rendered) but it is so much more complicated then that for so many reasons, like tilesets not being able to clip children sprite naturally, subviewports needing a subviewport container to render, and subviewports just being weird in general. If anyone knows how to do this, please help.
Subviewports don’t need containers to render. You can plug their textures into shaders. Not sure why you think they’re weird. They are just off-screen rendering buffers and they work as intended.
1 Like
Thanks for the tip, I’ve just been having trouble with them and they aren’t really the easiest thing to visualize and use. Either that or I’m just spoiled by Gamemaker’s surfaces 
My top-down game has a lot of TileMapLayers. I use a single Sprite2D for blood splatters, the Sprite2D is located between floor and wall layers. The game is a pixel graphics game (640x360) and the levels are quite small, so the blood splatter sprite’s size is somewhere below 2000x2000 px.
When a blood splatter appears, I place a small blood Sprite2D on top of the persistent large blood splatter sprite. The small sprite’s blood color is a bit brighter than the final persistent blood color. I use tween to fade out the small sprite, the fade time is 4 seconds, the tween also changes the color towards the final darker color. When the blood splatter appeared, it was also added to the update list of the persistent splatter sprite. I redraw the texture for the large sprite only once per 0.5 seconds if there are new splatters in the update list. When the small sprite is faded out and freed after 4 seconds, the persistent sprite is surely redrawn.
If my levels were bigger, let’s say I would need a 10k x 10k px splatter sprite, I could divide it to, for example, 512 x 512 px tiles. These smaller splatter tiles would be created only when needed. So if there are parts of a level where nobody dies, no need for a blood splatter tile there. The tile borders would need to be handled so that the splatter is drawn to both tiles.
I’m sorry for the late reply, but for clarification what your saying is that you have a blank textured sprite 2d node that you put on certain parts of your level and you then draw the splats on the giant sprite 2d? If that is what your saying, then that is a genius idea. I’ll have to tinker around with that idea a bit but I think I’m on the right track
Yes, one big texture on Sprite2D that covers the entire level (as my levels are small enough).
This is how I create the texture intially:
h_img = Image.create_empty(map_size.x * 24, map_size.y * 24, false, Image.FORMAT_RGBA8)
h_img.fill(Color(0, 0, 0, 0)) # fully trasparent
self.texture = ImageTexture.create_from_image(h_img) # "self" is a Sprite2D here
Small splatter textures are drawn to h_img using blend_rect(), something like this:
h_img.blend_rect(splatter_img, src_rect, splatter_pos)
After drawing the new splatters, I draw a few masks using blit_rect_mask(), that just erases splatters from some areas. The floor has holes (staircases) and I don’t want the blood splatters to cover them.
Finally the image is updated to the Sprite2D’s texture:
self.texture = ImageTexture.create_from_image(h_img)
Quick update:
I’ve been able to get the basics of the blank sprite method done, but now I’m working on fully finishing it. Something nice I found was that even though the game should lag when I have a big image I was able to get a 4000x4000 sprite to draw an image on itself every frame without a hitch, but I still need to check and make sure my friend’s pcs can handle it. I’ll post the solution here when I’m done, thanks guys.
The approach posted by @Dizzy_Caterpillar is only adequate it the texture is small. It won’t scale well because it basically does all of the pixel munching on the cpu side, uploading a new texture to video ram on each update. All of that is quite expensive compared to doing it on the gpu using a subviewport.
This reply gave me an idea on how I could do this that uses both of your methods. For a basic explanation, I plan to apply the splatters through area 2d based particles. When one hits a surface, it will create a blank image with the size and position of the tile, draw itself onto the blank image with its current position, render itself to a sub viewport that doesn’t clear, and then delete. This will not only solve the problem of tilemaplayers not clipping children, it will also make this at least somewhat more optimized compared to having a big texture update every time a particle is spawned. If this method has some glaring issue I’m not aware of please let me know.
This is precisely how you should do it. Render stuff into viewport and draw the viewport only when something is changing. You might get away with drawing it every frame. If your map is very large, split into several viewports.
If your splatters fade regularly or never - you can use particles. If you need more “manual” control over deletion - use 2D multimesh instances. You can also just use regular sprites.