• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Hexagon tiles keyboard/mouse control system

streng

Member
Hi,

I want to implement controls system on hexagonal tileset. The tileset should be the same as image in attachment. I want to be able to mark each tile by arrow keys or mouse click.

In mouse click should be easy, if I click for example on tile 5, then tile 5 hexagon is marked (meaning that change color to blue), when I click on tile 17, then tile 5 change color back to white and tile 17 change color to blue etc.

Same should work if I want to mark tile hexagon by keyboard, but with difference when I’m on tile 1 and press rightkey, should move on tile 2, next rightkey on tile 3 next on the tile 4, but leftkey pressed when I’m on tile 4 move me back to tile 3.

The hardest part be, when I’m on 1 and press downkey should move to tile 5, another downkey move me to tile 10 upkey to tile 5 etc.

The question is, how to achieve this in some efficient way?

If there will be implemented only movement from left to right and right to left, then can be easily done by changing some control variable to + -1, but I don’t know how to do it for Up and down control mechanism.

Thank you for answers
 

Attachments

jo-thijs

Member
Hi,

I want to implement controls system on hexagonal tileset. The tileset should be the same as image in attachment. I want to be able to mark each tile by arrow keys or mouse click.

In mouse click should be easy, if I click for example on tile 5, then tile 5 hexagon is marked (meaning that change color to blue), when I click on tile 17, then tile 5 change color back to white and tile 17 change color to blue etc.

Same should work if I want to mark tile hexagon by keyboard, but with difference when I’m on tile 1 and press rightkey, should move on tile 2, next rightkey on tile 3 next on the tile 4, but leftkey pressed when I’m on tile 4 move me back to tile 3.

The hardest part be, when I’m on 1 and press downkey should move to tile 5, another downkey move me to tile 10 upkey to tile 5 etc.

The question is, how to achieve this in some efficient way?

If there will be implemented only movement from left to right and right to left, then can be easily done by changing some control variable to + -1, but I don’t know how to do it for Up and down control mechanism.

Thank you for answers
Hi and welcome to the GMC!

I understand how you assigned indices to the tiles, but it feels somewhat strange to me.
If I want to move from tile 11 to tile 8 (which are neighbors), then I have to press 3 keys, which will first bring me on the other side of the field before bringing me to the neighboring location.
Usually a key press always moves you to a neighbouring tile.

Anyway, to your question: you will first need to decide how you will represent your tilles.
Are they object instances? Are the tiles (before GM:S 2)? Are they sprites from the asset layer (in GM:S 2)? Are they controlled by a single controller object?
It is also important to consider the fields you'll be using.
Are your fields always hexagons? Are they always the same size?

If you go with the instance per tile approach, you can do the vertical movement through collision checks and horizontal movement through with-structures.
These are not super efficient, but will probably suffice.
You could also create a global array of all the tiles and then you just need to calculate the number of the next tile.
To do the latter for vertical movement, you will need to be able to make some assumptions about the field shape.

If you go with the controller approach, you can organize the tiles in a grid.
It still helps here if you can make some assumptions about the field shape.
 

streng

Member
Hi and welcome to the GMC!

I understand how you assigned indices to the tiles, but it feels somewhat strange to me.
If I want to move from tile 11 to tile 8 (which are neighbors), then I have to press 3 keys, which will first bring me on the other side of the field before bringing me to the neighboring location.
Usually a key press always moves you to a neighbouring tile.

Anyway, to your question: you will first need to decide how you will represent your tilles.
Are they object instances? Are the tiles (before GM:S 2)? Are they sprites from the asset layer (in GM:S 2)? Are they controlled by a single controller object?
It is also important to consider the fields you'll be using.
Are your fields always hexagons? Are they always the same size?

If you go with the instance per tile approach, you can do the vertical movement through collision checks and horizontal movement through with-structures.
These are not super efficient, but will probably suffice.
You could also create a global array of all the tiles and then you just need to calculate the number of the next tile.
To do the latter for vertical movement, you will need to be able to make some assumptions about the field shape.

If you go with the controller approach, you can organize the tiles in a grid.
It still helps here if you can make some assumptions about the field shape.
I understand your strange feeling, but imagine if you are on the tile 10 and press left arrow, what will be destination in this case? 7 or 12? What is closest neighbor?

I’m using GMS2. Currently objects in game (like main menu) are a simple sprites. I only changing variable index to + -1 when player hit upkey or downkey and officially change sprite graphic on this event and are controlled by one control object. This method seems to be ineffective to achieve same with hexagonal. I have open mind to any other approach, so it can be instances instead of sprites. In other worlds, I like to chose approach which will be easier to write/understand
 

jo-thijs

Member
I understand your strange feeling, but imagine if you are on the tile 10 and press left arrow, what will be destination in this case? 7 or 12? What is closest neighbor?

I’m using GMS2. Currently objects in game (like main menu) are a simple sprites. I only changing variable index to + -1 when player hit upkey or downkey and officially change sprite graphic on this event and are controlled by one control object. This method seems to be ineffective to achieve same with hexagonal. I have open mind to any other approach, so it can be instances instead of sprites. In other worlds, I like to chose approach which will be easier to write/understand
I don't think you'll run into performance issues here, so I would suggest this (assuming all tiles are placed in the room editor and none are removed or added in run-time):
Create event of tile objects:
Code:
var hFirstTile = noone;
var hLastTile = noone;
var vFirstTile = noone;
var vLastTile = noone;
hNextTile = noone;
hPrevTile = noone;
vNextTile = noone;
vPrevTile = noone;

