[SUPPORT] Advanced Turn Based Tile Toolkit

Hello ,

Since I have worked with multiplayer code before, I was wondering before I get to far along before I get into multiplayer mode, if I do like a random damage and change health with a random value instead of a constant damage will that health change be sent over the internet connection? Also if I add new variables to BP_Unit and change those variables will those be sent over connection or will I have to set up variables some how or manually send those variables some how?

Thanks,
Loco

Ok, thanks. This should do the trick:

I’m guessing what you want is to prevent a tile from being moved to? If so use the MakeTileImpassable function in BP_GridManager.

Could you describe the result you are seeing? Are you getting an error message or is something else happening?

As long as the variable is replicated the change will happen on all clients. Be aware that if you are using a random value for damage, the value has to be the same for what you subtract from health and what you input into the Hurt action. In other words you have to store the result of your RNG method and input the same number for both nodes. Before jumping into multiplayer with ATBTT you should read up on some of the more general UE4 networking documentation. I highly recommend this guide by @eXi as a starting point.

@DeliciousTea is Show Inherited Variables checked? I faced the exact same ā€œproblemā€ when I first watched the video and then realized I simply needed to toggle that on :slight_smile: bp7.jpg

I should have elaborated, i have an enemy that corrupts tiles, they deal damage and are slower to enter.
When the enemy is killed his tiles should be removed but when i do that, the tiles still cost more despite tile actor being killed.

@ Fabulous! Thank you so much! I can’t imagine why that would be turned off by default…seems incredibly silly, I didn’t even think to look for a toggle.

I just posted bookmarks in the Action System 2/3 video for future reference, and I’ll probably do the same for the other videos that dont have it as I rewatch them. I’m also writing some pseudo documentation docs basically writing down everything 's saying about each blueprint. I’ll post what I have so far after I rewatch the Action System 3/3 video if it’s welcomed here.

I still think 's videos are the best material for newcomers to start learning this by the way. But if these writeups or bookmarks help anyone that wants to revisit a specific topic, but dont want to eat the entire 20+ minutes again to make sure they’re not missing anything, I’d be happy to do so. Plus ctrl+F is so fast it never hurts to have more docs, so long as they do not misinform.

Ok, I see. If you have not specified that the edges should reset their costs after the actor is killed this will not happen. One way to do this is that when your unit corrupts tiles it stores the previous edge costs of these tiles in the corruptor itself. When it is killed it loops over these stored values and reset the edges to their own values. You could use a TMap of the same type as the one used for the GridEdges in BP_GridManager. When corrupting tiles you would get the appropriate tile index and edges and add this to the corruptor’s custom TMap. Does that make sense to you?

You understood the request better than me, @. Thanks for chiming in. @DeliciousTea: It is probably for the best that inherited variables are hidden by default, as things can quickly become messy otherwise. If this should be the default option can of course be discussed.

Thanks a lot for adding the bookmarks, ! That will probably be useful for a lot of people. Truth be told I should probably be doing the same myself, but if I have the time to go through my videos and add such time points I probably have the time to start making a new video, so it is a difficult time priority. As for written documentation I was thinking of making a wiki for the toolkit soon, where I will likewise copy in and link to various support stuff in this thread. If it is a wiki then it will be open for you and others to edit as well. Sound good to you?

bp9.jpg
This is the internet ha, of course the community will tell you that. I don’t know how wikis work to be honest - if possible I think whoever is allowed to edit should always have their content Awaiting Approval, or sth alike, from you, right? People (I for example) could make a honest mistake editing sth and until you come in and correct it yourself, someone else could have read that and went off misinformed to do who knows what devilry.

Speaking of mistaken edits, if I heard you correctly what you just said was…

Yes!

It does, i am trying to do that like in the photo, but it does not work. Is there something else im supposed to do?

Haven’t made a wiki before, so I’m not 100% sure how it all works, but I think it should be a good fit. Having edits pending approval could be a good way to do things, but if it is possible it might be better to give the possibility to post freely, but to add a header signifying if I have read through it or not.

