Counter Movement
Long time since my last post, but it’s because I’ve been going full throttle with the development. Fixin’ bugs and takin’ names! Last time I discussed how I went about building a dynamic wargame hex grid with counters. To get this feeling like a game, the next thing we need to do is make those counters move! So let’s jump right in and see how I did this and some of the hurdles I encountered. Note that the map and counters were placeholders used back in the infancy of the project before any work on the art had been done.
Breaking Down Movement
As with all programming, the very first thing we should do is take what we want to happen and break it down into smaller tasks. What we want to happen is the player (and later, the computer AI) to be able to move his or her pieces around the hex map. When I broke that down into steps, it looked something like this:
- Player selects a counter
- Highlight its possible destination hexes
- Player selects a destination hex
- Move counter to that hex
Now if you play wargames, chances are you’ve come across a few that allow for stacking. That’s where more than one counter can occupy a hex. If you’re building a game that doesn’t allow stacking, then the steps above might be all you need for movement. For my own game, I was pretty sure I wanted to allow stacking and I also wanted to keep the door open for the possibility of re-using my code later for a different game that allowed it. That means that my steps had to change a little bit to allow for choosing units in a stack.
Additionally, in a real game counters typically don’t get unlimited movement. Once they use up their movement points or turn (depending on the rules), they are no longer eligible to move again until next turn.
With these changes in mind, I revised my steps:
- Player selects a hex with at least one counter that’s eligible to move in it
- If there’s only one counter there, select it; otherwise there are more than one eligible counters so give the player an easy way to select just one
- Highlight its possible destination hexes
- Player selects a destination hex
- Move counter to that hex
- Mark that counter as having moved this turn
I’ve learned that breaking down larger actions into smaller steps like this is critical to programming. Before you add any code to a new feature, it’s helpful to add these as plain-talk comments for yourself. If it feels too “big,” break it down into smaller bite-sized chunks. This will help make it clear what code you’ll have to add when it’s time to do so, but it’ll also often highlight potential issues before you have a bunch of code to move around or re-write.
Selecting a Hex
Something to note here is that in many programming languages, it’s not as easy to register clicks/touches to individual hexes (or other polygons) as it is with rectangles or circles. Images have a natural bounding box that’s a rectangle which is easy to set up for selections, and using circle radiuses is fairly easy as well. You might be tempted to use one of those shapes as a selection tool around hexes, but you can see how that fails in a grid by looking at this image:
The rectangular bounding boxes of the two hexes overlap and you’ll probably end up choosing whichever is on top, which may or may not be the hex you wanted. If they were further apart, you might be able to get away with this but because they’re a tight grid it just won’t work.
Luckily for my game, I’m using SVGs (Scalable Vector Graphics) for the hexes. SVGs are vector-based meaning that they’re treated as objects, not bitmap images. This means that when you attach events to them, their natural shape is the “hit” area. If you were making a game using an HTML canvas element or some other programming language that renders graphics as pixels, you’d have to take special care – and do more math – to account for the unique shape of the hexagons. There are discussions about that here and here.
Possible Destination Hexes
If all of our counters could only move one hex per turn, our job here would be pretty easy. We’d just get all the hexes around the chosen counter and highlight them. Using each counter’s allotted movement points to see their possible destination hexes isn’t much more difficult. In fact, you just repeatedly get adjacent hexes. First, you get all the adjacent hexes to the one the counter is on, then you get the adjacent hexes of all of those adjacent hexes, then you get the adjacent hexes of those, etc. You do this as many times as the counter has movement points, adding all the new hexes to the array of possible hexes. Don’t forget to skip the hexes that are already in the array, so you’re only adding new hexes with each iteration. When you highlight the hexes, you should see something like this:
As you loop through all the new hexes, you do all your checks for whether or not the hex is valid, checking things like stacking limits (even if that limit is one counter per hex) before adding it to the array of valid destinations. One of those checks should relate to terrain. Are your troops allowed to climb those mountains? Are they allowed to cross that wide river? The answer might be no, but the other possibility is that they can, but it costs more.
Terrain Costs
For a simple game, terrain might not be an issue, but in most physical hex-and-counter games, units have an allotment of movement points and hexes have a terrain cost. Applying them to a digital game means we add a wrinkle to the iterations that check if a hex is a valid destination.
First, we have to assign a cost to every type of terrain. For example, maybe a clear hex requires 1 MP to reach but a forest requires 2, and a mountain requires 3. We also have to keep a running tally of how many movement points (MPs) a unit has left to spend as we’re checking hexes.
Additionally, when we save each hex, we have to include the cost it takes us to get to that hex. Imagine you’re an infantry counter (whether your corners are clipped or not is up to you). To the south you would have to travel through forests spending 2 MPs per hex. Up north, it’s all clear at 1 MP per hex. To the west, is a mix of the two.
You have to record each step as you calculate how far you can go. Every time you save a counter to your array you save it with the amount of MPs you’ve used to get there. Lastly, if the hex you’re checking is already in your array, you compare the movement costs spent for each iteration and save the most efficient one. This is an important last step because it ensures you find all the hexes possible to reach.
Now as we check those adjacent hexes we’re checking the following:
- Is the unit allowed in this hex (stacking, etc)?
- How many movement points will it take to reach the hex?
- Do I have that many left to spend?
- If yes, is this hex already in my array of possible destinations?
- If it isn’t in my array, subtract the MPs to reach it from what I have left to spend and save it with the hex.
- If it is in my array, subtract the MPs to reach it and compare what’s left with the saved version of the hex. If this version uses fewer MPs (so, a more efficient route) to reach, save this new version of the hex.
Once you get this working properly in your code, you’ll wish you had a computer’s processing power when you’re calculating movement hexes in physical games. With varying terrain types, you’ll get something that looks like this:
This was pretty tough for me to wrap my head around at first, so if you’re finding it difficult, my advice is two-fold. Break it down into smaller steps, commenting along the way. Secondly, use the same logic as when learning a new wargame, but instead of “pushing pieces around,” push code around. More precisely, put down some code and see what works.
The next step after this would likely be having terrain costs for each type of unit. For example, entering a forest hex might cost more for cavalry units. This just means each terrain has multiple costs for all your types of units. I recommend only tackling this once you’re comfortable with each terrain having only one cost.
That’s all for this time. As always, thanks for reading and I hope this helps your own coding in some way. Happy gaming!
Leave a Reply