You know that eager to implement AI for you characters but they should follow the terrain or stairs of a building? The first step to achieve this goal was to look for a solution where the placement of the feet was corrected by using the actual scene. This was found within the locomotion system within the tutorial session of Unity3D. After following the tutorials this was easily done. Now came the hard part: make it intelligent. As a goal we stated the AI should only be: aware we exist, get to us and if we are close enough start to attack.

There are a lot of AI packages some are only targeting one aspect from AI like Pathfinding, Behavior Trees, NavMeshes, … But we wanted the whole bunch and preferable providing closely tight integration between all the AI’s components. This is where we discovered something called Rain{One} from RivalTheory. Tutorials where available, API calls documented and a support forum was there as well so we decided to buy it and tried to implement the attaching behavior as described above.

After following the tutorials over and over again we succeeded quite fast in setting up a pathfinding solution but making the character ‘think’ was not so easy. For implementing a behavior tree we got very good support from RivalTheory. We asked and they answered in a proper timeframe. Although the behavior tree was up and running we didn’t succeed in triggering the attack sequence. This was because the character must be exactly at the same spot as the target to succeed. Which of course cannot happen because that would mean the enemy and the player should be on top of each other!

That’s where the code from the Rain{One} user Siflandoly came in (link here) showing how to interact between your behavior tree and the actions. Very interesting was how an action was called to update the variables used within the behavior tree.

Using that code as an example we recreated our behavior tree and implemented an action to determine when the attack can be triggered. This is how the behavior tree looks like in XML format:

<?xml version="1.0" encoding="utf-8"?>
<behaviortree name="spider" repeatuntil="">
  <variable name="distanceToTarget" initialvalue="0" ></variable>
  <variable name="closeEnough" initialvalue="5" ></variable>
  <sequencer name="root" repeatuntil="running">
    <parallel name="Parallel progressing" repeatuntil="" fail="all" succeed="any" tiebreaker="fail">
      <sequencer name="DetectTarget" repeatuntil="">
        <detect name="detect target" repeatuntil="" aspect="player" variable="target" ></detect>
        <assign name="found target" repeatuntil="" expression="hastarget= 1" min="0" max="1" ></assign>
      </sequencer>
      <sequencer name="Move To Target" repeatuntil="">
        <condition name="hasTarget" repeatuntil="" expression="hastarget== 1" ></condition>
        <move name="MovetoTarget" repeatuntil="" movetarget="target" movespeed="4" animation="locomotion" 
animationbasespeed="1" ></move>
      </sequencer>
      <sequencer name="Attack" repeatuntil="">
        <action name="CheckDistance" repeatuntil="" classname="CheckDistance" ></action>
        <condition name="Close enough to target" repeatuntil="" expression="closeEnough >= distanceToTarget" >
</condition>
        <animate name="Attack Animation" repeatuntil="" animation="attack" waitforsec="1" ></animate>
      </sequencer>
    </parallel>
  </sequencer>
</behaviortree>

And this is how the behavior tree looks like in Rain{One}'s behavior tree editor:

Behavior Tree

The trick was to call the CheckDistance routine within the attack sequence which updates the distanceToTarget variable. After this succeeded the condition Close enough to target (closeEnough >= distanceToTarget) is checked and when that one is succeeded the actual attack animation is played.

For every action within Rain{One} you need a script executing some stuck. In this case checking the distance between the target and the current agent. This is the C# code we used:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using RAIN.Core;
using RAIN.Belief;
using RAIN.Action;

public class CheckDistance : Action
{
    private float closeEnough = 5f;
    private float distanceToTarget = 0f;

    public CheckDistance()
    {
        actionName = "CheckDistance";
    }

    public override ActionResult Start(Agent agent, float deltaTime)
    {
        if (agent.actionContext.ContextItemExists("closeEnough"))
            closeEnough = agent.actionContext.GetContextItem<float>("closeEnough");
        if (agent.actionContext.ContextItemExists("distanceToTargetX"))
        {
            distanceToTarget = 100f;
            agent.actionContext.SetContextItem<float>("distanceToTarget", distanceToTarget);
        }
        return ActionResult.SUCCESS;
    }

    public override ActionResult Execute(Agent agent, float deltaTime)
    {
        GameObject playerObj = agent.actionContext.GetContextItem<GameObject>("target");
        if (playerObj != null)
        {            
            if (agent.actionContext.ContextItemExists("distanceToTarget"))
            {
                float distanceToTarget = (agent.Avatar.gameObject.transform.position - 
                        playerObj.transform.position).sqrMagnitude;
                agent.actionContext.SetContextItem<float>("distanceToTarget", distanceToTarget);
                Debug.Log(string.Format("----> distanceToTarget : {0}", distanceToTarget));
            }
            return ActionResult.SUCCESS;
        }
        else
        {
            //Debug.Log("No target assigned.");
            return ActionResult.FAILURE;
        }
    }

    public override ActionResult Stop(Agent agent, float deltaTime)
    {
        return ActionResult.SUCCESS;
    }
}

But how can you trigger the locomotion from within this behavior tree?

This can easely be achieved by setting different Motion Groups within the Leg Controller of the Locomotion system. This motion groups can be used as names for the animation within the behavior tree's Animation primitive.

Properties

You see I created a new Motion Group attack which contains the Attack animation. Within my behavior tree I'm calling attack instead of Attack. That did the trick. An article on how to use the motion groups in depth is available in the Locomotion documentation.

See you next time!

Next Post Previous Post