baylej / tmx

C tmx map loader
http://libtmx.rtfd.io/
BSD 2-Clause "Simplified" License
242 stars 54 forks source link

Tile count available in layer struct #40

Closed ForeverZer0 closed 4 years ago

ForeverZer0 commented 5 years ago

Currently working on a 2D game API, a Ruby C extension. I am exposing each component of a map to Ruby via C, where each type in this library has its own Ruby type. All of this is pretty straightforward with the exception of wrapping a tmx_layer with a type of L_LAYER. The content union for this type simply uses a pointer to an array of integers, without any clear indication at this particular point how many GIDs are in the array.

This information can be reached higher up the chain, but does not translate well into wrapping into an object-oriented type. The only solution I have found is setting the tile count to the user_data field when I am enumerating the map's layers.

Hopefully this can help clarify:

static VALUE rpg_tmx_layer_contents(VALUE self) {
    // "self" is a Ruby instance of a layer
    tmx_layer *l = DATA_PTR(self);
    enum tmx_layer_type type = l->type;
    switch (type) {
        case L_LAYER: {
            int32_t *gids = l->content.gids;  // No way to know how many GIDs are in this array at this point

            // Convert to Ruby array
            VALUE ary = rb_ary_new();
            for (int i = 0; i < UNKNOWN_AMOUNT; i++) {  // <--- Can't loop without knowing total
                rb_ary_push(ary, INT2NUM(gids[i]));
            }
            return ary;

            // ..... Other cases removed for brevity
        }   
    }
}

My current solution, though not very elegant:

static VALUE rpg_tilemap_each_layer(VALUE self) {
    RETURN_ENUMERATOR(self, 0, NULL);
    tmx_map *map = DATA_PTR(self);
    for (tmx_layer *layer = map->ly_head; layer != NULL; layer = layer->next) {
        if (!layer->user_data.integer) {
            layer->user_data.integer = (int) map->tilecount;
        }
        rb_yield(Data_Wrap_Struct(rb_cTilemapLayer, NULL, NULL, layer));
    }
    return self;
}

This allows for the layer to hold a value to know how many tiles are in the map the layer belongs to, but not ideal. This isn't much of a problem in the C side during rendering etc, but feels very clumsy in my specific scenario.

baylej commented 4 years ago

Hi,

The datastructure in libTMX reflects the TMX file format, so you can navigate through it just like you'd navigate the XML tree. The map's height and width is defined once at root level ( element) in the TMX format, so these two properties are located in the root element (tmx_map) of the datastructure. I'm not planning on changing that design.

I don't know ruby so I can't really help you. Sorry for the late reply.