Heartbeast makes use here of a singleton for the player stats which I personally am not fond of so I will continue doing this with components!
So first we add the health component to our player with 4 max health:
with this we also need an "on_message" function on the player controller:
next we add a hurtbox to the bat:
and lastly we handle the collision of said hurtbox with the player:
the only issue now is that the player basically instantly dies but we can easily fix this by increasing our iframe which Heartbeast also implements in this video (but we did this earlier to solve an issue I had :D)
First we will also try to render a simple text for our current health value so first we send a message from the health component to the object via:
which we then send to the UI from inside the player controller:
Everything wired up time to create the health UI:
and next a health ui gui script with the following content:
When we receive an "update_health" message we just update the text, can't get any simpler!
And lastly we also set an initial value otherwise the thing will stay at 0:
Inside the health component we just send an updated health message in init once:
and now the UI updates as expected :)!
Next Heartbeasts is using a textured rectangle for the hearts inside Godot which we obviously do not have here so we have to solve this problem completely different. This could probably be much better solved with a shader but since I wanted to keep this simple I just added 4 hearts and will toggle them full/empty via code:
Next we just need the code:
and now we have a proper health UI! If you want to have more max hearts all you gotta do is create more boxes, a bit cumbersome but realistically good enough :).
For some reason the last bit of this part was a tree so lets make one:
and some shadows to everything and we are done with this part! :)
First we implement the required math function from Godot which is very simple this time:
then we setup a new collision shape on our bat:
Important part here that the group and mask are both "soft_collisions" and then instead of adding a new component since this is fairly trivial we just integrate this into our bat controller:
what we did different here, we just added a small nudge to the current velocity when the bat collides with another bat (or something else having said collision group) and that keeps them nicely from overlapping with each other
First we set up a new game object for our camera which contains a camera component and a camera controller script
Since this is pixel art I also set the zoom to x2 and made the camera use an orthographic projection.
Next in our script:
we define a property which lets us follow a game object since the camera is not associated with the player which will give us more flexibility on what to do with it.
then we tell turn the camera active and lastly
we get our position, the followed game object position and use that objects position as our own except for the Z coordinate because we want to have some distance here.
Next we add the camera object to our main.collection, set the Z position to -1 so again there is some distance to the rest of the world and then tell the script to follow the player:
and with that we have a nice camera following our player:
Since we use message passing instead of calling objects directly it felt like integrating the "wander controller" directly into our bat controller was more natural, so we first do that:
we define some constants and in init set the start position and create a timer that randomly updates the target position to something new within the given the wander range.
For the next part we add a new utility function to our modules directory:
which implements a function that removes a random element from a table.
Now that we have that lets change the init function slightly
so that we are picking a random state and then create a new wander timer which sets a new target position.
next we update idle state to match this:
we check if the timer is still running (timer.get_info() returns nil when the timer isn't) and if it isn't we just rerun the code from before again aka pick another random state and create a new target position
and lastly we also update the wander state:
which firstly does the same thing as before, set the velocity to our target position and lastly pick another new position (or idle) should we get close to the target position to not wobble around the prior one.
Since we have this code snippet now 4 times we might as well make a function out of this:
also as a nice bonus, we update the fixed_update function to look like this:
which makes the bat sprite always look into the direction it is moving! There we go, we now have wandering bats!
(Two of them couldn't stop themselves from hugging each other I guess)
First we want to add the swipe sound, for that we need to add a sound component to our player
and then in attack state:
next we do the same for the enemy death effect
here we modify the script a bit more so that we can have effects with sound:
same with the hit effect.
Next we add another sound component to the player to do the evade sound (also renamed swipe_sound to sound_swipe so that they are grouped)
and again in code:
and that concludes our sound effects!
Lastly we need to tackle the hit effect which we will implement using sprite tint instead of the shader that Heartbeast was using:
First in our generic health component we add two new functions:
this allows us to modify the alpha value of the sprites tint which looks a bit different from what Heartbeast is doing but for simplicities sake this is good enough, we ramp it up to 2 which creates a glow like this:
and then when the iframe is over we just go back to the normal tint.
Next we gotta add these functions to the appropriate locations, first we modify the on_message function:
which starts the animation when the entity is hit or it got a "start iframe" message, which we will send from our roll state like this:
and lastly in update we need to end the iframe effect: