├── LICENSE ├── README.md └── src ├── Fibula.js └── TileMap ├── IsometricRenderer.js ├── OrthogonalRenderer.js ├── Tile.js ├── TileMap.js ├── TileMapLayer.js ├── TileMapParser.js └── TileSet.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Ribeiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Fibula 1.0.0 2 | ====== 3 | A tile-based HTML5 Canvas & WebGL engine with support for orthogonal and axonometric – isometric, dimetric and trimetric – projections. 4 | 5 | 6 | Version: 1.0.0 – Released 6th May 2014 7 | 8 | By **Daniel Ribeiro**, [@drgomesp](http://twitter.com/drgomesp). 9 | 10 | Features 11 | ----- 12 | 13 | - WebGL rendering 14 | - Orthogonal tile maps 15 | - Isometric tile maps 16 | - Tile map layers with visibility and opacity 17 | - Camera simulation on tile maps 18 | 19 | *Coming soon* 20 | 21 | - Dimetric tile maps 22 | - Trimetric tile maps 23 | - [Tiled](http://www.mapeditor.org/) support for easily creating tile maps 24 | 25 | Dependencies 26 | ----- 27 | 28 | - [Pixi.js](https://github.com/GoodBoyDigital/pixi.js) – 2d WebGL renderer with canvas fallback 29 | 30 | Navigation 31 | ----- 32 | 33 | - [Getting Started](https://github.com/drgomesp/Fibula#getting-started) 34 | - [The simplest way](https://github.com/drgomesp/Fibula#the-simplest-way) 35 | - [Orthogonal tile map](https://github.com/drgomesp/Fibula#orthogonal-tile-map) 36 | - [Isometric tile map](https://github.com/drgomesp/Fibula#isometric-tile-map) 37 | 38 | Getting Started 39 | ----- 40 | [Go back to top](https://github.com/drgomesp/Fibula#navigation) 41 | 42 | ### The simplest way 43 | [Go back to top](https://github.com/drgomesp/Fibula#navigation) 44 | 45 | The simplest way to draw a tile map is to manually create the tile set, the layers 46 | and the actual map itself. Those are the basic components that make a tile map at the end. 47 | 48 | > **Notice:** **Fibula** requires all image resources to be previously loaded. You can solve this 49 | > issue by using asset management libraries, such as [PxLoader](https://github.com/thinkpixellab/PxLoader). 50 | 51 | #### Orthogonal tile map 52 | [Go back to top](https://github.com/drgomesp/Fibula#navigation) 53 | 54 | > **Notice:** This example was built using this [tutorial](http://blog.sklambert.com/create-a-canvas-tileset-background/). 55 | > Thanks to [Steven Lambert](https://github.com/straker) for writing such an amazing 56 | > guide full of examples! 57 | 58 | Suppose you have the following tile set: 59 | 60 | ![orthogonal-tileset](http://i1.wp.com/blog.sklambert.com/wp-content/uploads/2013/07/tileset.png?resize=512%2C512) 61 | 62 | To get a simple example working, you first need to create a `TileSet` object. We're going to use 63 | PxLoader to show you how to solve the asset management issue: 64 | 65 | ```javascript 66 | var loader = new PxLoader(), 67 | tileSetImage = loader.addImage("http://i1.wp.com/blog.sklambert.com/wp-content/uploads/2013/07/tileset.png"); 68 | ``` 69 | 70 | Now, we'll place our code inside of the callback function of the PxLoader library, 71 | which will run as soon as the image is loaded and ready: 72 | 73 | ```javascript 74 | loader.addCompletionListener(function() { 75 | var tileSet = new Fibula.TileSet(tileSetImage); 76 | }); 77 | ``` 78 | 79 | Here, we've passed the background image. 80 | 81 | After having created a `TileSet` object, we need two more steps: 82 | 83 | 1. Create a first tile layer 84 | 2. Create the tile map and add the first layer to it 85 | 3. Create a renderer and actually render the tile map 86 | 87 | So let's create the first tile layer, using the `TileMapLayer` object: 88 | 89 | > *Notice:* All code from here will be place inside the addCompletionListener callback 90 | function. 91 | 92 | ```javascript 93 | loader.addCompletionListener(function() { 94 | var tileSet = new Fibula.TileSet(tileSetImage); 95 | }); 96 | ``` 97 | 98 | Now, let's create a first layer that will go on the tile map: 99 | 100 | ```javascript 101 | loader.addCompletionListener(function() { 102 | var tileSet = new Fibula.TileSet(tileSetImage), 103 | layer1data = [ 104 | [172, 172, 172, 79, 34, 34, 34, 34, 34, 34, 34, 34, 56, 57, 54, 55, 56, 147, 67, 67, 68, 79, 79, 171, 172, 172, 173, 79, 79, 55, 55, 55], 105 | [172, 172, 172, 79, 34, 34, 34, 34, 34, 34, 146, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172, 159, 189, 79, 79, 55, 55, 55], 106 | [172, 172, 172, 79, 79, 34, 34, 34, 34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 159, 189, 79, 79, 79, 55, 55, 55], 107 | [188, 188, 188, 79, 79, 79, 79, 34, 34, 34, 36, 172, 172, 143, 142, 157, 79, 79, 79, 79, 79, 79, 187, 159, 189, 79, 79, 79, 55, 55, 55, 55], 108 | [79, 79, 79, 79, 79, 79, 79, 79, 34, 34, 36, 172, 159, 158, 172, 143, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 39, 51, 51, 51, 55, 55], 109 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 36, 172, 143, 142, 172, 172, 143, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 55], 110 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 52, 172, 172, 172, 172, 172, 172, 143, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79], 111 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 52, 172, 172, 172, 172, 172, 172, 159, 188, 189, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79], 112 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 188, 158, 172, 172, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 187, 158, 159, 189, 79, 79, 79], 113 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 159, 188, 189, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 114 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 115 | [155, 142, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 187, 188, 188, 189, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 116 | [171, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 117 | [171, 172, 143, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 187, 189, 79, 79, 79, 79], 118 | [187, 188, 158, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79], 119 | [79, 79, 79, 188, 189, 79, 79, 79, 79, 79, 79, 155, 156, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 156], 120 | [34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172], 121 | [34, 34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172], 122 | [34, 34, 34, 34, 79, 79, 79, 79, 79, 79, 155, 172, 172, 159, 189, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172], 123 | [34, 34, 34, 34, 34, 34, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172, 172] 124 | ]; 125 | 126 | var layer1 = new Fibula.TileMapLayer({ 127 | name: 'layer 1', 128 | tileSet: tileSet, 129 | data: layer1data, 130 | visible: true, 131 | opacity: 1 132 | }); 133 | }); 134 | ``` 135 | 136 | Here we are doing some basic 2D mapping. The `layer1data` variable holds a simple 2d array 137 | with 20 rows and 32 columns – which are the dimensions – in *tiles* – for our tile map, as you'll see 138 | later. The numbers of each individual cell represents a spot on the tile set. 139 | 140 | To understand this better, imagine you have each individual tile on the tile set 141 | marked with a number - considering each tile having 32x32 dimensions, like the 142 | following image: 143 | 144 | ![orthogonal-tileset-marked](http://i2.wp.com/blog.sklambert.com/wp-content/uploads/2013/07/tileset_marked.png?resize=513%2C513) 145 | 146 | So the next step is to create the `TileMap` object and the `Renderer` that will render it: 147 | 148 | ```javascript 149 | loader.addCompletionListener(function() { 150 | var tileSet = new Fibula.TileSet(tileSetImage), 151 | layer1data = [ 152 | [172, 172, 172, 79, 34, 34, 34, 34, 34, 34, 34, 34, 56, 57, 54, 55, 56, 147, 67, 67, 68, 79, 79, 171, 172, 172, 173, 79, 79, 55, 55, 55], 153 | [172, 172, 172, 79, 34, 34, 34, 34, 34, 34, 146, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172, 159, 189, 79, 79, 55, 55, 55], 154 | [172, 172, 172, 79, 79, 34, 34, 34, 34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 159, 189, 79, 79, 79, 55, 55, 55], 155 | [188, 188, 188, 79, 79, 79, 79, 34, 34, 34, 36, 172, 172, 143, 142, 157, 79, 79, 79, 79, 79, 79, 187, 159, 189, 79, 79, 79, 55, 55, 55, 55], 156 | [79, 79, 79, 79, 79, 79, 79, 79, 34, 34, 36, 172, 159, 158, 172, 143, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 39, 51, 51, 51, 55, 55], 157 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 36, 172, 143, 142, 172, 172, 143, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 55], 158 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 52, 172, 172, 172, 172, 172, 172, 143, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79], 159 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 34, 52, 172, 172, 172, 172, 172, 172, 159, 188, 189, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79], 160 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 188, 158, 172, 172, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 187, 158, 159, 189, 79, 79, 79], 161 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 159, 188, 189, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 162 | [79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 163 | [155, 142, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 187, 188, 188, 189, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 164 | [171, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 173, 79, 79, 79, 79], 165 | [171, 172, 143, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 187, 189, 79, 79, 79, 79], 166 | [187, 188, 158, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79], 167 | [79, 79, 79, 188, 189, 79, 79, 79, 79, 79, 79, 155, 156, 156, 157, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 156], 168 | [34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172], 169 | [34, 34, 34, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172], 170 | [34, 34, 34, 34, 79, 79, 79, 79, 79, 79, 155, 172, 172, 159, 189, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 171, 172, 172], 171 | [34, 34, 34, 34, 34, 34, 79, 79, 79, 79, 171, 172, 172, 173, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 155, 142, 172, 172] 172 | ]; 173 | 174 | var layer1 = new Fibula.TileMapLayer({ 175 | name: 'layer 1', 176 | tileSet: tileSet, 177 | data: layer1data, 178 | visible: true, 179 | opacity: 1 180 | }); 181 | 182 | var tileMap = new Fibula.TileMap({ 183 | tileWidth: 32, 184 | tileHeight: 32, 185 | layers: [layer1] 186 | }); 187 | 188 | var renderer = new Fibula.OrthogonalRenderer({ 189 | tileMap: tileMap 190 | }); 191 | }); 192 | ``` 193 | 194 | Here you see the usage of the `OrthogonalRenderer`, that knows how to render an 195 | orthogonal tile map, and the actual `TileMap` object, holding the first layer. 196 | 197 | The last step is to render the tile map. Here's an interesting part: you get to 198 | simulate a camera viewport and choose what part of the map is going to be rendered 199 | and also the size of the camera viewport: 200 | 201 | ```javascript 202 | renderer.render({ 203 | x: 0, 204 | y: 0, 205 | width: 1024, 206 | height: 640 207 | }); 208 | ``` 209 | 210 | Here, we are rendering the whole map, starting by 0 on the `x` axis – left to right – and 211 | 0 on the `y` axis – from top to bottom. 212 | 213 | Now, outside of the PxLoader callback function, we'll start to load the images and 214 | the callback function will be triggered as soon as everything is ready: 215 | 216 | ```javascript 217 | loader.start(); 218 | ``` 219 | 220 | We should get a result like this: 221 | 222 | ![orthogonal-tilemap-ground](http://i1.wp.com/blog.sklambert.com/wp-content/uploads/2013/07/tileset_ground.png?resize=512%2C320) 223 | 224 | As you can see, this is a one-layer tile map - which represents the ground of our 225 | map, and it's not very interesting. Let's create a second layer to add some objects 226 | on top of the ground: 227 | 228 | ```javascript 229 | var layer2data = [ 230 | [0, 0, 32, 33, 0, 236, 0, 0, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 32, 33], 231 | [0, 0, 48, 49, 0, 236, 220, 220, 236, 0, 0, 147, 72, 73, 70, 71, 72, 73, 83, 83, 84, 85, 0, 0, 0, 0, 0, 48, 49], 232 | [0, 0, 64, 65, 54, 0, 236, 236, 0, 0, 162, 163, 84, 89, 86, 87, 88, 89, 99, 99, 100, 101, 0, 0, 0, 0, 7, 112, 113], 233 | [0, 0, 80, 81, 70, 54, 55, 50, 0, 0, 0, 179, 100, 105, 102, 103, 104, 105, 0, 0, 0, 0, 0, 0, 16, 22, 23, 39], 234 | [0, 0, 96, 97, 86, 70, 65, 144, 193, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 49], 235 | [0, 0, 0, 0, 102, 86, 81, 160, 161, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 174, 175, 67, 66, 54], 236 | [0, 0, 0, 0, 0, 102, 97, 176, 177, 0, 0, 37, 0, 252, 0, 0, 0, 201, 202, 0, 0, 0, 0, 0, 80, 81, 190, 191, 83, 82, 70, 71], 237 | [0, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 53, 0, 0, 0, 0, 0, 217, 218, 0, 0, 0, 0, 0, 96, 97, 222, 223, 99, 98, 86, 87], 238 | [201, 202, 0, 0, 0, 0, 0, 64, 65, 66, 68, 69, 0, 0, 0, 0, 0, 233, 234, 0, 0, 0, 0, 0, 238, 239, 0, 0, 238, 239, 102, 103], 239 | [217, 218, 0, 0, 0, 0, 0, 80, 81, 82, 84, 85, 0, 0, 0, 0, 0, 249, 250, 0, 0, 0, 0, 0, 254, 255, 0, 0, 254, 255], 240 | [233, 234, 0, 0, 0, 0, 0, 96, 97, 98, 100, 101, 0, 0, 0, 0, 0, 0, 0], 241 | [249, 250, 0, 0, 201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 238, 239, 0, 0, 238, 239], 242 | [0, 0, 0, 0, 217, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 0, 0, 254, 255], 243 | [0, 0, 0, 0, 233, 234, 196, 197, 198], 244 | [2, 3, 4, 0, 249, 250, 228, 229, 230], 245 | [18, 19, 20, 8, 0, 0, 244, 245, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 202], 246 | [0, 35, 40, 24, 25, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 217, 218], 247 | [0, 0, 0, 40, 41, 20, 8, 9, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 19, 20, 21, 0, 0, 0, 0, 0, 0, 0, 233, 234], 248 | [0, 0, 0, 0, 40, 19, 24, 25, 8, 9, 0, 0, 0, 0, 0, 48, 49, 50, 51, 52, 115, 3, 4, 0, 0, 0, 0, 0, 249, 250], 249 | [0, 0, 0, 0, 0, 0, 40, 41, 20, 21, 0, 0, 0, 0, 0, 64, 65, 66, 67, 52, 19, 19, 20, 21] 250 | ]; 251 | 252 | var layer2 = new Fibula.TileMapLayer({ 253 | name: 'layer 2', 254 | tileSet: tileSet, 255 | data: layer2data, 256 | visible: true, 257 | opacity: 1 258 | }); 259 | ``` 260 | 261 | After the render, the result should look like this: 262 | 263 | ![orthogonal-tilemap-two-layers](http://i0.wp.com/blog.sklambert.com/wp-content/uploads/2013/07/tileset_ground_and_layer.png?resize=512%2C320) 264 | 265 | Amazing, right?! 266 | 267 | With the layering system, you can work with collision detection by having the ability 268 | to decide which layer will the player collide against and which the player will not. 269 | 270 | Now, you can simulate a camera by passing custom view areas for the renderer: 271 | 272 | ```javascript 273 | renderer.render({ 274 | x: 200, 275 | y: 200, 276 | width: 300, 277 | height: 300 278 | }); 279 | ``` 280 | 281 | Here, we're using a camera with 300x300 dimensions and we start rendering the map on the 282 | 200 pixel of the `x` axis and 200 pixel of the `y` axis. The result should look like this: 283 | 284 | ![orthogonal-tilemap-two-layers-camera](http://s13.postimg.org/9gdse24tz/Captura_de_Tela_2014_04_29_s_20_45_13.png) 285 | 286 | #### Isometric tile map 287 | [Go back to top](https://github.com/drgomesp/Fibula#navigation) 288 | 289 | If you want to create an isometric tile map, the code for it is almost the same as 290 | for the orthogonal. Suppose you have the following isometric tile set: 291 | 292 | ![isometric-tileset](http://s27.postimg.org/6c9sa3s0j/isometric_grass_and_water.png) 293 | 294 | The first thing we need to do is create the first layer that will go into the tile map: 295 | 296 | ```javascript 297 | var loader = new PxLoader(), 298 | tileSetImage = loader.addImage('assets/isometric.png'); 299 | 300 | loader.addCompletionListener(function() { 301 | var tileSet = new Fibula.TileSet(tileSetImage), 302 | layer1data = [ 303 | [3, 3, 3, 3, 3], 304 | [3, 3, 3, 3, 3], 305 | [3, 3, 3, 3, 3], 306 | [3, 3, 3, 3, 3], 307 | [3, 3, 3, 3, 0] 308 | ]; 309 | 310 | var layer1 = new Fibula.TileMapLayer({ 311 | name: 'layer 1', 312 | tileSet: tileSet, 313 | data: layer1data, 314 | visible: true, 315 | opacity: 1 316 | }); 317 | }); 318 | ``` 319 | 320 | Now, we need to create our tile map object and the renderer: 321 | 322 | ```javascript 323 | var loader = new PxLoader(), 324 | tileSetImage = loader.addImage('assets/isometric.png'); 325 | 326 | loader.addCompletionListener(function() { 327 | var tileSet = new Fibula.TileSet(tileSetImage), 328 | layer1data = [ 329 | [3, 3, 3, 3, 3], 330 | [3, 3, 3, 3, 3], 331 | [3, 3, 3, 3, 3], 332 | [3, 3, 3, 3, 3], 333 | [3, 3, 3, 3, 0] 334 | ]; 335 | 336 | var layer1 = new Fibula.TileMapLayer({ 337 | name: 'layer 1', 338 | tileSet: tileSet, 339 | data: layer1data, 340 | visible: true, 341 | opacity: 1 342 | }); 343 | 344 | var tileMap = new Fibula.TileMap({ 345 | tileWidth: 64, 346 | tileHeight: 32, 347 | layers: [layer1] 348 | }); 349 | 350 | var renderer = new Fibula.IsometricRenderer({ 351 | tileMap: tileMap 352 | }); 353 | }); 354 | ``` 355 | 356 | Now let's render our tile map: 357 | 358 | ```javascript 359 | renderer.render({ 360 | x: 0, 361 | y: 0, 362 | width: 320, 363 | height: 320 364 | }); 365 | ``` 366 | 367 | > Notice here the usage of a 64x32 tile size for the map, which differs from the size 368 | > of the tiles on the tile set in this case – 64x64 on tile set. 369 | 370 | The result should look like this: 371 | 372 | ![isometric-tilemap-ground](http://s27.postimg.org/nqlsh6kr7/Captura_de_Tela_2014_04_25_s_15_31_15.png) 373 | 374 | Now, let's add a new layer and make this map a little bit more interesting: 375 | 376 | ```javascript 377 | var layer2data = [ 378 | [null, null, null, 4, 16], 379 | [null, null, null, 19, 23], 380 | [null, null, null, 19, 23], 381 | [null, null, null, 19, 23], 382 | [null, null, null, 19, 23] 383 | ]; 384 | 385 | var layer2 = new Fibula.TileMapLayer({ 386 | name: 'layer 2', 387 | tileSet: tileSet, 388 | data: layer2data, 389 | visible: true, 390 | opacity: 1 391 | }); 392 | ``` 393 | 394 | After the render, the result should look something like this: 395 | 396 | ![isometric-tilemap-two-layers](http://s14.postimg.org/po0e3sic1/Captura_de_Tela_2014_04_25_s_15_34_12.png) 397 | 398 | Amazing, right!? 399 | -------------------------------------------------------------------------------- /src/Fibula.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | var Fibula = Fibula || 11 | { 12 | 13 | }; 14 | -------------------------------------------------------------------------------- /src/TileMap/IsometricRenderer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new IsometricRenderer object. 12 | * 13 | * @class Fibula.IsometricRenderer 14 | * @constructor 15 | * @param {Object} settings The settings object 16 | */ 17 | Fibula.IsometricRenderer = function(settings) 18 | { 19 | /** 20 | * The settings object. 21 | * @type {Object} 22 | */ 23 | settings = settings || this.settings; 24 | 25 | /** 26 | * The PIXI Stage object, which is the root of the display tree. If you don't provide this, it will be auto-created. 27 | * @type {PIXI.Stage} 28 | */ 29 | this.stage = settings.stage || this.stage; 30 | 31 | /** 32 | * The PIXI renderer object. If you don't provide this, it will be auto-detected. 33 | * @type {PIXI.WebGLRenderer|PIXI.CanvasRenderer} 34 | */ 35 | this.renderer = settings.renderer || this.renderer; 36 | 37 | /** 38 | * The tile map object. 39 | * @type {Fibula.TileMap} 40 | */ 41 | this.tileMap = settings.tileMap || this.tileMap; 42 | 43 | /** 44 | * The view area object. 45 | * @type {Object} 46 | */ 47 | this.viewArea = { 48 | x: this.viewArea.x, 49 | y: this.viewArea.y, 50 | width: this.viewArea.width, 51 | height: this.viewArea.height 52 | }; 53 | }; 54 | 55 | Fibula.IsometricRenderer.prototype = { 56 | stage: new PIXI.Stage(0xffffff, true), 57 | renderer: PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight, null), 58 | viewArea: { 59 | x: 0, 60 | y: 0, 61 | width: 100, 62 | height: 100 63 | } 64 | }; 65 | 66 | /** 67 | * Renders the tile map to the canvas. 68 | * @param {Object} viewArea The object that defines the view area to render. 69 | */ 70 | Fibula.IsometricRenderer.prototype.render = function(viewArea) 71 | { 72 | document.body.appendChild(this.renderer.view); 73 | 74 | this.createTileMap(viewArea); 75 | var me = this; 76 | 77 | function animate() 78 | { 79 | requestAnimFrame(animate); 80 | me.renderer.render(me.stage); 81 | } 82 | 83 | requestAnimFrame(animate); 84 | }; 85 | 86 | Fibula.IsometricRenderer.prototype.createTileMap = function(viewArea) 87 | { 88 | var viewX = viewArea.x || this.viewArea.x, 89 | viewY = viewArea.y || this.viewArea.y, 90 | viewWidth = viewArea.width || this.viewArea.width, 91 | viewHeight = viewArea.height || this.viewArea.height; 92 | 93 | this.tileMap.layers.forEach(function(layer) { 94 | if (layer.visible) { 95 | this.createLayers(layer, viewX, viewY, viewWidth, viewHeight, this.tileMap, this.stage); 96 | } 97 | }, this); 98 | }; 99 | 100 | /** 101 | * Renders the layer to the specific context using a rendering area. 102 | * 103 | * @param {Fibula.TileMapLayer} layer The layer to render. 104 | * @param {number} viewX The x point from where to start rendering. 105 | * @param {number} viewY The y point from where to start rendering. 106 | * @param {number} viewWidth The width of the rendering area. 107 | * @param {number} viewHeight The height of the rendering area. 108 | * @param {Fibula.TileMap} tileMap The tile map. 109 | * @param {PIXI.DisplayObjectContainer} stage The PIXI Stage object. 110 | */ 111 | Fibula.IsometricRenderer.prototype.createLayers = function(layer, viewX, viewY, viewWidth, viewHeight, tileMap, stage) 112 | { 113 | var tileWidth = tileMap.tileWidth, 114 | tileHeight = tileMap.tileHeight * 2, 115 | tileOffsetX = Math.ceil(viewX / tileWidth), 116 | tileOffsetY = Math.ceil(viewY / tileHeight), 117 | 118 | viewTileWidth = Math.ceil(viewWidth / tileWidth), 119 | viewTileHeight = Math.ceil(viewHeight / tileHeight), 120 | 121 | // Set min and max to have one more tile for half visible tiles 122 | visibleTileMinX = tileOffsetX - 1, 123 | visibleTileMaxX = tileOffsetX + viewTileWidth + 1, 124 | 125 | visibleTileMinY = tileOffsetY - 1, 126 | visibleTileMaxY = tileOffsetY + viewTileHeight + 1, 127 | 128 | texture = PIXI.Texture.fromImage(layer.tileSet.image.src), 129 | 130 | x, y, tile, tileSetCoordinates, isometricX, isometricY, sprite; 131 | 132 | for(x = visibleTileMinX; x < visibleTileMaxX; x++) { 133 | for(y = visibleTileMinY; y < visibleTileMaxY; y++) { 134 | 135 | if (typeof layer.tiles[x] !== "undefined") { 136 | tile = layer.tiles[x][y]; 137 | } 138 | 139 | if (!tile) { 140 | continue; 141 | } 142 | 143 | tileSetCoordinates = layer.tileSet.findCoordinates(tile.tileSetPosition, tileWidth, tileHeight); 144 | 145 | isometricX = (x - y) * (tileWidth / 2); 146 | isometricY = (x + y) * (tileHeight / 4); // Divide by 4 to get the 2:1 ratio (i.e.: 64x32) 147 | 148 | isometricX += tileWidth * 2; // Adjust the center of the "camera" 149 | 150 | sprite = new PIXI.TilingSprite(texture, tileWidth, tileHeight); 151 | sprite.x = isometricX; 152 | sprite.y = isometricY; 153 | sprite.tilePosition.x = -tileSetCoordinates.x; 154 | sprite.tilePosition.y = -tileSetCoordinates.y; 155 | 156 | stage.addChild(sprite); 157 | } 158 | } 159 | }; 160 | 161 | Fibula.IsometricRenderer.prototype.constructor = Fibula.IsometricRenderer; 162 | -------------------------------------------------------------------------------- /src/TileMap/OrthogonalRenderer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new OrthogonalRenderer object. 12 | * 13 | * @class Fibula.OrthogonalRenderer 14 | * @constructor 15 | * @param {Object} settings The settings object 16 | */ 17 | Fibula.OrthogonalRenderer = function(settings) 18 | { 19 | /** 20 | * The settings object. 21 | * @type {Object} 22 | */ 23 | settings = settings || this.settings; 24 | 25 | /** 26 | * The PIXI Stage object, which is the root of the display tree. If you don't provide this, it will be auto-created. 27 | * @type {PIXI.Stage} 28 | */ 29 | this.stage = settings.stage || this.stage; 30 | 31 | /** 32 | * The PIXI renderer object. If you don't provide this, it will be auto-detected. 33 | * @type {PIXI.WebGLRenderer|PIXI.CanvasRenderer} 34 | */ 35 | this.renderer = settings.renderer || this.renderer; 36 | 37 | /** 38 | * The tile map object. 39 | * @type {Fibula.TileMap} 40 | */ 41 | this.tileMap = settings.tileMap || this.tileMap; 42 | 43 | /** 44 | * The view area object. 45 | * @type {Object} 46 | */ 47 | this.viewArea = { 48 | x: this.viewArea.x, 49 | y: this.viewArea.y, 50 | width: this.viewArea.width, 51 | height: this.viewArea.height 52 | }; 53 | }; 54 | 55 | Fibula.OrthogonalRenderer.prototype = { 56 | stage: new PIXI.Stage(0xffffff, true), 57 | renderer: PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight, null), 58 | tileMap: null, 59 | viewArea: { 60 | x: 0, 61 | y: 0, 62 | width: 100, 63 | height: 100 64 | } 65 | }; 66 | 67 | Fibula.OrthogonalRenderer.prototype.render = function(viewArea) 68 | { 69 | document.body.appendChild(this.renderer.view); 70 | 71 | this.createTileMap(viewArea); 72 | var me = this; 73 | 74 | function animate() 75 | { 76 | requestAnimFrame(animate); 77 | me.renderer.render(me.stage); 78 | } 79 | 80 | requestAnimFrame(animate); 81 | }; 82 | 83 | /** 84 | * Creates the tile map textures. 85 | * @param {Object} viewArea The object that defines the view area to render. 86 | */ 87 | Fibula.OrthogonalRenderer.prototype.createTileMap = function(viewArea) 88 | { 89 | var viewX = viewArea.x || this.viewArea.x, 90 | viewY = viewArea.y || this.viewArea.y, 91 | viewWidth = viewArea.width || this.viewArea.width, 92 | viewHeight = viewArea.height || this.viewArea.height; 93 | 94 | this.tileMap.layers.forEach(function(layer) { 95 | if (layer.visible) { 96 | this.createLayers(layer, viewX, viewY, viewWidth, viewHeight, this.tileMap, this.stage); 97 | } 98 | }, this); 99 | }; 100 | 101 | /** 102 | * Renders the layer to the specific context using a rendering area. 103 | * 104 | * @param {Fibula.TileMapLayer} layer The layer to render. 105 | * @param {number} viewX The x point from where to start rendering. 106 | * @param {number} viewY The y point from where to start rendering. 107 | * @param {number} viewWidth The width of the rendering area. 108 | * @param {number} viewHeight The height of the rendering area. 109 | * @param {Fibula.TileMap} tileMap The tile map. 110 | * @param {PIXI.DisplayObjectContainer} stage The PIXI Stage object. 111 | */ 112 | Fibula.OrthogonalRenderer.prototype.createLayers = function(layer, viewX, viewY, viewWidth, viewHeight, tileMap, stage) 113 | { 114 | var tileWidth = tileMap.tileWidth, 115 | tileHeight = tileMap.tileHeight, 116 | tileOffsetX = Math.ceil(viewX / tileWidth), 117 | tileOffsetY = Math.ceil(viewY / tileHeight), 118 | 119 | viewTileWidth = Math.ceil(viewWidth / tileWidth), 120 | viewTileHeight = Math.ceil(viewHeight / tileHeight), 121 | 122 | // Set min and max to have one more tile for half visible tiles 123 | visibleTileMinX = tileOffsetX - 1, 124 | visibleTileMaxX = tileOffsetX + viewTileWidth + 1, 125 | 126 | visibleTileMinY = tileOffsetY - 1, 127 | visibleTileMaxY = tileOffsetY + viewTileHeight + 1, 128 | 129 | texture = PIXI.Texture.fromImage(layer.tileSet.image.src), 130 | 131 | x, y, tile, tileSetCoordinates, orthogonalX, orthogonalY, sprite; 132 | 133 | for(x = visibleTileMinX; x < visibleTileMaxX; x++) { 134 | for(y = visibleTileMinY; y < visibleTileMaxY; y++) { 135 | 136 | if (typeof layer.tiles[x] !== "undefined") { 137 | tile = layer.tiles[x][y]; 138 | } 139 | 140 | if (!tile) { 141 | continue; 142 | } 143 | 144 | tileSetCoordinates = layer.tileSet.findCoordinates(tile.tileSetPosition, tileWidth, tileHeight); 145 | 146 | orthogonalX = x * tileWidth; 147 | orthogonalY = y * tileHeight; 148 | 149 | sprite = new PIXI.TilingSprite(texture, tileWidth, tileHeight); 150 | sprite.x = orthogonalX; 151 | sprite.y = orthogonalY; 152 | sprite.tilePosition.x = -tileSetCoordinates.x; 153 | sprite.tilePosition.y = -tileSetCoordinates.y; 154 | 155 | stage.addChild(sprite); 156 | } 157 | } 158 | }; 159 | 160 | Fibula.OrthogonalRenderer.prototype.constructor = Fibula.OrthogonalRenderer; 161 | -------------------------------------------------------------------------------- /src/TileMap/Tile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new Tile object. 12 | * 13 | * @class Fibula.Tile 14 | * @constructor 15 | * @param {number} x The x coordinate of the tile on the tile map. 16 | * @param {number} y The y coordinate of the tile on the tile map. 17 | * @param {number} tileSetPosition The position of the tile set of this tile. 18 | */ 19 | Fibula.Tile = function(x, y, tileSetPosition) 20 | { 21 | /** 22 | * The position of the tile set of this tile. 23 | * @type {number} 24 | */ 25 | this.tileSetPosition = tileSetPosition; 26 | 27 | /** 28 | * The x coordinate of the tile on the tile map. 29 | * @type {number} 30 | */ 31 | this.x = x; 32 | 33 | /** 34 | * The y coordinate of the tile on the tile map. 35 | * @type {number} 36 | */ 37 | this.y = y; 38 | }; 39 | 40 | Fibula.Tile.prototype.constructor = Fibula.Tile; 41 | -------------------------------------------------------------------------------- /src/TileMap/TileMap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new TileMap object. 12 | * 13 | * @class Fibula.TileMap 14 | * @constructor 15 | * @param {Object} settings The settings object. 16 | */ 17 | Fibula.TileMap = function(settings) 18 | { 19 | /** 20 | * The settings object. 21 | * @type {Object} 22 | */ 23 | settings = settings || {}; 24 | 25 | /** 26 | * The tile width for this tile map. 27 | * @type {number} 28 | */ 29 | this.tileWidth = settings.tileWidth || this.tileWidth; 30 | 31 | /** 32 | * The tile height of this tile map. 33 | * @type {number} 34 | */ 35 | this.tileHeight = settings.tileHeight || this.tileHeight; 36 | 37 | /** 38 | * The array of tile map layers. 39 | * @type {Array} 40 | */ 41 | this.layers = settings.layers || []; 42 | /** 43 | * The string key of the tile map. 44 | * @type {string} 45 | */ 46 | this.key = settings.key || this.key; 47 | 48 | }; 49 | 50 | Fibula.TileMap.prototype = { 51 | constructor: Fibula.TileMap, 52 | tileWidth: false, 53 | tileHeight: false, 54 | layers: false, 55 | key: 'no_key' 56 | }; 57 | -------------------------------------------------------------------------------- /src/TileMap/TileMapLayer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new TileMapLayer object. 12 | * 13 | * @class Fibula.TileMapLayer 14 | * @constructor 15 | * @param {Object} settings The settings object. 16 | */ 17 | Fibula.TileMapLayer = function(settings) 18 | { 19 | /** 20 | * The settings object. 21 | * @type {Object} 22 | */ 23 | settings = settings || {}; 24 | 25 | /** 26 | * The name of the layer. 27 | * @type {string} 28 | */ 29 | this.name = settings.name || this.name; 30 | 31 | /** 32 | * The opacity of the layer (1 == 100%). 33 | * @type {number} 34 | */ 35 | this.opacity = settings.opacity || this.opacity; 36 | 37 | /** 38 | * Weather the layer is visible or not. 39 | * @type {boolean} 40 | */ 41 | this.visible = typeof settings.visible !== "undefined" ? settings.visible : this.visible; 42 | 43 | /** 44 | * The tile set to be used with this layer. 45 | * @type {Fibula.TileSet} 46 | */ 47 | this.tileSet = settings.tileSet || this.tileSet; 48 | 49 | /** 50 | * The array of tiles of this layer. 51 | * @type {Array} 52 | */ 53 | this.tiles = []; 54 | 55 | /** 56 | * The width of the layer. 57 | * @type {number} 58 | */ 59 | this.width; 60 | 61 | /** 62 | * The height of the layer. 63 | * @type {number} 64 | */ 65 | this.height; 66 | 67 | if (settings.data) { 68 | this.fillTiles(settings.data); 69 | } 70 | }; 71 | 72 | Fibula.TileMapLayer.prototype = { 73 | name: 'no_name', 74 | tiles: false, 75 | opacity: 1, 76 | visible: true, 77 | tileSet: null, 78 | width: false, 79 | height: false 80 | }; 81 | 82 | /** 83 | * Fills the layer with tiles using the data information of the tile set. 84 | * 85 | * @param {Array} data The tile set data array for the layer. 86 | */ 87 | Fibula.TileMapLayer.prototype.fillTiles = function(data) 88 | { 89 | this.tiles = []; 90 | this.height = data.length; 91 | this.width= data[0].length; 92 | 93 | var pos; 94 | 95 | for(var x = 0; x < this.width; x++) { 96 | this.tiles[x] = []; 97 | 98 | for(var y = 0; y < this.height; y++) { 99 | pos = data[y][x] != null ? data[y][x] : NaN; 100 | this.tiles[x][y] = new Fibula.Tile(x, y, pos); 101 | } 102 | } 103 | }; 104 | 105 | Fibula.TileMapLayer.prototype.constructor = Fibula.TileMapLayer; 106 | -------------------------------------------------------------------------------- /src/TileMap/TileMapParser.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new TileMapParser object. 12 | * 13 | * @class Fibula.TileMapParser 14 | * @constructor 15 | */ 16 | Fibula.TileMapParser = function() 17 | { 18 | 19 | }; 20 | 21 | Fibula.TileMapParser.prototype.constructor = Fibula.TileMapParser; 22 | -------------------------------------------------------------------------------- /src/TileMap/TileSet.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Fibula package. 3 | * 4 | * (c) Daniel Ribeiro 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | /** 11 | * Creates a new TileSet object. 12 | * 13 | * @class Fibula.TileSet 14 | * @constructor 15 | * @param {HTMLImageElement} image The image of the tile set. 16 | */ 17 | Fibula.TileSet = function(image) 18 | { 19 | /** 20 | * The image to be used on the tile set. 21 | * @type {HTMLImageElement} 22 | */ 23 | this.image = image; 24 | 25 | /** 26 | * The width of the tile set. 27 | * @type {number} 28 | */ 29 | this.width = image.width; 30 | 31 | /** 32 | * The height of the tile set. 33 | * @type {number} 34 | */ 35 | this.height = image.height; 36 | }; 37 | 38 | Fibula.TileSet.prototype = { 39 | image: false, 40 | width: false, 41 | height: false 42 | }; 43 | 44 | /** 45 | * Finds the coordinates for a given tile on the tile set. 46 | * 47 | * @param {number} index The tile index. 48 | * @param {number} tileWidth The tile width. 49 | * @param {number} tileHeight The tile height. 50 | * @returns {{x: number, y: number}} 51 | */ 52 | Fibula.TileSet.prototype.findCoordinates = function(index, tileWidth, tileHeight) 53 | { 54 | var tilesPerColumn = this.width / tileWidth, 55 | tileX = Math.floor(index / tilesPerColumn), 56 | tileY = Math.floor(index % tilesPerColumn), 57 | x = tileY * tileHeight, 58 | y = tileX * tileWidth; 59 | 60 | return {x: x, y: y}; 61 | }; 62 | 63 | Fibula.TileSet.prototype.constructor = Fibula.TileSet; 64 | --------------------------------------------------------------------------------