Help with RTS style movement

  1. What do you want to achieve?

I want to achieve a way for my units to be organized in a grid like formation towards a certain position without causing any intersecting paths. Here is an example of what I want to achieve. (the red dots are a position for each unit)
image

  1. What is the issue?

My units are being given random positions to the grid that causes units to cross each other causing traffic and pushing. Here is an example of what my units look like.
image

  1. What solutions have you tried so far?

I tried to research on other forums on how I would fix this to no avail.

Sometimes the units are parallel when moving to their positions but when they are given a new position in a different direction all of the units are being given opposite positions to what they should be getting, this makes their paths look like an X instead of looking like ||

This is also based on the troop movement system from the Roblox game Medieval RTS.

Here is my current code:

local Info = {
	{[1] = Unit1};
	{[1] = Unit2};
	{[1] = Unit3};
}

local function GridUnits(TargetCFrame, Info)

	local Direction = -TargetCFrame.LookVector * Vector3.new(1, 0, 1) -- Reverse and ignore Y-axis
	local numTroops = #Info
	local StepbackZ = 1
	local StepbackX = 1
	local Columns = math.ceil(math.sqrt(numTroops))
	local UnitPos = {}

	if numTroops > Columns * Columns then
		Columns = Columns + 1
	end

	Columns = math.max(Columns, 3)

	if numTroops > 3 and numTroops < 7 then
		StepbackZ = 2
	elseif numTroops < 4 then
		StepbackZ = 3
	end

	if numTroops == 1 then
		StepbackX = 3
	elseif numTroops == 2 then
		StepbackX = 2
	end

	for i, v in ipairs(Info) do

		local _, UnitSize = v[1]:GetBoundingBox() 
		local UnitSpacing = (UnitSize.X + UnitSize.Z) / 2
		local CenterPos = TargetCFrame.Position - Direction * ((Columns - StepbackZ) * UnitSpacing / 2) - Direction:Cross(Vector3.new(0, 1, 0)) * ((Columns - StepbackX) * UnitSpacing / 2)

		local Rows = math.ceil(i / Columns)
		local Cols = (i - 1) % Columns + 1

		local Offset = Direction * ((Rows - 1) * UnitSpacing) + Direction:Cross(Vector3.new(0, 1, 0)) * ((Cols - 1) * UnitSpacing)
		local Final = CenterPos + Offset

		if _G.Config.Debug then
			local Debug = Instance.new("Part", workspace.Debris)
			Debug.Size = UnitSize
			Debug.Anchored = true
			Debug.CanCollide = false
			Debug.Position = Final
			Debug.Color = Color3.fromRGB(20 * i, 20 * i, 20 * i)
			game.Debris:AddItem(Debug, 1)
		end

		UnitPos[v[1]:GetAttribute("UnitSeed")] = Final
	end

	return UnitPos
end
2 Likes

What I personally would do is: take the average position of all troops, get a directional vector located from the origin (the average position) to the clicked position; then I would iterate over all troops and add that directional to vector to each of their independent positions. So they will all move in a certain direction rather than move to a fixed point.

2 Likes

That could work but I need the troops to be in a grid like formation

Ok, I understand your problem more clearly now. I thought a bit about it, I honestly was not expecting this to be that difficult. What if you had two different directional vectors at the same time, one as I previously stated, however, another one that is found by testing a troop’s closest point and using the directional vector toward that point to align it in the group(this would only be done initially)? This way if the player selects a group of unorganized units they will automatically organize themselves.

Though the drawbacks I see to this are, if a player: deselected, selected, and then moved the group; the group would unnecessarily rearrange itself. (Just wanted to get this out there even though the thought process is incomplete, I am continuing to think about this.)

Yeah when a player reselects a group of units it would just rearrange it self