Learning Game AI Programming with Lua
上QQ阅读APP看书,第一时间看更新

Creating a group of followers

In this example, we're going to build another AI type called a follower agent. This time, a group of followers will stay together and move toward their leader. The leader, on the other hand, is still the same seeking agent that will randomly move around the sandbox, completely oblivious to the group of followers behind it.

Group-based movement using separation, cohesion, and alignment

To create followers, we'll use multiple steering forces to combine, separate, and align our agents to the leader agent they are following.

Create the Lua file as follows:

src/my_sandbox/script/FollowerAgent.lua

FollowerAgent.lua:

require "AgentUtilities";

local leader;

function Agent_Initialize(agent)
    AgentUtilities_CreateAgentRepresentation(
        agent, agent:GetHeight(), agent:GetRadius());

    -- Randomly assign a position to the agent.
    agent:SetPosition(Vector.new(
        math.random(-50, 50), 0, math.random(-50, 50)));

    -- Assign the first valid agent as the leader to follow.
    local agents = Sandbox.GetAgents(agent:GetSandbox());
    for index = 1, #agents do
        if (agents[index] ~= agent) then
            leader = agents[index];
            break;
        end
    end
end

function Agent_Update(agent, deltaTimeInMillis)
    local deltaTimeInSeconds = deltaTimeInMillis / 1000;
    local sandboxAgents = Sandbox.GetAgents(agent:GetSandbox());

    -- Calculate a combining force so long as the leader stays
    -- within a 100 meter range from the agent, and has less than
    -- 180 degree difference in forward direction.
    local forceToCombine =
        Agent.ForceToCombine(agent, 100, 180, { leader } );

    -- Force to stay away from other agents that are closer than
    -- 2 meters and have a maximum forward degree difference of
    -- less than 180 degrees.
    local forceToSeparate =
        Agent.ForceToSeparate(agent, 2, 180, sandboxAgents );

    -- Force to stay away from getting too close to the leader if
    -- within 5 meters of the leader and having a maximum forward
    -- degree difference of less than 45 degrees.
    local forceToAlign =
        Agent.ForceToSeparate(agent, 5, 45, { leader } );

    -- Summation of all separation and cohesion forces.
    local totalForces =
        forceToCombine + forceToSeparate * 1.15 + forceToAlign;
    
    -- Apply all steering forces.
    AgentUtilities_ApplyPhysicsSteeringForce(
        agent, totalForces, deltaTimeInSeconds);
    AgentUtilities_ClampHorizontalSpeed(agent);
    
    local targetRadius = agent:GetTargetRadius();
    local position = agent:GetPosition();
    local destination = leader:GetPosition();
    
    -- Draw debug information for target and target radius.
    Core.DrawCircle(
        position, 1, Vector.new(1, 1, 0));
    Core.DrawCircle(
        destination, targetRadius, Vector.new(1, 0, 0));
    Core.DrawLine(position, destination, Vector.new(0, 1, 0));
end

Our following agent is very similar to the pathing agent; it first finds a leader during the initialization and then uses cohesion and alignment steering forces to try and stay as close to the leader as possible while using a separation force at the same time to stay at least two meters away from all other agents, including the leader:

Sandbox.lua:

function Sandbox_Initialize(sandbox)

    ...
    
    -- Create a pursuing agent to follow the seeking agent.
    Sandbox.CreateAgent(sandbox, "PursuingAgent.lua");

    -- Create a group of followers that follow the seeking agent.
    for i=1, 5 do
        Sandbox.CreateAgent(sandbox, "FollowerAgent.lua");
    end

    ...

end

Creating five followers and drawing debug circles around them for their separation areas makes it very easy to see how each force applies to the agent's movements.

Creating a group leader and followers.