• 🏆 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!

[vJASS] UserCamera

Level 9
Joined
Jun 21, 2012
Messages
432
JASS:
library UserCamera/*
************************************************************************************
*
*   UserCamera (StaticCam)
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   v1.0.0.0
*   by Trigger edge
* 
*   Intuitive API to apply camera for users with some additional features.
*
*   API:
*   ¯¯¯
*   struct Camera extends array
*      
*        Operators:
*        ¯¯¯¯¯¯¯¯¯
*       static method operator [] takes player p returns thistype
*
*       method operator angleOfAttack takes nothing returns real
*       method operator fieldOfView takes nothing returns real
*       method operator roll takes nothing returns real
*       method operator rotation takes nothing returns real
*       method operator targetDistance takes nothing returns real
*
*       method operator angleOfAttack= takes real value returns nothing
*       method operator fieldOfView= takes real value returns nothing
*       method operator roll= takes real value returns nothing
*       method operator rotation= takes real value returns nothing
*       method operator targetDistance= takes real value returns nothing
*
*       method operator locked takes nothing returns boolean
*       method operator locked= takes boolean flag returns nothing
*
*        Methods:
*        ¯¯¯¯¯¯¯
*       method setPosition takes real x, real y returns nothing
*       method enableTargetController takes unit target, real x, real y returns nothing
*       method disableTargetController takes nothing returns nothing
*       method withDuration takes real r returns thistype
*       method reset takes nothing returns nothing
*
************************************************************************************/

    /*
     Configuration
    */
    globals
        /*
        Id of dummy unit to be used to lock the minimap
        */
        private constant integer DUMMY_LOCKING_ID        = 'hpea'
        private constant real    CHECKER_PERIOD          = 0.020//0.03125
    endglobals
    /*
     Endconfiguration
    */
  
    struct Camera extends array
        private static player localPlayer=null
        private static thistype localPlayerId=0
        private static integer size=0
        private static unit minipapLockingUnit=null
        private static timer coreTimer=CreateTimer()
        private static trigger eventLeave=CreateTrigger()
      
        private static unit Target=null
      
        private static real TargetX=0
        private static real TargetY=0
        private static real duration=0
  
        private static real AngleOfAttack=0
        private static real FieldOfView=0
        private static real Roll=0
        private static real Rotation=0
        private static real TargetDistance=0
      
        private static boolean Locked=false
        private static boolean EnableTargetController=false
      
        private static method onLoop takes nothing returns nothing
            if(Locked)then
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,AngleOfAttack,0)
                call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW,FieldOfView,0)
                call SetCameraField(CAMERA_FIELD_ROLL,Roll,0)
                call SetCameraField(CAMERA_FIELD_ROTATION,Rotation,0)              
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,TargetDistance,0)
                if(EnableTargetController)then
                    if(null!=Target)then
                        set TargetX=GetUnitX(Target)
                        set TargetY=GetUnitY(Target)
                    endif
                    /*
                    The following two functions disables mouse wheel, arrows and mini-map
                    drag, but one not disables mouse wheel and ther other not disables
                    the mini-map drag, therefore I have combined them...
                    Credits to Aniki by the PanCamera trick.
                    */
                    call PanCameraToTimed(0x7FFFFFFF,0x7FFFFFFF,0x7FFFFFFF)
                    call SetCameraTargetController(minipapLockingUnit,TargetX,TargetY,false)
                endif
            endif
        endmethod
      
        static method operator [] takes player p returns Camera
            return GetPlayerId(p)
        endmethod
      
        method setPosition takes real x, real y returns nothing
            if(localPlayerId==this)then
                call PanCameraToTimed(x,y,0)
            endif
        endmethod

        method enableTargetController takes unit target, real x, real y returns nothing
            if(localPlayerId==this)then
                set Target=target
                set TargetX=x
                set TargetY=y
                set EnableTargetController=true
            endif
        endmethod
      
        method disableTargetController takes nothing returns nothing
            if(localPlayerId==this)then
                set EnableTargetController=false
            endif
        endmethod
      
        method withDuration takes real r returns thistype
            if(localPlayerId==this)then
                set duration=r
            endif
            return this
        endmethod
      
        method reset takes nothing returns nothing
            if(localPlayerId==this)then
                call ResetToGameCamera(0)
            endif
        endmethod
      
        //! textmacro UserCamera_field_method takes METHOD_NAME,VAR_NAME,FIELD_NAME,FIX
            method operator $METHOD_NAME$ takes nothing returns real
                if(localPlayerId==this)then
                    return GetCameraField($FIELD_NAME$)$FIX$/0.0174533
                else
                    return 0.
                endif
            endmethod
          
            method operator $METHOD_NAME$= takes real value returns nothing
                if(localPlayerId==this)then
                    set $VAR_NAME$=value
                    call SetCameraField($FIELD_NAME$,value,duration)
                    set duration=0
                endif
            endmethod
        //! endtextmacro
      
        //! runtextmacro UserCamera_field_method("angleOfAttack","AngleOfAttack","CAMERA_FIELD_ANGLE_OF_ATTACK","")
        //! runtextmacro UserCamera_field_method("fieldOfView","FieldOfView","CAMERA_FIELD_FIELD_OF_VIEW","")
        //! runtextmacro UserCamera_field_method("roll","Roll","CAMERA_FIELD_ROLL","")
        //! runtextmacro UserCamera_field_method("rotation","Rotation","CAMERA_FIELD_ROTATION","")
        //! runtextmacro UserCamera_field_method("targetDistance","TargetDistance","CAMERA_FIELD_TARGET_DISTANCE","//")
      
        method operator locked takes nothing returns boolean
            if(localPlayerId==this)then
                return Locked
            else
                return false
            endif
        endmethod
      
        method operator locked= takes boolean flag returns nothing
            if(localPlayerId==this and PLAYER_SLOT_STATE_PLAYING==GetPlayerSlotState(localPlayer) and MAP_CONTROL_USER==GetPlayerController(localPlayer) and null!=localPlayer)then
                if(flag and not Locked)then
                    set Locked=true
                    call onLoop()
                    call TimerStart(coreTimer,CHECKER_PERIOD,true,function thistype.onLoop)
                elseif(not flag and Locked)then
                    set Locked=false
                    call ResetToGameCamera(0)
                    set Target=null
                    set EnableTargetController=false
                    call PauseTimer(coreTimer)
                endif
            endif
        endmethod
      
        private static method onPlayerLeave takes nothing returns nothing
            local thistype this=GetPlayerId(GetTriggerPlayer())
            if(.locked)then
                set .locked=false
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            set localPlayer=GetLocalPlayer()
            set localPlayerId=GetPlayerId(localPlayer)
            set AngleOfAttack=localPlayerId.angleOfAttack
            set FieldOfView=localPlayerId.fieldOfView
            set Roll=localPlayerId.roll
            set Rotation=localPlayerId.rotation
            set TargetDistance=localPlayerId.targetDistance
            if(PLAYER_SLOT_STATE_PLAYING==GetPlayerSlotState(localPlayer) and MAP_CONTROL_USER==GetPlayerController(localPlayer))then
                call TriggerRegisterPlayerEvent(eventLeave,localPlayer,EVENT_PLAYER_LEAVE)
            endif
            set minipapLockingUnit=CreateUnit(Player(15),DUMMY_LOCKING_ID,0,0,0)
            call UnitAddAbility(minipapLockingUnit,'Aloc')
            call UnitRemoveAbility(minipapLockingUnit,'Amov')
            call ShowUnit(minipapLockingUnit,false)
            call TriggerAddCondition(eventLeave,Filter(function thistype.onPlayerLeave))
        endmethod
    endstruct
