Improved action mechanics

So, I’ve made some improvements in the action-interaction system. It has been a long-needed update. In different games player is able to perform different actions, not only just run, jump and shoot. Well, honestly, a major part the gameplay nowadays is built around actions and interactions, so inadequate and ineffective solutions are unacceptable. This leads us to a completely new domain of problems: we need a separate mechanism for performing character’s actions. The concept is simple: there are some actions, each can be performed by the character. An action has certain properties, like it’s relationship with animation — an action may or may not have an animation, and it’s relationship with character state and other actions — an action may or may not have effects on the game pawn (except the animations effect). Thus, we come to a fairly easy solution — the LBBasicCharacaterController, which is capable of performing actions, which means the ability to play animations and change pawn’s states (virtually, of course). Basically, we’ll be working only with current character’s logic, leaving all underlying activity to the basic character controller. So, we’re interested in only in these methods:

function HandleActionStart(int startedaction)
{ }
function HandleActionStop(int stoppedaction)
{ }
function HandleAnimNotify(int actioncode, int actiondata, 
ActionNotifyTypes notifytype)
{ }

Which do handle action start, action end and animation notifies, which may or may not trigger during the action animation. If we don’t need any character-specific logic or we just want a Garry’s Mod-styled animation player, we can just use the editor. For the designer it’s really simple — all what’s required is to fill out the Character Action List in the editor, which contains all the necessary data.

char_interact_params_1_1.PNG

For example, here I’ve made four basic actions: touch, pick up, put down, carry, three last interactions I’ve been describing earlier, the touch action and the appropriate interaction I’ll describe later. Also I’ve made some auxiliary actions, which really don’t have any effect in the game, except they do play animation. All options of this actions are shown below, for example, them options of the touch action mean that this action is controlled by the certain animation: the action starts with the animation and ends with this animation, also it may have activation restrictions and switch links. What is activation restriction? It’s simple — the action cannot be performed from any actions (state-actions), except these. And the switch link is a type of  a bridge between the actions — upon its finish, the action can be automatically switched to another one. Well, I’ve made some comments (been trying my best) in sources on GitHub.

char_interact_params_1_2

Next thing, what is required — the proper animation, set up and linked to the LBBlendByAction animnode, which blends all animations. There’s also a default node, which is used to blend out, when the default action is active.

animtree_nodes_1_1.PNG

Thus, all, what’s required now is to call a needed action by the SetParamInt() function with param name ‘BeginAction‘ and param value containing the action code. Also, this can be performed from any sub-system, but I’ve chosen Kismet and its keyboard events.

kismet_nodes_4

Well, let’s test this functionality. I’ve made some random (though interdependent) animation sequences (though craggy) for the character, then plugged them into the new action system. There’s a state-action, which defines character’s state — the ‘Sit_Tie_Idle‘ action. There are also two gateway-actions — the ‘Sit_Tie_In‘ as an entrance into this state and the ‘Sit_Tie_Out‘ as an exit. And there are two actions, performed exclusively from the ‘Sit_Tie_Idle‘ state — the ‘Sit_Tie_Gym_1‘ and the ‘Sit_Tie_Gym_2‘.

Now we can make our character do different things. For example, we can watch our strange character doing its strange exercises (some kind of gym, maybe?). However, everything happens in the right order, for example, it can’t just start walking or jumping from a stretched pose, it has to stand up first.

Improved action mechanics

Interaction mechanics

Lets make another step into a complex interaction system. The final target is the ability to interact with most objects in the level. There are many possible kinds of interactions. As I see, they are:

  • Pick up, hold, drop down (charater->object)
  • Enter, stay, leave some area (character->world)
  • Touch, hit some object (characters<->objects)
  • Intersect the line (ray) (characters<->objects)
  • Triggered interaction (player, script->characters, objects)
  • Conditional, programmed (script<->characters, objects)
  • Combinations of listed above

First of all, LBInteractableMechanism provides a basic framework for actor interactions, this mechanism carries out all data transaction between LBActor, LBPawn and other actor classes. Two first interactions are implemented by LBInventoryPawnMechanism, LBAreaCheckingMechanism and LBLocationTriggerMechanism. Line and ray intersections are implemented by LBTargetingPawnMechanism. Triggered interactions are partially implemented by LBKismetEventActivatorLBGet***SeqLBSetP***Seq.

Let’s test this interaction system on a pre-set character in a test level. The desired result is an ability to put objects in certain places, where their presence causes certain events. In this example only three interaction types are involved: the first, the second and the fifth. The procedure should be like this:

  • The player initiates the drop-down action by the character
  • The carried object is released from the inventory
  • If this object is put in a specified location:
    • The object is forced to move to specific point
    • The special effect is shown

