0 votes

Hey everyone!

I'm attempting to make a mechanic where the player (and other things) can fracture a tile. That fractured tile will check the nearby tiles, and if any of those are already fractured, they will break and fracture will travel—checking every tile around those tiles, and so on.

That's where my issues lies. I have difficulty looping this without the program freezing.

Example code:

func set_tile_effect(tile, effect_name) -> void:

grid[tile.x][tile.y][1] = effect_name

match effect_name:
    "GROUND":
        pass
    "FRACTURE":

        set_cellv(tile, tile_frac)

        # we check the surrounding tiles four (no diagonals)
        var spread = reachable_tiles_mapped(tile, 1)

        # remove the first member, since it is the orig tile
        spread.pop_front()

        # check if these tiles are already frac'ed and append them
        for available_tile in spread:
            var tile_state = check_tile_state(available_tile)
            if tile_state == "FRACTURE":
                if not spreadable_tiles.has(available_tile):
                    spreadable_tiles.append(available_tile)

So this is the basic mechanic. My problem lies in what to do after appending those tiles.
Is there a way I can run this method again on each of the tiles we've stored?
So that it checks the exact same logic for each tile, and continuing until there are no more tiles to spread to.

I hope this is enough, but if more info is needed feel free to comment.

in Engine by (23 points)

1 Answer

0 votes

I hope this is enough [information]

Well, it would certainly help to know when and where set_tile_effect is called, what the reachable_tiles_mapped- or check_tile_state-methods do or if you're only checking the direct neighbor cells or consider diagonal neighbors ones as well.

That being said, as long as you don't keep a list of visited tiles you'll always end up with an endless loop and your game freezing. Here's an example for direct neighbors:

extends TileMap

const directions = [
        Vector2.UP,
        Vector2.RIGHT,
        Vector2.DOWN,
        Vector2.LEFT
]

# your id's might differ
const BROKEN_TILE_ID = -1
const FRACTURED_TILE_ID = 1

var cells_to_check = []
var cells_already_visited = []

func _ready():
    var cell = Vector2(2, 2)
    fracture_tile(cell)

func fracture_tile(cell : Vector2):
    set_cellv(cell, FRACTURED_TILE_ID)
    cells_already_visited.push_back(cell)

    spread_to_neighbors(cell)

    while not cells_to_check.empty():
        spread_fracture(cells_to_check.pop_front())

    cells_already_visited = [] # reset in the end

func spread_to_neighbors(cell : Vector2):
    for direction in directions:
        var neighbor_cell = cell + direction
        if not neighbor_cell in cells_already_visited:
            cells_to_check.push_back(neighbor_cell)

func spread_fracture(cell : Vector2):
    if get_cellv(cell) == FRACTURED_TILE_ID:
        set_cellv(cell, BROKEN_TILE_ID)
        spread_to_neighbors(cell)
by (10,247 points)

Hey Njamster!

I will have to give this code a try! I had come to my own ... less than efficient solution in the mean time.

What I got to was this:

func set_tile_effect(map_pos, effect_name) -> void:

grid[map_pos.x][map_pos.y][1] = effect_name

match effect_name:
    "GROUND":
        pass
    "FRACTURE":
        set_cellv(map_pos, tile_type.FRACTURE)

        check_surrounding_tiles(map_pos, 1, "FRACTURE")

So that was very straight forward, just check the effect name given when the method is called and check those again the two (and later more) effects.

Then I call check_surrounding_tiles(), which is how I tried to append things. It .. works actually but looking at yours, this is probably far from the best way of resolving this.

func check_surrounding_tiles(tile, reach, tile_eff) -> void:
# we check the surrounding tiles
var spread = reachable_tiles_mapped(tile, reach)

# remove the first member, since it is the orig tile
spread.pop_front()

# check if these tiles are already frac'ed and append them
for available_tile in spread:
    var tile_state = check_tile_state(available_tile)
    if tile_state == tile_eff:
        if not spreadable_tiles.has(available_tile):
            spreadable_tiles.append(available_tile)


for available_tile in spreadable_tiles:

    spread = reachable_tiles_mapped(available_tile, reach)

    for i in spread:
        var tile_state = check_tile_state(i)
        if tile_state == tile_eff:
            if not spreadable_tiles.has(i):
                spreadable_tiles.append(i)

set_multi_tile_effect(spreadable_tiles, tile_eff)

And then check_surrounding_tiles() calls set_multi_tile_effect which is nearly a carbon copy of the first method settileeffect() but one that allows looping and destroys the tiles instead of fracturing them.

func set_multi_tile_effect(tiles, effect_name) -> void:
for tile in tiles:

    grid[tile.x][tile.y][1] = effect_name

    match effect_name:
        "GROUND":
            pass
        "FRACTURE":
            remove_tile(tile)

result can be seen here:
https://media.giphy.com/media/RNQRRtR4YiFUFlgQ1S/giphy.gif

I've run into an entirely new problem, which has to do with my grid array. When I try to target tiles that sit along the left bottom or upper right edge the program crashes saying that my array doesn't hold a certain number.
My grid code must have some hilarious mistakes in it because it works different based on if my playing field is set on top of the origin point in godot, or to the right of it, or even further to the right and down slightly.

I thought the tilemap methods (and my own) only considered the tilemap as it stood, not it's location relative to the origin point. Put for some reason it messes greatly with the arrays I generate.

Anyway that's a totally different problem, it only popped up when I ''fixed'' my fracturing problem.
Thanks for the help again :)

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.