System Mass Movement System

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
The Mass Movement System is a system to facilitate and optimize the ordering of large amounts of units. It may come in handy to improve any type of footy map, TD maps or cinematics involving entire armies of moving units. As follows, a short :rolleyes: description, two screenies and the test map


1) What can it do?


It can order large amounts of units to move or attack-move to a certain position in a structured manner.​

2) The concept: Why not grab all units and send them to x/y?


Especially with large amounts of units, this is very likely to create movement lags. Also experience has proven that it is more efficient to order large amounts of units in unit groups.

3) How does the mass-movement system order units?

It counts how many units there are to be ordered, then creates grouping clusters according to each units position and the unit density. Each new group has a group captain whose distance to the target decides about the timing of the call to march off. So the units march off in structured groups and with a moderate timing to avoid congestions.

4) Requirements

Actually none apart from WE (since the code is kept on an old-school level).​


5) Code please!

JASS:

//%%%%%%%%%%%%%%%%%%%%% Custom Settings %%%%%%%%%%%%%%%%%%%%%%%%%%

function scatter_size takes integer n,real k returns real
	return 64000.0 / ( n * Pow(bj_E , k) )
endfunction

// The scatter size describes the size of the area, i.e. half the size of the rect 
// which is used to group clustered units into subgroups.
// It is set dependend on the unit amount n, and on an iteration factor k.

constant function scatter_size_min takes nothing returns real
	return 80.0
endfunction

// The maximum amount of units in a cluster before iterating the grouping with a factor k larger by 1.
// Should be quite a bit larger than 12, but definitely smaller than 50: the higher,
// the more operations needed, the smaller, the less likely suitable groups will be formed

constant function max_cluster takes nothing returns integer
	return 30
endfunction

//%%%%%%%%%%%%%%%%%%% Helper Functions %%%%%%%%%%%%%%%%%%%%%%

function All_filter takes nothing returns boolean
    return IsUnitType(GetFilterUnit() , UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit() , UNIT_TYPE_PEON) == false and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

function OneId_filter takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == bj_groupEnumTypeId and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

function TwoId_filter takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == bj_groupEnumTypeId or GetUnitTypeId(GetFilterUnit()) == bj_forceCountPlayers and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction

function Group_filter takes nothing returns boolean
    return IsUnitInGroup(GetFilterUnit() , bj_randomSubGroupGroup)
endfunction

//%%%%%%%%%%%%%%%%%%%%% Main Functions %%%%%%%%%%%%%%%%%%%%%%%%

function NearUnitArray takes nothing returns nothing
    local unit e=GetEnumUnit()
    local real dx= bj_meleeNearestMineDist - GetUnitX(e)
    local real dy= bj_randomSubGroupChance - GetUnitY(e)
    local real d= bj_enumDestructableRadius
    if ( dx < d and dx > - d ) and ( dy < d and dy > - d ) then
        set bj_ghoul[bj_destRandomConsidered]=e
        set bj_randDistID[bj_destRandomConsidered]=R2I(dx * dx + dy * dy)
        set bj_destRandomConsidered = bj_destRandomConsidered + 1
    endif
    set e = null
endfunction

function NearestSubGroup takes nothing returns group
    local group g= CreateGroup() //the final subgroup to be returned
    local integer n= bj_destRandomConsidered //the cluster size
    local integer i= 0
    local integer j= 0
    local integer d
    local unit u
    // bj_ghoul[] : the unit currently looked at
    //bj_randDistID : the unit's distance to the group 'captain'
    if ( n <= 12 ) then
        loop
	    exitwhen ( i > n )
	    call GroupAddUnit(g , bj_ghoul<i>)
	    call GroupRemoveUnit(bj_groupRemoveGroupDest , bj_ghoul<i>)
	    set bj_ghoul<i>=null
	    set i = i + 1
        endloop
    else
	loop
	    exitwhen ( i == n )
	    set j = i + 1
	    loop
		exitwhen ( j == n )
		if bj_randDistID<i> &lt; bj_randDistID[j] then
		    set u = bj_ghoul<i>
		    set d = bj_randDistID<i>
		    set bj_ghoul<i>=bj_ghoul[j]
		    set bj_randDistID<i>=bj_randDistID[j]
		    set bj_ghoul[j]=u
		    set bj_randDistID[j]=d
		endif
		set j = j + 1
	     endloop
	     set i = i + 1
	endloop
	loop
	    exitwhen i &lt; 0
            if i &gt; j - 12 then
                call GroupAddUnit(g , bj_ghoul<i>)
		call GroupRemoveUnit(bj_groupRemoveGroupDest , bj_ghoul<i>)
            endif
	    set bj_ghoul<i>=null
	    set i = i - 1
	endloop
	endif
	set u = null
    return g
