RPG Grid Tiling Theory

Page 1 of 1

6 Replies - 11806 Views - Last Post: 23 February 2008 - 06:54 PM

#1 grimpirate

• Pirate King

Reputation: 149
• Posts: 717
• Joined: 03-August 06

RPG Grid Tiling Theory

Posted 06 October 2006 - 09:49 PM

Alright well this will be very nerdy, but ay we all do what we can. I've realized that most of the RPG games that use a grid primarily seem to focus around a square grid. Which means that your character would lie in the center and have 8 adjacent grids that would be touching it. However, there's always that problem with the circular radius of movement. The distance to move up, down, left, or right, isn't the same as trying to move along a diagonal (pythagoras's theorem proves that). As I see it the problem lay with the type of grid. Which is determined by the number of sides the polygon used to compose it has. Ideally, I would imagine an equilateral triangle based grid would be best, then there's the square, and a hexagon. I don't know if any other n-sided polygon would work, but I know that pentagons and octagons leave voids which have to be filled with polygons of other sizes. Therefore, I set out to program and test a hexagonal grid. The idea being that the character lies in the center, and then there are 6 adjacent hexagons around it, and all of their centers are equidistant from the character's center. All objects are drawn dynamically as MovieClips. The only object that needs to be created is a text box called locate_txt. It's responsible for showing the coordinates of each hexagon tile based on two axes one that is collinear with the y-axis, and another that is offset 30 degrees from the x-axis in a counter-clockwise direction.
Here's the code:

Layer 1 : Frame 1
```var screen_w:Number = 600;
var screen_h:Number = 400;
var grid_w:Number = 600;
var grid_h:Number = 400;
var scale:Number = 10;
var hex_w:Number = Math.sqrt(3) / 2 * scale;
var hex_h:Number = 2 * Math.tan(Math.PI / 6) * scale;

this.createEmptyMovieClip("grid_mc", 1);
grid_mc._x = 0;
grid_mc._y = 0;
grid_mc.lineStyle(1, 0x000000, 100);

for(var i:Number = 0; i < Math.floor(grid_w / hex_w); i++) {
for(var j:Number = 0; j < Math.floor(grid_h / hex_h); j++) {
var tileOffset:Array = new Array(i * hex_w, j * hex_h);
if(i % 2 == 0) {
drawLeft(tileOffset);
} else {
drawRight(tileOffset);
}
}
}

var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

this.createEmptyMovieClip("tileFocus_mc", 2);
tileFocus_mc.lineStyle(2, 0xff0000, 100);
tileFocus_mc.moveTo(0, hex_h / 2);
tileFocus_mc.lineTo(hex_w / 3, 0);
tileFocus_mc.lineTo(hex_w, 0);
tileFocus_mc.lineTo(hex_w * (1 + 1 / 3), hex_h / 2);
tileFocus_mc.lineTo(hex_w, hex_h);
tileFocus_mc.lineTo(hex_w / 3, hex_h);
tileFocus_mc.lineTo(0, hex_h / 2);

function drawLeft(offset:Array) {
grid_mc.moveTo(offset[0], offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w, offset[1]);
grid_mc.moveTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h);
}

function drawRight(offset:Array) {
grid_mc.moveTo(offset[0], offset[1]);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h / 2);
grid_mc.moveTo(offset[0], offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
}

function hexCoords(point:Array):Array {
return new Array(Math.round(point[0] / hex_w), Math.round(point[0] / (2 * hex_w) + point[1] / hex_h));
}

function euclidCoords(point:Array):Array {
return new Array(point[0] * hex_w, -point[0] * hex_h / 2 + point[1] * hex_h);
}
```
Layer 1 : Frame 2
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];
```
Layer 1 : Frame 3
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

if(c[1] > Math.floor(c[0] / 2) && c[1] < Math.floor(c[0] / 2) + Math.floor(grid_h / hex_h) + c[0] % 2 && c[0] > 0 && c[0] < Math.floor(grid_w / hex_w)) {
var locateTile:Array = euclidCoords(c);
tileFocus_mc._x = locateTile[0] - hex_w * 2 / 3;
tileFocus_mc._y = locateTile[1] - hex_h / 2;
}
```
Layer 1 : Frame 4
```gotoAndPlay(2);
```
So what's my question/gripe/debate? Umm... suggestions for some alternatives to the way of locating the tiles, how to place the focus on them (red hexagon), alternative ways of drawing, etc.
NOTE: The attachment includes the SWF file and the FLA file.

Attached File(s)

Is This A Good Question/Topic? 0

Replies To: RPG Grid Tiling Theory

#2 grimpirate

• Pirate King

Reputation: 149
• Posts: 717
• Joined: 03-August 06

