+ I learned about it way back when I was just a kid building 2D Minecraft clones using [Construct 2](https://www.construct.net/en/construct-2/manuals/construct-2), and I wanted my terrain to look nice as it does in Terraria
- `at` has a `setAt` counterpart which sets tiles instead of getting them.
- `TileEditor` provides a graphical editor for a `Tilemap` based on a `<canvas>`.
- this editor is _Certified Battery Efficient™_, so it won't redraw unless it needs to!\
we'll need to keep this in mind for later when we try to draw images, which may not be loaded during the initial draw.
- to kick this off, let's set off a goal. I would like the tiles in our little renderer to connect together, like this:
![red rectangle with a black outline, made out of 3x3 tiles][pic:01HPYW5SNTY0Z0ENDE5K3XWMTH]
- let's break this down into smaller steps. drawing a border around the rectangle will involve:
- determining *on which tiles* to draw it,
- determining *where in these tiles* to draw it,
- and actually drawing it!
- so let's zoom in a bit and look at the tiles one by one. in particular, let's focus on *these* two tiles:
![the same red rectangle, now with a focus on the northern tile at its center][pic:01HPYWPJB1P0GK53BSJFJFRAGR]
- notice how the two highlighted tiles are *different.* therefore, we can infer we should probably connect together any tiles that are *the same*.
- knowing that, we can extract the logic to a function:
```javascript tairu
export function shouldConnect(a, b) {
return a == b;
}
```
+ now, also note that the border around this particular tile is only drawn on its *northern* edge -
therefore we can infer that borders should only be drawn on edges for whom `shouldConnect(thisTile, adjacentTile)` is **`false`** (not `true`!).
a tile generally has four edges - east, south, west, north - so we need to perform this check for all of them, and draw our border accordingly.
- you might be wondering why I'm using this particular order for cardinal directions - why not [north, south, east, west]? or [north, east, south, west]?
- the reason comes from math - `[cos(0) sin(0)]` is a vector pointing rightwards, not upwards!
and I chose clockwise order, because that's how the vector rotates as we increase the angle, in a coordinate space where +Y points downward - such as the `<canvas>` coordinate space.
- this choice yields some nice orderliness in the code that handles fetching tiles for connections - first you check `+X`, then `+Y`, then `-X`, and then `-Y` -
which my pedantic mind really appreciates :ahyes:\
as `X` is first alphabetically, so checking `Y` first would feel wrong.
- to do that, I'm gonna override the tile editor's `drawTilemap` function - as this is where the actual tilemap rendering happens!
```javascript tairu
import { TileEditor } from "tairu/editor.js";
export class TileEditorWithBorders extends TileEditor {
constructor({ borderWidth, ...options }) {
super(options);
this.borderWidth = borderWidth;
this.colorScheme.borderColor = "#000000";
}
drawTilemap() {
// Let the base class render out the infill, we'll just handle the borders.
this.ctx.fillRect(tx, ty + tileSize - borderWidth, tileSize, borderWidth);
}
if (disjointWithWest) {
this.ctx.fillRect(tx, ty, borderWidth, tileSize);
}
if (disjointWithNorth) {
this.ctx.fillRect(tx, ty, tileSize, borderWidth);
}
}
}
}
}
```
and here's the result:
```javascript tairu
new TileEditorWithBorders({
tilemap: tilemapSquare,
tileSize: 40,
borderWidth: 4,
});
```
```output tairu
```
- this looks pretty perfect - maybe sans corners, which I'll conveniently skip for now - because most games don't actually render graphics in a vectorial way like this!
instead, the more common way is to use a tileset - a big texture with a bunch of sprites to use for rendering each tile.
- not only does this have the advantage of allowing for richer graphics, but it is also a lot easier to modify by artists, because you no longer need knowledge of graphics APIs to draw tiles.
- for example, here's a tileset I drew for the 3rd iteration of my game [Planet Overgamma] - though tweaked a bit because I had never used it before writing this post :hueh:
![heavy metal sheet tileset from Planet Overgamma, made out of 16 tiles. it looks like heavy embossed sheets of metal, resembling steel in its heavyness][pic:01HPHVDRV0F0251MD0A2EG66C4]
- we can split this tileset up into 16 individual tiles, each one 8 × 8 pixels; people choose various resolutions, I chose a fairly low one to hide my lack of artistic skill.
- the keen eyed among you have probably noticed that this is very similar to the case we had before with drawing procedural borders -
except that instead of determining which borders to draw based on a tile's neighbors, this time we'll determine which *whole tile* to draw based on its neighbors!
- previously we represented which single border to draw with a single boolean.
now we will represent which single tile to draw with *four* booleans, because each tile can connect to four different directions.
- four booleans like this can easily be packed into a single integer using some bitwise operations, hence we get ***bitwise autotiling*** - autotiling using bitwise operations!
- now the clever part of bitwise autotiling is that we can use this packed integer *as an array index* - therefore selecting which tile to draw can be determined using just a single lookup table! neat, huh?
- but because I'm lazy, and CPU time is valuable, instead of using an array I'll just rearrange the tileset texture a bit to be able to slice it in place using this index.
- that means we'll need to arrange our tiles like so, where the leftmost tile is at index 0 (`0b0000`) and the rightmost tile is at index 15 (`0b1111`):
- now it's time to actually implement it as code! I'll start by defining a *tile index* function as a general way of looking up tiles in a tileset.
- I want to make the tile renderer a bit more general, so being able to attach a different tile lookup function to each tileset sounds like a great feature.
- just imagine some game where glass connects to metal, but metal doesn't connect to glass - I bet that would look pretty great!
- …but anyways, here's the basic bitwise magic function:
```javascript tairu
export function tileIndexInBitwiseTileset(tilemap, x, y) {
- you might think that at this point we'll need 8 bits to represent our tiles, and that would make...
***256 tiles!?***
nobody in their right mind would actually draw 256 separate tiles, right? ***RIGHT???***
% template = true
id = "01HPQCCV4RX13VR4DJAP2F9PFA"
- ...right! if you experiment with the bit combinations, you'll quickly find out that there is no difference if, relative to a single center tile, we have tiles on the corners:
these should all render the same way, despite technically having some [new neighbors](https://en.wikipedia.org/wiki/Moore_neighborhood).
% classes.branch = "tileset-four-to-eight-demo"
id = "01HPQCCV4RHZ8A7VMT2KM7T27P"
- what we can do about this is to ignore corners whenever zero or one of the tiles at their cardinal directions is connected -
for example, in the case of `E | SE | S`:
<ul class="directions-square e-s">
<li class="east">E</li>
<li class="south-east">SE</li>
<li class="south">S</li>
</ul>
we can completely ignore what happens in the northeast, northwest, and southwest, because the tile's cardinal directions do not fully contain any of these direction pairs.
% id = "01HPQCCV4R557T2SN7ES7Z4EJ7"
- we can verify this logic with a bit of code; with a bit of luck, we should be able to narrow down our tileset into something a lot more manageable.
- think that with this redundancy elimination approach most of the tiles will never even be looked up by the renderer, because the bit combinations will be collapsed into a more canonical form before the lookup.
- we could also use the approach I mentioned briefly [here][branch:01HPQCCV4RB65D5Q4RANJKGC0D], which involves introducing a lookup table - which sounds reasonable, so let's do it!
- then we'll turn that array upside down... in other words, invert the index-value relationship, so that we can look up which X position in the tile strip to use for a specific connection combination.
remember that our array has only 256 values, so it should be pretty cheap to represent using a `Uint8Array`:
```javascript ordinal-directions
export let connectionBitSetToX = new Uint8Array(256);
for (let i = 0; i < xToConnectionBitSet.length; ++i) {
- and there we go! we now have a mapping from our bitset to positions within the tile strip. try to play around with the code example to see which bitsets correspond to which position!
- with the lookup table generated, we are now able to prepare a tile strip like before - except now it's even more tedious work arranging the pieces together :ralsei_dead:
anyways I spent like 20 minutes doing that by hand, and now we have a neat tile strip just like before, except way longer:
![horizontal tile strip of 47 8x8 pixel metal tiles][pic:01HPW47SHMSVAH7C0JR9HWXWCM]
- as I mentioned before, [I've known it since my Construct 2 days][branch:01HPD4XQPW6VK3FDW5QRCE6HSS], but when it comes to any released games [Planet Overgamma] would probably be the first to utilize it properly.
- for context rxi is the genius behind the Lua-powered, simple, and modular text editor [lite](https://github.com/rxi/lite) that I was using for quite a while
- the cool thing with Tilekit is that it's *more* than just your average bitwise autotiling - of course it *can* do basic autotiling, but it can also do so much more
- if I had to describe it, it's basically something of a *shader langauge for tilesets.* this makes it really powerful, as you can do little programs like
- I mean, after all - bitwise autotiling is basically a clever solution to an `if` complexity problem, so why not extend that with more logic and rules and stuff to let you build more complex maps?