Hehe, some slight misquotation going on there, but point taken. Making tutorials on the grid arrays is the most pressing, I think, and most support request I’ve gotten in the last couple of weeks are tied to such things. It is also a prerequisite to be able to explain the big unit functionality.

Good start, but some changes need to be done. The biggest issue is how edge costs are normally handled by the toolkit. The cost to enter a tile is stored in the tiles that can enter the tile (normally the tiles that surround it). So the GridEdges TMap for a single grid index really stores the cost it takes to leave the tile, but not to enter it. To do this you need to get all the surrounding tiles and modify their edges (using GetIndexesInRange, for instance). Then you would need to store the costs of all of these tiles before modifying them so you can restore these costs later.

This is a tiny bit cumbersome, but not terribly difficult to set up. However, it becomes more challenging as soon as you have multiple corrupted tiles at the same time. Say you have two corrupter units that corrupt tiles that are adjacent. The first spawned corrupt tile modifies the edges of all surrounding tiles, which will overlap several tiles modified by the other corruptor’s corrupt tile. When the second corrupt tile is spawned it stores the current state of the GridEdges TMap for all tiles, which includes those modified by the first corrupt tile. When it is destroyed it will thus use these values for the edges of surrounding tiles, meaning that the surrounding tiles that overlapped would never be restored to their original value.

This is basically the same problem as I faced when I added big units to the game. These units modify a GridBigIndexes TMap whenever they move, and the tiles they modify in this fashion can be modified by multiple units at once, which might die or move in any order. I solved this through a fairly complex solution, where I also store the number of units that modify a specific tile and keep track of which units modify each tile in what way.

As you see in your case this creates lots of complications for what should ideally be a pretty trivial thing to do (temporarily modifying the cost of a tile). Luckily for you I added a feature a few updates ago to simplify exactly these sorts of problems. If you set bUseSimpleCosts in BP_GridManager to true and set the PathfindingType of all your units to Simple it will allow you to use so called simple tile costs. These are a single integer for each tile that represents the cost of entering this tile from any direction (zero being impassable), and are stored within the relevant grid index itself and not the neighbors. If you use the Simple PathfindingType the cost of entering the tile will be whatever is higher of the cost stored in GridEdges and GridSimpleCosts. Now for your corrupted tiles you can quite easily modify the GridSimpleCosts TMap and hopefully get the result you are after.

Since you aren’t modifying tile costs in the normal way I would recommend basing your corrupted tiles on BP_GridActor (the parent of BP_GA_Tile) and not BP_GA_Tile. I tested this out, and here is how I set my corrupted tile up:


I set the SimpleCost to ExposeOnSpawn and did the following to test if it worked:


Note that if you have already activated a unit so that RunPathfinding has already finished and then add a corrupted tile it will not affect the current pathfinding, which was calculated with the state of the edge array when it was run. To have the corrupted tile affect the currently active pathfinding you would have to run RunPathfinding again.

Hope this gets you close to what you’re after. Note that if you want your units to modify specific edges and not the cost of an entire tile you’ll have to go with the more complex solution outlined above.

Hiya ,

This might be in forum somewhere, but what do I need to set on the AI controlled units that makes every action finish before moving to next? When the AI is controlling units they attack while moving instead of attacking after move finishes, other AI units start moving before the current AI units have finished moving, etc. In my turn-based game I want every AI action complete before next starts. Unless I broke how the AI works somehow, which is possible. Maybe withPrevious, InstantDeactivate, or Immediate set somewhere in AI code?
Thanks, Loco

hi

I’m trying to get access to ā€œpossible targetā€ set from bp_ability to other ability I made, but for some reason I get noting in return. what am I missing?

cheers
leo

Good evening all,

I am new to this forum, and just an hobbiyst, I am making (trying…) a game based on that superb turn based toolkit, but I am facing the same problem with touch input.As far as I only want to make it for android, i have an issue.

