Hey, glad you figured out something that worked for you. It can be done a lot cleaner and more efficient, though, and I think you will run into some issues once you introduce walls etc, since MakeTilePassable will not take into account surrounding walls etc. when it adds new edges. Removing and adding back all those edges each time you run pathfinding is excessive, and I would recommend just modifying the pathfinding directly. I am going to suggest a specific solution, but I need to clarify one thing with you first. What should happen in this sort of situation:
Would you want it to work like this (how it would work with your current setup, based on the rotation of the unit):
https://i.imgur.com/YIdGZkM.png
Or something more like this (based on which tiles are behind if we say the start of the game board is on the bottom left):
It’s a pity, but this words are not giving me enthusiasm . However, it’s your asset, you know best.
Indeed. But, I have, at least, one idea how to fix it in my project (the situation you’ve described, I think, simply can’t happen because of the project’s specific, but I have one bug, yes).
I’ll let you know tomorrow, how it will be look alike, alright?
PS: Sorry, but now I have 1:30 after midnight, and I’m dying for sleep.
Sorry, it was not my intention to be disparaging in my comment. You’ve put a lot of great work into this, and you are trying to modify some of the most complex parts of the toolkit and ones that I have not done a great job making tutorials for. The fact that you have something that is more or less functional is impressive, and I’m sure it has helped you understand the toolkit a lot better. But like you said, it is my asset and I have worked on it for many years, so I tend to have some pretty strong intuitions about the best ways to modify things.
Still early in the afternoon here Let me know when you can explain things a bit more (my question with the two pictures) and I will try to give an explanation where I both give you a suggestion and also try to explain the specific reasons why something might or might not be a good idea.
You are right to be overwhelmed. This is some of the most complicated stuff you can do with the toolkit. AI is one of the most complex thing to work with in any turn based strategy game and having to deal with units occupying multiple tiles is a complexity multiplier that makes it more difficult to add almost any feature. That is why I generally recommend people not to use big units unless their game depends on it. There is a reason XCOM 2 chose to drop the multi-tile units they used in XCOM 1. Too much hassle.
But I am up for the challenge. I added the big unit functionality to the toolkit, so I’ll have to live with the consequences. I will have to do this in steps, though, since this is no small task. First we have to add a knockback ability, then we have to make it work with the AI and then we need to make it work for big units. So I’ll start with the knockback ability. You can try to reproduce what I make and see if it behaves how you want it to. If so I can proceed to the next step.
Ok, so let us add the knockback ability. First lets implement the game-logic side of knockback. In BP_GridManager I have created a new function called FindKnockbackIndex. It takes a target index (where the target of our knockback efffect will be standing), a source index (where the knockback is coming from) and a distance (how far do we want to knock it back. This function finds the resulting GridIndex we want to knock back the unit too and returns if it is valid (if the tile does not exist or is already occupied we return false):
https://i.imgur.com/VxllW2v.png
Ok, next we want an action for displaying the knockback effect. I’ve decided to add the action to BP_Unit_Anim. Here is how it looks:
https://i.imgur.com/zODc8HS.png
The timeline is as follows:
https://i.imgur.com/DLnHFmj.png
Then I create another function that handles calling the Knockback action and moving the unit on the grid:
https://i.imgur.com/GIGnyUU.png
Next I create a knockback ability. What I have done is to duplicate BP_Ability_Move and create a new ability called BP_Ability_Knockback. You will get two compilation errors here where you need to delete and add back the event dispatcher calls for it to compile.
Then in the CheckIfValidTarget function (checked when a tile is clicked), I return valid if the tile is empty OR if it contains a unit that will be knocked back to a valid tile after we move onto the unit:
https://i.imgur.com/9lVCQM0.png
I also decouple the part of ServerHover that prevents a path from being displayed if there is a unit on it:
https://i.imgur.com/KM3TGRb.png
Finally, in ExecuteAbility I set up code for calling movement and knockback as appropriate:
https://i.imgur.com/Ze8vVwE.png
I add a couple of units to the map, set their default ability to the knockback ability, and there we are. Pretty basic, but a starting point:
Thank you. It’s great to watch GIFs, and it can correctly identify the direction of the attack.
This is also the part I am studying recently. I will look at the blueprint again, because it’s late at night and I have to go home.
My idea is a turn-based Soullike game, so there will be only one enemy of the big unit, so finding enemies based on the big unit is even more important
If there is only one big unit enemy, would it be simpler:D
I found that the function “find big indexes from indexes” is very useful, through which I achieved the “fear effect”, but there may be conflicts in the “search and add adjacent tiles big” (post two days ago), so I don’t Determine whether there are other association conflicts, and not sure whether it can take effect in AI, so come for help
It seems that I should “BindOnUnitEnterTileSimulate” check for collisions when moving? (I want to add an effect status to them when they collide)
unitA is the unit of being repelled. This is not his round.
If i want him to move ,will be an error in “run pathfinding”
Thank you again, Originally, I didn’t want to trouble you anymore, I wanted to modify it myself, but I found that the two types are quite different.
this type of knockback can also be applied to the movement of the monster as a side effect (the monster knocks back all units on the path during the movement, The same is to check the unit on the moving path)
haha, just when I thought it could get more complicated you also want to have actions within actions Hmm, in this case I would consider a slightly hacky soluton to avoid it becoming too complex. I would not queue the knockback actions (but do all the stuff affecting gameplay logic). Then I would add some overlap collison to the big monster unit and the smaller units (enabling collision on all units that will be knocked back before the movement action is animated) and animate the knockback animations as the collision volume of the big monster overlap the smaller units. You could store the knockback location within each unit, probably.
I’m not sure I understand your figure. “type 1” is the one I made, right? I don’t understand the other one, thoug. Is Unit A moving onto unit B and then bounces off?
Haha, yeah, I did a lot of actions in the action, and I try to make the sequence of actions in advance
I feel that the queue type “immediately” will bring some problems
But it’s still working well
Yes, you did is TYPE 1. Your understanding is correct, that unitA collides and bounce with unitB during the process of being forced to move to position 8 due to being knocked back (assuming knock back distance 7,knockback index 8)
Found a bit of a oversight in GetTileInDirection in BP_GridManager_Hex
It doesn’t add the grid index to the retun node, meaning the return is always just a base edge integer. Fixed easy enough by just adding a int + int node right before the return node, but it did mess me up for some time
If anyone else runs into the same, images below show fix.
sorry, but I’m still not sure I understand how this is different from what I did. are there two different types of knockback in your figure or just one? Perhaps it is simplest if you show two pictures from the toolkit. One of where the units stand before collision, one of where the player clicks and one which shows where the units should end up afterwards.
■■■■, sorry about that. I’ll make sure to fix it in the next update. That function is actually never used anywhere in the base toolkit, so this omission escaped my notice. Thanks for letting me know, and sorry for the headache!
ok, I think I understand now, but to be able to code this I need to know about what should happen in special cases. What if A stands on [1] and B stands on [2]. Then a third unit (C) moves on top of tile [1], repelling unit A. However, this is blocked by unit B. Because of this unit A wants to remain on tile [1], but now unit C is standing on that tile, so what should happen?
Sorry, there was an unexpected problem:eek::eek::eek:
Under the general rules, in order to avoid this situation, the unitC that caused the knockback effect is standing in place and will not occupy the position of A.(like <into the breach>)
However, this is the trampling effect when the monster moves, so it will enter the position of other units. Maybe unitA can’t stop because of the collision. I will add a knockdown effect(status effect,Already done) to unitA and B when the collision occurs.So there is enough safety distance
The main difficulty is the movement caused by the knockback, and the collision check;)
Maybe I have a better plan in the future, and I can also modify it
Hey , I hope you are doing well. Got a good progress in the last weeks
Just a short question. On a normal squaregrid when a tile is impassable and I make it passable at runtime (cost1) units will avoid that tile while pathfinding. So it looks a bit strange on the map. Can you please point me where to fix that? As the costs are always 1 currently. I have added a screenshot to clearify. 3007 was impassable then passable again.
Ok, thanks for the explanation. This is a mix of game design and programming, so we’ll have to think about all special cases regarding the rules to make this work as you want it. I’ll continue to suggest things one step at a time, so I do not waste both of our time by trying to solve all the problems in one go and potentially giving you something that is different from what you have in mind.
So this time let us start with knockback that stops if it is blocked by a unit or terrain. I have made a new knockback function that checks tiles in a line in the direction of the knockback and returns an array of grid indexes representing the path of the knockback:
https://i.imgur.com/jfJBtIf.png
Then a couple of modifications to the code we added last:
https://i.imgur.com/cCtELbk.png
https://i.imgur.com/4MxFmIT.png
Now knockback is stopped if blocked by units or there is no edge between any of the tiles on the path (meaning that the tiles are not connected, likely due to blocking terrain):
If you get this working and it works as you expect we can move on to the next step.
Hi Infest, glad to see your game is progressing! Looks great from the screenshot I’m assuming you’re using the MakeTilePassable and MakeTileImpassable functions. These functions are a bit “stupid”, in the sense that they don’t have much information about the map and are a bit limited in their usefulness.
MakeTileImpassable removes all edges heading to this tile from neightboring tiles and removes all edges from the tile itself. This function is generally unproblematic. However, MakeTilePassable works a bit unintuitively. It gets neighboring tiles and tries to add edges both ways to those tiles, provided these tiles already have at least one edge. This rule means that if you make several adjacent tiles impassable and then try to connect them using MakeTilePassable you might get some incorrect results where tiles are not connected because none of them have any edges. The function will also ignore things like walls etc. and connect the tile to its neighbor no matter what.
I have added a more flexible and intelligent variant later called UpdateTilesInRange, which essentially runs the grid setup code during runtime in a specified area, taking into account wall collision, height differences etc. (though not tile actors). You can use this instead. If you do, make sure you set the range of the function so that it includes the neighbors of all tiles you have modified.
However, if you have a game where you are planning to make tiles passable and impassable frequently (and globally, not just one edge, but for the entire tile), I recommend looking into a “hidden” feature of the toolkit called Simple Edges. If you set bUseSimpleCosts to true. This populates a grid sized map variable in the grid manager called GridSimpleCosts, which hols global move costs of entering a tile. All tiles get a cost of 1 at startup. If you set a tile’s simple cost to 0 it becomes impassable.
This makes it much easier to work with blocking and opening up tiles (and adding/removing difficult terrain) for games that don’t need to fiddle too much with individual edge costs. After enabling it you also need to set the pathfinding type of your units to simple. The regular edge costs will still function as normal, but if there is a difference between the regular and simple costs of entering a tile, this pathfinding type will use the highest cost of the two (blocked being the highest possible costs).
If you enable simple costs you can even use regular BP_GA_Tile actors to modify it. They have a SimpleCost variable that will be used if this is enabled in the grid manager.