• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Snapping units to grid ingame

Status
Not open for further replies.
Level 3
Joined
Feb 29, 2020
Messages
43
So I want my game to behave like warcraft 2. I want the units to snap to a grid ingame.

I made my footman 2x2, my farms 4x4, my barracks 6x6, and my town hall 8x8. The buildings work great and they are placing perfectly. They make a nice grid and are good for walling in. The units when I place them in the campaign editor also work perfectly. The problem is ingame the units dont line up like I want them to. They are all over the place. I want them to work like they do in the editor, where they occupy a tile on the grid and snap to it.

Example.

upload_2020-4-1_6-59-14.png



So in game it works great for the buildings, they stick to the grid. The units however seem to make their own rules. They stop anywhere and their selection squares over lap and all kinds of problems. When i place/move them in the editor it works perfectly, just ingame is the problem. Anyone know how i can make it more of a "occupy a tile" Type movement ingame? I tried making my units based of guard towers and game them movement, it doesn't work any better than using just normal units, and the pathing is terrible.
 
Level 14
Joined
Feb 7, 2020
Messages
386
I believe the WC3 engine uses circle point collision for units no matter how you set them up. There's also a small amount of "allowed overlap", or whatever you would call it, that you can see units doing when figuring out movement pathing in groups so they don't jitter in place at hard edges (clarification: they do still do this since the engine is dated; but this is pathing 101). I don't think this value can be controlled and is hard coded into the engine.

I think a more advanced solution is required for forcing them to occupy cells (unless someone else knows an editor exploit).
 
Level 12
Joined
Jan 30, 2020
Messages
875
Hello there.

Seems like you want units to behave like buildings !

What @Planetary said is true but you'll notice buildings with proper pathing map and collision do not have that unit "limitation".
Only way I can thing of is make units buildings with no ground texture (but keep the pathing map).

Then give them a hidden Root ability so that they can face their targets, and unfortunately you might want to simulate the movement with triggers...
 
Level 12
Joined
Jan 30, 2020
Messages
875
Hehe... no actually you didn't ^^
You gave them movement, and once you give them movement you lose the building properties.

Root can allow buildings to face their targets, but you must not use uproot to move the "building units", because they are not buildings anymore.

What you need to do is keep your unit as buildings, give them a hidden root to let them face targets. They must not have a ground texture of course, and when you need to move the units, you need to use the native native SetUnitPosition takes unit whichUnit, real newX, real newY returns nothing.

I understand that the problem would indeed be the movement simulation, but as your units would remain as buildings, everything else would work as intended.

EDIT : how did you actually give them "movement" by the way ?

EDIT 2: simulating movement with triggers could be worse thinking about it as you would also need to handle collision.....
 
Level 14
Joined
Feb 7, 2020
Messages
386
Two quick notes:

1) selection graphics (circles) are a UI representation, they don't represent actual collision size
2) I went in to clarify unit collision and it does maintain square collision rules, the problem is that you want units to move to a grid location right?
2b) if 2) is true, you are looking at a custom solution where you would split the entire map into cells and determine the center of each cell that a unit should be in when they are issued an order
 
Level 12
Joined
Jan 30, 2020
Messages
875
I see what you mean.
About the cells, I am unsure.

In my TD (link in my signature), I have a base tower that has no movement or attack.
But it has a spell that builds identical towers in a line until the targeted point. If i would move the tower to each new position instead of creating a tower, it would actually simulate the movement, respecting the size of the "cells" but without splitting the map in cells directly.

I can easily pull out my "Build blocking towers wall" function but it is in Jass as I don't use GUI.

Let me know if you are interested.
 
Level 3
Joined
Feb 29, 2020
Messages
43
Yes I was looking the cells and thinking the same thing. I drew some regions to simiulate cells, but I haven't had a chance to go very far with that. How would I order a unit to the centre of a region when it say, stops on a region? The unit enters a region trigger won't work if the whole map is different regions movement wouldn't work.
 
Level 12
Joined
Jan 30, 2020
Messages
875
There is a way.

You would need Jass though.