My question is, how can I validate the move / attack using a button like the end turn. Hover is working fine, also the idea is we keep the position and validate with one button.

I am sorry, but I didn’t succes in making it.

If you can help, I will appreciate a lot.

Kind regards.

That is a bug and not a feature. It is likely that you are calling EndAction too many times somewhere in your code, causing the action system to skip to the next action before one is done. One of the most common culprits of this behavior is if you have set some of your unit’s animation to looping, so that the end action notify keeps repeating. That is the first place I would look.

By default, if you do not override Activate or PlayerActivate, BP_Ability uses FinVisibleUnitsInRange with the range defined in the ability to fill PossibleTargets. If you override this you’ll have to fill it some other way. What are you doing at the moment?

hi

i did override the bp_ability

i have the sprint ability which is my default ability, and its run the regular pathfinding + the FinVisibleUnitsInRange .i made another weapon ability in which i need the value from the possible targets. i dont want to run all over again the finvisibleunitinrange when i run my new ability(i modified it and its pretty big know) .the laser ability is not a good example, its not activate the same way
no matter what i do, i get null from possible targets in my new ability.
is there a way to fill it in another way?

thanks in advance
leo

If you want to find possible targets you need to run the appropriate nodes for it. No other way around it. Just add the relevant code from BP_Ability to the end of player activate in your new ability and it should work. The size of a blueprint does not matter much, and a few more nodes should not make any difference.

hi

ok i get you

but just for me to understand this - its doesn’t make any sense for me. i stored the possible targets set the first time i run the node, so why cant i pull the data from the stored set and instead have to run the all function again?
why it is not acting like any other variable that you store?is it because it a set?

anyway thanks for the help

leo

Edit 19/03/2019: Completed first revision pass with 's answers

Added time stamps to the 3rd Action video in the comments. After rewatching the Action System set of videos, here are the notes I made, in case anyone finds it useful. I like having huge docs and Ctrl+Fing around when I feel like I need refreshing on certain topics. When there’s a wiki I can start filling some of those entries with this if you wish . If anyone finds anything wrong, you’re more than welcome to correct me guys; I’d love the knowledge. I will update this post with any corrected info.

I will also take this opportunity to ask you more Qs , on what these videos talked.

Event MoveToIndex

-Is generally called from Movement Abilities.
-Will simulate unit movement behind-the-scenes and then queue an action to animate this.

Outputs:

  • IndexPathEnd is the destination index where the unit wants to move to.
  • PathsMap is the output of a pathfinding function.

Function SimulateMove

This function primarily moves the unit reference between GridUnits indices and checks for any events that happen along the way. This includes adding and re-adding units to multiple tiles for big units and calling an event dispatcher every tile move.

Outputs:

  • NewActionsCount: Keeps track of any actions that happen during movement. This can be useful if new actions are called during movement. For instance, if you want to have the unit trigger an explosive half way through its movement you cannot have the whole move action play out and then play the explosion at the end, or the other way around. With this variable you have a way of knowing that new actions have been added to the queue.

Macro QueueAction

Outputs:

  • Immediate Action: Decides whether the action should be executed and animated immediately, or stacked at the bottom of the action queue if set to false. Most actions have this boolean set to false.
  • Executing Object: Defines the object that should execute the action.
  • Action name: All actions have one. These are used to let other functions identify specific actions and decide how to act accordingly.
  • Instant Deactivate: Decides whether the action is instantly considered as ā€˜Done’ by the Action Manager as soon as it starts and if it’s okay for the next action to roll out. Usually set to false for actions that are to be animated, since normally animations would play out first and then let the game continue.
  • At Custom Index: Specifies a custom order in the queue for the action. For movement, where queuing the movement action is called after SimulateMove (where other actions might be called), but you want movement to start before these actions trigger, this is handy.
  • With Previous: Decides whether this action should be played simultaneously with the one before it.
  • Activation Delay: How much time should be waited until this new action is activated, from the moment the previous action ended.

