usable also in node.js
=============
[https://badge.fury.io/js/tiled-utils.svg] (https://badge.fury.io/js/tiled-utils.svg)
This is an unofficial fork with commonjs require compatibility [and some bugfixes] forked from:
=============
Tile Utilities is a collection of helpful methods and objects for using Tiled Editor with the Pixi renderering engine.
makeTiledWorld
is the most important method, so read the documentation
ahead to find out how it works, and then read about how you can use
the other methods for supplementary features.
Yes, Tile Utilities also contains a full suite of tools for working with isometric maps! That means: isometric collision detection, mouse/touch pointer selection of isometric tiles, and importing of isometric maps created in Tiled Editor.
You can find working examples of how all these methods are used by the Hexi game engine in the links below (click the image links to play the examples and use the arrow keys to move the game characters):
Here's the source code.
Here's the source code.
These two examples are the best place to start learning about how to use the Tile Utilities methods and objects.
But Tile Utilities also works with isometric maps.
Here's the source code.
And, it has a shortestPath
function that will tell you the shortest path between two index points on a map array.
Which can be used to help a game character navigate through a maze.
If you need to implement a scrolling camera to your game world,
use the worldCamera
method from the Game Utilities library.
If you have any questions about how any of these methods work, just ask in this repository's Issues.
Installation
Setting up
makeTiledWorld: Create a Pixi game scene from a Tiled Editor JSON data file.
getObject: Access an object from a Tiled Editor map as a Pixi sprite.
Create sprites from map objects: Use a Tiled Editor object to make a Pixi sprite.
Layer groups: Access a Tiled Editor layer group.
GID values: Accessing and using Tiled Editor GID map array values.
Using the data array: Accessing and using Tiled Editor data
array values.
getObject: Accessing multiple sprites in a Tiled Editor map.
hitTestTile: An all-purpose collision method for tile-based games.
getIndex: Convert a sprite's x and y position to a map index value.
getTile: Convert a map index value into a sprite's x and y screen position.
surroundingCells: Find all the map array index numbers surrounding a center index number.
getPoints: Find all the map array index numbers surrounding a center index number.
byDepth: And array sort
function for isometric maps that depth-sorts sprites according to their z
properties.
hitTestIsoTile: Collision detection for isometric sprites.
getIsoPoints: The isometric equivalent of getPoints
.
makeIsoPointer: Add isometric properties to a mouse/touch pointer so that you can select isometric tiles.
isoRectangle: Creates an isometric rectangle that's useful for prototyping isometric maps.
addIsoProperties: Adds isometric properties to any sprite to automatically convert between Cartesian and isometric coordinates.
makeIsoTiledWorld: Creates an isometric world from a Tiled Editor JSON data file. The isometric verision of makeTiledWorld
.
shortestPath: An A-Star algorithm that finds the shortest path between two points in a map array.
tileBasedLineOfSight: Find out whether two sprites
are visible to each other inside a tile-based maze environment.
npm install pixi-tile-utilities --save
<script src="https://unpkg.com/pixi-tile-utilities/dist/tile-utilities.min.js"></script>
Link the tileUtilities.js
file from this repository's bin
folder
to your HTML document with a <script>
tag. (Or, load it into your JS
file using any module system you prefer.) Next, instantiate
TileUtilites
in your JS file like this:
let tu = new TileUtilities(PIXI);
The constructor requires a reference to the PIXI
object you have running in your application.
If you don't supply one, tileUtilites.js
will look for a global PIXI
object. If it can't find Pixi for
some reason, it will throw you an error to let you know.
You can now access all the TileUtilities
methods with the tu
object in
your application.
makeTiledWorld
is a quick and easy way to display a game world designed in
Tiled Editor. Supply makeTiledWorld
with 2 string file path arguments:
- A JSON file generated by Tiled Editor. Important: the Tile Layer Format has to be
CSV
format (Comma Seperated Value.) - A source image that represents the tileset you used to create the Tiled Editor world.
let world = tu.makeTiledWorld("tiledEditorMapData.json", "tileset.png");
(Note: makeTiledWorld
looks for the JSON data file in Pixi's loader.resources
object. So,
make sure you've loaded the JSON file using Pixi's loader
.)
makeTiledWorld
will return a Pixi Container
that contains all the things in your Tiled Editor
map as Pixi sprites.
All the image tiles you create in Tiled Editor are automatically converted into Pixi sprites
for you by makeTiledWorld
. You can access all of them using two methods: getObject
(for
single sprites) and getObjects
(with an "s") for multiple sprites. Let's find out how they work.
Tiled Editor lets you assign a "name" property to any object.
You can access any sprite by this name
using the getObject
method. getObject
searches for and
returns a sprite in the world
that has the same name
property that you assigned
in Tiled Editor. Here's how to use getObject
to look for an object called "alien"
in the Tiled map data and assign it to a variable called alien
let alien = world.getObject("alien");
alien
is now an ordinary Pixi sprite that you can control just like any other Pixi
sprite in your games.
Tiled Editor lets you create generic objects. These are objects that don't have images associated with them. Generic objects are handy to use, because they let you create complex game objects inside Tiled Editor, as pure data. You can then use that data your game code to build complex game objects.
For example, imagine that you want to create a complex animated walking sprite called "elf".
First, create the elf object in Tiled Editor as a generic object, but don't assign any image tiles
to it. Next, in your game code, create a new Pixi AnimatedSprite
called elf
and give it any textures you want
to use for its animation states.
let elf = new PIXI.extras.AnimatedSprite(elfSpriteTextures);
Then use the x
and y
data from the generic "elf" object you created in Tiled Editor to position the
elf
sprite.
elf.x = world.getObject("elf").x;
elf.y = world.getObject("elf").y;
This is a simple example, but you could make very complex data objects in Tiled Editor and use them to build complex sprites in the same way.
Tiled Editor lets you create layer groups. Each layer group you create
in Tiled Editor is automatically converted by makeTiledWorld
into a Pixi Container
object. You can access those containers using getObject
to extract the layer group
container.
Here's how you could extract the layer group called "objects" and add the
elf
sprite to it.
let objectsLayer = world.getObject("objects");
objectsLayer.addChild(elf);
If you want to add the sprite to a different world layer, you can do it like this:
world.getObject("treeTops").addChild(elf);
If you want to access all the sprites in a specific Tiled Editor layer, just supply
getObject
with the name of the layer. For example, if the layer name is "items", you
can access it like this:
let itemsLayer = world.getObject("items");
itemsLayer
is now a Pixi container with a children
array that contains all the sprites
on that layer.
To be safe, clone this array to create a new version that doesn't point to the original data file:
items = itemsLayer.children.slice(0);
You can now manipulate the items
array freely without worrying about changing
the original array. This can possibly help prevent some weird bugs in a complex game.
Tiled Editor uses "gid" numbers to identify different kinds of things in the world.
If you ever need to extract sprites with specific gid
numbers in a
layer that contains different kinds of things, you can do it like this:
let items = itemsLayer.children.map(sprite => {
if (sprite.gid !== 0) return sprite;
});
This example will give you a new array called items
that contains sprites
that don't have gid
values of 0.
Every sprite created by makeTiledWorld
has a gid
property with a value that matches its
Tiled Editor "gid" value.
Tiled Editor's layers have a data
property
that is an array containing all the grid id numbers (gid
) of
the tiles in that array. Imagine that you've got a layer full of similar
tiles representing the walls in a game. How do you access the array
containing all the "gid" numbers of the wall sprites in that layer? If the layer's name is called "wallLayer", you
can access the wallLayer
's data
array of sprites like this:
wallMapArray = world.getObject("wallLayer").data;
wallMapArray
is now an array of "gid" numbers referring to all the sprites on that
layer. You can now use this data for collision detection, or doing any other kind
of world building.
There's another method called getObjects
(with an "s"!) that lets you extract
an array of sprites from the Tiled Editor data. Imagine that you created three
game objects in Tiled Editor called "marmot", "skull" and "heart". makeTiledWorld
automatically turns them into sprites, and you can access
all of them as an array of sprites using getObjects
like this:
let gameItemsArray = world.getObjects("marmot", "skull", "heart");
hitTestTile
checks for a collision between a sprite and a tile in any map array that you
specify. It returns a collision
object. collision.hit
is a Boolean
that tells you if a sprite is colliding with the tile that you're checking.
collision.index
tells you the map array's index number of the colliding sprite. You can check for
a collision with the tile against "every" corner point on the
sprite, "some" corner points, or the sprite's "center" point.
tu.hitTestTile(sprite, array, collisionGid, world, pointsToCheck);
The world
object (the 4th argument) has to have these properties:
tileheight
, tilewidth
, widthInTiles
. pointsToCheck
can have
the string value "some", "every" or "center".
Here's how you could use hitTestTile
to check for a collision between a sprite
called alien
and an array of wall sprites with map gid numbers of 0.
let alienVsFloor = tu.hitTestTile(alien, wallMapArray, 0, world, "every");
alienVsFloor
will be object with two properties: hit
and index
.
You can use these values to resolve the collision.
For working example of hitTestTile
, and how to resolve collisions, see this example from the Hexi game engine.
The getIndex
helper method converts a sprite's x and y position to an array index number.
It returns a single index value that tells you the map array index number that the sprite is in.
tu.getIndex(sprite.x, sprite.y, tileWidth, tileHeight, mapWidthInTiles);
The getTile
helper method converts a tile's index number into x/y screen
coordinates, and captures the tile's grid index (gid
) number.
It returns an object with x
, y
, centerX
, centerY
, width
, height
, halfWidth
halffHeight
and gid
properties. (The gid
number is the value that the tile has in the
mapArray
) This lets you use the returned object with 2D geometric collision functions like hitTestRectangle
or rectangleCollision
from the Bump collision library.
Supply getTile
with a map array index number, the map array, and the
world
object that represents your game world.
tu.getTile(index, mapArray, world);
The world
object requires these properties: x
, y
, tilewidth
, tileheight
and widthInTiles
The surroundingCells
helper method returns an array containing 9
index numbers of map array cells around any given index number.
Use it for an efficient broadphase/narrowphase collision test.
The 2 arguments are the index number that represents the center cell,
and the width of the map array.
let cells = tu.surroundingCells(index, widthInTiles);
The getPoints
method takes a sprite and returns
an object that tells you what all its corner points are. The return
object has four properties, each of which is an object with x
and y
properties:
topLeft
:x
andy
properties describing the top left corner point.topRight
:x
andy
properties describing the top right corner point.bottomLeft
:x
andy
properties describing the bottom left corner point.bottomRight
:x
andy
properties describing the bottom right corner point.
If the sprite has a collisionArea
property that defines a
smaller rectangular area inside the sprite, that collision
area can be used for collisions instead of the sprite's dimensions. Here's
how you could define a collsionArea
on a sprite called elf
:
elf.collisionArea = {x: 22, y: 44, width: 20, height: 20};
Here's how you could use the getPoints
method to find all the collision area's corner points.
let cornerPoints = tu.getPoints(elf.collisionArea);
If your sprites in an isometric map have z
properties that define their depth layers, you can depth-sort them using the array byDepth
method like this:
world.children.sort(tu.byDepth);
Same API as hitTestTile
, except that it works with isometric sprites.
Make sure that your world
object has properties called
cartTileWidth
and cartTileHeight
that define the Cartesian width and
height of your tile cells, in pixels.
Get all the map index numbers surrounding an isometric map cell. The isometric equivalent to getPoints
Used to add a isometric properties to any mouse/touch pointer
object with
x
and y
properties. Supply makeIsoPointer
with the pointer object and
the isometric world
object. As long as your pointer
object has x
and y
position values, it should work.
tu.makeIsoPointer(pointer, world);
It adds the following properties to the pointer
object:
pointer.cartX //The Cartesian x position on the isometric map
pointer.cartY //The Cartesian y position on the isometric map
pointer.column //The isometric column over which the pointer is touching
pointer.row //The isometric row over which the pointer is touching
pointer.index //The map array index value of the current isometric tile
Creates an isometric rectangle (squashed diamond shape) that's useful for prototyping isometric maps. Its first two arguments are the Cartesian width and height of your map's tile cells, and the third is the color. For example:
sprite = tu.isoRectangle(world.cartTilewidth, world.cartTileheight, 0xCCCCFF);
Adds isometric properties to any sprite:
tu.addIsoProperties(anySprite);
The sprite now has these properties: cartX
, cartY
, isoX
, isoY
, cartWidth
, cartHeight
.
Creates an isometric world using Tiled Editor JSON map data.
Here's the source code.
makeIsoTiledWorld
uses the same API as its Cartesian equivalent makeTiledWorld
method. However, you need to make sure you set Tile Editor up correctly and add some custom map properties to make it work. Let's find out how.
###Configuring and building the map
Before you start creating your Tiled Editor map, prepare a sprite sheet with the isometric tiles that you want to use. And, very importantly, note down the isometric dimensions of sprites. Here are the pixel dimensions you need to know:
• tilewidth
: The width of the sprite, from its left to right edge.
• tileheight
: The height of the tileheighte’s base area. This is just the height of the squashed diamond shape which defines the base on which the isometric sprite is standing. Usually its half the tilewidth
value.
These properties are the property names that are used by Tiled Editor, and you’ll be able to access them in the JSON data file that Tiled Editor generates.
You can now use the values to create a new isometric map in Tiled Editor. Open Tiled Editor and select File ~TRA New from the main menu. In the New Map dialog box, select isometric as the Orientation, and use the tilewidth and tileheight values I described above for the Width and Height.
But we’re not done yet! There are three more values we need to figure out:
• tileDepth: The total height of the isometric sprite, in pixels. • cartWidth: The Cartesian width of each tile grid cell, in pixels. • cartHeight: The Cartesian height of each tile grid cell, in pixels.
You need to add these values as custom properties in Tiled Editor’s Map Properties panel.
When Tiled Editor generates the JSON map data, you'll be able to access these values in the properties
field.
"properties":
{
"cartTileheight":"32",
"cartTilewidth":"32",
"tileDepth":"64"
},
Now that you’ve got the Map Properties all set up, use your isometric tileset to build your world. Here's an example of what your Tiled Editor workspace might look like.
You can see in the image above that I’ve given the red cube a custom name
property with the value “player”
. I’ve also built the map using two layers: the playerLayer
just contains the red cube, and the wallLayer
contains all the maze walls.
When you're finished designing your map, export it as a JSON file, and you’re now ready to use it to start coding a game. Here's how to use makeIsoTiledWorld
from the JSON map data and isometric cubes.png
tileset.
world = tu.makeIsoTiledWorld(
"https://gitlab.com/griest/pexi/raw/master/src/tile-utilities/images/cubes.json",
"https://gitlab.com/griest/pexi/raw/master/src/tile-utilities/images/cubes.png"
);
An A-Star search algorithm that returns an array of grid index numbers that represent the shortest path between two points on a map. Use it like this:
let shortestPath = tu.shortestPath(
startIndex, //The start map index
destinationIndex, //The destination index
mapArray, //The map array
mapWidthInTiles, //Map wdith, in tiles
[1,2], //Obstacle gid array
"manhattan", //Heuristic to use: "manhatten", "euclidean" or diagonal"
true //Use diagonal routes (true) or orthogonally adjacent routes ()
);
Use the tileBasedLineOfSight
function to find out whether two sprites
are visible to each other inside a tile based maze environment.
let hasLineOfSight = tu.tileBasedLineOfSight(
spriteOne, //The first sprite, with `centerX` and `centerY` properties
spriteTwo, //The second sprite, with `centerX` and `centerY` properties
mapArray, //The tile map array
world, //The `world` object that contains the `tilewidth
//`tileheight` and `widthInTiles` properties
emptyGid = 0, //The Gid that represents and empty tile, usually `0`
segment = 32, //The distance between collision points
angles = [] //An array of angles to which you want to
//restrict the line of sight
);