romlok / godot-gdhexgrid

A GDScript hexagonal grid implementation for Godot.
MIT License
199 stars 24 forks source link

Change coordinate system #8

Open shazzner opened 5 years ago

shazzner commented 5 years ago

Hi there, I've been playing around with this plugin. Recently I wrote a json importer that takes a hexmap generated from the application Tiled (https://www.mapeditor.org/) and imports it into godot with some very minor shaders.

My issue lies with how:

Axial coordinates use +x => NE; +y => N;

Tiled, unfortunately, only allows the origin to be in the top-left and +y => S and +x => E.

I created a map that looks like this in Tiled: Screenshot from 2019-11-04 21-26-17

Unfortunately when I render it using get_hex_center(Vector2(x,y)) I get the map quite skewed: Screenshot from 2019-11-04 21-25-42

I'm wondering if there is a method to adjust the coordinate system to align closer to something like Tiled.

My rendering code on load:

func build_grid(id, y, x):
        # ...
    var coord = HexGrid.get_hex_center(Vector2(x,y))
    tile.translation.x = coord.x
    tile.translation.z = coord.y
    tile.visible = true
    add_child(tile)
shazzner commented 5 years ago

So I managed to 'fix' it somewhat by changing this line:

func obj_to_coords(val):
    # Returns suitable cube coordinates for the given object
    # The given object can an be one of:
    # * Vector3 of standard cube coords;
    # * Vector2 of axial coords;
    # * HexCell instance
    # Any other type of value will return null
    #
    # NB that offset coords are NOT supported, as they are
    # indistinguishable from axial coords.
    if typeof(val) == TYPE_VECTOR3:
        return val
    elif typeof(val) == TYPE_VECTOR2:
        return axial_to_cube_coords(val) # <- this line
    elif typeof(val) == TYPE_OBJECT and val.has_method("get_cube_coords"):
        return val.get_cube_coords()
    # Fall through to nothing
    return

to this: return offset_to_cube_coords(val)

And wrote a func based off the redblob site to get an offset (oddq) coord:

func offset_to_cube_coords(val):
    # Sets position from a Vector2 of offset coordinates
    var x = int(val.x)
    var z = int(val.y) - (x - (x & 1)) / 2
    return Vector3(x, -x-z, z)

This results in the hex grid rendering correctly based off of offset_coords: Screenshot from 2019-11-06 16-30-15

Getting close!

Unfortunately, this causes the get_hex_at method to kind of give me skewed results, it seems like it's attempting to give me a hex based on an axial lookup instead of an offset ie, moving the mouse cursor right gives me a hex lower and lower than it should be the further away from 0,0 I am.

The code:

    var plane_coords = self.transform.affine_inverse() * click_position
    plane_coords = Vector2(plane_coords.x, plane_coords.z)
    var hex_pos = HexGrid.get_hex_at(plane_coords).offset_coords

Any ideas?

martinfelis commented 2 years ago

Most of the operations internally use the axial coordinate system, hence obj_to_coords(val) interprets val as axial coordinates if it is a Vector2.

Instead of changing this behaviour you can leave everything as is and extend the HexGrid class to allow querying of the hex center when supplying offset coordinates, i.e. add this to HexGrid.gd:

func get_hex_center_from_offset(offset):
    # Returns hex's centre position at the given offset coordinates
    var hex = HexCell.new()
    hex.offset_coords = offset
    return hex_transform * hex.axial_coords

and then your loading code could look something like this:

func build_grid(id, y, x):
        # ...
    var coord = HexGrid.get_hex_center_from_offset(Vector2(x,y))
    tile.translation.x = coord.x
    tile.translation.z = coord.y
    tile.visible = true
    add_child(tile)