System Slide Movement

Status
Not open for further replies.

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I played Warlocks about 2 days ago, and I loved the sliding system it used. I've made many sliding abilities in the past (though I haven't really used many of them), and I always liked the idea of just dumping a system into your map, and it works fine right away.

However, this wasn't possible until I came back to mapping and learned about vJass. Everything used to come with implementation instructions, and required you to go through and change all kinds of stuff. I started with vJASS about 2 days ago, and I don't think I'm going to go back now. :p

I'm working on a system that lets you have universal sliding. Right now, I've only made the basics for the code, but I'm working on making many functions for it, starting with functions that slide a unit, and arrow key movement/sliding.

It works by attaching structs to units, and then having a timer that is always on (unless no units are sliding), moving units towards the velocity and acceleration that is attached to them.

I've gotten it working so that you can simply set units' velocity and acceleration through a few functions, and they will start moving, with just one line of custom script. For Example, if you wanted to make a unit acceleration towards an angle:

Code:
Custom script:   call IncUnitAccel(udg_u,60,1)

I made it so that units simply bounce off of the map bounds, and took a video of it. Take a look:

http://www.youtube.com/watch?v=OFDoLpzfxEI

The unit is stopped by calling StopUnit(whichUnit), so there isn't much use out of it yet, but soon I plan on adding functions like sliding to a unit, sliding to a point, sliding for a distance, sliding for a specific time, etc. This is where the use in the system will be discovered. :cool:

Obviously with so much customizability, there will be lag if it is used on many units at once, so it will probably be a system used for abilities, or smaller maps with fewer units.

Now that I've finished talking about it, you can see the coding behind it. (It's long, I know)

JASS:
library Cache
    globals
        gamecache cache = InitGameCache("cache")
    endglobals
    
    function LocalVars takes nothing returns gamecache
        return cache
    endfunction

    function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    function SetHandleInt takes handle h, string key, integer i returns nothing
        call StoreInteger(LocalVars(),I2S(H2I(h)),key,i)
    endfunction
    
    function GetHandleInt takes handle h, string key returns integer
        return GetStoredInteger(LocalVars(),I2S(H2I(h)),key)
    endfunction
endlibrary

library mapdata requires Cache

    globals
        real minx
        real maxx
        real miny
        real maxy
    endglobals

    function IsXInMap takes real x returns boolean
        return (x > minx and x < maxx)
    endfunction  
    
    function IsYInMap takes real y returns boolean
        return (y > miny and y < maxy)
    endfunction   
    
    function IsLocInMap takes location l returns boolean
        return ((GetLocationX(l) > minx and GetLocationX(l) < maxx) and (GetLocationY(l) > miny and GetLocationY(l) < maxy))
    endfunction
    
endlibrary
    