endfunction

function ImmediateOrder takes nothing returns nothing
    call IssueImmediateOrder(GetEnumUnit() , bj_cineFadeContinueTex)
endfunction

function MassMoveType takes integer Id1, integer Id2, string order, real x, real y, player p, real t returns nothing
    local group g=CreateGroup()
    local group array subgroup
    local real array dist
    local boolexpr b
    local unit v
    local real dy
    local real dx
    local integer i= 0
    local integer j= 0
    local integer n

    if Id1 == 0 and Id2 == 0 then
        set b = Filter(function All_filter)
    elseif Id1 != 0 and Id2 == 0 then
        set bj_groupEnumTypeId = Id1
        set b = Filter(function OneId_filter)
    elseif Id1 != 0 and Id2 != 0 then
        set bj_groupEnumTypeId = Id1
        set bj_forceCountPlayers = Id2
        set b = Filter(function TwoId_filter)
    endif

    if GetLocalPlayer() == p then
        call SetCameraQuickPosition(x , y)
    endif
  
    call GroupEnumUnitsOfPlayer(g , p , b)
  
    if order == &quot;stop&quot; then
        set bj_cineFadeContinueTex = order
        call ForGroup(g , function ImmediateOrder)
    elseif order==&quot;move&quot; or order==&quot;attack&quot; then
	set bj_groupCountUnits = 0
	call ForGroup(g , function CountUnitsInGroupEnum)
        set n = bj_groupCountUnits
		
	//call BJDebugMsg(&quot;ordering &quot; + I2S(n) + &quot; units&quot;)
               
        if n &gt; 0 and n &lt;= 12 then
	    call GroupPointOrder(g , order , x , y)
        elseif n &gt; 12 then
            loop
                set v = FirstOfGroup(g)
                exitwhen v == null
                set bj_meleeNearestMineDist = GetUnitX(v)
                set bj_randomSubGroupChance = GetUnitY(v)
		loop
		    if scatter_size(n , j) &gt;= scatter_size_min() then
		        set bj_enumDestructableRadius = scatter_size(n , j)
		    else
		  	set bj_enumDestructableRadius = scatter_size_min()
		    endif
		    set bj_destRandomConsidered = 0
          	    call ForGroup(g , function NearUnitArray)
 		    if bj_destRandomConsidered &gt; max_cluster() then
		        set j = j + 1
 		    endif
		    exitwhen bj_destRandomConsidered &lt;= max_cluster()
                endloop
		set j = 0
		set bj_groupRemoveGroupDest = g
		set subgroup<i>=NearestSubGroup()
		set g = bj_groupRemoveGroupDest
                set dx = x - bj_meleeNearestMineDist
                set dy = y - bj_randomSubGroupChance
                set dist<i>=SquareRoot(dx * dx + dy * dy)
                set i = i + 1
            endloop
            loop
                exitwhen ( j == i )
                set n = j + 1
                loop
                    exitwhen ( n == i )
                    if dist[j] &lt; dist[n] then
                        set g = subgroup[j]
                        set dx = dist[j]
                        set subgroup[j]=subgroup[n]
                        set dist[j]=dist[n]
                        set subgroup[n]=g
                        set dist[n]=dx
                    endif
                    set n = n + 1
                endloop
                set j = j + 1
            endloop
            loop
	        exitwhen i &lt; 0
                set v = FirstOfGroup(subgroup<i>)
                set dist<i>=0.28 * dist<i>
                set dx = bj_RADTODEG * Atan2(GetUnitY(v) - y , GetUnitX(v) - x)
                call GroupPointOrder(subgroup<i> , order , x + dist<i> * Cos(dx * bj_DEGTORAD) , y + dist<i> * Sin(dx * bj_DEGTORAD))
                call TriggerSleepAction(t)
                call DestroyGroup(subgroup<i>)
                set subgroup<i>=null
	        set i = i - 1
            endloop
        endif
    endif
    call DestroyBoolExpr(b)
    call DestroyGroup(g)
    set g = null
    set v = null
    set p = null
