HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

How far can I go? (local code)

02-20-2009, 05:18 PM#1
Opossum
I've been working on a 3rd person camera system and instead of using global arrays for the different values I tried only setting and calling these values locally.
This might sound dangerous but I just need that adrenaline! So I'd like to know if this method of using global variables will cause any desyncs later. In short testing there hasn't been any desyncs but maybe there are some long-term effects.

Here's the code:
Collapse JASS:
library OppiCam initializer Init requires AnyPlayerEvents

    globals
        //Anything here is set and called locally. 
        //DON'T USE THESE VARIABLES IN GLOBAL CODE!
        private location Loc = Location(0,0)
        private boolean Up = false
        private boolean Down = false
        private boolean Right = false
        private boolean Left = false
        private real ControlableAoA
        private real ControlableRot
        private boolean Cam = false
        private unit Unit
        //Up to here everything is set and called locally.
        
        //Use the x values as the angle of attack values. Y values are the according distance/offset values.
        //Example:
        //DISTANCEX1 = 345
        //DISTANCEY1 = 300
        //DISTANCEX2 = 295
        //DISTANCEY2 = 600
        //This means you want the camera distance to be 300 at an angle of 345 and 600 at an angle of 295.
        //The values inbetween will be calculated linearly.
        private constant real DISTANCEX1 = 345
        private constant real DISTANCEY1 = 300
        private constant real DISTANCEX2 = 295
        private constant real DISTANCEY2 = 600
        private constant real OFFSETX1 = 345
        private constant real OFFSETY1 = 0
        private constant real OFFSETX2 = 295
        private constant real OFFSETY2 = 200
        private constant real OFFSETLIMIT = 345 
        //if the angle is greater than this value the camera will pan to the unit instead of to an offset point
        private constant real ZOFFSET = 100 //basic camera offset
        private constant real CAMTIMEOUT = 0.1 //timeout for the camera timer
        private constant real KEYBOARDTIMEOUT = 0.1 //timerout for the keyboard timer
        private constant real PANDURATION = 0.4
        //duration over which the camera fields are set
        //higher values make the camera movement smoother but also slower
        private constant real MAXAOA = 355 
        //max controlable angle of attack; angles calculated to avoid clipping may be higher than his
        private constant real MINAOA = 295
        private constant real MAXROT = 105
        private constant real AOAINTERVAL = 3
        private constant real ROTINTERVAL = 7.5
        private constant real DEFAULTAOA = 345
        
        //These are the parameters for the linear functions used to calculate the desired camera distance and offset
        private real DistanceM = (DISTANCEY2-DISTANCEY1)/(DISTANCEX2-DISTANCEX1)
        private real DistanceT = DISTANCEY1-DISTANCEX1*DistanceM
        private real OffsetM = (OFFSETY2-OFFSETY1)/(OFFSETX2-OFFSETX1)
        private real OffsetT = OFFSETY1-OFFSETX1*OffsetM
    endglobals

    //Functions for distance and offset. These are linear mathematical functions y = mx+t
    private function GetDynamicDistance takes real angleOfAttack returns real
        return DistanceM * angleOfAttack + DistanceT
    endfunction

    private function GetDynamicOffset takes real angleOfAttack returns real
        return OffsetM * angleOfAttack + OffsetT
    endfunction
    
    private struct loc
        real x
        real y
        real z
    endstruct
    
    private function DistanceBetweenCoords takes real x1, real y1, real x2, real y2 returns real
        local real dx = x2-x1
        local real dy = y2-y1
        
        return SquareRoot(dx*dx+dy*dy)
    endfunction
    
    private function GetHighestPointInLine takes real x, real y, real angle, real distance returns loc
        local loc maxloc = loc.create()
        local real r = 10
        local real dx = Cos(bj_DEGTORAD*angle)
        local real dy = Sin(bj_DEGTORAD*angle)
        
        call MoveLocation(Loc, x, y)
        set maxloc.x = x
        set maxloc.y = y
        set maxloc.z = GetLocationZ(Loc)
        
        loop
            exitwhen r > distance
            call MoveLocation(Loc, x+r*dx, y+r*dy)
            if GetLocationZ(Loc) > maxloc.z then
                set maxloc.x = GetLocationX(Loc)
                set maxloc.y = GetLocationY(Loc)
                set maxloc.z = GetLocationZ(Loc)
            endif
            set r = r+10
        endloop
        
        return maxloc       
    endfunction

    //Camera actions, lots of stuff. All working :)
    private function ApplyOppiCam takes nothing returns nothing
        local real unitx = GetUnitX(Unit)
        local real unity = GetUnitY(Unit)
        local real unitz
        local real angleofattack = GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK)* bj_RADTODEG
        local loc offset = GetHighestPointInLine(unitx, unity, GetCameraField(CAMERA_FIELD_ROTATION)*bj_RADTODEG+180, 500)
        local real panx
        local real pany
        local real anticlipangle
        
        if angleofattack > OFFSETLIMIT then
            set panx = unitx
            set pany = unity
        else
            set panx = unitx + GetDynamicOffset(angleofattack) * Cos(bj_DEGTORAD*GetUnitFacing(Unit))
            set pany = unity + GetDynamicOffset(angleofattack) * Sin(bj_DEGTORAD*GetUnitFacing(Unit))
        endif
        call PanCameraToTimed(panx, pany, PANDURATION)
        
        call MoveLocation(Loc, unitx, unity)
        set unitz = GetLocationZ(Loc) + GetUnitFlyHeight(Unit)
        set anticlipangle = 350 - bj_RADTODEG * Atan2((offset.z-unitz-ZOFFSET), DistanceBetweenCoords(unitx, unity, offset.x, offset.y))

        if ControlableAoA > anticlipangle then
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, anticlipangle, PANDURATION)
        else
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, ControlableAoA, PANDURATION)
        endif
        
        call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, GetDynamicDistance(angleofattack), PANDURATION)
        call SetCameraField(CAMERA_FIELD_ZOFFSET, GetCameraField(CAMERA_FIELD_ZOFFSET) - GetCameraTargetPositionZ() + unitz + ZOFFSET, PANDURATION)
        call SetCameraField(CAMERA_FIELD_ROTATION, GetUnitFacing(Unit) + ControlableRot, PANDURATION)
        
        call offset.destroy()
    endfunction
    
    function UseOppiCam takes unit whichUnit, boolean use returns nothing
        if use == true then
            set Cam = true
            set Unit = whichUnit
        else
            set Cam = false
        endif
    endfunction
    
    function UseOppiCamForPlayer takes player whichPlayer, unit whichUnit, boolean use returns nothing
        if GetLocalPlayer() == whichPlayer then
            call UseOppiCam(whichUnit, use)
        endif
    endfunction
    
    private function CamPeriodic takes nothing returns nothing
        if Cam == true then
            call ApplyOppiCam()
        endif
    endfunction
    

    private function UpDown takes nothing returns nothing
        if GetLocalPlayer() == GetTriggerPlayer() then
            set Up = true
            if ControlableAoA > MINAOA then
                set ControlableAoA = ControlableAoA - AOAINTERVAL
            endif
        endif
    endfunction
    
    private function UpUp takes nothing returns nothing
        set Up = false
    endfunction
    
    
    private function DownDown takes nothing returns nothing
        if GetLocalPlayer() == GetTriggerPlayer() then
            set Down = true
            if ControlableAoA < MAXAOA then
                set ControlableAoA = ControlableAoA + AOAINTERVAL
            endif
        endif
    endfunction
    
    private function DownUp takes nothing returns nothing
        set Down = false
    endfunction
    
    
    private function LeftDown takes nothing returns nothing
        if GetLocalPlayer() == GetTriggerPlayer() then
            set Left = true
            if ControlableRot > -MAXROT then
                set ControlableRot = ControlableRot - ROTINTERVAL
            endif
        endif
    endfunction
    
    private function LeftUp takes nothing returns nothing
        set Left = false
    endfunction
    
    
    private function RightDown takes nothing returns nothing
        if GetLocalPlayer() == GetTriggerPlayer() then
            set Right = true
            if ControlableRot < MAXROT then
                set ControlableRot = ControlableRot + ROTINTERVAL
            endif
        endif
    endfunction
    
    private function RightUp takes nothing returns nothing
        set Right = false
    endfunction
    
    private function KeyboardPeriodic takes nothing returns nothing
        if Cam == true then
            if Up == true then
                if ControlableAoA > MINAOA then
                    set ControlableAoA = ControlableAoA - AOAINTERVAL
                endif
            endif
            if Down == true then
                if ControlableAoA < MAXAOA then
                    set ControlableAoA = ControlableAoA + AOAINTERVAL
                endif
            endif
            if Left == true then
                if ControlableRot > -MAXROT then
                    set ControlableRot = ControlableRot - ROTINTERVAL
                endif
            endif
            if Right == true then
                if ControlableRot < MAXROT then
                    set ControlableRot = ControlableRot + ROTINTERVAL
                endif
            endif
        endif
    endfunction
    
    private function Reset takes nothing returns nothing
        if GetLocalPlayer() == GetTriggerPlayer() then
            set ControlableAoA = DEFAULTAOA
            set ControlableRot = 0
        endif
    endfunction
    
    private function CheckCam takes nothing returns boolean
        return Cam
    endfunction
    
    private function Init takes nothing returns nothing
        local timer t
        local trigger trig
        
        set ControlableAoA = DEFAULTAOA
        set ControlableRot = 0
        
        set t = CreateTimer()
        call TimerStart(t, CAMTIMEOUT, true, function CamPeriodic)
        
        set t = CreateTimer()
        call TimerStart(t, KEYBOARDTIMEOUT, true, function KeyboardPeriodic)
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_UP_DOWN)
        call TriggerAddAction(trig, function UpDown)
        call TriggerAddCondition(trig, Condition(function CheckCam))
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_UP_UP)
        call TriggerAddAction(trig, function UpUp)
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_DOWN_DOWN)
        call TriggerAddAction(trig, function DownDown)
        call TriggerAddCondition(trig, Condition(function CheckCam))
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_DOWN_UP)
        call TriggerAddAction(trig, function DownUp)
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_LEFT_DOWN)
        call TriggerAddAction(trig, function LeftDown)
        call TriggerAddCondition(trig, Condition(function CheckCam))
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_LEFT_UP)
        call TriggerAddAction(trig, function LeftUp)
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_RIGHT_DOWN)
        call TriggerAddAction(trig, function RightDown)
        call TriggerAddCondition(trig, Condition(function CheckCam))
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerEvent(trig, EVENT_PLAYER_ARROW_RIGHT_UP)
        call TriggerAddAction(trig, function RightUp)
        
        
        set trig = CreateTrigger()
        call TriggerRegisterAnyPlayerChatEvent(trig, "-reset", true)
        call TriggerAddAction(trig, function Reset)
        call TriggerAddCondition(trig, Condition(function CheckCam))
        
        set t = null
        set trig = null
    endfunction