library SlidingMovement requires mapdata
    globals
        group sliders = CreateGroup()
        timer autoslide = CreateTimer()
        boolean running = false
    endglobals
    
    struct movedata
        real vel_x = 0
        real vel_y = 0
        real accel_x = 0
        real accel_y = 0
        real frict = 1.5
    endstruct
    
    function Sign takes real i returns real
        if i >= 0 then
            return 1
        endif
        return -1.
    endfunction
    
    function Abs takes real a returns real
        if (a >= 0) then
            return a
        else
            return -a
        endif
    endfunction
        
    private function Automove takes nothing returns nothing
        local movedata m
        local real cur_x = 0
        local real cur_y = 0
        local unit fog
        local group g = CreateGroup()
        call GroupAddGroup(sliders,g)
        if CountUnitsInGroup(sliders) == 0 then
            call PauseTimer(autoslide)
            set running = false
        endif
        loop
            set fog = FirstOfGroup(g)
            exitwhen fog == null
            call GroupRemoveUnit(g,fog)
            set m = GetHandleInt(fog,"movedata")
            if not ((m.vel_x == 0 and m.accel_x == 0) and (m.vel_y == 0 and m.accel_y == 0)) then
                if IsXInMap(GetUnitX(fog) + m.vel_x) then
                    call SetUnitX(fog,(GetUnitX(fog) + m.vel_x))
                else
                    set m.vel_x = 0 - m.vel_x
                endif
                if IsYInMap(GetUnitY(fog) + m.vel_y) then
                    call SetUnitY(fog,(GetUnitY(fog) + m.vel_y))
                else
                    set m.vel_y = 0 - m.vel_y
                endif
                if (m.frict <= Abs(m.vel_x + m.accel_x)) or (Abs(m.accel_x) != 0 and m.frict < Abs(m.accel_x)) then
                    set m.vel_x = m.vel_x + m.accel_x - (m.frict * Sign(m.vel_x))
                else
                    set m.vel_x = 0
                endif
                if (m.frict <= Abs(m.vel_y + m.accel_y)) or (Abs(m.accel_y) != 0 and m.frict < Abs(m.accel_y)) then
                    set m.vel_y = m.vel_y + m.accel_y - (m.frict * Sign(m.vel_y))
                else
                    set m.vel_y = 0
                endif
            else
                call movedata.destroy(m)
                call FlushStoredMission(LocalVars(),I2S(H2I(fog)))
                call GroupRemoveUnit(sliders,fog)
            endif
        endloop
        call DestroyGroup(g)
        set g = null
    endfunction
            
    function MakeUnitSlider takes unit whichUnit returns movedata
        local movedata m = GetHandleInt(whichUnit,"movedata")
        if m == 0 then
            set m = movedata.create()
            call SetHandleInt(whichUnit,"movedata",m)
        endif
        call GroupAddUnit(sliders,whichUnit)
        if running == false then
            call TimerStart(autoslide,0.04,true,function Automove)
            set running = true
        endif
        return m
    endfunction
    
    function StopUnit takes unit whichUnit returns nothing
        local movedata m = GetHandleInt(whichUnit,"movedata")
        if m != 0 then
            call movedata.destroy(m)
            call FlushStoredMission(LocalVars(),I2S(H2I(whichUnit)))
            call GroupRemoveUnit(sliders,whichUnit)   
            if CountUnitsInGroup(sliders) == 0 then
                call PauseTimer(autoslide)
                set running = false
            endif
        endif
    endfunction
    
    function IncUnitVelX takes unit whichUnit, real vel returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.vel_x = m.vel_x + vel
        return m
    endfunction
  
    function IncUnitVelY takes unit whichUnit, real vel returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.vel_y = m.vel_y + vel
        return m
    endfunction
  
    function IncUnitVel takes unit whichUnit, real vel, real ang returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.vel_x = m.vel_x + (vel * Cos(ang * bj_DEGTORAD))
        set m.vel_y = m.vel_y + (vel * Sin(ang * bj_DEGTORAD))
        return m
    endfunction
    
    function SetUnitVel takes unit whichUnit, real vel, real ang returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.vel_x = (vel * Cos(ang * bj_DEGTORAD))
        set m.vel_y = (vel * Sin(ang * bj_DEGTORAD))       
        return m
    endfunction
    
    function IncUnitAccelX takes unit whichUnit, real accel returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.accel_x = m.accel_x + accel
        return m
    endfunction
    
    function IncUnitAccelY takes unit whichUnit, real accel returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.accel_y = m.accel_y + accel
        return m
    endfunction
        
    function IncUnitAccel takes unit whichUnit, real accel, real ang returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.accel_x = m.accel_x + (accel * Cos(ang * bj_DEGTORAD))
        set m.accel_y = m.accel_y + (accel * Sin(ang * bj_DEGTORAD))
        return m
    endfunction
    
    function SetUnitAccel takes unit whichUnit, real accel, real ang returns movedata
        local movedata m = MakeUnitSlider(whichUnit)
        set m.accel_x = (accel * Cos(ang * bj_DEGTORAD))
        set m.accel_y = (accel * Sin(ang * bj_DEGTORAD))       
        return m
    endfunction
    
         
endlibrary


As you can see, the attaching is done through the gamecache/local handle vars. I will change this to custom value soon.

Arrow key movement and friction are finished!

Please post comments! :D
 

Attachments

  • Newgen Keytesting.w3x
    29.9 KB · Views: 233

Flare

Stops copies me!
Reaction score
662
Looks great from the video :)

Although:
JASS:
    function IsXInMap takes real x returns boolean
        return (x > -3312 and x < 3312)
    endfunction  
    
    function IsYInMap takes real y returns boolean
        return (y > -3568 and y < 3056)
    endfunction


If the mapsize is larger, won't those constant values be different?

And wouldn't it be easier for the end-user if they didn't have to do the StopMove call i.e. all sliding is done within the one function, and the units automatically slow down and stop. May not be as configurable (since you don't get to determine when/where exactly the unit stops) though

Overall though, it looks pretty neat.
 

darkbeer

Beer is Good!
Reaction score
84
sweet system^^

a few suggestions from me:
As flare already said, people will have to customize this, but you could simply change it to:
Code:
    function IsXInMap takes real x returns boolean
        return (x > GetRectMinX(bj_mapInitialCameraBounds) and x < GetRectMaxX(bj_mapInitialCameraBounds) )
    endfunction  
    
    function IsYInMap takes real y returns boolean
        return (y > GetRectMinY(bj_mapInitialCameraBounds)  and y < GetRectMaxY(bj_mapInitialCameraBounds) )
    endfunction

so it will already take the current camera bounds as valid region

2nd i suggest you make a global which simulates the friction, so you wll slow down over time and you could add a command to change that global in-game


well thats all from my side :)