endlibrary
 

Attachments

  • UserCamera test.w3x
    20.6 KB · Views: 96
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
The case that immediately pops to mind of using a "static camera" like this is of course a "full screen gui" (ex. BX-TRS II).
I've have something similar called CameraLock, now I need the FSG itself which seems very very hard to write =)...

JASS:
// In order to lock the camera we must somehow disable the arrow keys which move
// the camera by default. We can do this by using the SetCameraTargetController native.
// In order to prevent the Insert/Delete and PageUp/PageDown keys to rotate the camera
// we keep applying the camerasetup repeatedly using the CameraSetupApplyForceDuration native.
// We also keep calling SetCameraTargetController as well because the player can "escape" it
// by selecting a unit and pressing Ctrl+C.

struct CameraLock extends array
    static timer array timers
    static integer first_timer_offset

    static constant integer CAMERA_TARGET_CONTROLLER_ID = 'hpea'
    static unit array camera_target_controllers
    static camerasetup array cameras

    static method init takes nothing returns nothing
        local integer pid
        local timer   t
        local unit u
        local real map_min_x = 0
        local real map_min_y = GetCameraBoundMinY()

        set pid = 0
        set timers[pid] = CreateTimer()
        set first_timer_offset = GetHandleId(timers[pid]) - 0x100000
        set pid = 1
        loop
            exitwhen pid >= bj_MAX_PLAYERS
            set timers[pid] = CreateTimer()
            set pid = pid + 1
        endloop

        set pid = 0
        loop
            exitwhen pid >= bj_MAX_PLAYERS

            set u = CreateUnit(Player(pid), CAMERA_TARGET_CONTROLLER_ID, map_min_x, map_min_y, 270)
            call UnitAddAbility(u, 'Aloc')
            call ShowUnit(u, false)
            call SetUnitPathing(u, false)
            set camera_target_controllers[pid] = u

            set pid = pid + 1
        endloop
    endmethod

    static method keep_locking takes nothing returns nothing
        local integer pid = GetHandleId(GetExpiredTimer()) - (first_timer_offset + 0x100000)

        if GetLocalPlayer() == Player(pid) then
            call SetCameraTargetController(camera_target_controllers[pid], 0.0, 0.0, false)
            call CameraSetupApplyForceDuration(cameras[pid], true, 1.0)
        endif
    endmethod

    static method lock takes camerasetup camera, real target_x, real target_y, player p returns nothing
        local integer pid = GetPlayerId(p)
        local unit ctc = camera_target_controllers[pid]
        set cameras[pid] = camera

        call SetUnitPosition(ctc, target_x, target_y)

        if GetLocalPlayer() == p then
            call SetCameraTargetController(ctc, 0.0, 0.0, false)

            // Initialy don't pan the camera (the flase argument), let the SetCameraTargetController
            // lock the camera to the target (ctc) position.
            call CameraSetupApplyForceDuration(camera, false, 0.0)
        endif

        // These seem to be required for the initial SetCameraTargetController call
        // to lock the camera.
        call IssuePointOrder(ctc, "move", GetUnitX(ctc), GetUnitY(ctc) - 0.05)
        call SetUnitY(ctc, GetUnitY(ctc) + 0.05)

        call TimerStart(timers[pid], 0.1, true, function thistype.keep_locking)
    endmethod

    static method unlock takes player p returns nothing
        call PauseTimer(timers[GetPlayerId(p)])
        if GetLocalPlayer() == p then
            call ResetToGameCamera(0)
        endif
    endmethod

