[SUPPORT] Advanced Turn Based Tile Toolkit

Both of the features you are asking for are actually already part of the toolkit. Finding all targets that could possibly be in range, taking into account move range, can be found in BP_Ability as the FindAllPossibleTargets function, which itself calls on several functions in BP_GridManager to achieve this end. Take a look at AssessAbilityValue in BP_Ability to see how it is set up.

The answer to your second question can be found a few nodes further down in the same graph. Namely the EvaluateMoveTargets and GetPregeneratedTileValues functions in BP_Ability. EvaluateMoveTargets is a function meant to be overridden by the specific ability you are using, where you can change the paramaters that determines how much value to assign to moving to a tile. Whether an AI unit decides to move to that tile then becomes a factor of the value of the tile and the value of the targets that can be targeted from that tile.

In the advanced example map JungleRaid I do something a bit more fancy, where I use the ability system to generate a safety value of each tile based on the cover and distance from enemy units. It is also set up so that it only needs to be set up once each turn for AI units that have multiple different abilities, by storing the values temporarily in the unitā€™s ability system.

AI is one of the most complicated topics in turn based strategy game design, and are thus some of the more complex systems in the toolkit. So let me know if you get stuck or have any questions.

Cool, FindAllPossibleTargets could do the trick. By default It gives unit indexes but looking at it I think it would be relatively easy to modify to give tile indexes.
I feel stupid missing that but a lot of changes have been made in the updated version with multi tile units and Iā€™m still following the changes.

Well Itā€™s a little deeper than that :). To give a value of a tile to AI (which is not that hard) Iā€™d need to get that value from somewhere first. In Jungle raid itā€™s based on pregenerated cover array and distance to enemy units. In my case this is where FindAllPossibleTargets (tiles?) comes in (or crudely modified Pathfinding alternative that I made). Iā€™m checking each enemy pawn to see if it is able to attack this specific tile. If yes then danger value of the tile == the damage value of that pawn/or a sum of damage values in case multiple pawns are able to reach the tile.

The thing is my project is based on Jungle Raid but it is melee, not ranged - with different weapons equipped by pawns (each weapon activating a specific set of skills) different types of armor protecting from specific types of damage etc. And typical attack range is 1-2 tiles. So thereā€™s a need for more complex calculations for AI to keep units in line and not rush into enemy-controlled areas with poor armor and weak weapons. Also a protection bonus comes not from the covers but from friendly pawns at adjacent tiles.

Ok, if I understand you right you need to account for both the attack and move range of enemy units to figure out what tiles they threaten. To get perfect information on this I think you would need to run pathfinding for each separate unit and then find tiles in range for each of the tiles they can reach. Those are some very heavy calculations indeed, so lets try to think of ways to reduce this.

One very quick way is to simply GetIndexesInRange around each enemy unit, with a range equal to its move range plus its attack range. This will get you a fairly close heuristic for very open maps, but it would quickly break down if the map has a lot of obstacles, so this is unlikely to be what you want.

A better way to approach this, if your enemy units have roughly the same move and attack ranges, is to run a single pathfinding where you input the grid indexes as each enemy unit in the Open List of the pathfinding, and run the pathfinding with a move set to the average (or slighly above average) move range + attack range. Any tile found by the pathfinding will on average be attackable by at least one enemy. This will still not give perfect information by any means, but is super quick. Generally AI does not need to be perfect for the AI to provide a challenge and feel like it is making informed decisions.

A step up from this is to do the same thing, but separately for each unit. We are still not using FindTilesInRange for every single tile each unit can move to, but instead increasing its move range equal to its range. You can even alter the last steps of the pathfinding to emulate finding tiles in sight range by creating a custom pathfinding type that uses GridBaseEdges instead of GridEdges when finding connected tiles. This should give you pretty good results if you do not have units with attack ranges that are likely to have sight range blocked, but can potentially have some issues if your levels are such that there can often be ā€œislandsā€ within the pathfinding range that can be attacked but not reached through movement. If this is an issue it can be improved by after running the first pathfinding equal to move range, you run a new one that takes the indexes from FindPathfindingEdge as the first grid indexes in the open list and continues movement (using the GridBaseEdges suggestion) from here.

Those are the suggestions I can think up on the fly, at least. What can be done depends a bit on your game. How long are the longest move and attack ranges of your units? What is the size of the largest maps and how many units is the maximum amount that would be in one level? Are you using multi-level grids? Do you have many tiles that block sight on your maps? Are the maps single or multi-level? All of these factors will determine how far you can go with your algorithms before performance starts to become an issue.

You can of course consider making C++ functions for this AI search instead, though if you use a lot of line traces to check for sight, these will have a bigger impact on performance than the extra overhead from blueprint will.

Ok, the first two ways are simple enough but Iā€™m not sure if I got the third right. Are you suggesting we should run Pathfinding (maybe customized) for each unit separately? Iā€™m doing something like that now.

