GithubHelp home page GithubHelp logo

butano-tiled's Introduction

TMX Tiled Map Library for Butano

This is a Tiled file map converter for Butano. It takes *.tmx files and makes their graphics, objects and tiles easily accessible from Butano.

Maintainership Warning

This is an extremely low priority project for me and I have tons of much more important things to do, so I may not reply at all. In such case, feel free to fork the project.

Setup

In your makefile create:

  • the MAPS variable listing the maps directory

In your makefile add:

  • the $(BUILD)/src directory to SOURCES
  • the $(BUILD)/include directory to INCLUDES
  • the $(BUILD)/graphics directory to GRAPHICS

Then, if you don't have an external tool defined in EXTTOOL in your makefile, add @$(PYTHON) -B bntmx.py --target=butano --build=$(BUILD) $(MAPS) to EXTTOOL. If you already have a Python external tool, add this to it:

import bntmx
bntmx.process("butano", ["maps"], "build")

The Butano target requires Butano 15.6.0 or greater.

Usage

Given a map named mymap, you should have the following files:

  • maps/mymap.json
  • maps/mymap.tmx

The script will generate the following files:

  • build/graphics/mymap.bmp
  • build/graphics/mymap.json
  • build/include/bntmx_maps_mymap.h
  • build/src/bntmx_maps_mymap.cpp

Each map is converted into an implementation of the abstract class bntmx::map listed in the bntmx::maps namespace. In our example, the class bntmx::maps::mymap is created and can be accessed via bntmx_maps_mymap.h.

A map is rebuilt only if only of the files describing it changed, including the tilesets and their graphics.

Maps

Maps are *.tmx files you can build with Tiled.

Each map must have a *.json descriptor whose root object expects the following fields:

  • "graphics": the paths to the layers to draw as a bn::regular_bg_item
  • "objects": the paths to the layers whose objects should be exported
  • "tiles": the paths to the layers whose tiles should be exported

The paths to layers are the names of the groups and layers from the *.tmx file joined with the / separator. You can access the layer "mylayer" in the group "mygroup" by writing a path with the / separator like this : "mygroup/mylayer".

The layers they will be exported and indexed in the order they are listed.

Layers also be an array of layers that will be merged together to give you more freedom in your file:

  • graphics layers from an array will be drawn one onto the other to form a single layer
  • objects layers from an array will have all their objects exported into a single layer
  • tiles layers from an array will be merged one onto the other to form a single layer

Here is an example of what a *.json decriptor could look like:

{
    "graphics": [
        "canopy",
        [
            "floor",
            "walls"
        ]
    ],
    "objects": [
        [],
        [
            "doors",
            "enemies",
            "npcs",
            "teleporters"
        ],
    ],
    "tiles": [
        "wall_collisions",
        "ground_collisions"
    ]
}

Graphics

Each graphics layer is drawn centered on the smallest possible image whose width and height are multiples of 256 and that can contain it. These images are compiled into a single image and converted into a single bn::regular_bg_item containing them all and named like your map.

You can access the graphics via bntmx::map::regular_bg_item() or like any other bundled bn::regular_bg_item asset.

Objects

Each objects layer is exported as lists of objects of type bntmx::map_object, a list of object IDs and a list of object classes. Objects have an unique ID and a position. Object IDs are integers in the [0..65535] range, so you can have up to 65536 different objects in a map, which should be more than enough.

The names and classes of the objects are exported as IDs in enum object_id and enum object_class found in the namespace of the generated map class. Objects of each class as well as classless objects are stored separately from each other for each layer. Please use valid C++ enumeration value names for your object names and classes.

The ID of an object isn't the one defined in the map file, so you have to name an object to refer to it. The position of an object isn't the one defined in the map file but its center.

You can access the objects via bntmx::map::objects().

Tiles

Each tiles layer is exported as a list of tile IDs of type bntmx::map_tile ordered from left to right and from top to bottom. Tile IDs are integers in the [0..65535] range, so you can have up to 65536 different tiles in a map, which should be more than enough.