Event AnimateAction

-This event is generally called from the Action Manager. When an action that needs to be animated comes to the top of the Queue, a signal is sent to the Executing Object (defined in the QueueAction macro) to execute this event.
-This event is contained within BPI Action. Therefore, every BP that should be animated by the action system should have this BPI added.
-This event is multicast, meaning it is run on every client and should automatically work for displaying correctly in Multiplayer.

*-*In order to prevent logic errors and player cheating, it is a good practice to not include any Grid Manager-centered functions in the BP Units, like pathfinding math. If a unit is killed, for instance, it is instantly removed from the GridUnits TMap. If you then try to access this index during the action where it is killed, it has already been removed, causing the code to fail. If you only work with variables that represent the ā€œphysicalā€ game world, such as locations, actors etc. you can be sure you never have this sort of mismatch. Also, since game logic stuff, such as most of the grid manager stuff, is not replicated, accessing these variables will fail for clients in multiplayer.

Event AnimateMovement

-Units move through a set of spline points that are generated by the Set Spline Points function, which does so using the given path locations.

Action Manager
Event(s) Multicast & InitializeQueuedActions:

-These events are called periodically throughout the game, generally at the end of the unit’s turn.
-It empties the temporal array filled by QueueAction macros, sending it to all clients and appending it to the CurrentActions array. After this, each client then starts the first action and plays through them in turn.
-There are two events because the first part of the event (which sorts through actions and stores them in lists) only works server-side, so no need to call it client-side.

Event Dispatcher onUnitEndMovementSimulate

-An event dispatcher that activates when any unit ends its movement during simulation. It is called at the end of the unit’s movement, the Simulate Move function.

Other questions:

8-(Video 3/3; 14:45) You use GetActorLocation to obtain the Teleporter’s coordinates, and later on (18:40) the Grid Manager’s Grid Locations map, to obtain the grid index’ center point. Isn’t it faster and simpler to use the Grid Manager’s ConvertIndexToLocation function for this?


: Finding locations in a map uses UE4’s built-in C++ functions and is really fast. Certainly faster than my blueprint function.

9-(Video 3/3; 16:47) Why are only Copies available for the array elements of BP Units, and not References too, like with a vector or integer array?
: This is a C++ thing. For some types of variables it is preferable to create a copy in memory instead of pointing directly to its memory address. Epic have chosen to only allow passing variables referencing blueprints as copies. This is not something you can change in blueprints, but it will generally not matter.

10-Grid Units is a Map array inside Grid Manager. It contains only the grid indices of units. This includes dead units too, right?
: No, they are removed from this array immediately on death. To the grid manager game logic a dead unit means a unit that is neither in the GridUnits TMap or the InitiativeOrder array. If a unit was kept in GridUnits after death, AI units would still try to target them with abilities, and units would not be able to move into the tile they died, for instance.

You should only have to run it once, so if you are not overriding the Activate or PlayerActivate events calling these nodes again should not be necessary. Maybe you could post a pastebin of the Activate nodes for your custom ability?

Much appreciated! I’ll go through your points and see if I want to make any interjections.

Yup, you got it right.

I guess I haven’t covered this one yet. Might be good to do when I make my pathfinding tutorial. The basic purpose of this function is to move the unit reference between GridUnits indexes and check for any events that happen along the way, including adding and re-adding units to multiple tiles for big units and calling an event dispatcher every tile move. NewActionsCount keeps track of any actions that happen during movement. This can be useful if you have new actions that are called during movement. For instance, if you want to have the unit trigger an explosive half way through its movement you cannot have the whole move action play out and then play the explosion at the end, or the other way around. With this variable you have a way of knowing that new actions have been added to the queue.

Well explained. For AtCustomIndex, by default an action will be added to the end of the array of actions. If you specify a custom index you can put it earlier in the ordering of actions. For movement, where queuing the movement action is called after SimulateMove (where other actions might be called), but you want movement to start before these actions trigger, this is handy.

