Maybe some of you have any experience or an idea on how to make a 2D fire propagation?
I’m thinking about creating small objects - fire elements with one collision object that would create a fire particlefx when it is colliding with other “combustible” collision object. I prototyped this and it’s ok, but when I want to add propagation I’m trying to create 4 fire objects around the triggered one - those object should behave the same - check if each of them is colliding with flammable object and if so, trigger particlefx. It’s working up to the point when I reach particlefx or collision limit…
Here’s the problem:
Create one fire element and if it’s colliding with flamable box - trigger particlefx. When it’s triggered create 4 fire element objects around:
Each element behaves the same - it checks if it’s colliding with flammables, if so, triggers itself and creates another 4 objects around:
With blue color I marked the problem - two objects are created in the place of the one existing previously and because it behaves the same it’s the beginning of the chain reaction
To overcome that I would need to store all created elements and before creating new one - check if some other is existing in that place. So I would slice my world into a grid of possible positions.
I don’t think it’s a good solution, so at this point I would like to ask you for some tips or other ideas?
Yes, simply by disabling the flammable collision object, but in that case, the fire will stop propagating In the so called “picture” I attached above, the purple box should be all burning. I think I might add 2 collision objects for one fire element - one will be for detecting if it’s not colliding with other fire element, and second would check if it’s colliding with a combustible object. I can’t decide which solution would be better - creating so many collisions or attaching all objects to a fixed grid and storing the information about a particular position on the grid if it’s locked or not.
I have not much experience with propagation but spawning fire objects is the wrong approach.
I would either use that cellular grid or attach a small fire script to all the existing objects which can catch fire. (Like a branch catches fire and sends a message to all surrounding (colliding) branches and stones. The stones have no script and don’t react, the other branches have the script and start burning and send ignite messages other burnable objects).
Basically both solutions use the same concept but are implemented differently.
The quick and dirty prototype is working (still needs to add damage to the bush, so it can be destroyed when burned out):
It works as I desribed above, but I’ve added the first collision object that detects if there is existing objecy, and then it is disabled and the second collision object is enabled ti check if it can be ignited. It’s not perfect, so I’ll probably think about implementing the grid for bushes and grounds, but remain with a solution that the mobs will have their own particlefx triggered if it’s state will be changed to “burning” - it works like that:
The profile snapshot looks ok. There’s some time spent in scripts and messages. There’s 144 script instances and 220 messages. That’s quite a few to be honest. Can it be optimized somehow?
I guess I will only add even more scripts and messages So, is it better to have things in one long script than in few smaller? I generally use one script per go, but those scripts are using a lot of modules. I guess I must soon start optimizing it, though I tried to refactor it several times
Using many modules doesn’t matter much. It’s more costly to have many script files, especially if they have update() and on_message() functions as those will be called very frequently.
You should start thinking about where you have your scripts and how to structure the code. One good example is how to deal with bullets/projectiles for instance:
Bullets moving in a straight line should use go.animate(). You shouldn’t move in an update() function.
Bullets with complex movement such as heat seeking missiles probably need to be moved frame by frame in an update() function.
The question is in which update() function? Should it be on a script on the missile or should it be on a central script holding a list of all missiles? This can probably be answered by thinking of how to deal with collisions.
Bullet collisions can be dealt with in the script on each bullet or in the script of whatever kind of objects the bullets can hit.
If you have many bullets moving in a straight line and fewer targets it makes the most sense to animate using go.animate(), have no script on the bullets and handle collisions on the targets.
If you have few bullets moving in a straight line but many targets it might make sense to have scripts on the bullets to deal with the collisions, but this depends on how the targets are moved, do they already have scripts you can reuse or not?
For heat seeking missiles and many other scenarios it makes more sense to have a list of bullets and a single script that moves all of them.
One of the pillars of my game Witchcrafter: Empire Legends I want to have is fire spreading.
I started tinkering with it one year ago as you can see here and recently I got back to this feature with fresh mind and new ideas.
About this topic - a lot of things changed, I learned a lot about cellular automata, AABB and many other game dev crucial topics. Like from the beginning, the biggest inspiration is still Far Cry fire spreading made in Dunia (derivative of CryEngine)
2D board of cells/tiles
So I decided to make it simple - it’s for the 2D game, so everything is like a simple board with cartesian coordinates. I finally managed to use tilemaps for this purpose. Every tile in a tilesource in Defold has its ID, so I can have the crucial information about any tile and recognise that object easily. I can get the tile ID by calculating the tilemap coordinates easily, when it has its left bottom corner in the origin of the world coordinates:
local function convert_world_to_tilemap_coords(x,y)
return math.ceil(math.ceil(x)/M.TILE_SIZE), math.ceil(math.ceil(y)/M.TILE_SIZE) + 1
end
local function convert_tile_to_world_coords(x,y)
return ((x*M.TILE_SIZE) + (M.TILE_SIZE/2)), ((y*M.TILE_SIZE) + (M.TILE_SIZE/2))
end
M.TILE_SIZE which is 16 is my game is enough for the size of the cell.
So if I want to interact with or change the particular tiles I can use both position of the player or something or use a small collision object like this fireball:
–gif here-
I can get the position of collision object and translate it to tilemap coordinates and check what the tile id or adjacent tiles ids are and change that tile to another tile id - this is a basic idea of changing the visual representation of the tile - but also using another tile id, we can have some other information about the tile saved now. How we can use that information?
Things that burn
Some simple assumptions about spreading fire in games are that “burnable” things are made of wood or fabrics or are part of flora. Those things can catch fire and in some cases, fire can propagate over them (like on grass and leaves in Far Cry games). Using information about tile ids we can associate the information about the tile “material” with tile id. To simplify it, I made a “sections” in a tile source:
You can see that first line is used to specify the “form” of the tilemap - it is used for collisions - you can take a look at True Tile Collision to have a glimpse at the idea behind it.
The second section represents the burnable grass tiles (those are also animated using Defold Tilemap Animator, thus that alignment)
Third section represents wooden objects that will be burnable too and fire can spread over them, but not so easily as over the grass.
Forth section represents the ground - I think one should be able to put a fire over it, but shouldn’t allow to propagate the fire - this is something to consider probably.
Last section includes not burnable tiles such as rocks, stones, walls, background elements and water.
Now I have a module that includes the information about the starting and ending tile id of each section and I can now get any tile and check to which section it belongs!
Visual representation of burning
In many games fire is only the visual effect (shader vfx or particle fx, as I made it above as well) and deals some damage in contact with lifeforms - be it player or mobs/enemies in the game and vanishes after some time. But in some cases devs decide to add visual representation of damage taken to the actual object - burnt wooden box, burnt grass, “burnout stain” on the floor or wall. It can be a shader thing (with texture or procedural) or change of the whole texture of the object to the manualy drawn one. The latter demands a lot of work, while the first could be very tricky. I tried both:
Shader approach:
With shading the basic idea I had in my mind was drawing “burnout stains” in a separate render target the blending it with the tilemap:
I am not satisfied with the results above, so I tinkered a little bit with the texture and shader used this, but I can’t get better results It’s rather more appropriate for some paint stains than burnouts. And though I used particles here to attain the irregular shape, the shader should still modify the animation and shape of the tile to look realistic. If you have any ideas here, I would be glad to hear them. Take a look how Remedy managed to do something analogical with cracks in Control (a mix of a HD texture and a great shader (+Ray Tracing ):
Tilesource approach:
So I tried with hand-drawn textures:
The idea is simple here, because you have that part of tilesource below the regular ones and you change the tile to the burnt version of the same tile:
We naturally want that “burnout stain” to have irregular shape, not rectangular shape I know of course, what the solution for this looks like - I should create a tilesource that has all the versions of one burnt tile in regards to its adjacent tiles - if it is burnt or not - something, that I could “autotile” later. And drawing this will take hours and I will need to realign tiles in the tilesource and maintain the proper autotiling for each tile that is burnable. In the point I am, I don’t wont to draw this
I’m thinking that the tilemap can have a layer “in front” of all the others, with the burn’t tiles (simply a black and white mask), and let the/a shader do the special rendering. E.g. using an “if z >= burn_z” then modify the the colors. Or perhaps do a separate render pass with the tilemap, but with a different material. In any case, having black and white masks should be a lot easier to maintain.
Ok, this is something very similar to the approach with particles above - In place of burnout I’m emitting particles that have a long lifetime, one of special textures:
and a special material with tag “burnout” and are rendered to the separate render target (self.burnout_rt), then I pass predicates (3: tiles_sprites_etc, lights, burnouts) to draw on a quad together:
render_to_quad(self, function(self) -- wrapper that renders to quad
clear(BLACK, nil, nil) -- clear texture with black color
multiply_to_quad(self, self.normal_rt, self.light_rt, self.burnout_rt) -- enables textures and "multiply_material" on quad
end)
The “multiply” material is no longer multiply only: for burnouts I’m doing the shader thing with changing colors - and probably this is a place to improve the shader - I have all the information in that place, but I’m not so advanced that I can make a reasonable fragment program here
My problem in both cases is visible in the last image - I have sharp edges of the burnout, while I want some kind of irregular line of edge with some kind of fading
Basically what I proposed is similar to a stencil mask. You draw white (any shape!), on a black texture, and use this texture as a mask for the next render pass. In the next render pass, you’ll draw your geometry again, and the stencil mask will only let through those pixels that have been marked as “pass”, so you can safely apply your custom shader now.
Now, I’m not an expert in using these effects, but I think some other forum user can probably give some good examples or tips.