Search This Blog

Wednesday, April 27, 2016

Coordinate transformations

Any graphical game will at some point transform coordinate from one kind to another. For example, for a grid map, you will need to transform the map to screen coordinate, which at first is really easy: screenX=mapX*tileWidth and the same for the Y coordinate.

On the other side, what if you want to center the screen around your player? Already there there is a bit more transformations, with some offsetX and Y for the top left corner for example. What if your maps are split in different areas? Like an area is a 100x100 like in my case, as soon as you goes out of this area you need to change the area index and restart the X,Y coordinate on the map.

You slowly see how I'm heading? The more features the more complexity for your coordinate system. And guess what? What you do in one direction you will most likely need to have in the other, what if I click on the screen and need to know on which cell of my map I'm? And here you are with the reverse of the previous calculation.

Therefore, it's smarter and certainly safer to have those transformation stored in two functions, and then always call those function. That will allows to debug only once and then be assured it will always work (or at least so it should be).

Example of my screen to map coordinate transformation:
public ScreenToMap(x: number, y: number): RenderScreenCoordinate
{
    var pos = $("#gameCanvas").position();
    var tileWidth = game.World.tileSetDefinition.background.width;
    var tileHeight = game.World.tileSetDefinition.background.height;
    var orx = Math.abs(this.offsetX) % tileWidth * (this.offsetX < 0 ? -1 : 1);
    var ory = Math.abs(this.offsetY) % tileHeight * (this.offsetY < 0 ? -1 : 1);
    var x = (x - pos.left) + this.offsetX;
    var y = (y - pos.top) + this.offsetY;
    var ox = x % tileWidth;
    var oy = y % tileHeight;
    x = Math.floor(x / tileWidth);
    y = Math.floor(y / tileHeight);
    var cx = this.areaX + Math.floor(x / this.world.areaWidth);
    var cy = this.areaY + Math.floor(y / this.world.areaHeight);
    var tx = x;
    var ty = y;
    if (tx < 0)
        tx = (this.world.areaWidth - 1) - (Math.abs(tx + 1) % this.world.areaWidth);
    else        tx %= this.world.areaWidth;
    if (ty < 0)
        ty = (this.world.areaHeight - 1) - (Math.abs(ty + 1) % this.world.areaHeight);
    else        ty %= this.world.areaHeight
    var rx = tx + (cx - this.areaX) * (this.world.areaWidth - ((cx - this.areaX) < 0 ? 1 : 0));
    var ry = ty + (cy - this.areaY) * (this.world.areaHeight - ((cy - this.areaY) < 0 ? 1 : 0));
    return { TileX: tx, TileY: ty, AreaX: cx, AreaY: cy, RelativeX: rx, RelativeY: ry, OffsetX: ox, OffsetY: oy };
}
As you see, not really a 2 line function. Yes some comments would also help to read what's going on, but that's more for an example than anything else.

No comments:

Post a Comment