KnuckleCracker / CW4-bug-tracker

The gathering point for bugs and supporting information, or suggestions about additional features.
Creative Commons Attribution Share Alike 4.0 International
13 stars 1 forks source link

[4RPL Addition] GetUnitsInPolygon() function #822

Closed zer0ner closed 1 year ago

zer0ner commented 3 years ago

Is your feature request related to a problem? Please describe. Not exactly a problem at the moment, just rather inconvenient and inefficient as map makers like me are currently limited to squares and circles as shapes in calls to GetUnits().

Describe the solution you'd like I'd like to be able to pass in a list of X/Z cell position coordinates representing a closed polygon and get back a list of units that match that are located within that polygon.

Describe alternatives you've considered Currently limited to checking a series of smaller squares and circles. However, multiple calls to GetUnits() have to be made (I'm regularly making about 30 GetUnits() calls in a loop in my current map) and, as a result, there can be overlap of the units returned. If an action is taken against all units, units in the overlap regions will have the same actions issued 2-4 times.

zer0ner commented 3 years ago

I came up with a pure 4RPL version of the function for map 1161 (The Aquarium). It is not the fastest function but it works well enough.

# GetUnitsInPolygon(unitType polyList enemyState builtState imperviousState)
:GetUnitsInPolygon
    ->guip_imperviousState
    ->guip_builtState
    ->guip_enemyState
    ->guip_polyList
    ->guip_unitType

    # Get all units on the map that match the non-position requirements.
    GetUnits(<-guip_unitType V3(128 128 128) 512 false false false <-guip_enemyState <-guip_builtState <-guip_imperviousState) ->guip_units

    CreateList ->guip_retval

    <-guip_units 0 do
        <-guip_units[I] ->guip_tempunit

        if (@IsPointInPoly(V2(GetUnitCell(<-guip_tempunit)) <-guip_polyList))
            AppendToList(<-guip_retval <-guip_tempunit)
        endif
    loop

    <-guip_retval

Example usage (a triangle):

    List(
        V2(18 148)
        V2(138 102)
        V2(44 29)
    ) ->testPoly

    @GetUnitsInPolygon("" <-testPoly 2 0 1) ->units

    TraceAllSp("Num units " GetListCount(<-units))

    <-units 0 do
        <-units[I] ->tempunit

        CreateEffect("sparks" GetUnitPosition(<-tempunit) V3(1 1 1))
    loop

Getting every single unit on a map that matches a set of conditions just to filter them through a set of polygon coordinates works but is kind of expensive and may slow down the frame rate considerably if called too frequently.

RealArgonwolf commented 3 years ago

In one of my now-scrapped custom maps I kept a list with one entry for every map cell, containing a 0 if no unit had its center there and the unit's ID if one did. Once every second it used GetUnits to get every unit on the map and put it in the correct place if it didn't already exist, and every frame it swept through 1/30 (or as close as I could get) of the list and checked every existing unit it found to make sure it still existed. It let me simulate a blocky raycast against said units (combining their position in the list with the terrain height there) without stepping forward a short distance and re-checking a radius every time.

I don't know if it would serve you well (a possible one-second delay for creation/destruction of units might be too long, or you might not like having to use GetType to manually sort them yourself) but if you've got an obscene number of calls to your existing dot-product polygon intersect checker, you can pare them down to the polygon's bounding square and just check those cells for your broad phase then use the actual method for precise results. Would definitely involve major improvements when checking lots of small polys.

Even without the specific structure I used, an initial bounding-box check (assuming you haven't implemented one already) can save a lot of CPU cycles.

Just some thoughts on what might help until a more formal solution comes about. Happy coding!