#Total War Type Army

1 messages · Page 1 of 1 (latest)

stable stump
#

Hello, I'm trying to make an army line-war movement system like Total War, where a group of soldiers in a certain row/column formation can move wherever the player right mouse clicks and reforms the line when an event is triggered, and all the humanoids of all of the soldiers change WalkSpeed when they are commanded to run/walk.

I already made a script for the army, but I would like to add a system where the soldiers can rotate the entire formation when turning backwards. There are a couple problems, which include the soldiers bugging out really easily by taking a step every three seconds, and also when the soldiers are tasked with moving to the location of the player's mouseclick, they sometimes push each other around and break formation and it creates this big blob of soldiers who forgot their training or something.

I will post the script in the replies below if it lets me.

#

First part vv

local PathfindingService = game:GetService("PathfindingService")
local army = workspace.Army 
local spacing = 6
local columns = 8

local currentOrderIds = {}

local function getLivingSoldiers()
    local living = {}
    for _, model in pairs(army:GetChildren()) do
        if model:IsA("Model") and model ~= army.PrimaryPart then
            local hum = model:FindFirstChildOfClass("Humanoid")
            local root = model:FindFirstChild("HumanoidRootPart")
            if hum and root and hum.Health > 0 then
                table.insert(living, model)
            end
        end
    end
    return living
end
#
local function moveIndividualSoldier(soldier, targetPoint, orderId)
    local root = soldier:FindFirstChild("HumanoidRootPart")
    local hum = soldier:FindFirstChildOfClass("Humanoid")
    if not root or not hum then return end

    local path = PathfindingService:CreatePath({
        AgentRadius = 2.0, -- Smaller radius prevents unit collision "stutter"
        AgentCanJump = true,
        WaypointSpacing = 6 -- Higher spacing = smoother movement
    })

    local success, _ = pcall(function() path:ComputeAsync(root.Position, targetPoint) end)

    if success and path.Status == Enum.PathStatus.Success then
        local waypoints = path:GetWaypoints()
        for i = 2, #waypoints do
            if currentOrderIds[soldier] ~= orderId then return end

            local waypoint = waypoints[i]
            hum:MoveTo(waypoint.Position)

            -- The "Anti-Stutter" Loop: Fast check for arrival
            local moveActive = true
            local timeout = 0
            while moveActive do
                local dist = (root.Position - waypoint.Position).Magnitude
                if dist < 4 or currentOrderIds[soldier] ~= orderId then
                    moveActive = false
                end
                -- If they get stuck for 1 second, force next waypoint
                timeout += task.wait() 
                if timeout > 1 then moveActive = false end
            end
        end
    else
        hum:MoveTo(targetPoint)
    end
end
#
local function applyMovementLogic(targetPos, isReforming)
    local soldiers = getLivingSoldiers()
    local totalSoldiers = #soldiers
    if totalSoldiers == 0 or not army.PrimaryPart then return end

    local pivotPos = targetPos
    if isReforming then
        local totalPos = Vector3.new(0,0,0)
        for _, s in ipairs(soldiers) do totalPos += s.HumanoidRootPart.Position end
        pivotPos = totalPos / totalSoldiers
    end

    local targetCFrame = CFrame.new(pivotPos) * army.PrimaryPart.CFrame.Rotation

    local totalRows = math.ceil(totalSoldiers / columns)
    local fullRowWidth = (columns - 1) * spacing

    for i, soldier in ipairs(soldiers) do
        local newOrderId = (currentOrderIds[soldier] or 0) + 1
        currentOrderIds[soldier] = newOrderId

        local row = math.floor((i - 1) / columns)
        local col = (i - 1) % columns

        -- CENTER THE BACK ROW LOGIC
        local rowOffset = 0
        if row == totalRows - 1 then -- This is the last row
            local unitsInLastRow = totalSoldiers % columns
            if unitsInLastRow == 0 then unitsInLastRow = columns end

            local lastRowWidth = (unitsInLastRow - 1) * spacing
            -- Calculate the difference to center it against the front rows
            rowOffset = (fullRowWidth - lastRowWidth) / 2
        end

        local localOffset = Vector3.new(
            (col * spacing) - (fullRowWidth / 2) + rowOffset, 
            0, 
            (row * spacing)
        )

        local finalWorldPos = (targetCFrame * CFrame.new(localOffset)).Position
        task.spawn(moveIndividualSoldier, soldier, finalWorldPos, newOrderId)
    end

    army.PrimaryPart.Position = pivotPos
end

game.ReplicatedStorage.MoveArmy.OnServerEvent:Connect(function(player, position)
    applyMovementLogic(position, false)
end)

game.ReplicatedStorage.ReformArmy.OnServerEvent:Connect(function(player)
    applyMovementLogic(nil, true)
end)
harsh zodiac
#

I am pretty sure I can help

harsh zodiac
fair warrenBOT
#

studio** You are now Level 1! **studio

harsh zodiac
#

Try that

dire beacon
#

you might want to get into boids and Flow Field Pathfinding

timid bridge
#

have u solved the issue yet?

stable stump
fair warrenBOT
#

studio** You are now Level 1! **studio

stable stump
stable stump
timid bridge
stable stump
#

Is there a way to work around that

timid bridge
#

u can use MoveToFinished to chain waypoints

stable stump
#

Oo

timid bridge
#

super simple dw, but it will be a pretty common concept that u should get used to for ur type of game

stable stump
#

Thank you

#

Oh yeah that was pretty ez