Re: RPG Grid Tiling Theory

Posted 08 October 2006 - 10:57 AM

I reworked the code some more and this time changed Frame 1 and Frame 3. The application will now highlight steppable tiles in green, and nonsteppable tiles, as well as outer boundaries that are not tiled, in red. Frame 2 and Frame 4 remain unchanged.
Layer 1 : Frame 1
```var screen_w:Number = 600;
var screen_h:Number = 400;
var grid_w:Number = 600;
var grid_h:Number = 400;
var scale:Number = 10;
var hex_w:Number = Math.sqrt(3) / 2 * scale;
var hex_h:Number = 2 * Math.tan(Math.PI / 6) * hex_w;

this.createEmptyMovieClip("grid_mc", 1);
grid_mc._x = 0;
grid_mc._y = 0;
grid_mc.lineStyle(1, 0x000000, 100);

for(var i:Number = 0; i < Math.floor(grid_w / hex_w); i++) {
for(var j:Number = 0; j < Math.floor(grid_h / hex_h); j++) {
var tileOffset:Array = new Array(i * hex_w, j * hex_h);
if(i % 2 == 0) {
drawLeft(tileOffset);
} else {
drawRight(tileOffset);
}
}
}

var steppable:Array = new Array();
for (var i:Number = 1; i < Math.floor(grid_w / hex_w); i++) {
steppable[i - 1] = new Array();
for (var j:Number = Math.floor(i / 2) + 1; j < Math.floor(i / 2) + Math.floor(grid_h/hex_h) + i % 2; j++) {
var flag:Number = Math.floor(Math.random() * 2);
if(flag == 0) {
steppable[i - 1][j - Math.floor(i / 2) - 1] = true;
} else {
steppable[i - 1][j - Math.floor(i / 2) - 1] = true;
}
}
}

var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

this.createEmptyMovieClip("tileFocus_mc", 2);

function drawFocus(color:Number) {
tileFocus_mc.lineStyle(2, color, 100);
tileFocus_mc.moveTo(0, hex_h / 2);
tileFocus_mc.lineTo(hex_w / 3, 0);
tileFocus_mc.lineTo(hex_w, 0);
tileFocus_mc.lineTo(hex_w * (1 + 1 / 3), hex_h / 2);
tileFocus_mc.lineTo(hex_w, hex_h);
tileFocus_mc.lineTo(hex_w / 3, hex_h);
tileFocus_mc.lineTo(0, hex_h / 2);
}

function drawLeft(offset:Array) {
grid_mc.moveTo(offset[0], offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w, offset[1]);
grid_mc.moveTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h);
}

function drawRight(offset:Array) {
grid_mc.moveTo(offset[0], offset[1]);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h / 2);
grid_mc.moveTo(offset[0], offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
}

function hexCoords(point:Array):Array {
return new Array(Math.round(point[0] / hex_w), Math.round(point[0] / (2 * hex_w) + point[1] / hex_h));
}

function euclidCoords(point:Array):Array {
return new Array(point[0] * hex_w, -point[0] * hex_h / 2 + point[1] * hex_h);
}
```
Layer 1 : Frame 3
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

var locateTile:Array = euclidCoords(c);
tileFocus_mc._x = locateTile[0] - hex_w * 2 / 3;
tileFocus_mc._y = locateTile[1] - hex_h / 2;

