Advertisement

moving sprite on a hex grid

Started by June 14, 2022 08:51 PM
73 comments, last by Tom Sloper 2 years, 4 months ago

I am parsing over a 2D array. here is my array.

int HexGrid[12][10] = { {0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{2,2,2,2,2,0,1,1,2,2},{0,0,2,2,0,1,0,1,0,0},
						{0,0,0,3,1,0,1,0,0,0},{0,0,2,1,1,0,1,0,0,0},{2,2,2,2,2,1,0,0,0,2},{0,0,0,2,2,1,0,0,1,1},
						{2,2,0,0,2,1,0,1,0,0},{0,0,0,2,2,0,1,1,0,0},{0,0,0,2,2,2,1,0,0,2},{0,0,0,0,0,0,0,0,0,0} };
int Xloc = 9, Yloc = 7;
int HexType = HexGrid[Yloc][Xloc];

Phil, you gave the impression on page 5 of this thread that you were all done. There is a new problem with moving sprites on a hex grid? Or are you just sharing code? If you just want to share code, it's best to do that in a blog. If you need help with something, you need to tell us what you need help with. The title of this thread is “moving sprite on a hex grid.” If you're done moving sprites on a hex grid and you're doing something else now, you need to start a new thread.

-- Tom Sloper -- sloperama.com

Advertisement

pbivens67 said:
class Point
int ComputeDistanceHexGrid()

I'm happy to see you try to use data structures to represent points or vectors, and functions which will be useful in many places. :D
That's the proper way forward. Just have some patience to see the benefit with time.

pbivens67 said:
I have stubbed out some code to determine the distance between hexes that are flat topped, unfortunately my hex board is point topped, how do I convert my code to work with pointy topped hexes.

I do not understand the code in detail, but it seems you have hard coded one of the two potential conventions, e.g. here:
> size_t diagonalDistance = abs(diagonalMovement.x);

It seems x has a special meaning unlike y, so eventually you'd need to swap x and y to get the other convention.
I can not really help on the problem, but that's an example of a problem we face very often: Assumptions made - but not clarified - from the code.
We often write a function which does its job, but only because we fulfill all assumptions the function makes on data and context. The flaw goes unnoticed as long as we use it as initially intended.
Then, later we might want to use the same function from a different application / situation. And only then we might figure out it no longer works as expected, because we no longer fulfill assumptions the function makes. And only now we become aware of the issue, often forcing us to do heavy changes, or even rewrite from scratch.

That's often frustrating and an obstacle to build up a useful codebase. Still, it's the way to learn from our mistakes. And to learn, it's often better trying to do changes to existing code than just starting from scratch, although the latter might feel more attractive.

That said, eventually you could support both point / flat topped conventions without much extra effort.
And you could rename your function so it's more clear what it is calculating. By ‘Distance’ i would assume Cartesian distance, so the length of the straight line between two points: length(A - B).
You probably calculate not that, but you construct two (or multiple) line segments, both aligned to 60 degree hex tiles, so you get the length of a walkable path respecting those angular constraints of a hex tile map.
If you would call your function ‘Manhattan Distance’, that would have been clear to me without digging into the code.

Eventually a solution to support both conventions would be as simple as that:

int ComputeManahttanDistanceFlatToppedHexGrid(const Point & A, const Point & B);

int ComputeManahttanDistancePointToppedHexGrid(const Point & A, const Point & B)
{
	Point swappedA (A.y, A.x);
	Point swappedB (B.y, B.x);
	return ComputeManahttenDistanceFlatToppedHexGrid(swappedA, swappedB);
}

I'm not saying you should support any potential conventions. I just use that to give an example illustrating the assumptions problem, which we often can already avoid by naming things more specifically.

Also notice size_t is an unsigned type, so using it for arithmetic calculations can cause unintended side effects.
Actually i suggest you don't use integer types at all here. I would make the function return a float, not an int. You go from float to size_t then to int. That's many potential rounding issues, precision loss, and slower than just sticking at float for everything.

int Xloc = 9, Yloc = 7;
int HexType = HexGrid[Yloc][Xloc];

It's better to never write such assignments.

Instead immediately write a function like

int getTypeOfGridLocation(int xpos, int ypos) { ... }

You eventually need such a function anyway in your game.

here is the code for moving a sprite along a path on my hex grid, I just want to know if my code is on the right track.

	case GLUT_KEY_UP:
		if(rotate_tank==3)
		{
		drawTank_one = false;
		Xloc++;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 15.0f;
		}
		cout << Yloc << " " << Xloc << " " << HexType << endl;
		}

		if (rotate_tank == 0)
		{
		drawTank = false;
		Xloc--;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 15.0f;
		} 
		cout << Yloc << " " << Xloc << " " << HexType << endl;
		}

		if (rotate_tank == 2)
		{
		drawTank_two = false;
		++Xloc;
		++Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 8.0f;
		move_y += 13.5f;
		}
		}

		if (rotate_tank == 1)
		{
		drawTank_three = false;
		--Xloc;
		++Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 8.0f;
		move_y += 13.5f;
		}
		}

		if (rotate_tank == 4)
		{
		drawTank_four = false;
		++Xloc;
		--Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 8.0f;
		move_y -= 13.5f;
		}
		}

		if (rotate_tank == 5)
		{
		drawTank_five = false;
		--Xloc;
		--Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 8.0f;
		move_y -= 13.5f;
		}
		}
		break;
case GLUT_KEY_UP:
		if(rotate_tank==3)
		{
		drawTank_one = false;
		Xloc++;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 15.0f;
		}
		cout << Yloc << " " << Xloc << " " << HexType << endl;
		}

What happens to Xloc and move_x if I try this case 3 times and HexType is not 1 ?

