diff --git a/README.md b/README.md index 552d474..a423bf8 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,24 @@ # tile-bleed-margin-generator -Automatically stretch out the 1px edge of each tile in a sprite sheet to compensate for tile bleed in 2D game development. -## bleed.sh +Automatically stretch out the 1px edge of each tile in a tile sheet to compensate for tile bleed in 2D game development. -Change the script to be specific to your case. +This should solve rounding errors which can cause tiles to slightly bleed into their neighbors, such as the very common "gap between tiles" problem in Unity. The script started as a fork but I added so many features that it ended up quite different from [the original](https://github.com/cjonasw/tile-bleed-margin-generator). -### You'll need to figure out a few things: +Alternatively, you might want to look at chonkypixel's solution which is integrated in the Unity editor. I haven't tested it myself, but it has a UI which does exactly the same thing. https://assetstore.unity.com/packages/2d/textures-materials/tiles/2d-tile-gap-fixing-tool-157060 -1. First you need to know how big your tiles are; mine are *16x16* (`TILE_SIZE = 16`) -2. then how big your spritesheet is; mine is *640x576* (`SPRITESHEET_WIDTH = 640`, `SPRITESHEET_HEIGHT = 576`) -3. How many columns there are: -``` -COLUMN_COUNT = (SPRITESHEET_WIDTH / TILE_SIZE) -``` +## Dependencies -so mine is: `(640/16)` +This script was written for bash and depends on ImageMagick (tested using `ImageMagick 6.9.7-4`). The `convert`, `montage` and `identify` commands must be in your PATH. -4. How many rows there are: +Until someone makes a Windows version (`.cmd` or `.ps`) you should be able to run it on Windows 10 using bash in the Windows Subsystem for Linux, provided you've installed ImageMagick within your virtual linux. -``` -ROW_COUNT = (SPRITESHEET_HEIGHT / TILE_SIZE) -``` +## Sample usage -so mine is: `(576/16)` + ./bleed.sh input.png output.png --tile-size 16 16 --bleed 3 --input-gap 4 4 --output-gap 1 1 --input-offset 20 20 --output-offset 3 3 -5. How many tiles are in the spritesheet: +The script determines the number of tiles based on the size of the file. It ignores content above and the the left of the specified `--input-offset`, and any leftover space that is smaller than a tile on the bottom right. See the sample files in the repository. -``` -NUMBER_OF_TILES = COLUMN_COUNT * ROW_COUNT -``` +## Known issues -so in my case I had to do: `(640/16)*(576/16)` which is 1440. - -``` -NUMBER_OF_TILES = 1440 -``` - -6. You need the file name of the spritesheet too. - -That's all you need. so now: - -### Modify the script to your case - -1. Change all instances of `16` to your `TILE_SIZE` -2. Change `spritesheet.png` to your file -3. Change all instances of `1439` to your `NUMBER_OF_TILES - 1` so in my case it was *`1439`* instead of `1440`. (This is due to indexing starting at 0) -4. Change all instances of `48` to your `TILESIZE` multiplied by 3 (`TILESIZE * 3` so mine was `16*3`) -5. Change all instances of `40` to your `COLUMN_COUNT` -6. Change all instances of `36` to your `ROW_COUNT` - -Make sure your script is in the same directory as the file, open Terminal and `cd` to this folder and run: - -`sh bleed.sh` - -you should now have loads of files, but the only one you are interested in is: - -`spritesheet-bleed.png` - -## TODO: Improvements - -Make all of these calculations automatic and based on arguments. +- Unknown options or options with the wrong number of parameters produce unclear error messages. +- The `--help` flag doesn't work properly because the command fails on missing arguments first. Mitigated by showing the usage on missing arguments. diff --git a/bleed.sh b/bleed.sh index 4f3a6ce..93c1c98 100644 --- a/bleed.sh +++ b/bleed.sh @@ -1,3 +1,226 @@ -convert -crop 16x16 spritesheet.png sprite.png -convert sprite-{0..1439}.png -set option:distort:viewport 48x48-16-16 -virtual-pixel Edge -filter point -distort SRT 0 +repage sprite-bleed.png -montage sprite-bleed-{0..1439}.png -tile 40x36 -geometry 48x48+0+0 -background none spritesheet-bleed.png +#!/bin/bash + +usage() { + cat <<-EOM + Usage: bleed INPUT_FILE OUTPUT_FILE [OPTIONS...] + + Extends the edge of of every tile in a tileset by 1px to avoid issues with + neighboring tile bleeding and gaps between rendered tiles. + + Tested with ImageMagick 6.9.7-4 + + Options must appear after the positional arguments: + -t, --tile-size XX YY the size of the tiles + (defaults to 16x16) + -b, --bleed XX how much bleeding should be produced around + each generated tile + (defaults to 1) + -g, --input-gap XX YY the horizontal and vertical gap between + tiles in the input image + (defaults to 0x0) + -o, --input-offset XX YY the offset of the first tile from the + top left corner of the input image + (defaults to 0x0) + -G, --output-gap XX YY the horizontal and vertical gap to leave + between tiles in the output image as + transparent pixels + (defaults to 0x0) + -O, --output-offset XX YY the margins added to the output image + (defaults to 0x0) + -f, --force overwrite the output file if it exists + -v, --verbose display computations during execution + -h, --help display this help text and exit + EOM +} + + +#### INITIALIZE VARIABLES + +positional_args=() + +input_path= +input_tile_width=16 +input_tile_height=16 +input_offset_x=0 +input_offset_y=0 +input_gap_x=0 +input_gap_y=0 + +output_path= +output_offset_x=0 +output_offset_y=0 +output_gap_x=0 +output_gap_y=0 +output_bleed=1 + + +#### PARSE ARGUMENTS + +if [[ $# -lt 2 ]] ; then + echo "Missing arguments." + usage + exit 1 +fi + +input_path=$1 +shift +output_path=$1 +shift + +while [[ $# > 0 ]] ; do + case $1 in + -t | --tile-size ) + shift + input_tile_width=$1 + shift + input_tile_height=$1 + ;; + -b | --bleed ) + shift + output_bleed=$1 + ;; + -g | --input-gap ) + shift + input_gap_x=$1 + shift + input_gap_y=$1 + ;; + -o | --input-offset ) + shift + input_offset_x=$1 + shift + input_offset_y=$1 + ;; + -G | --output-gap ) + shift + output_gap_x=$1 + shift + output_gap_y=$1 + ;; + -O | --output-offset ) + shift + output_offset_x=$1 + shift + output_offset_y=$1 + ;; + -f | --force ) + force=true + ;; + -v | --verbose ) + verbose=true + ;; + -fv | -vf ) + force=true + verbose=true + ;; + -h | --help ) + usage + exit + ;; + * ) + # todo: make sure --help flag is always checked + echo "Incorrect option sequence." >&2 + usage >&2 + exit 1 + esac + shift +done + + +#### VALIDATE ARGUMENTS + +if [[ ! -f $input_path ]] ; then + echo "Input file not found." >&2; exit 1 +fi + +if [[ -e $output_path && $force != true ]] ; then + echo "A file with the specified output path already exists." >&2; exit 1 +fi + +int_regex='^[0-9]+$' +assert_int() { + if ! [[ $2 =~ $int_regex ]] ; then + echo "$1 must be an integer" >&2; exit 1 + fi +} +assert_coords() { + if ! [[ $2 =~ $int_regex && $3 =~ $int_regex ]] ; then + echo "$1 must be two integers separated by a space" >&2; exit 1 + fi +} + +assert_coords "--tile-size" $input_tile_width $input_tile_height +assert_int "--bleed" $output_bleed +assert_coords "--input-gap" $input_gap_x $input_gap_y +assert_coords "--input-offset" $input_offset_x $input_offset_y +assert_coords "--output-gap" $output_gap_x $output_gap_y +assert_coords "--output-offset" $output_offset_x $output_offset_y + + +#### BEGIN COMPUTATION + +# ENABLE VERBOSE MODE IF DESIRED +if [[ $verbose == true ]] ; then + set -x +fi + +# GET IMAGE SIZE +input_file_width=`identify -format %w $input_path` +input_file_height=`identify -format %h $input_path` + +# CROP MARGINS +input_right_margin=$(( ($input_file_width + $input_gap_x - $input_offset_x) % ($input_tile_width + $input_gap_x) )) +input_bottom_margin=$(( ($input_file_height + $input_gap_y - $input_offset_y) % ($input_tile_height + $input_gap_y) )) +input_usefull_width=$(( $input_file_width - $input_offset_x - $input_right_margin )) +input_usefull_height=$(( $input_file_height - $input_offset_y - $input_bottom_margin )) +crop_margins_geometry="${input_usefull_width}x${input_usefull_height}+${input_offset_x}+${input_offset_y}" + +# SLICE INTO TILES +slice_width=$(($input_tile_width + $input_gap_x)) +slice_height=$(($input_tile_height + $input_gap_y)) +slice_to_tiles_geometry="${slice_width}x${slice_height}" + +# REMOVE TILE GAP +crop_tile_gap_geometry="${input_tile_width}x${input_tile_width}+0+0" + +# ADD BLEEDING +bleeding_width=$(($input_tile_width + (2 * $output_bleed) )) +bleeding_height=$(($input_tile_height + (2 * $output_bleed) )) +distort_bleed_geometry="${bleeding_width}x${bleeding_height}-${output_bleed}-${output_bleed}" + +# ADD TILE GAP +output_tile_width=$(($bleeding_width + $output_gap_x )) +output_tile_height=$(($bleeding_height + $output_gap_y )) +output_tile_geometry="${output_tile_width}x${output_tile_height}+0+0" + +# MERGE TILES +columns=$(( ($input_file_width + $input_gap_x - $input_offset_x) / ($input_tile_width + $input_gap_x) )) +rows=$(( ($input_file_height + $input_gap_y - $input_offset_y) / ($input_tile_height + $input_gap_y) )) + +# REMOVE LAST GAP +output_usefull_width=$(( ($output_tile_width * $columns) - $output_gap_x )) +output_usefull_height=$(( ($output_tile_height * $rows) - $output_gap_y )) +crop_extra_gap_geometry="${output_usefull_width}x${output_usefull_height}+0+0" + +# ADD MARGIN +output_total_width=$(($output_usefull_width + (2 * $output_offset_x) )) +output_total_height=$(($output_usefull_height + (2 * $output_offset_y) )) +output_size_geometry="${output_total_width}x${output_total_height}" + +# DISABLE VERBOSE OUTPUT +{ set +x; } 2>/dev/null + + +#### SUBMIT TASK TO IMAGEMAGICK + +convert $input_path \ + -crop $crop_margins_geometry +repage +gravity \ + -crop $slice_to_tiles_geometry +repage \ + -crop $crop_tile_gap_geometry +repage \ + -define distort:viewport=$distort_bleed_geometry -filter point -distort SRT 0 +repage \ + -background none -extent $output_tile_geometry miff:- | +montage - -tile ${columns}x${rows} -geometry +0+0 -background none +repage miff:- | +convert - \ + -crop $crop_extra_gap_geometry +repage \ + -gravity center -background none -extent $output_size_geometry \ + $output_path \ No newline at end of file diff --git a/input.png b/input.png new file mode 100644 index 0000000..8dee02c Binary files /dev/null and b/input.png differ diff --git a/output.png b/output.png new file mode 100644 index 0000000..e5e4418 Binary files /dev/null and b/output.png differ