if(steppable[c[0] - 1][c[1] - Math.floor(c[0] / 2) - 1] == true) {	//if(c[1] > Math.floor(c[0] / 2) && c[1] < Math.floor(c[0] / 2) + Math.floor(grid_h / hex_h) + c[0] % 2 && c[0] > 0 && c[0] < Math.floor(grid_w / hex_w)) {
drawFocus(0x00ff00);
} else {
drawFocus(0xff0000);
}
```

NOTE: I also corrected a problem in that the hexagons weren't actually circle inscribed, the previous code was producing an elliptical hexagon. The code for determining the steppable tiles might not make sense because it produces all the viewable tiles as true, but I was testing it with some false values to create holes in the viewable grid (the tiles were false).

#3 WolfCoder

• Isn't a volcano just an angry hill?

Reputation: 821
• Posts: 7,692
• Joined: 05-May 05

Re: RPG Grid Tiling Theory

Posted 08 October 2006 - 12:56 PM

Dude, just use isometrics, it's much easier than hexagons. It's all skewed a bit, but it's what alot of the games that rely on turn-based tactical RPG do (like OgreTactics, Final Fantasy Tactics Advance, ect...).

In an old advance wars gameboy game, I saw it where it was squares, but they were aligned in a hex pattern as such:

```][][][
[][][]
][][][

```

To make a board like this:

```][][][][][][][][
[][][][][][][][]
][][][][][][][][
[][][][][][][][]
][][][][][][][][

```

Try that out, it's a little rough, but it's a great solution. Another solution is to ditch the grids altogether and have a distance circular based system like Phantom Brave II.

This post has been edited by WolfCoder: 08 October 2006 - 12:57 PM

#4 grimpirate

• Pirate King

Reputation: 149
• Posts: 717
• Joined: 03-August 06

Re: RPG Grid Tiling Theory

Posted 08 October 2006 - 07:31 PM

No doubt WolfCoder, I plan to make it isometric eventually, but isometric with hexagons, buhuhuhahaha. I don't know if you dled the SWF, but I got it working just fine. Not so bad, just some coordinate changing, but it's the same idea as that GBA example you listed. Which by the way thanks for. I would've never thought of that, and it is a very simplified approach to achieve sorta the same thing.

I thought about that system like the one you're describing in Phantom Brave II but that seems a lot harder to implement and I'm not that great a coder or anything, I've never coded a game even. This is sorta my first attempt, it's actually a game which I came up with but it works with a card battling system and the like, so I actually worked it out in the physical world before delving into any sort of programming. Hence, why it works on that hex grid.

Yours is the best help I've gotten thus far though (I have this posted at another forum as well). If you got any more ideas on how to rework the system I'm proposing (like that GBA trick) I certainly could use the suggestions.

#5 grimpirate

• Pirate King

Reputation: 149
• Posts: 717
• Joined: 03-August 06

Re: RPG Grid Tiling Theory

Posted 11 October 2006 - 12:23 AM

Well I worked it out so now you can actually click on a tile and then drag and it will highlight in blue the area that you are dragging on (approximately).
Layer 1 : Frame 1
```var screen_w:Number = 600;
var screen_h:Number = 400;
var grid_w:Number = 600;
var grid_h:Number = 400;
var scale:Number = 20;
var hex_w:Number = Math.sqrt(3) / 2 * scale;
var hex_h:Number = 2 * Math.tan(Math.PI / 6) * hex_w;

this.createEmptyMovieClip("grid_mc", 1);
grid_mc._x = 0;
grid_mc._y = 0;
grid_mc.lineStyle(1, 0x000000, 100);

for(var i:Number = 0; i < Math.floor(grid_w / hex_w); i++) {
for(var j:Number = 0; j < Math.floor(grid_h / hex_h); j++) {
var tileOffset:Array = new Array(i * hex_w, j * hex_h);
if(i % 2 == 0) {
drawLeft(tileOffset);
} else {
drawRight(tileOffset);
}
}
}

var steppable:Array = new Array();
for (var i:Number = 1; i < Math.floor(grid_w / hex_w); i++) {
steppable[i - 1] = new Array();
for (var j:Number = Math.floor(i / 2) + 1; j < Math.floor(i / 2) + Math.floor(grid_h/hex_h) + i % 2; j++) {
var flag:Number = Math.floor(Math.random() * 2);
if(flag == 0) {
steppable[i - 1][j - Math.floor(i / 2) - 1] = true;
} else {
steppable[i - 1][j - Math.floor(i / 2) - 1] = true;
}
}
}

var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

this.createEmptyMovieClip("tileFocus_mc", 2);

function drawFocus(color:Number) {
tileFocus_mc.lineStyle(2, color, 100);
tileFocus_mc.moveTo(0, hex_h / 2);
tileFocus_mc.lineTo(hex_w / 3, 0);
tileFocus_mc.lineTo(hex_w, 0);
tileFocus_mc.lineTo(hex_w * (1 + 1 / 3), hex_h / 2);
tileFocus_mc.lineTo(hex_w, hex_h);
tileFocus_mc.lineTo(hex_w / 3, hex_h);
tileFocus_mc.lineTo(0, hex_h / 2);
}

function drawLeft(offset:Array) {
grid_mc.moveTo(offset[0], offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w, offset[1]);
grid_mc.moveTo(offset[0] + hex_w / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h);
}

function drawRight(offset:Array) {
grid_mc.moveTo(offset[0], offset[1]);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1]);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
grid_mc.lineTo(offset[0] + hex_w, offset[1] + hex_h / 2);
grid_mc.moveTo(offset[0], offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w / 3, offset[1] + hex_h);
grid_mc.lineTo(offset[0] + hex_w * 2 / 3, offset[1] + hex_h / 2);
}

function hexCoords(point:Array):Array {
return new Array(Math.round(point[0] / hex_w), Math.round(point[0] / (2 * hex_w) + point[1] / hex_h));
}