In Jass the GUI Regions are actually Rects, and you can create local rects, and add them to a local region and then dynamically create a trigger that checks if a unit enters this region.

Only issue in that case would be to destroy the rect once not needed anymore.

Other possibility to simulate a movement in a direction with buildings using something like my build wall function i mentioned earlier. The only problem it has is the movement would be instant. To make it move at a normal speed following the cells, all we'd need is create a repeatable timer.When the timer expires, move the unit to the new position, then start the timer again, until the destination is reached.

This of course would require to save some values in a hashtable using the handle id of the timer as a parent key. But this would be doable.

EDIT :
If anyone is interested and can read Jass, here is my Build Wall ability trigger :
JASS:
function BuildTheWall takes nothing returns boolean
    local unit blockingTower=null
    local unit newTower=null
    local player owner=null
    local integer goldLeft
    local real oX
    local real oY
    local real tX
    local real tY
    local real incX
    local real incY
    local real sX
    local real sY
    local real angle

    if (GetSpellAbilityId() == BBTWID) then
        set blockingTower=GetSpellAbilityUnit()
        set owner=GetOwningPlayer(blockingTower)
        set goldLeft=GetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD)
        set oX=GetUnitX(blockingTower)
        set oY=GetUnitY(blockingTower)
        set blockingTower=null
        set tX=GetSpellTargetX()
        set tY=GetSpellTargetY()
        loop
            set angle=bj_RADTODEG*Atan2(tY-oY,tX-oX)
            if (angle<0) then
                set angle=angle+360.00
            endif
            set angle=Round(angle/22.50)*22.50
            if (angle>=0.00) and (angle<=180.00) then
                set sY=1.00
            else
                set sY=- 1.00
            endif
            if ((angle>=270.00) and (angle<=360.00)) or ((angle>=0) and (angle<=90.00)) then
                set sX=1.00
            else
                set sX=- 1.00
            endif
            if (angle != 180.00) then
                set angle=ModuloReal(angle, 180.00)
            endif
            if (angle==0.00) or (angle==180.00) then
                set incX=WallSize
                set incY=0
            endif
            if (angle==22.50) or (angle==157.50) then
                set incX=WallSize
                set incY=WallSize / 2.00
            endif
            if (angle==45.00) or (angle==135.00) then
                set incX=WallSize
                set incY=WallSize
            endif
            if (angle==67.5) or (angle==112.50) then
                set incX=WallSize / 2.00
                set incY=WallSize
            endif
            if (angle==90) then
                set incX=0
                set incY=WallSize
            endif
            exitwhen ((tX-oX)*(tX-oX)+(tY-oY)*(tY-oY)<WallSize*WallSize) or (goldLeft<1)
            set oX=oX+sX*incX
            set oY=oY+sY*incY
            set newTower=null
            set newTower=CreateUnit(owner, BlockingID, oX, oY, 270.00)
            call AddUnitToStock(newTower, SellID, 1, 1)
            call LinkAround(newTower , true)
            if (Abs(oX-GetUnitX(newTower))>16.00) or (Abs(oY-GetUnitY(newTower))>16.00) then
                call LinkAround(newTower , false)
                call KillUnit(newTower)
                call RemoveUnit(newTower)
            else
                set goldLeft=goldLeft-1
            endif
            set newTower=null
        endloop
        call SetPlayerState(owner, PLAYER_STATE_RESOURCE_GOLD, goldLeft)
        set owner=null
    endif
    return false
endfunction


function BuildBlockingTowersWall takes nothing returns nothing
    local integer i=0
    local trigger buildBlockingTowersWall=CreateTrigger()

    loop
        if ((GetPlayerSlotState(Player(i))==PLAYER_SLOT_STATE_PLAYING)) then
            call TriggerRegisterPlayerUnitEvent(buildBlockingTowersWall, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null)
        endif
        set i=i+1
        exitwhen i>3
    endloop
    call TriggerAddCondition(buildBlockingTowersWall, function BuildTheWall)
    set buildBlockingTowersWall=null
endfunction

