094 - Firing a Bow (Part 3/4)

November 22, 2020

The bulk of this week was spent on continuing to implement the CoordinatedSequence so that you can see a bow being fired in the game.

The CoordinatedSequence for the bow involves playing an animation for the player to fire a bow, spawning an arrow at the right time, animating the bow's string at the right time and then firing the arrow towards the enemy at the right time.

In the future new sequences should be quick and easy to add, but because this is the first one I have to first lay down all of the groundwork for sequences.


By next week the CoordinatedSequenceSystem will have enough implemented for me to show a bow firing.

For now, here's a video of me attacking an enemy without any animations.

Firing a bow while maintaining distance. We aren't yet playing any animations or the spawning an arrow to fly towards the enemy. That will come next week.

CoordinatedSequence - Fire Bow

A CoordinatedSequence can have one or more tracks that advance in parallel.

Each track can have one or more steps.

Every game tick the CoordinatedSequenceSystem runs the current step of each track, and advances if the step is completed.

Here's how the steps for firing a bow currently look. After using this abstraction in a few more scenarios I may consider defining sequences in data files instead of code.

pub(super) fn fire_bow_sequence(
    server_eid: EID,
    sys: &GameServerReceivedMessagesSystemData,
    attacked_target: AttackedTargetTrigger,
) -> CoordinatedSequence {
    let now = sys.game_clock.most_recent_tick_start();

    let mut steps = Vec::with_capacity(6);
    let mut sequence = CoordinatedSequence::new(now, Duration::from_secs_f32(TIMEOUT_SECONDS));

    steps.push(step_start_character_animation_to_lift_and_fire_bow(
        server_eid,
        attacked_target,
    ));
    steps.push(step_wait_for_grab_arrow(server_eid));
    steps.push(step_spawn_arrow_in_hand(server_eid));
    steps.push(step_wait_for_pull_bow_string(server_eid));
    steps.push(step_play_firing_animation_on_bow_equipment(server_eid));
    steps.push(step_launch_arrow_projectile(attacked_target));

    sequence.push_track(CoordinatedSequenceTrack::new(steps));
    sequence
}

fn step_start_character_animation_to_lift_and_fire_bow(
    server_eid: EID,
    attacked_target: AttackedTargetTrigger,
) -> CoordinatedSequenceStep {
    CoordinatedSequenceStep::StartAnimation(CseqStepStartAnimation::new(
        server_eid,
        AnimationTrigger::AttackedTarget(attacked_target),
    ))
}

fn step_wait_for_grab_arrow(server_eid: EID) -> CoordinatedSequenceStep {
    CoordinatedSequenceStep::WaitForPoseMarker(CseqStepWaitForPoseMarker::new(
        server_eid,
        EntityArmatureLookup::EntityRenderable,
        POSE_MARKER_GRAB_ARROW,
    ))
}

Other Notes / Progress

  • Added the DamageQueueComponent and DamageQueueSystem for handling dealing damage to entities.

  • Added the ammunition equipment slot so that we can equip arrows.

  • Stopped using postgres enums and I instead now use reference tables. A bit easier to manage migrations for and more portable to other databases.

  • Automatically running up migrations on the production database in CI.

Next Journal Entry

My focus this week is on making firing a bow at an enemy feel good.

I will start by making it so that when you fire a bow the CoordinatedSequence that is created gets handled properly by the CoordinatedSequenceSystem.

After that I will incrementally polish and improve the feeling of firing the bow until it feels good enough for now.

I'll also be adding in models for an arrow, a quiver, a possum to attack, and doing some other art polish.

By the next journal entry the Bowman skill should be looking and feeling pretty decent!


Made an animation in Blender for the bow. Not amazing, but I'm definitely getting better week by week.


Cya next time!

- CFN