Is that wanted?

Advertisement

@pbivens67 : Regarding your code. . .

As Alberth has alluded to, the simple statement “HexType = HexGrid[Yloc][Xloc];” can lead to problems, if you don't make sure that Yloc and Xloc are actually on your grid. For example, if you have a 10X10 Grid, then the legal ranges are from 0 to 9. If the tank is at 0 and wants to move to -1 that is bad. If the tank is at 9 and wants to move to grid loc 10, that is bad. I'd suggest having a function to make sure your x and y locs are grid-legal. Probably something like this code below would do that. . . (I'm assuming you'd create some kind of HexGrid class)

bool HexGrid::IsSafeGridLocation(int HexX, int HexY)
{
   bool SafeLoc = true; 
   
   if(HexX < 0 || HexX ≥ MaxGridX)
      SafeLoc = false; // HexX is outside of the Grid
      
   if(HexY < 0 || HexY ≥ MaxGridY
     SafeLoc = false; // HexY is outside of the Grid

   return SafeLoc;
}

then you can make a function to incorporate your safe loc test whenever you try to get a hex. . .

int HexGrid::GetSafeGridData(int HexX, int HexY)
{
     int HexType = 0;
     if(IsSafeGridLocation(HexX, HexY))
             HexType = HexGrid[Yloc][Xloc];
     else
             HexType = -1;
     return HexType;
}

Using this type of function has some other advantages as well. If you decide to eventually store your map data in some other way than the simple 2D array, that will not matter externally, and you can simply change the way you access the data inside the GetData function.

Looking at other aspects of your code. . .

You also still seem to be tracking both the GridLoc and DrawLoc. I would just think in terms of Hex Grid Locs, and only use the OpenGL coordinates when you are actually drawing. That way, you'd always keep the Grid Locations, and have a function to convert from Hex Grid Locs to OpenGL coordinates. Then you'd use that function to convert over when drawing your tank.

Well, I am parsing over a 2D array. What I want to do is that when I index into the array, I get a value of 1, I move one hex space along a grassy sprite path. However, when I try to move in a water or mountain path it does not move, which is what I want it to do. It indexes into a value of 0 or 2. My problem is that when I increment Xloc it does not move the tank sprite but then the Xloc variable increments to a HexType value of 1. In other words, I want the array of 1 value to be the path and the values of 2 and 0 to be obstacles. let me know if need you more explanation.

		if(rotate_tank==3)
		{
		drawTank_one = false;
		Xloc++;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 15.0f;
		}
		cout << Yloc << " " << Xloc << " " << HexType << endl;
		}

		if (rotate_tank == 0)
		{
		drawTank = false;
		Xloc--;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 15.0f;
		} 
		cout << Yloc << " " << Xloc << " " << HexType << endl;
		}

		if (rotate_tank == 2)
		{
		drawTank_two = false;
		++Xloc;
		++Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 8.0f;
		move_y += 13.5f;
		}
		}

		if (rotate_tank == 1)
		{
		drawTank_three = false;
		--Xloc;
		++Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 8.0f;
		move_y += 13.5f;
		}
		}

		if (rotate_tank == 4)
		{
		drawTank_four = false;
		++Xloc;
		--Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x += 8.0f;
		move_y -= 13.5f;
		}
		}

		if (rotate_tank == 5)
		{
		drawTank_five = false;
		--Xloc;
		--Yloc;
		HexType = HexGrid[Yloc][Xloc];
		if (HexType == 1)
		{
		move_x -= 8.0f;
		move_y -= 13.5f;
		}
		}
		break;
	case GLUT_KEY_DOWN:
		if (rotate_tank == 3)
		{
			drawTank_one = false;
			move_x -= 15.0f;
		}
		if (rotate_tank == 0)
		{
			drawTank = false;
			move_x += 15.0f;
		}
		if (rotate_tank == 2)
		{
			drawTank_two = false;
			move_x -= 8.0f;
			move_y -= 13.5f;
		}
		if (rotate_tank == 1)
		{
			drawTank_three = false;
			move_x += 8.0f;
			move_y -= 13.5f;
		}
		if (rotate_tank == 4)
		{
			drawTank_four = false;
			move_x -= 8.0f;
			move_y += 13.5f;
		}
		if (rotate_tank == 5)
		{
			drawTank_five = false;
			move_x += 8.0f;
			move_y += 13.5f;
		}
		break;
	}

int HexGrid[12][10] = { {0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},{2,2,2,2,2,0,1,1,2,2},{0,0,2,2,0,1,0,1,0,0},
						{0,0,0,3,1,0,1,0,0,0},{0,0,2,1,1,0,1,0,0,0},{2,2,2,2,2,1,0,0,0,2},{0,0,0,2,2,1,0,0,1,1},
						{2,2,0,0,2,1,0,1,0,0},{0,0,0,2,2,0,1,1,0,0},{0,0,0,2,2,2,1,0,0,2},{0,0,0,0,0,0,0,0,0,0} };
int Xloc = 9, Yloc = 7;
int HexType = HexGrid[Yloc][Xloc];
   

@pbivens67 : Regarding your statement : “My problem is that when I increment Xloc it does not move the tank sprite.”

The solution is this : you need to make sure that, when you draw the tank, its position is based on Xloc. Therefore when you increment Xloc, it will also update the tank's position.

Of course, you can't use Xloc directly when you issue draw statements to OpenGL. Instead you have to translate Xloc and Yloc to your OpenGL drawing coordinates first. And then give those translated coordinates to OpenGL.

But since the translated coordinates will be based upon Xloc, when you increment Xloc, that will move the tank.

actually, it does move the tank my problem is as I stated above.

This topic is closed to new replies.

Advertisement