The bounds of each tileset is exported as IDs in enum tile_id found in the namespace of the generated map class. Tiles in tilesets are indexed from left to right and from top to bottom.

Given a tileset named "mytileset", the ID of its first tile is MYTILESET, and the ID of its last tile is MYTILESET_LAST, the tileset contains MYTILESET_LAST + 1 - MYTILESET tiles, and the 3rd tile in the set is MYTILESET + 2.

You can access the tiles via bntmx::map::tiles().

Name Mangling

To ensure compatibility with C and C++, map file names, tileset file names, object names and object class names are mangled. Name mangling can lead to collisions as two originaly different names can lead to the same mangled name, please be sure to use different-enough names to avoid that.

Names are mangled in the following way:

  • leading characters that aren't ASCII letters are trimmed
  • trailing characters that aren't ASCII letters or digits are trimmed
  • sequences of characters that aren't ASCII letters or digits are replaced by a single underscore character
  • names are lowercase in namespace names, type names, and function names
  • names are uppercase in enumeration values, and inclusion guards

To Do

Split the objects in 256x256 chunks to allow saving some collision detections.

Rename the script and maybe move it to a directory, if deemed relevant. Maybe it should be __init__.py?

Find free graphics (tileset, objects) and build a map with them.

Maybe split the tile arrays in chunks, compress the chunks and decompress only a few chunks at a time in RAM. Or keep them

Allow parametrizing the BG and its type.

Maybe allow getting the layer (and class?) for a given object ID? And position apart too.

Change ID to name where relevant

butano-tiled's People

Contributors

kekun avatar max-vogler avatar

Stargazers

Rein van der Woerd avatar Rodrigo avatar ocai0 avatar  avatar Joel Winter avatar

Watchers

 avatar  avatar Joel Winter avatar

Forkers

max-vogler

butano-tiled's Issues

empty "graphics" in JSON causes PIL error

If map.json has no graphics:

{
  "graphics": [],
  "objects": [],
  "tiles": ["collision"]
}

butano-tiled throws an error:

Traceback (most recent call last):
  File "/utano-tiled/bntmx.py", line 293, in <module>
    process(args.mapsdirs, args.build)
  File "butano-tiled/bntmx.py", line 267, in process
    gfx_im.save(bmp_filename, "BMP")
  File "/usr/local/lib/python3.11/site-packages/PIL/Image.py", line 2431, in save
    save_handler(self, fp, filename)
  File "/usr/local/lib/python3.11/site-packages/PIL/BmpImagePlugin.py", line 451, in _save
    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])
  File "/usr/local/lib/python3.11/site-packages/PIL/ImageFile.py", line 520, in _save
    _encode_tile(im, fp, tile, bufsize, fh)
  File "/usr/local/lib/python3.11/site-packages/PIL/ImageFile.py", line 533, in _encode_tile
    encoder.setimage(im.im, b)
SystemError: tile cannot extend outside image
make: *** [all] Error 1

Support rendering Tile object sprite

As mentioned on Discord, it'd be super powerful to allow rendering the sprites of Tile objects.

image

The main use case is to place sprites in Tiled and then dynamically show/hide/move them, e.g. collectible items, enemies, NPCs, or dynamic environments.

I've implemented a prototype towards this in max-vogler@a75ce91. When placing all tile objects using a tileset that is also imported into Butano, the GID can be converted to the sprite id:

for (auto map_item : _map->objects(0, bntmx::maps::examplemap::object_class::ITEM))
{
  auto sprite_id = map_item.sprite_id - bntmx::maps::examplemap::tile_id::OBJECTS;
  auto sprite = bn::sprite_items::objects.create_sprite(map_item.position, sprite_id);
  // ...
}

What do you think of this approach? Happy to file a PR with more polished code. I'm mostly unsure about the most space-efficient way of storing another potentially unset integer in the struct map_object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.