endstruct
 
If you need a full screen gui, check out DGUI for it's implementation.
It's pretty cool. It uses vectors and billboarded models to create trackables always on screen of the current camera view.

However, this method is only suited for singleplayer maps. In multiplayer, the camera natives are too slow.
 
Camera natives are faster in singleplayer than multiplayer? Could you elaborate on that?
GetCameraEyePositionX and GetCameraTargetPositionX update their values at a much higher frequency in singleplayer (>32fps) than in multiplayer (~15fps ... slow enough to make it look ugly unless you do interpolation in which - good luck with that - your UI elements will jerk around whenever you start/stop swaying the camera).

And don't ask why, because these natives aren't synced anyway. I have no answer to that, but Blizzard.
 
onLock you don't need to reset values. The default values can be assigned once onInit and then all depends just on user.
There enable/disable should really just enablde/disable an instance, but not change values.

I think call PanCameraToTimed(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF) trick should be used, shown here: [vJASS] - LockedCamera

Actually, I don't think loop is needed, since everything can just happen localy to all players.
So you could just set thistype to the localId, and make an .active check
+ when a user leaves you can just disbale the instance, because the instance would not be checked anymore by itself if the periodic call runs localy.

Could you provide a little demo code or demo map?
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
I might have overlooked something but instead of this
JASS:
        private static method onLoop takes nothing returns nothing
            local thistype this=11
            loop
                if(.active and localPlayer==.user)then
                    if(null!=.target)then
                        set .sourceX=GetUnitX(.target)
                        set .sourceY=GetUnitY(.target)
                    endif
                    call PanCameraToTimed(.sourceX,.sourceY,0)
                    call SetCameraField(CAMERA_FIELD_ROLL,.roll,0)
                    call SetCameraField(CAMERA_FIELD_ROTATION,.rotation,0)
                    call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW,.fieldOfView,0)
                    call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,.angleOfAttack,0)
                    call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,.targetDistance,0)
                endif
                exitwhen(0==this)
                set this=this-1
            endloop
        endmethod