with object_index {
    if hFirstTile == noone || y < hFirstTile.y || (y == hFirstTile.y && x < hFirstTile.x) {
        hFirstTile = id;
    }
    if hLastTile == noone || y > hLastTile.y || (y == hLastTile.y && x > hlastTile.x) {
        hLastTile = id;
    }
    if vFirstTile == noone || x < vFirstTile.x || (x == vFirstTile.x && y < vFirstTile.y) {
        vFirstTile = id;
    }
    if vLastTile == noone || x > vLastTile.x || (x == vLastTile.x && y > vlastTile.y) {
        hLastTile = id;
    }
    if (y > other.y || (y == other.y && x > other.x))
    && (hNextTile == noone || y < hNextTile.y || (y == hNextTile.y && x < hNextTile.x)) {
        hNextTile = id;
    }
    if (y < other.y || (y == other.y && x < other.x))
    && (hPrevTile == noone || y > hPrevTile.y || (y == hPrevTile.y && x > hPrevTile.x)) {
        hPrevTile = id;
    }
    if (x > other.x || (x == other.x && y > other.y))
    && (vNextTile == noone || x < vNextTile.x || (x == vNextTile.x && y < vNextTile.y)) {
        vNextTile = id;
    }
    if (x < other.x || (x == other.x && y < other.y))
    && (vPrevTile == noone || x > vPrevTile.x || (x == vPrevTile.x && y > vPrevTile.y)) {
        vPrevTile = id;
    }
}

if hNextTile == noone {
    hNextTile = hFirstTile;
}
if hPrevTile == noone {
    hPrevTile = hLastTile;
}
if vNextTile == noone {
    vNextTile = vFirstTile;
}
if vPrevTile == noone {
    vPrevTile = vLastTile;
}
step event of tile object:
Code:
if prevSprite == sprSelected {
    if keyboard_check(vk_right) {
        sprite_index = sprUnselected;
        hNextTile.sprite_index = sprSelected;
    } else if keyboard_check(vk_left) {
        sprite_index = sprUnselected;
        hPrevTile.sprite_index = sprSelected;
    } else if keyboard_check(vk_down) {
        sprite_index = sprUnselected;
        vNextTile.sprite_index = sprSelected;
    } else if keyboard_check(vk_up) {
        sprite_index = sprUnselected;
        vPrevTile.sprite_index = sprSelected;
    }
}
Begin step event of tile object:
Code:
prevSprite = sprite_index;
If you do need to remove or add tiles at run-time, you can use this as create event instead:
Code:
hNextTile = id;
hPrevTile = id;
vNextTile = id;
vPrevTile = id;

if global.hFirstTile == noone {
    global.hFirstTile = id;
    global.vFirstTile = id;
} else {
    var cTile;
    
    // Horizontally
    cTile = global.hFirstTile;
    if y < cTile.y || (y == cTile.y && x < cTile.x) {
        global.hFirstTile = id;
    } else {
        do {
            cTile = cTile.hNextTile;
        until cTile == global.hFirstTile || (y < cTile.y || (y == cTile.y && x < cTile.x));
    }
    
    hNextTile = cTile;
    hPrevTile = cTile.hPrevTile;
    hPrevTile.hNextTile = id;
    cTile.hPrevTile = id;
    
    // Vertically
    cTile = global.vFirstTile;
    if x < cTile.x || (x == cTile.x && y < cTile.y) {
        global.vFirstTile = id;
    } else {
        do {
            cTile = cTile.vNextTile;
        until cTile == global.vFirstTile || (x < cTile.x || (x == cTile.x && y < cTile.y));
    }
    
    vNextTile = cTile;
    vPrevTile = cTile.vPrevTile;
    vPrevTile.vNextTile = id;
    vTile.vPrevTile = id;
}

sprSelected = ...;
sprUnselected = ...;

sprite_index = id == hFirstTile ? sprSelected : sprUnselected;
prevSprite = sprite_index;
and add this to the destroy event:
Code:
hNextTile.hPrevTile = hPrevTile;
hPrevTile.hNextTile = hNextTile;
vNextTile.vPrevTile = vPrevTile;
vPrevTile.vNextTile = vNextTile;

if global.hFirstTile == id {
    global.hFirstTile = hNextTile == id ? noone : hNextTile;
}

if global.vFirstTile == id {
    global.vFirstTile = vNextTile == id ? noone : vNextTile;
}
You'll have to initialize global.hFirstTile and global.vFirstTile to noone before creating the tiles (e.g. before entering the room) with this approach.
 

Rob

Member
You could always use 6 letters/keys for movement rarer than 4.

I wouldn't like to move around a hex map with keys if I were limited to 4 - at least at first anyway.
 

woods

Member
i prefer using a rotation and forward/back when selecting on a hex grid.. like A/D rotate by 60^ and W/S moves forward and back one cell... but .. there are many many ways to handle it ;o)
 
Top