RVO Temporarily Stop Avoiding Specific Object

Hey Again Aron. Thanks again for writing this and supporting in the forums as well, makes life so much easier.

I have a new situation. I have a bunch of agents that are on different teams. All of the agents can move around. I need agents to avoid agents on all teams, except for when its time to attack an agent, then it needs to stop avoiding that agent as long as its attacking it, and if it stops attacking it needs to start avoiding again.

I was thinking of how I could do this with layers but I’m not sure its possible. Like if i have 2 agents attacking each other, i could put them in a separate layer together and tell them not to avoid each other. But then if i have another pair of agents attacking each other, if i put them in the same attacking layer then these 4 agents will all stop avoiding each other, when it should be 2 pairs of agents only ignoring the other agent in the pair (in other words I would need 2 layers). And if you have a whole bunch of agents attacking each other, a whole bunch of layers would be required.

It would be much easier if i could just keep the agents in the same avoidance layer as when they are not attacking, and then somehow specify a specific agent being attacked should be excluded from the attacking agent’s avoidance calculations. Is this possible?

Thanks

I thought of doing this, where when the attacking agent is doing a move calculation toward an agent it wants to attack, the agent it wants to attack is very briefly put in a different layer that this attacking object does not collide with. as soon as the movement vector is calculated, they’re put back into the same layer together again. If this is done for each attacking agent on the map, in theory i think it should work. IT does not however, and the attacking agent still tries to avoid the agent being attacked as they move together

    // Update is called once per frame
    public void MoveCharacter(Vector3 targetPoint, float deltaTime)
    {
        RVOController enemyRVOController = null; // RVO controller on the agent this agent is attacking
        if ( basicProperties.focusTarget != null ) // this agent has something to attack
        {
            enemyRVOController = basicProperties.focusTarget.GetComponent<RVOController>();
            enemyRVOController.layer = RVOLayer.Layer20; // Layer 20 isnt used for anything, just put enemy here temporarily while movement vector is calculated, 
            //so i would think it would be excluded from movement vector calculations.
        }

        // do the movement calculations
        controller.SetTarget(targetPoint, basicProperties.minMoveSpeedLimit, basicProperties.maxMoveSpeedLimit);
        Vector3 delta = controller.CalculateMovementDelta(transform.position, deltaTime * 1);

        if (basicProperties.focusTarget != null)
        {
            // put the enemy back into the default layer before this script is executed for any other agents on the map, 
            // so that any other agent will continue to avoid the enemy of this agent
            enemyRVOController.layer = RVOLayer.DefaultAgent;
        }

        characterController.Move(delta); 
    }

Hi

This is not possible at the moment without making a few changes to the code.
You can add a single field ignoredAgent to the RVOController, the IAgent interface and the Pathfinding.RVO.Agent class. Then in the agent class in the InsertAgentNeighbour add a check at the beginning of the method to see if the agent is the ignored agent, and if so, just return.

The rvo simulation happens at the same time for all agents at a time controlled by the RVOSimulator component, that’s why your method of changing the layer temporarily does not work.

Hi Aron. Thanks for your reply.

I’m not sure what you mean because I don’t 100% understand how all of the components work together.

I added this at the top of the RVOAgent class (RVOAgent.cs):

public class Agent : IAgent {
    //Current values for double buffer calculation

    public string agentName;
    public string ignoredBy;

    ...

In this same class i added this to the top of InsertAgentNeighbour

	internal float InsertAgentNeighbour (Agent agent, float rangeSq) {

        if ( this.agentName == agent.ignoredBy )
        {
            return rangeSq;
        }

		// Check if this agent collides with the other agent
		if (this == agent || (agent.layer & collidesWith) == 0) return rangeSq;

so that if the neighbour ignoredBy value equals this agent’s agentName value, this agent will ignore the neighbour agent.

I don’t know where to go from here though, and I’m not sure what you mean by the IAgent interface. I need a way to get and set the values for ignoredBy and agentName during runtime. Could you please help me out with the specific code change(s) that would be required?

Thanks

Hi

If you want to go with your name approach, here is a diff which I think will make all the changes you need: https://pastebin.com/MrLbL6WS