top of page
  • Writer's pictureDaniel Bellido Chueco

YAMAKASI MODE ON

Or how to implement a Parkour System (Part 1)



It's wartime! The circumstances of this last academic year push me to juggle too many demanding deadlines and too little time.

Like today's protagonist, our Agent Alpha, or Double-A for friends, who has learned to do some parkour and with it contributes to partially completing one of the most feared aspects when designing this game: Interaction with the environment using animations according to what is happening. I can't afford to create my own animations with little time, and the Mixamo library is too small for me. For this reason, it is possible that performing certain actions such as carrying the dead body of an enemy has to be ruled out, although it is still too early to confirm it.


In the latest update, Double A could walk, run, sprint, crouch, stealth walk, prone, and crawl like his idol Solid Snake.


What's new in this update?


In this update, Double-A with the press of a button can perform different actions depending on the obstacle in front of him, so he recognizes different objects in the scene with different heights and reacts to them with different animations. In this way, Double A can perform actions called “StepUp”, “JumpUp”, “ClimbUp”, “VaultFence” and “WallClimb”.



As can be seen, each action has an animation or combination of several animations, and each action is triggered within a minimum and maximum height range. But how does Double-A distinguish between different objects based on height? The truth is that it is not only based on the height, it is also helped by very appropriate components for this type of cases such as Layers, Tags, and, of course, some scripts.


How does it work?


Before wondering how it works, it would be worth asking what it takes for Double-A to perform these actions. And what Double-A needs is to communicate with the environment and for the environment to tell it what properties it has. So the first thing Double-A needs is a scanner that allows it to distinguish what object is in front of him. How is this achieved? With a function that projects a small Raycast from the player forward and once the Raycast collides with an object, the Raycast returns true when the object it hits has a certain LayerMask.


For this, two scripts are initially needed, one that scans the scenario and sends the received data, and another that receives this data to determine what to do with it.


The EnvironmentScanner Script contains the public function ObstacleCheck() which is called from the ParkourController Script. This function has an instance of a data holder of type struct that sends data to determine if the object has been raycast. The ObstacleCheck() function also checks the height of the struck object. It does this by casting a second raycast up from the point where the object was hit when the first raycast returns true. This second raycast has a greater length and determines what maximum height it scans, in this case, the length of the second raycast is 5 meters.



The ParkourController Script checks in its Update function if this boolean is true or false. If true then it activates the parkour action, telling the Animator Component which animation to play and briefly disabling the Move() function of the PlayerController script which will prevent the player from moving Double-A while the action is taking place. Notably, the Parkour Controller script also disables the Character Controller Component to prevent the collider or gravity from affecting the animation.


Scriptable Objects


But how are the different animations based on the height of an object triggered?


Simply by creating a scriptable object for each action Double-A needs to do. A scriptable Object is a data container that allows the storage of the necessary information for each animation. In this way, objects that contain a certain animation with certain parameters can be created without the need to be coding each parameter for each animation in a Monobehaviour script.


To start implementing different actions in the parkour system, it is necessary to create a script called ParkourAction inherited from the ScritableObject class. This script contains the data, variables, and parameters necessary to play any animation related to the Parkour System. This script specifies data such as the name of the animation, the minimum, and maximum height at which each animation must be fired, the label of the obstacle, booleans so that the agent can rotate or not depending on the animation, or even the option to add an animation. post-delay action to regain control of Double-A in case multiple animations are combined and it is necessary to wait for the animations to complete before the player can move the character again.


Once implemented, it is possible to create ParkourAction instances from the Unity editor.



After creating different actions, the ParkourController script must handle them. To do this, it is necessary to declare an action list in the el script. The ParkourController will have to iterate through each action every time the ObstacleCheck() function returns true and the button is pressed.


To determine that an action is possible, the ParkourAction script contains a function called CheckIfPossible() that takes as parameters the data obtained by the raycast and the player transform. This function calculates the height of the obstacle by subtracting the distance between the Y position of the height hit point by the Y position of the player. If the calculated height is between the minimum and maximum values assigned to the asset, then the action is possible. But what happens if obstacles of the same height require different actions? In these cases, it is necessary to apply a tag to the obstacle and assign a said tag to the asset so that when the ParkourAction script compares the string it knows what the object is.

