Exemplo n.º 1
0
    public void MoveCohort(Vector2 goTo, Unit_local toFollow)
    {
        Stop();
        masterTask = new Task(null, Task.actions.move, goTo, toFollow);
        List <Unit_local> thisIsToSupressWarnings = new List <Unit_local>(members);
        float             weakLinkETA             = 0;

        foreach (Unit_local toMove in thisIsToSupressWarnings)
        {
            if (toMove.stats.isMobile == false)
            {
                throw new InvalidOperationException("Cannot move a cohort that includes immobile members. Units should be sorted out in Gamestate.CombineActiveCohorts.");
            }
            else
            {
                float thisMoversETA = Vector2.Distance(toMove.transform.position, goTo) / toMove.stats.speed;
                if (thisMoversETA > weakLinkETA)
                {
                    weakLinkETA = thisMoversETA;
                }
            }
        }
        UnitRelativePositionSorter vsGoTo = new UnitRelativePositionSorter(goTo);

        vsGoTo.DirectionMode();
        List <Unit_local> unitsByDirection = new List <Unit_local>(members);

// This is a list of units sorted by their compass-direction from the destination point.
        (unitsByDirection).Sort(vsGoTo);
        vsGoTo.DistanceMode();
        List <Unit_local> unitsByDistance = new List <Unit_local>(members);

        unitsByDistance.Sort(vsGoTo);
// Units are removed from unitsByDirection (and unitsByDistance) as they are assigned to groups, so this is a way of saying "while there are unassigned units."
        while (unitsByDirection.Count > 0)
        {
// We take the current closest unit to the distination... (previous cycles of grouping will have removed closer units)
            Unit_local        sliceLeader                   = unitsByDistance[0];
            int               leaderDirectionIndex          = unitsByDirection.IndexOf(sliceLeader);
            float             leaderDistanceFromDestination = vsGoTo.DistanceOf(sliceLeader);
            float             leaderRadius                  = sliceLeader.bodyCircle.radius;
            int               totalUnaccounted              = unitsByDirection.Count;
            List <Unit_local> slice = new List <Unit_local> {
                sliceLeader
            };
// That unit will be the group leader. We check the units clockwise and counter-clockwise from its' position on the imaginary circle of unit positions aronud the destination.
            for (int sign = -1; sign <= 1 && slice.Count < unitsByDirection.Count; sign = sign + 2)
            {
                // this is a loop-breaker variable
                for (int indexOffset = sign; indexOffset < 1000; indexOffset += sign)
                {
                    Unit_local inQuestion = unitsByDirection[(leaderDirectionIndex + indexOffset + totalUnaccounted) % totalUnaccounted];
// These are all in radians...
                    float directionOfLeader       = vsGoTo.DirectionOf(sliceLeader);
                    float directionInQuestion     = vsGoTo.DirectionOf(inQuestion);
                    float circumferencialDistance = Mathf.Abs(directionOfLeader - directionInQuestion);
                    circumferencialDistance = Mathf.Min(circumferencialDistance, 2 * Mathf.PI - circumferencialDistance);
// ...until here, when circumferentialDistance becomes a measure of real distance.
                    circumferencialDistance *= leaderDistanceFromDestination;
// In both directions, we stop checking when the next-closest (by direction) unit doesn't fall within the shadow cast by the leader, if you imagine the destination as a light source.
                    if (circumferencialDistance < leaderRadius && inQuestion != sliceLeader)
                    {
                        slice.Add(inQuestion);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            slice.Sort(vsGoTo);
// This line makes it so that all groups arive at the same time.
// PROBLEM: this will make fast units move slugishly for very short journeys.
            float leaderSpeed = Mathf.Clamp(Vector2.Distance(sliceLeader.transform.position, goTo) / weakLinkETA, 0, sliceLeader.stats.speed);
            sliceLeader.work(new Task(sliceLeader, Task.actions.move, goTo, toFollow, -1, leaderSpeed));
            for (int followerIndex = 1; followerIndex < slice.Count; ++followerIndex)
            {
                slice[followerIndex].work(new Task(slice[followerIndex], Task.actions.move, goTo, sliceLeader));
            }
            foreach (MobileUnit_local sliceMember in slice)
            {
                assignments.Add(sliceMember.task);
                unitsByDirection.Remove(sliceMember);
                unitsByDistance.Remove(sliceMember);
            }
        }
    }