Notes :
- BBTWID is the global storing my wall ability ID, it could be replaced with a dummy "move" ability
- WallSize is a global real, I have set this value to 128.0

Note it stops building when the distance with the target is low enough or when the gold is zero, so that gold part needs to be removed.

So if instead of placing a tower at each new position, one could just store the positions in two local arrays for the coordinates like MoveX and MoveY, and putting 0 as last value when the target is reached, then create a repeatable timer, store the 2 arrays in a hashtable with the timer handle id as parent key and 0 & 1 as child keys, you can also store the unit with the child key 2.

Then have a trigger that reacts to the expiration o that timer, get the parent key back using the expired timer handle id, and as the child keys for the 2 arrays and the unit are 0, 1 and 2, you can retrieve them from the hashtable and move the unit to the next position at MovX, MoveY.
I just realized that for this we would also need to save the index of the current move too.
Once unit is moved, we restart the timer until the coordinates are 0 (we could chose other values outside of the map in case there would be a risk to conflict with map center.



Anyways, I know this sounds really complicated, but I know it would work.

IF anyone has clever ideas for simpler ways to simulate a movement that would allow units to remain buildings, feel free to suggest it.
 
Last edited:
Level 14
Joined
Feb 7, 2020
Messages
386
Yes I was looking the cells and thinking the same thing. I drew some regions to simiulate cells, but I haven't had a chance to go very far with that. How would I order a unit to the centre of a region when it say, stops on a region? The unit enters a region trigger won't work if the whole map is different regions movement wouldn't work.
This is a pretty beefy feature if you're not used to programmatic systems in game engines.

You would not manually use regions as that'd be the most painful thing ever. You'd run a loop through the map after fetching its maximum size in Cartesian coordinates and generate a table or hashtable of cell data. After that, you'd have helper functions that sort out wherever a unit order is being issued (check X,Y of a region, see which "cell" it is in, then calculate the "new center location" of that cell).
 
Level 12
Joined
Jan 30, 2020
Messages
875
@Planetary : honestly, this would really be too intensive for the game engine. cell data must not be stored.

Thats why I was suggesting my method, although it is not that easy to program, and would even maybe be impossible in GUI.

But I will be honest :

If you want to make anything advanced in mapmaking, you should really give up GUI asap unless you want to drive yourself crazy.

GUI is nice when you want to do clever things that do not require too much brainstorming, but once you want the game to react differently than what it was designed for, GUI becomes a heavy weight that will stop you at every attempt.
 
Level 14
Joined
Feb 7, 2020
Messages
386
@Planetary : honestly, this would really be too intensive for the game engine. cell data must not be stored.

Thats why I was suggesting my method, although it is not that easy to program, and would even maybe be impossible in GUI.

But I will be honest :

If you want to make anything advanced in mapmaking, you should really give up GUI asap unless you want to drive yourself crazy.

GUI is nice when you want to do clever things that do not require too much brainstorming, but once you want the game to react differently than what it was designed for, GUI becomes a heavy weight that will stop you at every attempt.
I thought about it a bit more and I think you could simulate what a cell is based on coordinates since you already know the default offset, and you wouldn't have to store any data.

But, I doubt this project is big enough to warrant such a complex system. I would honestly just work with the default unit collision at this point.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Yes thats true, knowing the offset would allow to calculate each coordinate on the fly. Hadn't thought of that.

I think it could be implemented with Jass (or LUA ^^). I am not sure unit collision would suffice as they'd still move smoothly between two cells instead of jumping from one cell to the other.

Anyways, if OP wants to make this happen, it will require appropriate decisions or rather appropriate choices ^^
 
Level 3
Joined
Feb 29, 2020
Messages
43
I thought about it a bit more and I think you could simulate what a cell is based on coordinates since you already know the default offset, and you wouldn't have to store any data.

But, I doubt this project is big enough to warrant such a complex system. I would honestly just work with the default unit collision at this point.

This is a little over my head. I understand what your talking about but I dont understand how to implement this. This is a good solution I think. Can you help with this please? Or point me in the right direction?
 
Status
Not open for further replies.
Top