2026-02-09 2
12:47:22, atom feed.
Here is the curated commit log from this last week's development.
2026-02-03
feat: removed entity action timer; moves are now turn-blocking anims.
2026-02-03
feat: render entity index in graphical turn queue debug visualization
2026-02-03
feat: allow mult. entities in turn queue to move on same frame
2026-02-03
feat: introduce reaction to entity death after attack animations play
2026-02-06
feat: add component to support entity pos interp. during movement
This also updates the camera to move with the player as position is
interpolated, having the effect of a smooth-scrolling camera instead of
a camera that moves in units of whole tiles.
I started this last week of development by simplifying my collection of timer values. I used to have cooldowns for many things: player idling, player movement, player action, player attack, enemy action, enemy attack, etc. Some of these values were used to stall the progression of turns while animations played, and others were just used to determine when to switch sprites in an animation. The different values for timers that would stall turn progression introduced wall-clock-time-related bugs in turn progression, though. In short, this didn't need to be the case in a game where turn progression is gated on player initiative or in other words is not a realtime game. So I reduced the number of timer values and also removed one of my timer components and now just use the general entity animation timer in combination with the entity state to determine when turn progression should be blocked. This allows individual entities to perform turn-blocking animations at different speeds without having to make sure other timer values align well.
I then extended my graphical debugging utility by rendering entity turn indices over entity sprites in the turn queue. This was an obviously-missing feature from the implementation, as there was no way to tell which ghost sprite was which in the turn queue.
After simplifying the my timer and cooldown situation, I then realized that the
player moves faster if there are fewer enemies alive. This is because I now also
block turn progression on "move cooldown." I do this so that I can show movement
animations, which I'll touch on later. If the move cooldown for everyone is C
and there are N entities alive, then the player needs to wait at least
C * (N - 1) units of time before the player can move again. To address player
move speed appearing to change relative to the number of entities alive, I block
turn progression on player movement and also on enemy movement, but multiple
enemies can initate movement on the same frame (unlike if the player moved) so
that their animations play simultaneously. Then, the player waits at most
C * 2 units of time before they can move again (maximally, once if the player
moves and then again if any number of enemies move after player movement
finishes).
Up to this point, I was despawning enemies immediately when their health reached
zero. This means that on the same frame the player attacks an enemy and their
health reaches zero, the enemy sprite would disappear. The player attack
animation still plays for some time, though. I felt it would be more satisfying
if the attack animation finished, then a death animation started afterward, and
then an enemy despawned. This sequence allows for more visual (and eventually
audible) separation between events. To do this, I introduced a new entity state
which is .DYING. If an enemy is dying, it starts a death animation, and when
the timer on the death animation expires, the entity is deleted. Similar to move
and attack animations, death animations block the progression through the turn
queue.
Lastly, I added smooth position interpolation during entity movement. Previously entities moved 1 tile instantaneously. Now, an entity's position is linearly interpolated during its move cooldown by the simple ratio of
move_cooldown_remaining / total_move_cooldown
I added a new component in the ECS, moved_from. This simply stores the
position an entity moved from if the entity performed movement. The entity's
basic position is still immediately set to the destination since game logic
operates on map-tile-based coordinates, but for the sake of the render system,
we need to temporarily remember where we moved from so that we can interpolate
render position of the entity sprite correctly.
On each frame, the camera takes the basic position of the player, so the camera
appeared to jump ahead of the player and the player had to catch up to the
camera. This, performed quickly, just made everything look jittery. So I
exposed the routine to interpolate position during movement from within my
Entity module so that my Game module could leverage it, and now the Game
module handles computing the new instantaneous camera position based on the
player position at the top-level game-update procedure. Once nice consequence
of this change is that the camera movement appears much smoother, no longer
jumps in visibly discreet map units as the player moves.
The entity death animation timer in this video is set to a dramatic 1.0 seconds, and the current death "animation" just fades the sprite out. Also, there is no movement animation, just position interpolation.