Projected to a Kismet-Mechanism model, this procedure takes the following form.

First, the key is pressed by a player, which tells the LBSetParamIntSeq to set ActivateInteraction parameter of the Pawn_Interactable_Controller to 6. Then the put down sequence is executed by the pawn and it’s mechanisms.

activate_interaction_seq

After the object is on the floor, it interacts with LBLocationTriggerMechanism, which checks its area every n-th second (tick) of game time (yes, it’s quite expensive). If the objects passes center-to-center distance test, a special Kismet event is activated, which is connected to a group of LBSetParamBoolSeq. The first LBSetParamBoolSeq node sets the parameter bEnabled of LBTargetedMovementMechanism in our object to true, so it could move to the center point. The second node sets the parameter bVisible of LBVisibilityModifier in special effect object to true, so we could see the glowing.

event_handle_seq.PNG

That’s it. Now the objects have an ability to several consequences. In our case, the capsule, carried by a character to certain stand, moves to it center and triggers a glow effect.

interaction

Of course, it’s not enough, because there are three more interactions (at least), that need to be implemented. Some of them have their preliminary handlers, and some of them don’t. The further research and development is carried out.

Interaction mechanics

Inventory mechanics

Well, it’s up. Time to test some inventory mechanics. Start with an LBPawn (xc_char), which has been already set up. The character itself doesn’t seem to have any hands, so it uses its mouth to pick up objects. It’s quite a complex task to make a correct body and neck bend animation (it’s still not correct). Well, handling hand animations is even hareder. But in future I’ll think about it. Maybe.

To handle all interactions the pawn interactable controller is used (LBPawnInteractableController), which is a part of mechanism system. All actions are set in the editor by setting names of corresponding animations.

pawn_action_list

All animation names are linked to animtree nodes, and the slider node (blendbyaction) is controlled from the code.

animtree_example

For example, the slider for drop down action is set to child number five on activation of interaction number six (full code).

else if (value == 6)
 {
  //activate interaction: Drop down
  if (curaction != 0)
   return; 
  if (!CheckInteractioConditions(3))
  {
   LogError("proc: ActivateInteraction() return: [CheckInteractioConditions(3)] returned false!"); 
   return; 
  }
  blendbyaction.SetActiveChild(6, 0.5);
  actionseqs[5].SetPosition(0.0, false);
  actionseqs[5].PlayAnim(false, 0.8, 0.0); 
 }

Yes, slider control is hard-coded in the mechanism, it’s a shame, I know. Then, the animation is played. It fires animation notifies, which are set in the begining, the middle, where the action is performed and in the very end. Each one of them calls its own part of code (full code).

else if (notifynode.NodeName == 'Interaction_DropDown')
 {
  if (notifytype == AnimNotifyTypes_ActionStart)
  {
  curaction=6;
  }
  else if (notifytype == AnimNotifyTypes_PerformAction)
  {
   PerformInteraction(3);
  }
  else if (notifytype == AnimNotifyTypes_ActionEnd)
  {
   blendbyaction.SetActiveChild(0, 0.5);
   curaction=0;
  }
 }

The PerformInteraction function performes the real work: sets all properties of invoked objects. In this case it just tells the held object’s mechanism that it’s free to go.

All inventory objects are equipped with LBAttachMechanism.( I don’t use the default attach system, because it’s a total hemorrhoid). And the pawn has its counterpart – the LBInventoryPawnMechanism, which does all the work.

inv_mech.png

It has a modifiable parameter – a socket, where all inventory is attached (in our case – a mouth). When needed, the parameter of this mechanism (InventoryMechanism) is set to certain actor. After several checks it adds this actor to inventory or doesn’t add this actor because it’s impossible. It could be implemented with one interaction from pawn controller mechanism.

SetTargetParam(parent, InventoryMechanism, 'AddObject', otheractor);

The other actor is selected from all actors in specified area in LBAreaCheckingMechanism based on the distance from the center of this area. It’s still not the best solution, because each time it uses an iterator, wich iterates through many objects in the level. I’m not using the default collision-overlapping system, because it’s a total hemorrhoid and moreover it’s a slow solution too.

Well, this was a first step towards a complex interaction system. It’s interesting, because many modern games do have quite a simple interaction systems. Even RPG games are tend to have more and more plain character-static world interaction system, but I think you can prove me wrong. Well, the result is shown below – the character is now able to pick up and put down any objects in the level.

 

Inventory mechanics