As you know I’m a dev not an illustrator and it’s hard for me to draw images for my game. I love doing it and I choose a simple style for this reason but when I can, I try to reduce the work load using code. This is an example of exactly that.
As you must know if you’re reading this when you do a 2D game using a tilemap you need to handle connected tiles. If you have no idea how connected tiles works in 2D games I suggest you go read : Adventures in Bitmasking.
In my top down game it’s usefull for walls, mountains and rivers for example.
It’s simple, for each connected tile, we check the North, South, East ,West neighbour and add a value if the connection exist. This give us an int between 0 and 15 we just need to show the correct image for this tile.
So we need 16 image for a Wall, but it’s a pain to draw 16 images for each connected item.
If we look at the final image we can see that we can use the corners of 4 of the images to generate 12 of them:
Here are the images we will use:
- 0 connections (0)
- 2 connections (E/W - 6)
- 2 connections (N/S - 9)
- 4 connections (N/S/E/W - 15)
After that we just need to copy each corner at the correct position, you can find a simplified version of my code below (I use a custom library for drawing and reading images, but you can use PIL in python or do this directly in Unity if you want to)
Tile connections:
public enum Direction : sbyte {
S = 0, SW = 1, W = 2, NW = 3, N = 4, NE = 5, E = 6, SE = 7
}
/*
In DirectionExtensions:
DirectionExtensions.connections = new int[4];
DirectionExtensions.connections[0] = 1;
DirectionExtensions.connections[1] = 2;
DirectionExtensions.connections[2] = 4;
DirectionExtensions.connections[3] = 8;
*/
Class ItemConnected {
//...
void SetConnections() {
int connections = 0;
int i = 0;
foreach (Direction d in DirectionExtensions.cardinals) {
if (this.HasLink(this.neighbour[d])) {
connections += Direction.connections[i];
}
i++;
}
// Check corners.
if (connections != this.connections) {
this.connections = connections;
this.NeedGraphicUpdate();
}
}
//...
}
Image generator:
"""
Connected (Tiles)
~~~~~~~~~~~~~~~~
Copyright Gasquez Florian <florian@fy.to> 2019.
"""
class Connected(object):
res = 128
margin = 32
def conneted_tile_from_corners(self, top_left, top_right, bottom_left, bottom_right):
unit = (int)(Connected.res/2)
res = Texture2D(Point(Connected.res, Connected.res))
for x in range(0, unit):
for y in range(0, Connected.margin):
res.set_pixel(x, y, top_left.get_pixel(x, y))
for x in range(unit, unit*2):
for y in range(0, Connected.margin):
res.set_pixel(x, y, top_right.get_pixel(x, y))
for x in range(0, unit):
for y in range(Connected.margin, Connected.res):
res.set_pixel(x, y, bottom_left.get_pixel(x, y))
for x in range (unit, unit*2):
for y in range(Connected.margin, Connected.res):
res.set_pixel(x,y, bottom_right.get_pixel(x, y))
return res
def gen(self):
self.result = []
#: 0
self.result.insert(0, self.texture_base[0])
#: 1
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[2],
self.texture_base[2],
self.texture_base[0],
self.texture_base[0]
)
self.result.insert(1, tex)
#: 2
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[1],
self.texture_base[0],
self.texture_base[1],
self.texture_base[0]
)
self.result.insert(2, tex)
#: 3
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[3],
self.texture_base[2],
self.texture_base[1],
self.texture_base[0]
)
self.result.insert(3, tex)
#: 4
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[0],
self.texture_base[1],
self.texture_base[0],
self.texture_base[1]
)
self.result.insert(4, tex)
#: 5
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[2],
self.texture_base[3],
self.texture_base[0],
self.texture_base[1]
)
self.result.insert(5, tex)
#: 6
self.result.insert(6, self.texture_base[1])
#: 7
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[3],
self.texture_base[3],
self.texture_base[1],
self.texture_base[1]
)
self.result.insert(7, tex)
#: 8
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[0],
self.texture_base[0],
self.texture_base[2],
self.texture_base[2]
)
self.result.insert(8, tex)
#: 9
self.result.insert(9, self.texture_base[2])
#: 10
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[1],
self.texture_base[0],
self.texture_base[3],
self.texture_base[2]
)
self.result.insert(10, tex)
#: 11
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[3],
self.texture_base[2],
self.texture_base[3],
self.texture_base[2]
)
self.result.insert(11, tex)
#: 12
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[0],
self.texture_base[1],
self.texture_base[2],
self.texture_base[3]
)
self.result.insert(12, tex)
#: 13
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[2],
self.texture_base[3],
self.texture_base[2],
self.texture_base[3]
)
self.result.insert(13, tex)
#: 14
tex = Texture2D(Point(Connected.res, Connected.res))
tex = self.conneted_tile_from_corners(
self.texture_base[1],
self.texture_base[1],
self.texture_base[3],
self.texture_base[3]
)
self.result.insert(14, tex)
#: 15
self.result.insert(15, self.texture_base[3])