Daniel Lip
Daniel Lip

Reputation: 11329

How can I calculate and create positions of triangle shape?

private void FormationTriangle()
{
    newpositions = new List<Vector3>();
    for (int x = 0; x < squadMembers.Count; x++)
    {
        for (int y = x; y < 2 * (squadMembers.Count - x) - 1; y++)
        {
            Vector3 position = new Vector3(x, y);
            newpositions.Add(position);
        }
    }

    move = true;
    formation = Formation.Square;
}

The loops are wrong. It put the squadMembers in one line one above the other. Not even close to a triangle shape. I want the squadMembers to stand in a triangle shape.

This is the moving part: But the problem is with the loops calculating the triangle shape positions. Other formations I did are working fine.

private void MoveToNextFormation()
{
    if (randomSpeed == false)
    {
        if (step.Length > 0)
            step[0] = moveSpeed * Time.deltaTime;
    }

    for (int i = 0; i < squadMembers.Count; i++)
    {
        squadMembers[i].transform.LookAt(newpositions[i]);
        if (randomSpeed == true)
        {
            squadMembers[i].transform.position = Vector3.MoveTowards(
                squadMembers[i].transform.position, newpositions[i], step[i]);
        }
        else
        {
            squadMembers[i].transform.position = Vector3.MoveTowards(
                squadMembers[i].transform.position, newpositions[i], step[0]);
        }
        if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < 
            threshold)
        {
            if (squareFormation == true)
            {
                Vector3 degrees = new Vector3(0, 0, 0);
                Quaternion quaternion = Quaternion.Euler(degrees);
                squadMembers[i].transform.rotation = Quaternion.Slerp(
                    squadMembers[i].transform.rotation, quaternion, 
                    rotateSpeed * Time.deltaTime);
            }
            else
            {
                squadMembers[i].transform.rotation = Quaternion.Slerp(
                    squadMembers[i].transform.rotation, quaternions[i], 
                    rotateSpeed * Time.deltaTime);
            }
        }
    }
}

Upvotes: 4

Views: 657

Answers (2)

Ruzihm
Ruzihm

Reputation: 20249

This answer will produce a triangle arranged like this:

    x
   x x
  x x x
 x x x x
x x x x x 

Or, if there aren't enough to fill a full triangle:

    x
   x x
  x x x
 x x x x
x   x   x 

Since you aren't guaranteed a perfectly triangular number of units, you should overestimate how big your triangle is, keep count of how many units you have placed, and then quit placing them when you reach your limit.

First, find the height of the smallest triangular number greater than your number of units, and that triangular number itself:

int height = Mathf.CeilToInt( (Mathf.Sqrt(8*squadMembers.Count+1f)-1f)/2 ) 
int slots = (int)(height * (height+1f)/2f)

Then, find the position of the first unit. We need to know how many rows of slots we have and how wide the bottom row of slots is:

float verticalModifier = 0.8f;  // 0.8f to decrease vertical space
float horizontalModifier = 1.25f; // 1.25f to increase horizontal space

float width = 0.5f * (height-1f);
Vector3 startPos = new Vector3(width* horizontalModifier, 0f, (float)(height-1f) * verticalModifier);

Then, add until you've added enough

int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0 ; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++) {
    for (int i = 0 ; i < rowNum+1 && newpositions.Count < squadMembers.Count ; i++ ) {
        float xOffset = 0f;

        if (rowNum+1 == height) {
            // If we're in the last row, stretch it ...
            if (finalRowCount !=1) {
                // Unless there's only one item in the last row. 
                // If that's the case, leave it centered.

                xOffset = Mathf.Lerp(
                        rowNum/2f,
                        -rowNum/2f,
                        i/(finalRowCount-1f)
                        ) * horizontalModifier;
            }
        }
        else {
            xOffset = (i-rowNum /2f) * horizontalModifier; 
        }

        float yOffset = (float)rowNum * verticalModifier; 

        Vector3 position = new Vector3(
                startPos.x + xOffset, 0f, startPos.y - yOffset);
        newpositions.Add(position);

    }
}

Upvotes: 6

Let's see what the list of positions contains for a simple value, n = 3

First, loop x from 0 to 2 (3 - 1) Then for each x, loop from x to 4-x (3*2 - x - 1 - 1)
Remembering that a<b is the same as a<=b-1

That gives us...

0,0
0,1
0,2
0,3
0,4
1,1
1,2
1,3
2,2

Which is a lot of positions. Certainly more than 3 units can occupy! At least it is a triangle:

X\Y 0  1  2  3  4  
0   #  #  #  #  #  
1      #  #  #  
2         #  

The main problem is that you're generating way more positions than needed and expecting to fill it somehow.

You need to calculate your width and height based on the area formula for a triangle: A = (b*h)/2 and you may even want b=h, where A = number of units.

So, something like this:

int b = Mathf.CeilToInt(Mathf.Sqrt(squadMembers.Count));
    for (int x = 0; x < b; x++)
    {
        //the divide by 2 is accounted for with this 2*
        for (int y = x; y < 2 * (b - x) - 1; y++)
        {
            Vector3 position = new Vector3(x, y);
            newpositions.Add(position);
        }
    }

Upvotes: 6

Related Questions