This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| topic:havok_behavior_editing [2025/02/25 14:53] – admin | topic:havok_behavior_editing [2025/02/27 00:44] (current) – admin | ||
|---|---|---|---|
| Line 63: | Line 63: | ||
| Transition effects specify how animations transition from one to the next. Among other things it also determines whether animations are blended when transitioning into themselves. | Transition effects specify how animations transition from one to the next. Among other things it also determines whether animations are blended when transitioning into themselves. | ||
| - | The most commonly used transition effect objects are [[[reference: | + | The most commonly used transition effect objects are '' |
| ==== Classes and HkParams ==== | ==== Classes and HkParams ==== | ||
| - | See [[[*reference:havok_behavior_editing | + | See [[common-refmat:havok_behavior_reference |
| ===== Havok Script Files ===== | ===== Havok Script Files ===== | ||
| - | Havok script files are lua scripts which controls which behaviors are triggered when. It is also where [[[*reference:havok_behavior_editing#hkbVariable | + | Havok script files are lua scripts which controls which behaviors are triggered when. It is also where [[common-refmat:havok_behavior_reference#hkbVariables |
| - | For the player c0000.hks handles which inputs trigger which animations. Enemies mainly use c9997.hks with their own hks typically containing very little. As c9997.hks has not been accurately decompiled, its contents are still mostly unclear, however it is definitely used to interface between AI animation calls and the behavior system. | + | For the player c0000.hks handles which inputs trigger which animations. Enemies mainly use c9997.hks with their own hks typically containing very little. As c9997.hks has not been accurately decompiled, its contents are still mostly unclear, however it is definitely used to interface between AI animation calls and the behavior system. |
| - | I will mainly be focusing on behavior edits here since they are less well documented, this will involve hks work, however hks-only edits will not be covered or will only covered in passing. Dedicated hks tutorials which focus on such edits can be found [*https:// | + | I will mainly be focusing on behavior edits here since they are less well documented, this will involve hks work, however hks-only edits will not be covered or will only covered in passing. Dedicated hks tutorials which focus on such edits can be found [[https:// |
| ==== CommonFunctions ==== | ==== CommonFunctions ==== | ||
| - | CommonFunctions are the functions used in [# | + | CommonFunctions are the functions used in [[havok_behavior_editing#onUpdate]] functions to interpret player inputs. Generally all substates of the '' |
| ==== Exec ==== | ==== Exec ==== | ||
| - | Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions | + | Exec functions check for player request inputs and execute animations by calling the lower level ExecEvent functions |
| ==== Env ==== | ==== Env ==== | ||
| Line 89: | Line 89: | ||
| The env function gets info about the character from the game, it has the following syntax: | The env function gets info about the character from the game, it has the following syntax: | ||
| - | < | + | < |
| env(ID, args) | env(ID, args) | ||
| </ | </ | ||
| Line 103: | Line 103: | ||
| The function has the following syntax: | The function has the following syntax: | ||
| - | < | + | < |
| act(ID, args) | act(ID, args) | ||
| </ | </ | ||
| Line 111: | Line 111: | ||
| ==== onUpdate ==== | ==== onUpdate ==== | ||
| - | Each CMSG, given a unique | + | Each CMSG, given a unique userData value and provided the '' |
| Most basic onUpdate functions have the following form: | Most basic onUpdate functions have the following form: | ||
| - | < | + | < |
| function SMSIName_onUpdate() | function SMSIName_onUpdate() | ||
| if SomeCommonFunction(args) == TRUE then | if SomeCommonFunction(args) == TRUE then | ||
| Line 126: | Line 126: | ||
| As an example the first one-handed r1 attack: | As an example the first one-handed r1 attack: | ||
| - | < | + | < |
| function AttackRightLight1_onUpdate() | function AttackRightLight1_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 139: | Line 139: | ||
| AttackCommonFunction/ | AttackCommonFunction/ | ||
| - | As mentioned previously, update functions can also be applied at any point in the behavior hierarchy to all child states using an [[[reference: | + | As mentioned previously, update functions can also be applied at any point in the behavior hierarchy to all child states using an hkbScriptGenerator. |
| ===== Basic Editing ===== | ===== Basic Editing ===== | ||
| - | The following section will cover the basics of behavior editing which should serve to establish a rudimentary understanding of the process, which due to the modular nature of the behavior system, should convey the necessary information to allow you to create any behavior structure you desire. See the [[[reference:havok_behavior_editing | + | The following section will cover the basics of behavior editing which should serve to establish a rudimentary understanding of the process, which due to the modular nature of the behavior system, should convey the necessary information to allow you to create any behavior structure you desire. See the [[common-refmat:havok_behavior_reference |
| Each example will build on what is discussed in the previous ones so I recommend looking at them in order if you do not yet possess a solid understanding of the behavior system. I will also cover a lot of general info, including tips and good practices, in the first few examples. | Each example will build on what is discussed in the previous ones so I recommend looking at them in order if you do not yet possess a solid understanding of the behavior system. I will also cover a lot of general info, including tips and good practices, in the first few examples. | ||
| - | All examples here except for adding magic casting animations are supported by my tool [*https:// | + | All examples here except for adding magic casting animations are supported by my tool [[https:// |
| ==== Registering an Existing Animation ID for a TAE Section ==== | ==== Registering an Existing Animation ID for a TAE Section ==== | ||
| Line 153: | Line 153: | ||
| To start, let's go over the most basic operation in behavior editing which you will need to do for every new instance of an animation ID you add to TAE. There have already been many quick tutorials on how to do this, so this tutorial will go more in depth to explain fundamental practices of editing behaviors to build a solid foundation for more advanced editing. | To start, let's go over the most basic operation in behavior editing which you will need to do for every new instance of an animation ID you add to TAE. There have already been many quick tutorials on how to do this, so this tutorial will go more in depth to explain fundamental practices of editing behaviors to build a solid foundation for more advanced editing. | ||
| - | First, unpack your hkx behavior file using [*https:// | + | First, unpack your hkx behavior file using [[https:// |
| In this example we will register the first one-handed r1 animation for TAE section 299. Replace animation ID 30000 and TAE 299 with your animation ID and TAE section of choice. | In this example we will register the first one-handed r1 animation for TAE section 299. Replace animation ID 30000 and TAE 299 with your animation ID and TAE section of choice. | ||
| - | Once we have opened the behavior file we will want to find the [[[*reference: | + | Once we have opened the behavior file we will want to find the CustomManualSelectorGenerator, |
| - | < | + | < |
| <hkparam name=" | <hkparam name=" | ||
| </ | </ | ||
| Line 165: | Line 165: | ||
| Thus an effective method for finding the CMSG you want is searching for: | Thus an effective method for finding the CMSG you want is searching for: | ||
| - | < | + | < |
| " | " | ||
| </ | </ | ||
| - | Once we have located our CMSG of choice, in this case #528, we need to add an [[[*reference: | + | Once we have located our CMSG of choice, in this case '' |
| To add any hkobject to your behavior file you need to do the following: | To add any hkobject to your behavior file you need to do the following: | ||
| Line 181: | Line 181: | ||
| Once we have that, we will scroll to the bottom of the behavior file and paste our new hkobject after the ending tag of the previous hkobject and before the ending tag of the hksection. | Once we have that, we will scroll to the bottom of the behavior file and paste our new hkobject after the ending tag of the previous hkobject and before the ending tag of the hksection. | ||
| - | Now we will edit the parameters of our newly created hkobject. Since we copied it from an instance which was registered under the same CMSG, we will most likely only need to edit the hkparams " | + | Now we will edit the parameters of our newly created hkobject. Since we copied it from an instance which was registered under the same CMSG, we will most likely only need to edit the hkparams " |
| - | It is unclear what the "name" | + | It is unclear what the '' |
| - | The hkparam which does matter, and which is responsible for registering your animation is "animationName". It is of the form a[TAE section]_[animation ID], so in this case a299_030000. | + | The hkparam which does matter, and which is responsible for registering your animation is '' |
| - | For step 3 we will need to look for the next available name ID. Hkobjects in the data hksection will always have a name attribute which is an ID prefixed with "#". They will always be numbered in the order they appear in the XML file, so to find the next available name ID you will want to look at the last hkobject in the file and add 1 to its ID. In vanilla the behavior file ends with the hkbBehaviorGraphStringData hkobject with name "#12801", thus the first object you add to your behavior file will have the ID "#12802". | + | For step 3 we will need to look for the next available name ID. Hkobjects in the data hksection will always have a name attribute which is an ID prefixed with '' |
| As stated previously, hkxpack will make sure your IDs are sequential and rename hkobjects and references to them accordingly, | As stated previously, hkxpack will make sure your IDs are sequential and rename hkobjects and references to them accordingly, | ||
| - | In this example the next free name ID is #12802, so we will change the name attribute of our new hkbClipGenerator to equal that. | + | In this example the next free name ID is '' |
| Here is the final version of our new hkbClipGenerator: | Here is the final version of our new hkbClipGenerator: | ||
| - | < | + | < |
| < | < | ||
| <hkparam name=" | <hkparam name=" | ||
| Line 215: | Line 215: | ||
| </ | </ | ||
| - | Finally we need to reference our new hkbClipGenerator at the corresponding CMSG. To do this, we add its name ID to the end of the list of generators. Make sure to include the "#" | + | Finally we need to reference our new hkbClipGenerator at the corresponding CMSG. To do this, we add its name ID to the end of the list of generators. Make sure to include the '' |
| - | It is important to always add your new reference to the end of reference lists, because some reference lists are indexed by hkbVariables. For instance | + | It is important to always add your new reference to the end of reference lists, because some reference lists are indexed by hkbVariables. For instance hkbManualSelectorGenerators use hkbVariables to select which generator to activate. An example of this in Dark Souls III are the selector generators for magic casting animations, where the index of the selected CMSG is set based on a value in the gameparam. If you were to insert a new generator higher in the list, it would offset all casting animations used in the game (see '' |
| You will notice that the " | You will notice that the " | ||
| Line 231: | Line 231: | ||
| === Creating a CMSG === | === Creating a CMSG === | ||
| - | In order to define a new animation ID we need to create a new CMSG. Like in our previous example, we will begin by copying an existing CMSG. We will want to copy a CMSG which is as similar in function to the one we wish to add as possible, in this case we will copy the CMSG for the third right-handed r1 as it immediately precedes our new animation. If you are unfamiliar with the naming scheme of attack animation generators, use the method outlined in the previous example to find this CMSG. The CMSG in question is named "AttackRightLight3_CMSG" | + | In order to define a new animation ID we need to create a new CMSG. Like in our previous example, we will begin by copying an existing CMSG. We will want to copy a CMSG which is as similar in function to the one we wish to add as possible, in this case we will copy the CMSG for the third right-handed r1 as it immediately precedes our new animation. If you are unfamiliar with the naming scheme of attack animation generators, use the method outlined in the previous example to find this CMSG. The CMSG in question is named '' |
| - | Now we will want to set up the parameters for our new CMSG. Provided you have copied a similar CMSG to the one which you want to create, you will need to change | + | Now we will want to set up the parameters for our new CMSG. Provided you have copied a similar CMSG to the one which you want to create, you will need to change |
| - | The generators hkparam is, as seen in the previous example, a reference list of all hkbClipGenerators with the corresponding animation ID. CMSGs must always have at least one child generator referenced here, so let's create a clipGenerator for our animation as described in [#registering-animations | + | The generators hkparam is, as seen in the previous example, a reference list of all hkbClipGenerators with the corresponding animation ID. CMSGs must always have at least one child generator referenced here, so let's create a clipGenerator for our animation as described in [[havok_behavior_editing# |
| - | The animId hkparam specifies our new animation ID, I will choose the anim ID 30040 for my animation, following the existing ID system. | + | The animId hkparam specifies our new animation ID, I will choose the anim ID '' |
| - | The userData hkparam must be unique for each state, otherwise our [#onUpdate | + | The userData hkparam must be unique for each state, otherwise our [[havok_behavior_editing#onUpdate]] function will not work. As mentioned in CMSGs are typically grouped by hkbStateMachines, |
| - | To find the parent hkbStateMachine where we wish to reference our CMSG, we search for the name ID of the original CMSG we copied (AttackRightLight3_CMSG) which is #639 and notice that it is referenced not in an hkbStateMachine, | + | To find the parent hkbStateMachine where we wish to reference our CMSG, we search for the name ID of the original CMSG we copied (AttackRightLight3_CMSG) which is '' |
| Child objects of hkbStateMachines are always of the class hkbStateMachineStateInfo and primarily serve to specify the state ID which is mapped to the event name of the state in the hkbStateMachineTransitionInfoArray. These state IDs are unique for all states of a state machine but are reused between state machines. Each hkbStateMachineStateInfo also references a child generator, in this case the CMSG AttackRightLight3. | Child objects of hkbStateMachines are always of the class hkbStateMachineStateInfo and primarily serve to specify the state ID which is mapped to the event name of the state in the hkbStateMachineTransitionInfoArray. These state IDs are unique for all states of a state machine but are reused between state machines. Each hkbStateMachineStateInfo also references a child generator, in this case the CMSG AttackRightLight3. | ||
| - | When creating a CMSG the state info object is also important for determining the userData to use. Since the userData is only unique for each separate state, every CMSG which shares an hkbStateMachineStateInfo object must also share userData. This occurs most frequently with the state info object referencing an hkbManualSelectorGenerator which selects between multiple CMSGs. All of those CMSGs belong to the same state and share eventName, userData and onUpdate function. This is explained further in the examples [#magic-casting-animation | + | When creating a CMSG the state info object is also important for determining the userData to use. Since the userData is only unique for each separate state, every CMSG which shares an hkbStateMachineStateInfo object must also share userData. This occurs most frequently with the state info object referencing an hkbManualSelectorGenerator which selects between multiple CMSGs. All of those CMSGs belong to the same state and share eventName, userData and onUpdate function. This is explained further in the examples [[havok_behavior_editing#Adding a Magic Casting Animation]] and [[havok_behavior_editing#Creating Weapon Art Behaviors]]. |
| - | To find the parent state machine, we once again search for the name ID of our object, this time hkbStateMachineStateInfo #638 and see that it is referenced in hkbStateMachine #285, called AttackRight_SM. This state machine, as we can see by the name, references all right hand attack states, so this is where we will reference our CMSG from. Before we do that, we will look for the last generator in the state list (#1180) and check its userData value, which is 17104921. Therefore we pick 17104922 as the userData value for our new CMSG. Performing a search for this value shows no other instances thereof, so we know it's safe to use. | + | To find the parent state machine, we once again search for the name ID of our object, this time hkbStateMachineStateInfo |
| - | I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the [[[reference:havok_behavior_editing# | + | I will not cover the hkparams of hkbStateMachines in depth in this example, please refer to the [[common-refmat:havok_behavior_reference |
| - | The "states" | + | The '' |
| - | The hkparam responsible for selecting the state is the " | + | The hkparam responsible for selecting the state is the " |
| When referencing our CMSG we must therefore do two things: | When referencing our CMSG we must therefore do two things: | ||
| Line 263: | Line 263: | ||
| To keep the behavior file somewhat tidy I like to have objects appear in hierarchical order whenever possible, so I will paste the hkbStateMachineStateInfo before my CMSG and make sure the name IDs reflect this order. | To keep the behavior file somewhat tidy I like to have objects appear in hierarchical order whenever possible, so I will paste the hkbStateMachineStateInfo before my CMSG and make sure the name IDs reflect this order. | ||
| - | Once we have created our new state info hkobject, we add a reference to it to the end of the state machine state list and change the " | + | Once we have created our new state info hkobject, we add a reference to it to the end of the state machine state list and change the " |
| - | HkbStateMachineTransitionInfoArrays consist of a single hkparam called " | + | HkbStateMachineTransitionInfoArrays consist of a single hkparam called " |
| After creating our new transition info object within the " | After creating our new transition info object within the " | ||
| - | Now all we need to do is create an eventName which we will use to trigger our new behavior. Event names are stored at the end of the vanilla behavior file in the [[[reference: | + | Now all we need to do is create an eventName which we will use to trigger our new behavior. Event names are stored at the end of the vanilla behavior file in the hkbBehaviorGraphStringData hkobject under the '' |
| We are now done with editing the behavior file. Don't forget to repack it to hkx and repack the behbnd. | We are now done with editing the behavior file. Don't forget to repack it to hkx and repack the behbnd. | ||
| Line 285: | Line 285: | ||
| To finish setting up our new attack behavior we will set up its onUpdate function and trigger it from the onUpdate function of the preceding attack, which looks like this: | To finish setting up our new attack behavior we will set up its onUpdate function and trigger it from the onUpdate function of the preceding attack, which looks like this: | ||
| - | < | + | < |
| function AttackRightLight3_onUpdate() | function AttackRightLight3_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 297: | Line 297: | ||
| As we can see it consists of a single function call to AttackCommonFunction which has the following arguments: | As we can see it consists of a single function call to AttackCommonFunction which has the following arguments: | ||
| - | < | + | < |
| AttackCommonFunction(r1, | AttackCommonFunction(r1, | ||
| </ | </ | ||
| - | As mentioned earlier this function calls various Exec functions, including ExecAttack which it passes its attack event args to. It may only accept arguments for attack inputs but like all other [# | + | As mentioned earlier this function calls various Exec functions, including ExecAttack which it passes its attack event args to. It may only accept arguments for attack inputs but like all other CommonFunctions it handles all inputs. Generally most state machines directly under Master_SM will have a corresponding CommonFunction, |
| If we wanted to implement our 4th r1 for all weapons, we would need to change the first argument of the function (" | If we wanted to implement our 4th r1 for all weapons, we would need to change the first argument of the function (" | ||
| Line 307: | Line 307: | ||
| The [#env env] to check for the override tae section of the player' | The [#env env] to check for the override tae section of the player' | ||
| - | < | + | < |
| env(" | env(" | ||
| </ | </ | ||
| Line 313: | Line 313: | ||
| Since this is a right hand attack we want to get the right hand weapon' | Since this is a right hand attack we want to get the right hand weapon' | ||
| - | < | + | < |
| function AttackRightLight3_onUpdate() | function AttackRightLight3_onUpdate() | ||
| local r1 = " | local r1 = " | ||
| Line 329: | Line 329: | ||
| For two handed attacks, you need to account for the possibility of the player two-handing their left hand weapon, which can be accomplished using the following code: | For two handed attacks, you need to account for the possibility of the player two-handing their left hand weapon, which can be accomplished using the following code: | ||
| - | < | + | < |
| local hand = HAND_RIGHT | local hand = HAND_RIGHT | ||
| if c_Style == HAND_LEFT_BOTH then | if c_Style == HAND_LEFT_BOTH then | ||
| Line 338: | Line 338: | ||
| Now we want to set up the onUpdate function for our new behavior. Make sure to use the name of its hkbStateMachineStateInfo along with the " | Now we want to set up the onUpdate function for our new behavior. Make sure to use the name of its hkbStateMachineStateInfo along with the " | ||
| - | < | + | < |
| function AttackRightLight4_onUpdate() | function AttackRightLight4_onUpdate() | ||
| if AttackCommonFunction(" | if AttackCommonFunction(" | ||
| Line 352: | Line 352: | ||
| ==== Creating Weapon Art Behaviors ==== | ==== Creating Weapon Art Behaviors ==== | ||
| - | Weapon art behaviors are a simple example of how [[[reference: | + | Weapon art behaviors are a simple example of how hkbManualSelectorGenerators (hkbMSGs) are used and illustrate how we can insert generators between CMSG and StateMachineStateInfo to change the way our character behaves. |
| Basic weapon arts consist of two animations, one which is played when the player has enough FP to perform the move and the other if the player does not. This could be done using only the previous method and making separate CMSG states for both animations, however this has multiple downsides. First of all, you would need to check the player' | Basic weapon arts consist of two animations, one which is played when the player has enough FP to perform the move and the other if the player does not. This could be done using only the previous method and making separate CMSG states for both animations, however this has multiple downsides. First of all, you would need to check the player' | ||
| Line 364: | Line 364: | ||
| In this example we will create a simple single-move, | In this example we will create a simple single-move, | ||
| - | Between our SMSI and our 2 CMSGs, one for the animation which is played when the player has sufficient FP to perform the weapon art and one when they don't, we will create an [[[reference: | + | Between our SMSI and our 2 CMSGs, one for the animation which is played when the player has sufficient FP to perform the weapon art and one when they don't, we will create an hkbManualSelectorGenerator which will be used to select between our CMSGs. We will call it " |
| ^ Hkparam ^ Value ^ | ^ Hkparam ^ Value ^ | ||
| Line 379: | Line 379: | ||
| === hkbVariableBindingSets === | === hkbVariableBindingSets === | ||
| - | Variables are bound to parameters using [[[reference: | + | Variables are bound to parameters using hkbVariableBindingSets which contain bindings. Almost all hkobjects in behavior files can reference a single binding set, which can however contain multiple bindings. Each binding specifies the index of an hkbVariable in their " |
| To bind the " | To bind the " | ||
| Line 391: | Line 391: | ||
| Looking at common_define we can see that each actionId has three corresponding global variables. One called " | Looking at common_define we can see that each actionId has three corresponding global variables. One called " | ||
| - | < | + | < |
| SWORDARTS_PARRY = 1 | SWORDARTS_PARRY = 1 | ||
| SWORDARTS_REQUEST_LEFT_PARRY = 101 | SWORDARTS_REQUEST_LEFT_PARRY = 101 | ||
| Line 399: | Line 399: | ||
| Thus for our new actionId 37 with name NewSwordArt we put the following: | Thus for our new actionId 37 with name NewSwordArt we put the following: | ||
| - | < | + | < |
| SWORDARTS_NEW = 37 | SWORDARTS_NEW = 37 | ||
| SWORDARTS_REQUEST_LEFT_NEW = 137 | SWORDARTS_REQUEST_LEFT_NEW = 137 | ||
| Line 409: | Line 409: | ||
| Unlike standard attacks, which weapon art to execute is not directly determined in onUpdate functions. Every CommonFunction calls ExecAttack, passing on its attack arguments which are set differently in each onUpdate function. ExecAttack in turn calls the GetAttackRequest function to determine which of the attacks to execute. GetAttackRequest checks which button is currently being pressed and returns the appropriate ATTACK_REQUEST, | Unlike standard attacks, which weapon art to execute is not directly determined in onUpdate functions. Every CommonFunction calls ExecAttack, passing on its attack arguments which are set differently in each onUpdate function. ExecAttack in turn calls the GetAttackRequest function to determine which of the attacks to execute. GetAttackRequest checks which button is currently being pressed and returns the appropriate ATTACK_REQUEST, | ||
| - | < | + | < |
| function GetSwordArtsRequest() | function GetSwordArtsRequest() | ||
| local style = c_Style | local style = c_Style | ||
| Line 432: | Line 432: | ||
| The variable arts_id corresponds to the actionId from the SwordArtsParam and thus our variable " | The variable arts_id corresponds to the actionId from the SwordArtsParam and thus our variable " | ||
| - | < | + | < |
| elseif request == SWORDARTS_REQUEST_RIGHT_NEW or request == SWORDARTS_REQUEST_LEFT_NEW then | elseif request == SWORDARTS_REQUEST_RIGHT_NEW or request == SWORDARTS_REQUEST_LEFT_NEW then | ||
| ExecEventAllBody(" | ExecEventAllBody(" | ||
| Line 441: | Line 441: | ||
| Finally we need to create an onUpdate function for our new weapon art, which unlike the update functions of regular attacks will call SwordArtsCommonFunction instead of AttackCommonFunction. | Finally we need to create an onUpdate function for our new weapon art, which unlike the update functions of regular attacks will call SwordArtsCommonFunction instead of AttackCommonFunction. | ||
| - | < | + | < |
| function ChargeContinue3_onUpdate() | function ChargeContinue3_onUpdate() | ||
| if SwordArtsCommonFunction(" | if SwordArtsCommonFunction(" | ||
| Line 465: | Line 465: | ||
| === Creating hkbVariables and Setting Them in Hks === | === Creating hkbVariables and Setting Them in Hks === | ||
| - | As mentioned in the [#Structure | + | As mentioned in the Structure section, hkbVariables are defined in the hkbBehaviorGraphData (#12799) and hkbBehaviorGraphStringData (#12801) hkobjects and their initial values are set in the hkbVariableValueSet (#12800) hkobject. When adding a new hkbVariable we need to do the following: |
| - Add entries to the " | - Add entries to the " | ||
| - Reference the variable from an hkbVariableBindingSet | - Reference the variable from an hkbVariableBindingSet | ||
| - Set the variable in hks. | - Set the variable in hks. | ||
| - | First, we will look for the " | + | First, we will look for the " |
| Next we will set the minimum and maximum values of our variable in the " | Next we will set the minimum and maximum values of our variable in the " | ||
| Line 482: | Line 482: | ||
| The function which sets the IsEnoughArtPoints variables in hks is called SetSwordArtsPointInfo. For our new followup attack, which is activated by pressing R2, we want to add the following condition under the " | The function which sets the IsEnoughArtPoints variables in hks is called SetSwordArtsPointInfo. For our new followup attack, which is activated by pressing R2, we want to add the following condition under the " | ||
| - | < | + | < |
| elseif IsNodeActive(" | elseif IsNodeActive(" | ||
| val = " | val = " | ||
| Line 519: | Line 519: | ||
| All of them are hkbTwistModifiers which, set the rotation of a range of bones, determined by start and end bone index, in a single axis which is determined by their axisOfRotation hkparams. This rotation is controlled by the variables bound to their " | All of them are hkbTwistModifiers which, set the rotation of a range of bones, determined by start and end bone index, in a single axis which is determined by their axisOfRotation hkparams. This rotation is controlled by the variables bound to their " | ||
| - | The hkbBlenderGenerator " | + | The hkbBlenderGenerator " |
| === Adding HalfBlend Behaviors === | === Adding HalfBlend Behaviors === | ||
| Line 531: | Line 531: | ||
| HalfBlend animations are triggered using the ExecEventHalfBlend function which has the following arguments: | HalfBlend animations are triggered using the ExecEventHalfBlend function which has the following arguments: | ||
| - | < | + | < |
| ExecEventHalfBlend(event_table, | ExecEventHalfBlend(event_table, | ||
| </ | </ | ||
| Line 539: | Line 539: | ||
| Event tables consist of the base event name and a state Id for every state machine between HalfBlend_SM and the behavior' | Event tables consist of the base event name and a state Id for every state machine between HalfBlend_SM and the behavior' | ||
| - | < | + | < |
| GESTURE_DEF0 = 22 | GESTURE_DEF0 = 22 | ||
| GESTURESTART_DEF1 = 0 | GESTURESTART_DEF1 = 0 | ||
| Line 547: | Line 547: | ||
| It is important that the stateIds are in hierarchical order because they are systematically assigned to the default state variables mentioned in the previous section by the function ExecEventHalfBlend. Looking at the ExecEventHalfBlend function we can also see the reason for the eventName naming scheme. | It is important that the stateIds are in hierarchical order because they are systematically assigned to the default state variables mentioned in the previous section by the function ExecEventHalfBlend. Looking at the ExecEventHalfBlend function we can also see the reason for the eventName naming scheme. | ||
| - | < | + | < |
| function ExecEventHalfBlend(event_table, | function ExecEventHalfBlend(event_table, | ||
| if blend_type == ALLBODY then | if blend_type == ALLBODY then | ||