This article was kindly provided by the Assassin’s Creed team. You can find the original blog here.
Welcome to our latest dev blog! We want to provide you with some insights on the creation process of the Missile Reversal skill.
First off, let’s take a look at the final product!
When working on any new feature, the design always comes first. The design intention for Missile Reversal was: catch an arrow mid-air that’s shot at you and send it right back using parrying. The next step was to create a prototype animation.
Once we created the animation, we plugged it into the game engine to create a prototype. We already had a system in place playing animations based on a set of inputs and conditions. Using this system, we listened for the parry input, and we checked a condition we had called the “aimed at/shot at condition” to determine if we should trigger the move. We decided to skip the catching aspect for the prototype, so the final piece was simply shooting the arrow back at the enemy that the player was locked on to.
With that, it was in the game in just a couple of hours!
Of course, you can already see that it’s a bit rough and wouldn’t stand up in the actual game. However, it was a valuable proof of concept – a first step to determine what pieces we needed to realize. We also wanted to do this for other projectiles, with some exceptions. It’s not possible with large projectiles, like barrels or bodies.
With the prototype in place, we needed to determine how detection should work to activate the skill. Since the “shot at condition” only measured if NPCs were in the process of shooting, you could activate the skill before shooting the arrow, or if they were far away, they could exit the shooting state before the arrow was close enough! As such, we needed a more reliable way to check if we are about to get hit with a projectile. Luckily, we keep a list of all currently airborne projectiles. We scan this list to check things like: “How long until impact?” or, “Is it heading towards you?”
With this new condition, the next step was shooting back the projectile! In the proto version, the shooting worked fine, but it targeted whomever you locked onto, and that’s a no-no in the actual game. Luckily, because we store the information about who shoots a projectile inside the projectile data itself (for damage reasons), we could correctly set the target using that info setting us up nicely to tackle the “shooting it back” part.
One of the final steps in early development was catching the projectiles. However, it looked fine in-game at this stage, so we decided to destroy the incoming arrow, then, at the right moment, we spawn a new one in your hand. Finally, we made the animation turn to face the shooter, and boom! We did it. You can now “catch” projectiles and return them to the sender!
Next up, some polishing.
Since the player can press the parry button when the projectile is about to hit them or when it’s super far away, the grab would be either too late or too early. We needed to find a way to time the grab animation with the correct position of the projectile. We did this by time scaling the animation: we’d make the animation play slower or faster.
We needed to calculate a time ratio to play the animation at 1 for normal speed and 0.5 for half speed, and so on. We did this in the animation state machine – which is where we control which animation gets played.
The pink box (bottom left) is the distance between yourself and the arrow. This information is fed back to the animation engine from the code.
Once all this is working, we add a camera to show the action.
Remember when we said we wanted to do this for every type of projectile? Everything so far was 1 out of 10. Fortunately, most of the pieces are the same.
While the detection code was the same, we needed to play different animations for different projectiles (e.g., arrows vs. shields). We also needed to put in place weapon catching. While we could re-use some of the tech to support picking up weapons, we needed to add a check to the projectile code saying, “If you’re attached to something, don’t keep running.” This is to ensure the projectile stops when caught. We then added a method of distinguishing between different types of projectiles to play the correct animation for each.
While it was a piece of cake for other weapons, not everything we were throwing back was a weapon! The “pick up weapon” tech didn’t work for rocks or bombs. We did have a method to attach an object to a “bone,” so we could attach the projectile to the “weapon bone” and it would look the same – even if it’s different behind the scenes.
Due to a tiny typo when setting it up, it attached to the person who shot the projectile instead of the projectile itself! Oops!
One good laugh and a few clicks later, that was all fixed. At this point, it was mainly tweaking – like making final timing adjustments.
We encountered some interesting bugs while developing this feature. For example, an internal case read, “Player starts doing yoga after pressing parry when arrows are nearby.” Uh-oh.
When we receive bug reports, they usually include a video, so of course, our first step is to watch.
Right away, it was clear that it was an issue with the time scaling in the animation, but we had no idea what caused it. We could’ve spent a long time trying to figure out the root cause, or we could add a failsafe to make sure it can’t happen. We went with the latter and added something to our previous math saying: “The projectile can’t be further away than 9m.” So, the most the animation will slow down to is about half speed.
One of the animators also sent the team a video called “Bye!”
While hilarious, this wasn’t super realistic to what we wanted to have in the game. There was a quick fix, as we can already override the ragdoll force from an attack. We tuned it down from “LOL” to “tasteful.”
One issue that kept coming up was that sometimes you’d play the animation but didn’t shoot anything. While it took much work to track down and fix all issues, one case was worth mentioning: A player removing their bow while trying to catch an arrow. Honestly, we didn’t even think of that!
We resolved this by making two checks: one if you have a bow and one if you don’t.
Now, this is not an issue with Missile Reversal itself. But the standard-bearer NPC threw their flag, which they really shouldn’t have.
It never gets old.
There are two main things we hope you take away from reading this:
- Making games is not an easy feat: There are countless factors you need to keep in mind. And even a task that seems small at first glance can take quite a while.
- But it’s also a fun journey: Problem-solving can be challenging at times. And you might get a rush thinking that you fixed an issue only to realize it’s still not working. While bugs are not ideal, games are not developed without them. The journey of resolving them can be fun – and the results are worth it!
Thank you for reading.