public void initializeController(GameObject[] agents, Vector2[] boundingPoly, World.TrajectoryMap trajectory, Vector2[] formationPositions, float agentHeight,
                                  float deltaX, float deltaY, float simulationFactor, float vehicleDt, bool formationDecreasingGoalVelocity)
 {
     this.simulationSpeedFactor = simulationFactor;
     this.vehicleDt             = vehicleDt;
     this.agentHeight           = agentHeight;
     this.agents = agents;
     this.formationDecreasingGoalVelocity = formationDecreasingGoalVelocity;
     // Get trajectory coordinates
     this.trajectory = new Vector3[trajectory.x.Length];
     for (int i = 0; i < trajectory.x.Length; i++)
     {
         this.trajectory [i] = new Vector3(trajectory.x [i], agentHeight, trajectory.y [i]);
     }
     // Get trajectory orientations
     this.trajectoryOrientation = new float[trajectory.theta.Length];
     for (int i = 0; i < trajectory.theta.Length; i++)
     {
         this.trajectoryOrientation [i] = trajectory.theta [i];
     }
     // Get timestamps for each step of the trajectory
     this.trajectoryTimestamps = new float[trajectory.t.Length];
     for (int i = 0; i < trajectory.t.Length; i++)
     {
         this.trajectoryTimestamps [i] = trajectory.t [i];
     }
     // set formationPositions in parent class to relative positions from the virtual center
     setRelativeFormationPositions(formationPositions, formationPositions.Length - 1);
     // Get starting absolute positions and relative to the leader positions.
     this.desiredRelativePositions = getDesiredPositions(agents.Length - 1, false);
     this.desiredAbsolutePositions = getDesiredPositions(agents.Length - 1);
     // Visualize starting desired positions
     //Visualizer.visualizePoints(this.desiredRelativePositions);
     //Visualizer.visualizePoints(this.desiredAbsolutePositions);
     // Set a controller within each agent
     agents [0].AddComponent <LeaderController> ();
     for (int i = 1; i < agents.Length; i++)
     {
         agents [i].AddComponent <FootballPlayerController> ();
     }
     formationRectangle = new  VirtualStructureRectangle(formationPositions, boundingPoly, deltaX, deltaY, agentHeight);
     // Get nearest agent to opponent player
     winnerIdx = agents.Length - 1;
     for (int t = 1; t < agents.Length - 1; t++)
     {
         // Get closest player to opponent
         float minDistance = float.MaxValue;
         for (int i = 1; i < agents.Length - 1; i++)
         {
             // If this agent has failed moving before, due to moving the formation out of the polygon
             float distance = Vector2.Distance(new Vector2(agents[0].transform.position.x, agents[0].transform.position.z), getDesiredPosition(i - 1));
             if (distance < minDistance)
             {
                 minDistance = distance;
                 winnerIdx   = i;
             }
         }
     }
     currentVelocities = new Vector3[agents.Length];
 }
 public VirtualStructureRectangle(VirtualStructureRectangle structure)
 {
     center   = new Vector3(structure.center.x, structure.center.y, structure.center.z);
     vertices = new PointInfo[structure.vertices.Length];
     for (int i = 0; i < vertices.Length; i++)
     {
         vertices[i] = new PointInfo(structure.vertices[i].pos, structure.vertices[i].vel, structure.vertices[i].orientation, structure.vertices[i].currentTime);
     }
 }
    private bool moveNearestAgent(Vector3 target, int nearestAgentIdx)
    {
        // Get next position information of the nearestAgentIdx
        FootballPlayerController nearestAgentController = agents[nearestAgentIdx].GetComponent <FootballPlayerController>();
        PointInfo lastPos = nearestAgentController.getLastPosInfo();
        BaseModel model   = nearestAgentController.getMotionModel();

        PointInfo nextPoint;

        if (formationDecreasingGoalVelocity)
        {
            PointInfo goalPointInfo = new PointInfo(target, Vector3.zero, Vector3.forward, lastPos.currentTime + vehicleDt);
            nextPoint = model.moveTowardsWithDecreasingVelocity(lastPos, goalPointInfo, nearestAgentController.getWorld(), false);
        }
        else
        {
            PointInfo        goalPointInfo = new PointInfo(target, getAgentVelocity(0), getAgentOrientation(0), lastPos.currentTime + vehicleDt);
            List <PointInfo> path          = model.completePath(lastPos, goalPointInfo, nearestAgentController.getWorld(), false);
            if (path != null && path.Count > 0)
            {
                nextPoint = path [0];
            }
            else
            {
                return(false);
            }
        }
        nearestAgentController.getWorld().currentVelocities[nearestAgentIdx - 1] = nextPoint.vel;
        setCurrentVelocity(nearestAgentIdx, nextPoint.vel);

        // Get previous position of agent
        Vector3 prevPos = new Vector3(agents[nearestAgentIdx].transform.position.x, agents[nearestAgentIdx].transform.position.y, agents[nearestAgentIdx].transform.position.z);

        agents[nearestAgentIdx].transform.position = nextPoint.pos;
        Vector2[] tmpDesiredRelativePositions = getDesiredPositions(winnerIdx, false, false);
        // Store previous positional information
        Vector2[] prevRelativeFormationPositions = new Vector2[this.relativeFormationPositions.Length];
        for (int i = 0; i < prevRelativeFormationPositions.Length; i++)
        {
            prevRelativeFormationPositions [i] = new Vector2(this.relativeFormationPositions[i].x, this.relativeFormationPositions[i].y);
        }
        Vector2[] prevDesiredRelative = getDesiredPositions(winnerIdx, false, false);
        Vector2[] prevDesiredAbsolute = getDesiredPositions(winnerIdx, false, true);
        // Calculate new positional information
        setRelativeFormationPositions(this.relativeFormationPositions, nearestAgentIdx - 1);
        this.desiredRelativePositions = getDesiredPositions(winnerIdx, false, false);
        this.desiredAbsolutePositions = getDesiredPositions(winnerIdx, false, true);

        // Get virtual center controller and its last position
        FootballPlayerController virtualCenterController = agents[agents.Length - 1].GetComponent <FootballPlayerController>();
        PointInfo lastCenterPos = virtualCenterController.getLastPosInfo();
        // Move center in the same way the nearest agent moves, anchoring the formation to the agent
        Vector2 desiredCenterPosition = getDesiredPosition(agents.Length - 2);
        Vector3 desiredCenter3D       = new Vector3(desiredCenterPosition.x, agentHeight, desiredCenterPosition.y);
        // Get copy of formation rectangle
        VirtualStructureRectangle tmp = new VirtualStructureRectangle(formationRectangle);

        // Update rectangle with new desired center
        tmp.updateRectangle(desiredCenter3D);

        // Check if new rectangle gets out of the bounding polygon in case the agent is not already near the boundary
        if (lastPos.currentTime > 30.0f && !tmp.isInPolygon(nearestAgentController.getWorld().boundingPolygon))
        {
            // If it is not entirely inside, restore changes and return false
            agents[nearestAgentIdx].transform.position = prevPos;
            this.relativeFormationPositions            = prevRelativeFormationPositions;
            this.desiredAbsolutePositions = prevDesiredAbsolute;
            this.desiredRelativePositions = prevDesiredRelative;
            return(false);
        }


        // Otherwise make all necessary changes to variables of the closest agent and formation center
        agents[nearestAgentIdx].transform.position = prevPos;
        agents [nearestAgentIdx].transform.LookAt(agents[nearestAgentIdx].transform.position + (target - agents[nearestAgentIdx].transform.position).normalized);
        PointInfo nextPos = new PointInfo(nextPoint.pos, Vector3.zero, nextPoint.orientation, nextPoint.currentTime + vehicleDt);

        nearestAgentController.setNextPosInfo(nextPoint);

        agents [agents.Length - 1].transform.position = desiredCenter3D;
        lastCenterPos.pos         = desiredCenter3D;
        lastCenterPos.vel         = nextPoint.vel;
        lastCenterPos.orientation = nextPoint.orientation;
        lastCenterPos.currentTime = nextPoint.currentTime;
        formationRectangle        = tmp;
        agents [agents.Length - 1].transform.LookAt(agents[agents.Length - 1].transform.position + (target - agents[agents.Length - 1].transform.position).normalized);
        virtualCenterController.setLastPosInfo(lastCenterPos);
        nextPos = new PointInfo(lastCenterPos.pos, Vector3.zero, lastCenterPos.orientation, lastCenterPos.currentTime + vehicleDt);
        virtualCenterController.setNextPosInfo(nextPos);
        virtualCenterController.setPlay(false);
        return(true);
    }