function euclidCoords(point:Array):Array {
return new Array(point[0] * hex_w, -point[0] * hex_h / 2 + point[1] * hex_h);
}

this.createEmptyMovieClip("tileSelect_mc", 3);
tileSelect_mc._x = 0;
tileSelect_mc._y = 0;

var center:Array;
var downFlag:Boolean = false;

grid_mc.onmousedown = function() {
tileSelect_mc.clear();
center = euclidCoords(hexCoords(new Array(_xmouse, _ymouse)));
downFlag = true;
}

grid_mc.onmouseup = function() {
downFlag = false;
}
```
Layer 1 : Frame 2
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

function drawFilled(offset:Array) {
tileSelect_mc.moveTo(offset[0], offset[1]+hex_h/2);
tileSelect_mc.beginFill(0x0000ff, 50);
tileSelect_mc.lineTo(offset[0]+hex_w/3, offset[1]);
tileSelect_mc.lineTo(offset[0]+hex_w, offset[1]);
tileSelect_mc.lineTo(offset[0]+hex_w*(1+1/3), offset[1]+hex_h/2);
tileSelect_mc.lineTo(offset[0]+hex_w, offset[1]+hex_h);
tileSelect_mc.lineTo(offset[0]+hex_w/3, offset[1]+hex_h);
tileSelect_mc.lineTo(offset[0], offset[1]+hex_h/2);
tileSelect_mc.endFill();
}

var tempCenter:Array;
var shiftTile:Array;
var tempEuclid:Array;
tileSelect_mc.clear();
for(var i:Number = 1; i <= radius; i++) {
for(var j:Number = 0; j < 6; j++) {
tempCenter = hexCoords(center);
var temp
switch(j) {
case 0:
tempCenter[1] += i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[0]++;
}
break;
case 1:
tempCenter[0] += i;
tempCenter[1] += i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[1]--;
}
break;
case 2:
tempCenter[0] += i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[0]--;
tempCenter[1]--;
}
break;
case 3:
tempCenter[1] -= i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[0]--;
}
break;
case 4:
tempCenter[0] -= i;
tempCenter[1] -= i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[1]++;
}
break;
case 5:
tempCenter[0] -= i;
for(var k:Number = 0; k < i; k++) {
tempEuclid = euclidCoords(tempCenter);
tempEuclid[0] -= hex_w * 2 / 3;
tempEuclid[1] -= hex_h / 2;
if(steppable[tempCenter[0]-1][tempCenter[1]-Math.floor(tempCenter[0]/2)-1]) {
drawFilled(tempEuclid);
}
tempCenter[0]++;
tempCenter[1]++;
}
break;
}
}
}
}
```
Layer 1 : Frame 3
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: "+c[0]+", cy: "+c[1];
var locateTile:Array = euclidCoords(c);
tileFocus_mc._x = locateTile[0]-hex_w*2/3;
tileFocus_mc._y = locateTile[1]-hex_h/2;
if (steppable[c[0]-1][c[1]-Math.floor(c[0]/2)-1]) {
drawFocus(0x00ff00);
} else {
drawFocus(0xff0000);
}

if(downFlag) {
var diffX:Number = _xmouse - center[0];
var diffY:Number = _ymouse - center[1];
var radialDisp = Math.floor((Math.sqrt(diffX * diffX + diffY * diffY) - hex_h / 2) / hex_h);
if(radialDisp > -1) {
} else {
tileSelect_mc.clear();
}
}
```
Layer 1 : Frame 4
```var c:Array = hexCoords(new Array(_xmouse, _ymouse));
locate_txt.text = "cx: " + c[0] + ", cy: " + c[1];

gotoAndPlay(2);
```
Lemme know what you think of how I coded it and how it plays. Suggestions are appreciated and welcome.

#6 NumberXIII

• New D.I.C Head

Reputation: 0
• Posts: 10
• Joined: 30-October 06

Re: RPG Grid Tiling Theory

Posted 30 October 2006 - 10:09 AM

Attached File(s) error
please Attach file again plz
thz

I'm interesting in flash rpg.
Plz PM to me if it have topic about rpg in flash.
thz

#7 cthulhugeek

• New D.I.C Head

Reputation: 0
• Posts: 2
• Joined: 23-February 08

Re: RPG Grid Tiling Theory

Posted 23 February 2008 - 06:54 PM

NumberXIII, on 30 Oct, 2006 - 10:09 AM, said:

Attached File(s) error
please Attach file again plz
thz

I'm interesting in flash rpg.
Plz PM to me if it have topic about rpg in flash.
thz

I am sorry this topic is no longer active. I am playing around with some ideas for flash games , and wanted to use a hex grid. You definitely got some cool stuff here. Are you there?

Don