So we use a layer mask to determine which objects are interactive and tags to determine the obstacle type, for instance, a step or a fence with the same range of height.



Target Matching technique


At this point, Double-A can position himself on top of different obstacles with different heights playing different animations but there is still a problem. And it is that the animations are generic and always reproduce the same movement without adapting well to the measurements of the obstacle, so there are clipping problems, that the hand does not position itself on the edge of the obstacle or the foot stops before touching soil. To work around this problem, Unity provides a method known as Target Matching.


With this technique, it is possible to achieve that the animations match obstacles of different dimensions without the need to implement different animations for each obstacle.


To implement Target Matching, it is necessary to expand the ParkourAction script by adding float variables that determine at which frame of the animation a certain body part is required to match the desired position. In some cases target matching will not be required, for this reason, it is also necessary to apply a boolean that determines whether or not an asset should apply target matching. You also need a variable of the AvatarTarget type to be able to select which part of the body you want to match with the obstacle in a certain frame. The AvatarTarget is an enum that communicates with parts of the rig like root, body, left foot, right foot, left hand, and right hand. It also has a variable of type Vector3 that allows you to match and correct some animation position definitions in order to avoid the clipping effect with obstacles. All these variables are accessible through the asset inspector created by the ParkourAction script.



To know which frame should be set as Match Start Time and Match Target Time, you have to select the desired animation and inspect it in the inspector timeline. For example, the Match Start Time would be the frame where the animation begins to jump, 15% of the animation, while the Match Target Time would be the frame where you want to place the desired body part, a value of approximately 33% animation. These percentage values must be normalized, so the final value to be assigned to the action asset would be 0.15 and 0.33.



WARNING: It is important to know that the type of animation to be set is “Humanoid”. If you choose to use a "Generic" type of animation, the animation will likely present some positioning bug at the end of the animation. In fact, this also happens with the Humanoid animation type but this animation type can correct this error if “Feet” is selected in the Based Upon (at Start ) within the Root Transform Position Y section.



What does this look like?


Conclusion


I have to admit that thanks to this project I am learning much more in-depth not only about Unity but many animation concepts and techniques that I was unaware of until recently.

Being able to work with Scriptable Objects that allows you to add sections in the editor seems to me to be something very useful that, once implemented, any animator or designer can expand the animations of the game without touching a single line of code and save a lot of time.


The Target Matching technique also seems to me to be a tremendously useful tool that values the few animations that may be available, since it allows you to reuse them, offering quite good results. It is true that in this case there are still animations to be polished but they will improve as the days go by.


The implementation of the Parkour System has not yet finished, as it has been possible to deduce from the title of the blog, but 80% will be implemented shortly and it will be possible to start working on another of the important pillars of this project: Artificial intelligence.


I'm also realizing that this project lacks a bit of ambition since when you design a decent game you don't think too much about the difficulties but rather about the possibilities. Being realistic, this project is big for me but the difficulty and the challenge have always been the engine of my motivation, besides, I like to think big!


What are the next milestones?

  1. Expand Parkour System actions:

  • Climb ladders.

  • Take cover with walls and obstacles.

2. Improve and expand camera control:

  • add an extra first-person camera (for ventilation ducts).

  • move properly while aiming.

  • adapt aiming animation in the aiming camera.

3. Implementation of basic AI:

  • first animated simple enemy


At the moment, I will briefly disconnect from the project due to other upcoming deadlines and my academic year could be complicated if I do not attend to these projects with some urgency. The most important right now is the modeling one whose deadline is at the beginning of January and it is a quite demanding assignment. On the other hand, I have the mobile game that has been somewhat neglected lately but is in a good place since the game itself is finished, although some things still need to be tweaked. And finally, the PlayStation 5 game, which at the moment I am not going to deal with too much but there is already an idea outlined and a project started.


To finish, say that by the end of January this project needs to present a demo that shows some of the basic pillars of the concept. We could say that 40% of the demo would be ready and the implementation of the AI it will help the project look much better for that day. Two more updates are planned for this project until the end of January. In one of them the parkour system will be implemented and in the next the implementation of AI. Until then, I hope to have you back for this blog. I promise not to make such a long post again (At least, I will try).

39 views0 comments

Recent Posts

See All

Comments


bottom of page