Godot 4 tile destruction
I've always liked games like Broforce. Partly because I could play with my siblings, but also because you could destroy the whole map.
Similar effect to this from Broforce can be easily reproduced in Godot.
Firstly Tilemap node has to be created.
Add any tileset as you wish. I've used https://anokolisa.itch.io/sidescroller-pixelart-sprites-asset-pack-forest-16x16
For the purpose of this demo I created beautiful blob of terrain :3
Now's the time for a sauce. Actually destroying tiles.
Firstly, I'm getting the positon of mouse when left mouse button is clicked.
func _input(event):
if event is InputEventMouseButton:
if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
apply_damage_to_tile(get_global_mouse_position(), 35)
Then the function to actually destroy tile is called. It does two simple things: reduce tile durability, and destroy it while spawning particles when durability reaches 0.
But to do this and be easily able to search tiles two arrays have to be created:
@onready var tilemap: TileMap = get_node("TileMap")
@onready var used_cells = tilemap.get_used_cells(0).duplicate()
@onready var durability = used_cells.map(func(x): return 100)
these are declared at the top of the file. Used cells stores all coordinates of tilemap cells currently containing actual tiles. With this searching for clicked tile is easy.
func apply_damage_to_tile(local_pos, damage):
var map_pos = tilemap.local_to_map(local_pos)
var cell_to_destroy_index = used_cells.find(map_pos)
Now final thing is to actually "destroy" the tile. With tile index in our arrays we can access it's durability. If it is lower than zero, the tile can be de "destroyed" using set_cell()
method.
if cell_to_destroy_index != null and used_cells[cell_to_destroy_index] != null and durability[cell_to_destroy_index] != null:
durability[cell_to_destroy_index] -= damage
if durability[cell_to_destroy_index] <= 0:
tilemap.set_cell(0, map_pos, -1, Vector2i(-1, -1), -1)
used_cells[cell_to_destroy_index] = null
durability[cell_to_destroy_index] = null
To make it a bit more aesthetically pleasing, lets add some particles. Spawning them on tile destruction is a piece of cake. I've actually created separate function, to mantain different effects when destroying tile, and when only damaging it.
@onready var destroy_particles = load("res://destroy_particles.tscn")
func spawn_particles(amount, pos):
var spawn: GPUParticles2D = destroy_particles.instantiate()
add_child(spawn)
spawn.set_position(pos)
spawn.emitting = true
spawn.amount = amount
await spawn.finished
spawn.queue_free()
func apply_damage_to_tile(local_pos, damage):
var map_pos = tilemap.local_to_map(local_pos)
var cell_to_destroy_index = used_cells.find(map_pos)
if cell_to_destroy_index != null and used_cells[cell_to_destroy_index] != null and durability[cell_to_destroy_index] != null:
durability[cell_to_destroy_index] -= damage
if durability[cell_to_destroy_index] <= 0:
tilemap.set_cell(0, map_pos, -1, Vector2i(-1, -1), -1)
used_cells[cell_to_destroy_index] = null
durability[cell_to_destroy_index] = null
spawn_particles(500, tilemap.map_to_local(map_pos))
else:
spawn_particles(100, tilemap.map_to_local(map_pos))
The final result:
Now there could be added some mechanisms that make blocks "fall", or show different textures when damaged.