In the previous part we have left off after developing a functioning action queue - for our turn-based logic. This time we are going to make a use of it and add some moving NPCs to the game.
Final code for this one is on the branch here: https://github.com/maciekglowka/hike_deck/tree/part_04
NPC Spawning
Last time we have already created an Actor
component that enables certain entities to perform actions within the game loop. Our only unit so far has been the player character, that is directly controlled by us (in terms of assigning the actions). Obviously, for the AI characters we are going to need a different approach when planning their movements.
Let's start by first defining another component (in the pieces
module):
// movement behaviour for non-player pieces
;
As written in the comment, we are going to spawn this component on the AI entities only. It will tell our systems that those units need to have their walk moves planned.
Before we start with our NPC movement systems though, we need some temporary units to work with. So let's turn the pieces
module into a Bevy plugin (remember to register!) and add a dirty system that will create two of those:
// pieces/mod.rs
use *;
use crate Position;
use crate MainState;
use crate Vector2Int;
;
We are going to revisit the units spawning later - after we make our dungeon generation. Now for the testing purposes it should be enough.
If we run the game we should see two question marks spawned on our board - the new units :). We have only modified the logic layer of our game, but they still get rendered. It's because our graphics system (that we created for the player) is generic enough to pick also those. Our code separations are starting to pay off :)
If you want you can assign different sprite indexes for the "NPC" piece kind if you want (in the graphics module)
Action planning
We can still move our player, but the NPCs stand still. No wonder, they are not even added to the ActorQueue
yet!
As you may remember from the previous part, we are initializing our queue in the player input system:
queue.0 = from;
(the entity
here is the player object)
So, now we have to create some system that would add all the NPC actors to the queue:
We are going to schedule this system to run after the player input, so we assume that the ActorQueue
resource already contains a single element Vec with the player entity inside. I am going to place this system in the actions/systems.rs
file and register it like so:
.add_system
As you can see the queue will be updated every time we leave the PlayerInput
state - so once per turn only.
The scheduling will be different though for the actual NPC movement planning. We will not create all the movement actions for all the units at once, at the beginning of the turn. As the subsequent actions might change the board layout, we will plan each NPC's move only right before it's sub-turn - using the current state. It might not matter now - as the units do not interact with each other. However, as we develop more systems later, it will be important that we for example do not plan some interactions with a unit that has been just killed in the previous move.
That's why we fire the NextActorEvent
at the end of each successful queue processing - to trigger running of the movement planning system - for the next entity in the queue.
This system is also very simple. We just try to take the first entity existing in our queue and than query for it's Position
and Actor
components. Next, with a help of the rand
crate, we pick a random Vector2Int
direction. ( ORTHO_DIRECTIONS
is just a 4 element array that we defined in the vectors
module, containing all the cardinal directions). Finally we create a new target position for the unit and construct the WalkAction
. It is then injected to the Actor
component.
We register this system in actions/mod.rs
like that:
.add_system
And because of all the heavy work we did last time - that should be it! If we run the game now the NPCs should perform random moves, one at a time and after the player.
In the next parts we are going to create some more action possibilities for our units - like melee hits. We will see how we can easily extend our planning phase by simply adding parallel systems that plan different kinds of actions.