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

Detect when a player is looking at a point

Status
Not open for further replies.
Level 12
Joined
Mar 13, 2012
Messages
1,121
JASS:
GetCameraTargetPositionY    takes nothing returns real
GetCameraTargetPositionY    takes nothing returns real

Mind though that these natives are completely local, so if you want to do anything non-local you have to sync values first.
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
These functions return the X/Y of the camera target, meaning the middle of the screen and therefore what you want I guess.

To use them you need two real variables created with the Variable Editor (Ctrl+B), lets call them "camX" and "camY".

Now you need to make use of the Custom Script trigger, like so:

  • Custom script: set udg_camX = GetCameraTargetPositionX()
  • Custom script: set udg_camY = GetCameraTargetPositionY()
Notice the added "udg_" in front of your variable names, this is necessary because all variables created in the variable editor get this addendum in the script as they are user generated globals.

Now comes the "care these functions are local!!" part. When you executed this every player will have a different value saved in camX and camY, so if you want to do anything that involves global gamestate changes, for example creating or damaging a unit, then you have to sync all desired values and then go on.

Syncing is a whole other topic, where nobody seems to know everything, so I can not link you to a definitive resource which is 100% correct, but this might be start. It also has some code examples.

If you do only local stuff with those cam coordinates you do not need the whole syncing topic of course.
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
That's what all the "local" fuss is about. It will return a different value for every player, namely that players camera X and Y.
 
If you are willing to use vJASS in your map (requires you to edit your map with JassNewGenPack), then I have an old system that might be of use. Note that it isn't super sensitive--it updates roughly every 2*latency seconds. It'll be on the order of 200 to 400 milliseconds on battle.net. If this is a single player map, then you don't need to use this system.

Here are some instructions:
  • Download JNGP if you haven't done so already. If Chrome warns you about a security risk, then just click "Details" and "Proceed Anyway". Just unzip the file, move the folder to your Warcraft III directory, and open 'JassNewGen Editor.exe'.
    .
  • Open your map. Create a new trigger and name it anything (e.g. PlayerCameraData). Go to Edit -> Convert to Custom Text. Erase everything that is there. Copy and paste the following code into that trigger:
    JASS:
    library PlayerCameraData
    /**
    *
    *   EnableCameraBroadcast(player p, boolean flag)
    *       - Enables/disables the storage (and syncing) of camera data for that player.
    *   RegisterCameraDataUpdate(boolexpr b)
    *       - Fires the boolexpr whenever the camera data is resynced.
    *
    *   PlayerCameraData.x[playerId] -> x-coordinate of camera for player 
    *   PlayerCameraData.y[playerId] -> y-coordinate of camera for player 
    *   PlayerCameraData.aoa[playerId] -> angle of attack for player
    *   PlayerCameraData.rotation[playerId] -> rotation for player
    *
    */
        globals
            private constant string CACHE_NAME = "PlayerCameraData"
            private constant string MISSION_PREFIX = "PCD_"
        
            private gamecache cache
            private player array playerStack
            private integer array playerIndex
            private integer stackIndex = 0
            private trigger syncTrigger = CreateTrigger()
            private trigger updateEvent = CreateTrigger()
        endglobals
        
        function RegisterCameraDataUpdate takes boolexpr b returns nothing 
            call TriggerAddCondition(updateEvent, b)
        endfunction
        
        private function SyncCameraData takes nothing returns nothing 
            local integer localStackIndex = stackIndex
            local integer i = localStackIndex
            local integer id
            local string  missionKey
            
            if i == 0 then
                return
            endif
            loop
                exitwhen i == 0
                
                set id = GetPlayerId(playerStack[i])
                set missionKey = MISSION_PREFIX + I2S(id)
                if GetLocalPlayer() == playerStack[i] then
                    call StoreReal(cache, missionKey, "x", GetCameraTargetPositionX())
                    call StoreReal(cache, missionKey, "y", GetCameraTargetPositionY())
                    call StoreReal(cache, missionKey, "aoa", GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK) * bj_RADTODEG)
                    call StoreReal(cache, missionKey, "rot", GetCameraField(CAMERA_FIELD_ROTATION) * bj_RADTODEG)
                    
                    call SyncStoredReal(cache, missionKey, "x")
                    call SyncStoredReal(cache, missionKey, "y")
                    call SyncStoredReal(cache, missionKey, "aoa")
                    call SyncStoredReal(cache, missionKey, "rot")
                endif
                
                set i = i - 1
            endloop
            call TriggerSyncReady()
            set i = localStackIndex
            loop 
                exitwhen i == 0
                set id = GetPlayerId(playerStack[i])
                set missionKey = MISSION_PREFIX + I2S(id)
                set PlayerCameraData.x[id] = GetStoredReal(cache, missionKey, "x")
                set PlayerCameraData.y[id] = GetStoredReal(cache, missionKey, "y")
                set PlayerCameraData.aoa[id] = GetStoredReal(cache, missionKey, "aoa")
                set PlayerCameraData.rotation[id] = GetStoredReal(cache, missionKey, "rot")
                set i = i - 1
            endloop
            call TriggerEvaluate(updateEvent)
            call TriggerExecute(syncTrigger)
        endfunction
        
        function EnableCameraBroadcast takes player p, boolean flag returns nothing 
            local integer id = GetPlayerId(p)
            if flag then
                if playerIndex[id] != 0 then 
                    return
                endif
                set stackIndex = stackIndex + 1
                set playerStack[stackIndex] = p
                set playerIndex[id] = stackIndex
                if stackIndex == 1 then
                    call TriggerExecute(syncTrigger)
                endif
            elseif playerIndex[id] != 0 then
                set playerStack[playerIndex[id]] = playerStack[stackIndex]
                set stackIndex = stackIndex - 1
                set playerIndex[id] = 0
            endif
        endfunction
        
        private module Init 
            private static method onInit takes nothing returns nothing 
                set cache = InitGameCache(CACHE_NAME)
                call TriggerAddAction(syncTrigger, function SyncCameraData)
            endmethod
        endmodule
        
        struct PlayerCameraData extends array
            static real array x
            static real array y
            static real array aoa
            static real array rotation
            
            implement Init
        endstruct
        
        function GetCameraTargetLocation takes player p returns location
            local integer id = GetPlayerId(p)
            return Location(PlayerCameraData.x[i], PlayerCameraData.y[i])
        endfunction
    
    endlibrary
    .
  • Create an initialization trigger like the one below. In my system, you have to specifically choose which players should have their cameras tracked. Since you aren't familiar with JASS, I recommend making a player variable named "TempPlayer" and organizing the code like so:
    • Player Camera Init
      • Events
        • Map Initialization
      • Conditions
      • Actions
        • Set TempPlayer = Player 1 (Red)
        • Custom script: call EnableCameraBroadcast(udg_TempPlayer, true)
        • Set TempPlayer = Player 2 (Blue)
        • Custom script: call EnableCameraBroadcast(udg_TempPlayer, true)
        • ... etc ...
    I've only done it for player 1 and player 2, but you can do it for as many players as you'd like. Make sure you spell everything correctly and capitalize properly, JASS is case-sensitive.
    .
  • Save the map and make sure it compiles without an error. If there is an error, post here.
    .
  • Now you just need to periodically check the location of the camera. For example, if you wanted to load the location of Player 1 (Red)'s camera into a point variable named TempLoc, then you just do this:
    • Check Camera
      • Events
        • Time - Every 0.2 seconds of game time
      • Conditions
      • Actions
        • Set TempPlayer = Player 1 (Red)
        • Custom script: set udg_TempLoc = GetCameraTargetLocation(udg_TempPlayer)
        • ... use 'TempLoc' for whatever you need ...
        • Custom script: call RemoveLocation(udg_TempLoc)

That should be it. If there are any errors or if it doesn't work, let me know. If you are doing something purely visual, then you don't need to sync at all (e.g. displaying some weather or creating a special effect). If it affects gameplay, then you must resort to syncing as Ezekiel mentioned (that is what my system will do for you).
 
Status
Not open for further replies.
Top