All in all i like your system, i might use some parts of it^^

EDIT: omg confused bj_mapInitialCameraBounds with bj_mapInitialPlayableArea -.- use bj_mapInitialPlayableArea its the right one,
thx flare for pointing that out
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Yes the mapsize can be larger in other cases. I didn't know about the functions that darkbeer posted. Those will come in handy! :D

This should be in Tutorials and Resources.

Right now it's a project, but when it's finished, it will be.

The friction is a good idea! I was planning on making acceleration how units would slow down, but that would work out great. :D

@Sooda

As you can see, the attaching is done through the gamecache/local handle vars. I will change this to custom value soon.

I'm more familiar with the gamecache.
 

trb92

Throwing science at the wall to see what sticks
Reaction score
142
bj_mapInitialCameraBounds refers to the boundaries the camera has to stay within, not where it starts. If you scroll all the way to any edge, the point where your camera is as close to that edge as it will go is what this variable stores. The camera bounds are normally slightly smaller then the playable area, but not enough to make a real difference. Either one would work fine here.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
A few updates to the system:

I added the function MakeUnitSlider so that all of the velocity/acceleration setting functions will be much shorter (for readability, and to simplify things while I'm coding right now). It adds it to the group of sliders, makes sure the timer is running, and attaches the struct.

I also made the Map Bounds checking use the bj_mapInitialPlayableArea rect, so that it will be correct for any map size, though I do have a question about it. I tried initializing the variables to this, and it did not work. I'm guessing the rect was not yet created, so I had to put in a map initialization trigger that set the global variables minx maxx etc to the Rect's min x max x etc. Is it possible to put something in the library to initialize a variable, and how?

I also made the map work with Arrow Keys. They basically increase the units' acceleration by 50 (pixels?) per second when pressed, and when released, put it back to 0.

The biggest difference is Friction. I have it set to slow units down at 37.5 (pixels?) per second. Arrow key movement is at 50, so it's accelerating around 12.5 (pixels?) per second.

It's still storing the local handles in the gamecache, but I'll be tackling that soon. :)
 

Flare

Stops copies me!
Reaction score
662
The biggest difference is Friction. I have it set to slow units down at 37.5 (pixels?) per second. Arrow key movement is at 50, so it's accelerating around 12.5 (pixels?) per second.

Is the friction constant, or can you alter it when calling IncUnitAccel?

I tried initializing the variables to this, and it did not work. I'm guessing the rect was not yet created, so I had to put in a map initialization trigger that set the global variables minx maxx etc to the Rect's min x max x etc. Is it possible to put something in the library to initialize a variable, and how?

You could have a trigger within the library that runs at Map Init (not sure if it would work within an initializer :S) and sets the variables.
 

darkbeer

Beer is Good!
Reaction score
84
add an initializer to the library like this:

JASS:
library Demo initializer Init
globals
    real MinX
    real MaxX....
endglobals
//heres your normal code

// now you just have to add the Init function:
private function Init takes nothing returns nothing
    set MinX = GetRectMinX(bj_mapInitialPlayableArea)
    set MaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    ......
endfunction

endlibrary


this will work :), i used it myself already
@ flare:
(not sure if it would work within an initializer :S)

it works ;)

And im sorry in my post above i confused bj_mapInitialCameraBounds with bj_mapInitialPlayableArea, though i think it only makes a difference in very small maps^^
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Thanks! The Initializer worked perfectly! :D

I am also still trying to work out a solution to how I can have limited slides (using distances/times to stop one slide) without affecting other slides. I am thinking about adding some arrays onto the struct attachment to allow for multiple instances of slides, but I'm still trying to find better methods (It seems a little sloppy to me :p) It may just turn out that I'll need 2 systems (one which I've already created, pretty much), one for permanent sliding, and manual stopping, and one for sliding for certain distances and times. I was kind of hoping to make sliding much easier simply by copy-pasting my system in a map, and then calling a few functions. People would be able to do anything from arrow key movement to spell sliding. I guess we'll see. :p

I'm really just posting this as a kind of update. I have quite a few projects going, so I'm postponing this for a little while. (Forest CTF, In Combat Status, and learning VB 6).
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
Holy Sh1t! This thing is amazing!

Just an FYI, instead of doing.

JASS:
call IncUnitAccel(udg_u,60,1)


You should do.

JASS:
call IncUnitAccel( unit MyUnit , idk, idk )


Also, why not just shove all the library things into one?
 
Status
Not open for further replies.
General chit-chat
Help Users

      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