Yep, all correct.

Not really a problem for performance. There are a couple of issues with doing game logic type stuff within actions. This stems from how things happen asynchronously. If a unit is killed, for instance, it is instantly removed from the GridUnits TMap. If you then try to access this index during the action where it is killed, it has already been removed, causing the code to fail. If you only work with variables that represent the ā€œplysicalā€ game world, such as locations, actors etc. you can be sure you never have this sort of mismatch. Also, since game logic stuff, such as most of the grid manager stuff, is not replicated, accessing these variables will fail for clients in multiplayer.

This was lazy of me and I regret doing so, as it is teaches bad practice. As long as you have a perfect overview of how server-side stuff is handled by the toolkit you can get away with using it in actions in some cases for single-player games. That is if you can be sure it will not be manipulated in such a way that there will be a mismatch. I would generally recommend against this, though, and should not have done so in a tutorial.

Yep

6-Why is there a need to have two events here? The Multicast event is never even called by other BPs so there is no need to separate it as a function from initializeQueuedActions. Is this due to the way Multiplayer needs client & server communication be written, or just an author’s coding habit?
[/QUOTE]

There are two events because the first part of the event (which sorts through actions and stores them in lists) only works server-side, so no need to call it client-side.

Indeed. Useful if you want stuff to happen at the end of movement.

Finding locations in a map uses UE4’s built-in C++ functions and is really fast. Certainly faster than my blueprint function.

This is a C++ thing. For some types of variables it is preferable to create a copy in memory instead of pointing directly to its memory address. Epic have chosen to only allow passing variables referencing blueprints as copies. This is not something you can change in blueprints, but it will generally not matter.

No, they are removed from this array immediately on death. To the grid manager game logic a dead unit means a unit that is neither in the GridUnits TMap or the InitiativeOrder array. If a unit was kept in GridUnits after death, AI units would still try to target them with abilities, and units would not be able to move into the tile they died, for instance.

Thanks, I’ll fix that in the next update.

Thanks again for doing this. Among other things it helps make it clear to me what subjects I have covered well and what I should revisit in later tutorials. If you don’t mind I’ll post a link to your post in the description of the relevant video.

Thank you for the fast response . It is my pleasure to put more of my free time in this when you’ve worked so hard in building this intricate system of logic. I will edit my previous post with the corrected info after you answer this one and I get some sleep. Feel free to link to it too.

This makes so much sense! That is very thoughtful of you.
1-Continuing with this example, if the unit stepped over an explosive, the explosion animation of this new action should probably be Queued with the Immediate Action boolean turned On, right? be queued normally and then have the Move Action be Queued with the Custom Index so that it animates before it. If the explosion’s Immediate Action boolean was true, it would play way before the unit moved. Maybe it should have the With Previous boolean set to True though? Now I’m wondering how to sync those two actions correctly haha, I think I know what the next challenge will be…

If I understand this correctly, continuing with the explosion example, normally the chain of events would be this: Simulate Move>Unit Reference steps over Explosive>Explosion Animation is Queued>Simulate Move ends>Move Animation is Queued. And because we do not want our explosion to set off before our unit animates its move, we Queue the Move Action with the Custom Index so that it activates first.
2-I have to ask though, what exactly prevents the Explosion Action from just activating? I can only think of using the Delay parameter, but that would be its own can of worms when considering dynamic distances I’d think…Even if the Move is set at a Custom Index before the explosion occurs, this is Queued way after the explosion Queue Action macro was called!

If possible I’d love to see a new vid on doing the same teleporter but ā€œthe correct wayā€ (:
3-Speaking of bad practices, could the way Teleport Event was implemented be considered as such? AFAIK it is being executed every time a unit ends a movement. Even if it doesn’t get past the first check, is this not inconvenient, or does Blueprint’s speed make this matter irrelevant?