How to implement an Agent Aspect

In this HowTo we will focus on creating an agent aspect. The agent aspect is what separates a mere object in the world from a 'thinking' entity. The agent aspect can use perceptors to receive information regarding its environment. Based on this information and (usually also) the internal state of the agent aspect, it can then determine the optimal reaction to the situation. He can then use his effectors to realize this goal. This means that the work necessary to implement an agent aspect is split into two parts: Creating its perceptors and effectors, and the actual think-operation during each simulation step.

The first step for each agent aspect is to secure its perceptors and effectors. This is done in the OnLink() function. This function is a feature of the object hierarchy. It is called automatically on a node after it has been inserted into the object hierarchy. This procedure can be seen as the point of construction within the hierarchy. Looking at the SurvivalAgentAspect (used in the Survival Simulation) we see that an agent aspect requests perceptors and effectors from the current simulations control aspect:

void SurvivalAgentAspect::OnLink()
{
  // locate control aspect
  shared_ptr<SurvivalControlAspect> control = shared_static_cast<SurvivalControlAspect>(GetScene()->GetChildOfClass("SurvivalControlAspect"));
  
  if (!control)
  {
    GetLog()->Error() << "ERROR: Could not locate SurvivalControlAspect..." << endl;
    return;
  }

  // we now have the control aspect

  shared_ptr<AgentAspect> agent = shared_static_cast<AgentAspect>(make_shared(GetSelf()));

  // request some effectors
  mForceEffector = shared_static_cast<ForceEffector>(control->RequestEffector(agent, "kerosin/ForceEffector"));
  
  // request some perceptors
  mVisionPerceptor    = shared_static_cast<VisionPerceptor>(control->RequestPerceptor(agent, "kerosin/VisionPerceptor"));
  mLineSegmentPerceptor  = shared_static_cast<LineSegmentPerceptor>(control->RequestPerceptor(agent, "LineSegmentPerceptor"));
}

The agent aspect first retrieves a control aspect. Using this control aspect it requests a ForceEffector, a VisionPerceptor and a LineSegmentPerceptor by specifying the correct class names. As we have already seen in the previous HowTo, the control aspect makes sure that it is 'legal' to request these perceptors and effectors.

Now, the simulation can begin. Every frame each agent aspect gets the chance to update its internal state and make use of its perceptors and effectors. This is done in the Think() function, which every agent aspect needs to implement. The interface for this function is dictated by the kerosin::AgentAspect class, from which all custom agent aspects must derive:

  //! this method must be implemented for the agents 'think' behavior
  virtual void Think(float deltaTime) = 0;

During the call to Think() each agent aspect just makes use of its perceptors and effectors as discussed in previous HowTos. There is not anything more to it.