endfunction</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
 

Attachments

  • mass_1.jpg
    mass_1.jpg
    218.1 KB · Views: 1,144
  • mass_2.jpg
    mass_2.jpg
    176.2 KB · Views: 1,004
  • Mass Movement System.w3x
    231.7 KB · Views: 645

Sim

Forum Administrator
Staff member
Reaction score
534
I suggest indenting a bit your wall of texts...

This is a real mess trying to read that :p
 

emjlr3

Change can be a good thing
Reaction score
395
looks kool, though I dont get it much
 

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
looks kool, though I dont get it much

1) in short: if ordering masses of units to a certain position is an issue, then this can help alleviate movement lags.

2) maybe things become clearer when you have a look at the test map
 

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
I really meant it, seriously. This isn't an essay ;)

Well, yeah. This is meant to illustrate the single problems that occur with lots of units and how they're solved to a large extent. It's by no means a must-read! It's rather some extra info for those who want to know it all. If it pleases your taste, then I can cut out the reading-part and paste it as trigger comment in the map. I already made all my efforts to format the text.
 

Cohadar

master of fugue
Reaction score
209
This would be soooo good to see in some footy map.

PS: Code looks like from stone age :)
 
N

NuBy

Guest
Wow, this is pretty cool lol, BUT you should remove the rat noise :p I swear its annoying. lol
 

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
@ NuBy: LOL, the rats sound. Funny, isn't it? If it starts getting annoying, switch over to sheep:D

@Daxtreme: Recent changes:
I refreshed the first post as well as the map itself: My detailed descriptions are packed into a map comment section. Also I fixed some leaks with the GUI example triggers in the map :rolleyes:
 

Sim

Forum Administrator
Staff member
Reaction score
534
So yea I created about 200 units and ordered them to attack.

Here are the results:



They walk, stop, walk, stop, walk, stop.

Some of them gather in groups. Others don't.

Some don't really move at all :p

Keep in mind I had some more units and I ordered them all to the same point using the system.
 

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
rough words over my MMS.

Unless your PC is poorly equipped (what are your stats, Dax?), I can't find much of an explanation for units not moving at all. There are some exceptional situations when one positioning of units may cause some strange effects to happen (sometimes if they stand extremely dense together), yet all in all the sorting algorithms are reliable. The stop-and-go motion is due to the warcraft engine overwhelmed with unit movements. At a certain point starting with ~300 units+ it starts to get too much calculation work for the movement despite the fact that the system really facilitates the job. So that's basically part of the essence Phyrex and me ended up with in the end: Warcraft3 simply isn't made for numbers of units that high. Yet given the fact that with a simple UnitPointOrder you're very likely to get tailbacks, congestions and severe movement lags with unit numbers of about 80+, the system does serve its purpose for unit amounts of ~ 80 to ~300.
Also, I see that you're commanding all units together, which means that the fast units (footmen) have to adapt their speed to the slow units (sheep), which causes additional tailbacks. Hence it is always more efficient (and allows more control) to command unit types seperately.
Feel free to have some more test runs. What you experienced can't be regular.
 

Sim

Forum Administrator
Staff member
Reaction score
534
> Unless your PC is poorly equipped

Can't be that, it's general and I've seen it happen in my own map too, as well as TDs with lots of units such as wintermauls.

> At a certain point starting with ~300 units+ it starts to get too much calculation work for the movement despite the fact that the system really facilitates the job. So that's basically part of the essence Phyrex and me ended up with in the end: Warcraft3 simply isn't made for numbers of units that high.

