Click here to Skip to main content
15,034,598 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hey Codeproject,

I wasn't sure how to phrase the question as I am not sure what this is exactly called. Anyways, to quickly get to the point I have a list of entities (players, ships, whatnot) that I send to a specific point using:

C#
GoToTarget(Entity Entity, Vector2 Position)
{
  Entity.GoToPosition(Vector2);
}


The problem with this method is that they will eventually all get to the same place and stack on top of each other. Now I'd usually say that adding collisions would solve this problem, but it does not look neat - and neither is it suppose to happen in my game (no this is not a homework assignment because I do not do homework.)

So that's why I asked how to make them in a "formation", formation being a way they will be ordered next to the target position - as can be seen in ()Age of Empires 2[^]

I don't want any fancy formations, because that to me is irrelevant at this stage. I just want them list of entities to be formated by the origin that is "Target".

Edit:

Assuming we have eight entities. We want two rows, meaning there's four on each row:
C#
int Rows = 2;

for(int i =0; i<rows; i++)  

  for(int x=0; x <Entities.Count / rows; x++) 
{ 
// At this point we know that there's two rows, and the result of X is a maximum of 4.
     
     Position = parOrigin  - ((Entities.Count / 2 * new Vector2(Entities[i].Size.X, i * Entities[i].Size.Y)) + new Vector2(Entities[i].Size.X, 0) * i);
     Entities[Entities.Count / Rows * i + x].GoToTarget(Position);
  } 
}

-- I think this is the solution? Feels like it in my head. Any comments?

Edit (15-5-2016):
Using the only answer currently given I still have problems getting them all to sort in a rectangle. Some of them still stack on top of each other.

What I have tried:

The following is pseudo code to explain my attempt at making a simple formation.

Vector2 parTarget = MousePosition;
Vector2 parOrigin = parTarget;

for(int i =0; i<entities.count;>{
parTarget = parOrigin - ((Entities.Count / 2 * new Vector2(Entities[i].Size.X, 0)) + new Vector2(Entities[i].Size.X, 0) * i);

if(Entities[i].Selected)
{
Entities[i].GoToPosition(parTarget);
}
}

As you can most likely read this in pseudo language should send them in a straight line having target as origin, but this does not give a rectangle formation and will thus spread them out over the entire map, and I do not want that.

I want the rectangle to be as small as necessary but I have no idea how to do this with only a one dimensional for loop. I'm not very good with math, so if this is obvious I do apologize - I am here to learn.
Posted
Updated 15-May-16 2:31am
v6

1 solution

To figure out the best-fitting square, you can use something like the following:

C#
// This will compute the number of columns needed to fit all the units in the
// squarest rectangle possible.
int columnCount = (int)(Math.Ceiling(Math.Sqrt(Entities.Count)));

// The number of rows may be less than columns if the number of units is not
// an exact square. Also, the last row may be incomplete.
int rowCount = (Entities.Count + columnCount - 1) / columnCount;

// Figure out the grid size needed (this step is not necessary if all entities 
// are the same size.)
// NOTE: These lines will throw an exception if Entities is empty.
int xSize = Entities.Max(e=>e.Size.X); 
int ySize = Entities.Max(e=>e.Size.Y); 

// This is the top-left corner of the grid, which is to be centered on
// parTarget.
Vector2 gridCornerOffset = parTarget
    + new Vector2(-xSize * columnCount / 2, -ySize * rowCount / 2);

// Send each entity to its position at the target.
for(int i = 0; i < Entities.Count; ++i)
{
    int gridX = i % columnCount;
    int gridY = i / columnCount;
	
    Vector2 position = gridCornerOffset
		+ new Vector2(xSize * gridX, ySize * gridY);
		
    Entities[i].GoToTarget(position);
}


EDIT: If you instead want a rectangle formation, you can change the first line to the following:

C#
// This is the width to height ratio of the desired rectangle.
double r = 2; // for 2:1 rectangle

int columnCount = (int)(Math.Round(r * Math.Sqrt(Entities.Count / r)));
   
v3
Comments
It's Eddie! 13-May-16 10:04am
   
Thank you so much Staffan for taking the time. I think I finally understand ".Max(e=>e.Size.X)" now. This is perfect!
- Eddie
It's Eddie! 14-May-16 9:25am
   
I just tried implimenting it the only issue seems to when there's less than four units all of them stack with a partner - not all in one position. If there's more or equal to six they formate correctly.

Any ideas?
Staffan Bruun 16-May-16 2:36am
   
I added a way to calculate the column count to arrange the entities in a rectangle, see the updated solution.
It's Eddie! 16-May-16 5:40am
   
Hey Staffan,

Thanks for once again taking the time to read my problem. I've updated the code as you've shown, and the grid Y is doing fine. However, the GridX is still resulting very odd numbers.

Here are the results after debugging:

In the for loop of 9 different entities going to the same given point.

i = 0 - GridX: 0
i = 1 - GridX: 0
i = 2 - GridX: 0
i = 3 - GridX: 4
i = 4 - GridX: 4
i = 5 - GridX: 4
i = 6 - GridX: 4
i = 7 - GridX: 0
i = 8 - GridX: 0

Perhaps there's something wrong with my code implimenting what you've given me. I'd like to send you a pastebin, if it's not too much trouble.

Also, could I have clarification on what "&" means within a mathematical sum in programming?

http://pastebin.com/LJYvPAau
Staffan Bruun 16-May-16 6:05am
   
Oh, my bad. That '&' is supposed to be '%', for modulo operator. Now it just does binary AND, causing the problem.
It's Eddie! 16-May-16 6:07am
   
Thank you. I thought that was the problem. I replaced it with devision but I realized it has to be on the grid, and can't be more than the ColumnCount - just didn't realize modulo would be the trick.

Cheers Staffan!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900