As to the details. In my case the average move range is 4 tiles per turn (more likely 2 tiles per turn if you want to move and attack in the same turn) and attack range is 1-2 tiles (except light ranged units but those donā€™t do much damage and itā€™s ok to leave their danger calculations distance-based as it is now). Maps are single-level and no more than 20x20 tiles. There arenā€™t many obstacles but a number of units can be pretty high though, at around 16 units per side, so the units can be obstacles themselves, that is why Iā€™m thinking that GetIndexesInRange is not enough.

I had a couple of ideas to speed up the process too. First is to check for danger only the tiles that can be reached through move by a current pawn. Second is to exclude enemy units that cannot attack those tiles by any means. Third is to run pathfinding only for a limited number of pawns (letā€™s say 4) that are closest to the current pawn/or move target tile. It could also emulate imperfect human thinking. I think so at least.

I agree that AI doesnā€™t need to be perfect but Iā€™d like to test how itā€™ll work at least. Currently Iā€™m getting perfectly accurate values by running customized Pathfinding for each pawn as I described before. I just donā€™t know how slow Itā€™ll be in the end. At worst I have an option of returning to default AI settings of the toolkit with a few light modifications as the units seem to behave themselves fairly good and I can even lose sometimes when the odds are even in terms of numbers. :slight_smile:

Ok, the limited move and attack ranges and having a single-level map makes this a lot simpler. I did some quick tests, and running pathfinding with a move range of 2 and a sight range of 2, using GetIndexesInRange to find all tiles in range for each found pathfinding tile, looping over 16 units in a worst-case scenario takes around 0.2ms on my computer. This means you can do this safely without needing to do anything fancy. You can probably also throw some ranged units into the mix without any issues. Come to think of it, for units with a range of 1 you do not need to use GetIndexesInRange, but can instead just add 1 to their move in the pathfinding function, and you should get the same result.

If you instead of GetIndexesInRange use FindTilesInRange, that uses line traces to check for visibility, weā€™re up to 3ms for 16 units, at which point you would skip a couple of frames. If you want to go for this you could split the loop up over multiple ticks with my ForLoopPerTickMacro, of course.

hi

if i may ask, how can you get how many ms takes to a specific function to run?

leo

The best way is to use UE4ā€™s built-in profiler. However, I often use a lazy custom solution I made when I first developed the toolkit for quickly comparing stuff while I am developing. I use the DebugPrintElapsedTime function in BP_GridManager. It displays the time in milliseconds since last time you called the DebugPrintElapsedTime function. So to test a single function I would first call it right before the function (setting DoNotLog to true, since there is nothing sensible to log for the first call) and then another one at the end. It breaks down for delays over a second, and if it outputs negative numbers add 1 to get the correct result. Fairly hacky, but it is quick and gets the job done. Note that packaged builds are faster than Play Standalone which again is faster than Play in Viewport, so keep that in mind when testing. If you package then you are doing so much work that you might as well use the profiler, but I advise at least testing in standalone mode.

hi

thanks !!! yes i am using the profiler, but the truth is that i am bit lazy to go every time when i want to test something and use the profiler. your solution sounds great for quick testing, i will give it a go.

hope everything is going well with your phd!

leo

Thanks! Yeah, Iā€™m often in the same situation, so this function is very useful for quick testing. You can also chain multiple of them together if you have a sequence of functions or nodes where you want to find out what part of the execution chain takes the most resources.

Hello , noticed on trello you did some more work, awsome but did you forget what is on the ā€œCurrent WIPā€ ?..bah hahā€¦how is it going with your Phd ? thanks for the latest update also!

Iā€™m on the end stretch now, so Iā€™ve actually been able to avoid working on the toolkit. Hard to stop my brain from working when I take a shower or when Iā€™m trying to fall asleep, though, and I make sure to enter such ideas into the trello. So the additions are not reflective of work, but rather future plans.

Hi, is it possible to run around freely until you engage in combat and then the game becomes turn based, like in Mutant Year Zero? Thanks

EDIT: Just found out about the free roam feature, but how do I activate it?

Hi there, sorry for the last answer. Turns out I must have deleted the free roam ability by accident when auto-clearing unused blueprints a couple of updates ago. I wasnā€™t satisfied with just migrating it over from an older project and also made some improvements, now allowing for changing move direction before the unit has reached a target. I have added this ability and also removed some debug nodes that was still in BP_GridManager. For those here who do not use the FreeRoam ability there is no reason to update the project, but make sure to delete the event that is called by pressing X in the event graph of BP_GridManager, or youā€™ll get some weird behavior if the player presses X.

Epic usually take a few days to process a new update so you can add the ability directly by downloading this file and adding it to you project in [Project Name]/Content/Base/Abilities.

To enter free roam mode, activate the Free Roam ability, with the unit you want to use it selected in the input. To exit free roam and activate the unit as normal, call SelectUnit on the same unit. Here is some debug code I added to the player controller to quickly test this, though you would want to call it server side somewhere else as part of your gameplay logic:

https://i.imgur.com/RhqvOzs.png