why not
JASS:
        private static method onLoop takes nothing returns nothing
            local thistype this=GetPlayerId(localPlayer)
            if .active then
                if(null!=.target)then
                    set .sourceX=GetUnitX(.target)
                    set .sourceY=GetUnitY(.target)
                endif
                call PanCameraToTimed(.sourceX,.sourceY,0)
                call SetCameraField(CAMERA_FIELD_ROLL,.roll,0)
                call SetCameraField(CAMERA_FIELD_ROTATION,.rotation,0)
                call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW,.fieldOfView,0)
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,.angleOfAttack,0)
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,.targetDistance,0)
            endif
        endmethod
 
Level 9
Joined
Jun 21, 2012
Messages
432
I think call PanCameraToTimed(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF) trick should be used, shown here: [vJASS] - LockedCamera
I have combined it with my old method SetCameraTargetController (minipapLockingUnit, 0,0, false) This way everything is disabled: mouse wheel, arrows and minimap drag...

Could you provide a little demo code or demo map?
Lol, it's complicated, but I will haha.
why not
JASS:
        private static method onLoop takes nothing returns nothing
            local thistype this=GetPlayerId(localPlayer)
            if .active then
                if(null!=.target)then
                    set .sourceX=GetUnitX(.target)
                    set .sourceY=GetUnitY(.target)
                endif
                call PanCameraToTimed(.sourceX,.sourceY,0)
                call SetCameraField(CAMERA_FIELD_ROLL,.roll,0)
                call SetCameraField(CAMERA_FIELD_ROTATION,.rotation,0)
                call SetCameraField(CAMERA_FIELD_FIELD_OF_VIEW,.fieldOfView,0)
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,.angleOfAttack,0)
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,.targetDistance,0)
            endif
        endmethod
Nice!

EDIT: I have the doubt if this does not desync?
JASS:
if(1==size and localPlayer==.user)then
    call TimerStart(coreTimer,CHECKER_PERIOD,true,function thistype.onLoop)
endif
 
It was a nice idea trying to share member variables, declared as static nonarray + abusing LocalPlayer, though there is a problem.
The usage becomes risky. When retreiving a player's values, the user expects to get global values, not local ones. So he might have to sync the data by himself, or only work in local blocks.

So I think the system should use real arrays to store player data, but work without loops still.

Then, I think it would be a good way to just make the player data public.
The user will be able to set/get all things like "target, targetX, ..." at any time without functions, and I don't think it's dangerous.
The "enabled" or "locked" member though should still be warapped into a method operator, so you internaly can care about start/pausing the timer.
 
Top