In a game like Fire Emblem, should units be aware of the board state?

Godot Version

4.6 beta

Question

Just a sort of general architectural question, and I’m sure it depends on the game. But I’m a total beginner to this kind of structure and am working on a super small FE-like game for a game jam (one map, no items, just a few characters).

I’ve created my Unit class, which just holds things like stats for now, and presumably things like types of attacks later.

I also have a GameGrid class, which holds info about all the tiles on the board and a dictionary for what tiles have Units in them.

Now I’m working on implementing AStarGrid2D to do movement. I started by putting that in the GameGrid class, and setting it up to set walls as solid points.

Then I wondered how Units should… actually use that to move. I sort of instinctively threw my tree together like this to start with:

So then I thought to myself, well, I guess the Map node should perhaps handle things like the game state and selecting units, since it is the parent of both. (Maybe Units don’t have to be a child of GameGrid, either.) So if the Map handles that sort of thing, maybe the Map should just give units the info they need when they move. i.e., it passes them an id_path using the GameGrid’s AStarGrid2D, then the units use that to move in their physics processes.

So, I implemented that and then got it working.

But then I got to thinking… what about when they need to attack? We need to find their available targets and such… and then movement ranges, too; we need to somehow limit the pathing to a range. Then if you were to add anything like board-based skills to the mix, or different movement types, too… I wondered if it made more sense to just give every unit their own AStarGrid2D and a reference to the GameGrid.

How, in general, would you go about approaching this? Does it make sense for each unit to have access to the grid?

It is a good question and many people will have different answers and opinions. There is not any single right way of course.

IMHO I would not have your units as a child of your GameGrid. I don’t think I would even keep them as a child of Map TBH. Your path finding will produce a list of tiles for your units to move to, to get to your target, so they do not need to be a child of the game grid.

I would have your units in the same level as your map. So something like:

  • Game
    – MapManager
    – TurnManager
    – EnemyUnits
    – PlayerUnits
    – EnemyManager (The enemy AI)
    – PlayerManager (Player inputs)
    – UI

Or whatever the rest of your structures are. As for deciding where the NPC should attack, the unit will ask the MapManager for a list of tiles that can be attacked in it’s range. The map manager returns a dictionary of attackable tiles. You would then query PlayerUnits to see if anything is on those tiles. Your NPC will then have some priorities (that might change based on it’s health and strength) and can choose either something to attack or to retreat.

Keep all your map queries in the top level of the map. Now you can change your map at will without breaking anything else. You might have one unit controller but since the player will be moving their units and your NPC controller will be deciding what to move and when, they are usually different enough to deserve their own controllers. (Unless it is more like chess, where the player units are the same as the NPC units, in which case you would generalise the unit controllers so they can be shared)

Either way, try to think in terms of single responsibility. This node manages the map. This node manages the NPCs. This node controls who’s turn it is. This node controls if the game is over or not. This node controls the UI. This node deals with player inputs etc.

A grid based turn-based game seems like an easy entry point. It is not. It is, or at least can be, a very difficult challenge. My first game was one of these, and it was hard! My next game was a lot easier. Anyway, good luck. I hope it goes well.

PS Every unit should be able to request a path when they need it from the map. They provide how far they can move on their next turn, or they give their target but only move a few tiles at a time. The map does not need to know anything about the unit. The unit does not need to know anything about the map TBH. It only needs tiles it can move to. It can then ask the map for the global position of this tile, or that tile, and move there. The unit does not need an AStarGrid, it is not a map. Only the map needs one. Imagine you control several radio controlled cars. Do the cars know anything about the landscape or track, or each other? No. You tell the car via the radio controller ‘move forward’ or ‘turn your wheels left’. It is a very similar thing here. Your units are dumb. When it is the computers turn, your NPC behaviour manager will decide what unit is going to move, and tell the dumb unit where to go. Move here and do an attack animation, say.

2 Likes

Thank you so much for this! This is helping me visualize it. I’m still a bit confused about some things you’ve said, though.

How does a unit “ask”? Wouldn’t it need a reference to the map…? Does it send a signal? Maybe I’m more confused about the actual code implementation for these things.

I think you’re saying the same thing here, I’m just unsure how this actually… works in code.
When does a unit even need a path, in the first place? Wouldn’t a higher Game manager determine when a unit needs a path…?

A note here is that, I think the “Map” node that you see in my tree above is really just a “Game” manager, and I should probably just make it a plain Node. I think the “MapManager” in your example would be the “GameGrid” that you see. I’ll probably rename these things. It makes sense for the “MapManager” to extend TileMapLayer, right? Or… maybe, looking at the tree example you provided, maybe I really should have “MapManager” be a Node2D like this, and create a separate Game node and such… hmmmmm… I’m just getting a bit confused with all the hierarchies now.

Ha, I’m kind of in a weird spot in my programming experience where I’ve made a very complicated but small RPG game in a different engine before as my first project, got super confused after about a year, then switched to Godot and started trying to remake it, then switched to this project instead to try learning some more. I know a lot of coding fundamentals and principles now, but I just get so confused thinking of what should have a reference to what and establishing these hierarchies. I’m trying to make this game jam game to practice that.

1 Like

This depends on your game architecture and how modular you want it. For instance for me the easiest way is to ask the parent. So in this instance the unit might do something like:

@onready var game: Node2D = get_parent()

func take_a_turn() -> void:
    var attackable_tiles = game.get_tiles_i_can_attack(my_max_moves)

    if attackable_tiles.is_empty():
        ... # handle that situation 

     var best_target_tile = null
     var best_target_score = 0
     
     for tile in attackable_tiles:
           var unit_info = game.get_info_about_unit_on_this_tile(tile)
           .... # Score it somehow, compare it to best target so far, set to best target if better

Now your unit has chosen the best attack tile.

For a game jam you may want to start with the enemy ai just saying “pick a random unit” followed by “pick a random attack tile”.

Either way, you ask the parent for any information you need. And you are right, this might not be done in the unit itself. The enemy AI node would probably the best place to do this. If you have time you could then, rather than picking a random unit, you could pick the unit with the biggest health, or run through all your units and pick the unit that gives the best score. Again all dependent on your game design.

To do this with signals, your unit would send a reference to itself and just not do anything until a reply is recieved. For example:

get_attackable_tiles(self, current_tile, max_moves_per_tile)

Then wherever the signal is recieded, it responds directly using the node reference.
This pattern can become difficult to debug though.

Anyway, I hope it all goes well! Good luck and remember, there is no ‘right’ way.

2 Likes

Hmm, thanks! I suppose there are a lot of ways of doing it, huh. I’ll have to think of any sort of dumb solution that works for me to get this done on time, haha. Your input has helped a lot in general though, thank you!

2 Likes