Edit: Forgot to add the link I sent you in the PM while fixing this for the benefit of other developers. We recently discussed various ways of implementing free movement a couple of pages ago in this thread. Head over hereto see the discussion.

Thanks a lot for the help :slight_smile:

EDIT: Youā€™re obviously busy so Iā€™ll stop spamming your with PMā€™s, instead Iā€™ll post here, then maybe others can benefit from the posts.

Hi, sorry to bother you again, but I donā€™t have a [Project Name]/Content/Base/Abilities folder instead I have a [Project Name]/Content\AdvancedTurnBasedTileToolkit\Core\Abilities folder. Is that the right one?

When I copy the uasset file into the folder via file explorer i canā€™t see it anywhere, even after restarting the editor. Any ideas?

Also Iā€™m having a really hard time understanding the asset. It seems that all the videos on YT are for an older version as there are a lot of differences in the naming/setup.

Iā€™m, trying to create a new project/level based on the advanced template, but when I add a new terrain to the scene I can no longer play in editor (nothing happens when pressing play). How do I properly add a terrain to the scene?

Thank you

Yes, the second project path is the correct one. That should be enough, but no longer necessary. The hotfixed version that includes the Free Roam ability is now live.

Many of my videos are indeed out of date. A lot of the same principles still apply, though there are also some deeper changes. I intend to make new beginner-facing tutorials, but I have put it on hold until Iā€™m done with my PhD (in a couple of months). Until then, try to make use of the tutorials as best you can and ask here if you run into any problems.

I would not really recommend starting with the advanced template when you are learning the toolkit as it is, well, advanced, but up to you. It should work well with any terrain in theory. This is provided the terrain overlaps the grid manager, that it is not higher or lower than the values designated in MaxGridHeight and MinGridHeight in BP_GridManager and the terrain blocks the PathTrace collision channel. Have you done all of this and still having issues?

Hi :slight_smile: I canā€™t see the hotfix anywhere (no updates in the launcher). I got the terrain working by moving it up a bit over the grid manager. Thank you

Sure you have the latest version (and UE4.21)? It works at my computer. The exact path at my computer is C:\Users[user name]\Documents\Unreal Projects[Fresh ATBTT project name]\Content\AdvancedTurnBasedTileToolkit\Core\Abilities. The ability is called BP_AbilityBase_FreeRoam

Happy to hear you got the terrain issue solved, though. Do you now know what you had set up incorrectly?

Ahh, Iā€™m using UE4 4.20, thatā€™s probably why the uasset file doesnā€™t work :wink:

The problem was that I had the new terrain above the grid manager threshold, so it didnā€™t work.

On another note, Iā€™m trying to create a rather large grid for my rather large game world, and am running into issues with the ā€œinfinite loop detetctedā€ when generating the grid tiles. Iā€™ve read that this can be solved by increasing the iteration count, but that doesnā€™t seem like the best way to handle large worlds (at least for me), and Iā€™m pretty sure that it doesnā€™t work with level streaming (I may be wrong here). So my question is this, wouldnā€™t it be better and more flexible to generate the grid around each player per turn? Say, maybe just generate a 20x20 grid around the player pawn on each turn, or am I missing something obvious?

Thanks

Iā€™ve been slowly but steadily putting more hours into this real-time-to-turn-based projectā€¦Thereā€™s something Iā€™m wondering now about the camera: if I wanted the same camera controls (Zoom, Pan, Rotate) for both gameplay situations, what would be an elegant way of solving it?

I see that they way its set up now, the PlayerController detects the input events, and triggers in the GridCamera BP other events which execute these actions.

For my real-time moments, right now I have a camera parented to a SpringArm in my character blueprint (, in the future I will follow your advice and turn this character BP to an Actor BP that spawn a character and attaches itself to it, like you mentioned in post #2374]([SUPPORT] Advanced Turn Based Tile Toolkit - #2412 by Monokkel - Marketplace - Epic Developer Community Forums)). I was trying to copy the events and setup, but I was wondering if there was a more intelligent way of doing this rather than copying the same code, and creating the same variables, twice.

Anyone whoā€™s willing to share their thoughts on this would be super appreciated! Cheers.

Ok, that explains the missing ability. Whenever I release a new update of UE4 it is always for the newest version. That is the version I use when developing and it is not possible to convert projects to older versions of UE4, so updating multiple versions would be too time consuming. Hope the free roam ability gives you a good starting point to achieve your intended goal.

As for large maps, I would generally recommend calculating at startup. The map generation stuff are some of the heaviest operations in the toolkit, so if you want to run a limited version of them every time you run pathfinding etc. it could quickly slow the game down. You could increase the iteration count, and if it is possible to reset this to the normal value after the first tick of the game, there arenā€™t many drawbacks to doing it this way. Another possibility is to run the startup function over multiple ticks using my ForLoopPerTick and ForEachLoopPerTick macros. It will take some reordering of things, but should not be terribly difficult to do.

How large a map are we talking here, by the way, and what options are you using (multi-level, trace for walls etc.)?

Any reason you canā€™t just use the grid camera in both modes of play?