False, in Starcraft Zone Control the maker definitely succeeded in ordering several hundreds of unit at the same time without any sort of congestion :p

In some TDs too a lot of units get ordered but don't congestionate.

I investigated but never got the answer.

> I see that you're commanding all units together, which means that the fast units (footmen) have to adapt their speed to the slow units (sheep),

In this screenshot I did, but later in the game I commanded only sheeps. Same results.
 

Doomhammer

Bob Kotick - Gamers' corporate spoilsport No. 1
Reaction score
67
>
> At a certain point starting with ~300 units+ it starts to get too much calculation work for the movement despite the fact that the system really facilitates the job. So that's basically part of the essence Phyrex and me ended up with in the end: Warcraft3 simply isn't made for numbers of units that high.

False, in Starcraft Zone Control the maker definitely succeeded in ordering several hundreds of unit at the same time without any sort of congestion :p

Not quite. Funny enough I happen to work together with the maker of Starcraft Zone Control for several months now. The maximum unit amount a single player could have and order was (up to v. 5.0.51 which is about the last released) set to 120, not to several hundred. Units wouldn't congest since on a single command they're less than 120 i.e. far less than ~300 (due to the rapid kill rate mostly a good deal less than 120 a player), starting rather widely diffused from different spots, and are shot away before they even could congest.

>
In some TDs too a lot of units get ordered but don't congestionate.

How much is a lot? Is it 30, is it 50, is it maybe 70 or is it 200?

> I see that you're commanding all units together, which means that the fast units (footmen) have to adapt their speed to the slow units (sheep),

In this screenshot I did, but later in the game I commanded only sheeps. Same results.
So you're saying that this system, which has worked very will in my zone control maps before
http://world-editor-tutorials.thehelper.net/maps.php?view=353
http://world-editor-tutorials.thehelper.net/maps.php?view=287
(even with older versions of the system which were far inferior in handling dense clusters of units), which works perfectly in the new Starcraft Zone Control, which works nicely in the current version of my Warcraft Zone Control, and which works well on my PC, when I have test runs in this very test map constantly causes tailbacks, units clustering and not moving, as well as severe congestions during your test runs, no matter how many units (starting from 10 to several hundred) of which type you're commanding around?
 

Sim

Forum Administrator
Staff member
Reaction score
534
Well I had thought there were more units while I was playing than 120, but then again it's a long time ago. It might have been around that.

> How much is a lot? Is it 30, is it 50, is it maybe 70 or is it 200?

Well for example in Element TD there might be around 200 units at the same time and they move perfectly well.

> no matter how many units (starting from 10 to several hundred) of which type you're commanding around?

Where did I mention 10 units...? We're speaking of 200+ units there and I said it.

It doesn't matter the type.

Anyways, this system is by far superior to others. I acknowledge the fact that there is a limit to the amount of units that can be moved, I just thought that maybe a system could maybe get rid of it ;)

Approved.
 

Legacyspy

New Member
Reaction score
19
Doomhammer
THANK YOU SO MUCH.
I've been looking for something like this for so long, as I had problems you mentioned with ordering units to move.
Thank YOU.
 

Technomancer

New Member
Reaction score
14
Well I had thought there were more units while I was playing than 120, but then again it's a long time ago. It might have been around that.

> How much is a lot? Is it 30, is it 50, is it maybe 70 or is it 200?

Well for example in Element TD there might be around 200 units at the same time and they move perfectly well.

> no matter how many units (starting from 10 to several hundred) of which type you're commanding around?

Where did I mention 10 units...? We're speaking of 200+ units there and I said it.

It doesn't matter the type.

Anyways, this system is by far superior to others. I acknowledge the fact that there is a limit to the amount of units that can be moved, I just thought that maybe a system could maybe get rid of it ;)

Approved.

I've always had issues with element TD, a good way to glitch that game is to spam any splash tower and Life towers because the animation causes the units to lag and then the life towers mop up giving you tons of lives and a very slow walkthrough rate.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top