endlibrary

What the cam does (not that important):
Hidden information:
-Smoothly pans to the unit instead of locking it
-Lets the player control Angle of Attack and Rotation of the camera by using the arrow keys up to a constant value
-Counters the terrain smoothing Blizzard bestowed upon us i.e. you can safely walk up and down hills and cliffs without the cam dropping below the terrain
-Tries to keep the camera above the terrain even if there's a cliff/hill behind the camera by finding the highest point behind the camera and then calculating a safe angle
-Pans the camera to a point in front of the unit at higher angles to keep the unit positioned at the lower half of the screen

I might at some point in the future release it if anyone's interested. It will also be used in the new Legacies: TotS version.


I already had a problem when using
Collapse JASS:
call TriggerRegisterPlayerEvent(trig, GetLocalPlayer(), PLAYER_EVENT)
//instead of
call TriggerRegisterAnyPlayerEvent(trig, PLAYER_EVENT)
//and then checking for GetLocalPlayer() == GetTriggerPlayer() in the actions function
That's basically what Antiquities was asking for a few days ago.
Turns out that what Rising_Dusk said is true.

I haven't had any problems with the rest of the code, though <<yet>>.
02-20-2009, 07:27 PM#2
Rising_Dusk
It is generally wise to do as little as possible in local blocks. Right now you are changing the value of a variable locally, which can cause desynchronizations in the checks if other players ever check those values. You should be careful with that, it seems 'risky.'
02-20-2009, 08:02 PM#3
Opossum
Yeah right, but do other players ever check these values if I don't use them in global code?
I think there's nothing wrong about using huge blocks of local code as long as you can be sure that it's safe. I mean I'm saving 11*8 variables/array members by using this method. There's no reason for keeping these values globally AS LONG as it's safe. The question is if it is :D.