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

3rd Person Camera

Status
Not open for further replies.
Level 18
Joined
Jan 21, 2006
Messages
2,552
I put this together for Midnighters initially, but since it worked so well I wanted to share it with the rest of the community. It is a 3rd-person camera library, which by itself really isn't anything special. The only thing that makes this camera library a preferable choice is that it contours with terrain level changes. I know for a fact many people have had the problem of the camera stooping too low or too high when the terrain nearby them is shifty, but this camera attempts to solve those problems with a couple of simple calculations.

First, put this code in your custom script (doesn't really matter if it is done first or not). It doesn't make a difference where it is added, since it is a scope and will be placed beneath the library. What it does is wait until the initialization thread is finished and then it applies the camera to a newly created Footman (for player 1).

JASS:
scope Setup initializer init
    public function initDelayed takes nothing returns nothing
        set camera.localCamera.controller = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
    
        call camera.localCamera.start(true)
        call DestroyTimer(GetExpiredTimer())
    endfunction
    public function init takes nothing returns nothing
        call TimerStart(CreateTimer(),0,false,function initDelayed)
    endfunction
endscope

JASS:
library Camera

    //############################################################################################
    globals
        //****************************************************************************************
        // Configuration Constants
        // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 
        //  Default constant values used for initializing each static camera instance.
        //
        public      constant real       DEFAULT_PAN_DURATION        = 0.7
        //
        //  The following configuration constants are preferred defaults. The camera is set
        //  to perform best under these circumstances, though adjustments *can* be made if it
        //  is necessary.
        //
        public      constant real       DEFAULT_TARGET_DISTANCE     = 1250
        public      constant real       DEFAULT_ZOFFSET             = 30
        public      constant real       DEFAULT_ANGLE_OF_ATTACK     = 340
        //
        //
        //****************************************************************************************
    endglobals
    
    globals
        //****************************************************************************************
        // Dynamic Storage Utility
        // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        // These are private storage variables that allow easy-access of utilities, such as
        // the GetLocationZ function which allows terrain-detection.
        private     constant location   core__tempLoc           = Location(0, 0)
        private     real                core__tempLocZ          
        //
        //
        //****************************************************************************************    
    endglobals
    
    //############################################################################################
    private struct cameradata
        public          real            targetDistance
        public          real            angleOfAttack
        public          real            zOffset
        
        static method create takes nothing returns thistype
            local thistype data=allocate()
            set data.targetDistance=DEFAULT_TARGET_DISTANCE
            set data.angleOfAttack=DEFAULT_ANGLE_OF_ATTACK
            set data.zOffset=DEFAULT_ZOFFSET
            return data
        endmethod
    endstruct
    
    //############################################################################################
    struct camera extends array
    
        private         unit            cam__controller
        private         cameradata      cam__specData
        readonly        player          controllerPlayer
        
        private         boolean         cam__enabled       
        
        public          boolean         inheritRotation
        public          real            panDuration         
        
        readonly        boolean         isLocal
        readonly static thistype        localCamera
        
        private static  timer           core__looptmr       = CreateTimer()
        private static  constant real   core__looptmrRef    = 0.04
        
        
        //############################################################################################
        method operator controller takes nothing returns unit
            return cam__controller
        endmethod
        method operator controller= takes unit u returns nothing
            set cam__controller=u
            if(isLocal) then
                call SetCameraTargetController(cam__controller, 0, 0, true)
            endif
        endmethod

        //############################################################################################
        method start takes boolean rotation returns boolean  
            set inheritRotation = rotation
            //If there is an available camera-target then .start() will return true. If no target is
            //available at all then "inheritRotation" will be queued for when .controller= is used
            return cam__controller != null
        endmethod
        
        method stop takes nothing returns nothing
            set inheritRotation = false
            if (isLocal) then
                call StopCamera()
                
            endif
        endmethod
        
        //############################################################################################
        static if(DEBUG_MODE) then
            //The following code will only be implemented if DEBUG_MODE is enabled.
            private static method debug takes nothing returns nothing
                
            endmethod
        endif
        
        //############################################################################################
        private static method loopFunc takes nothing returns nothing
            local real r0=SquareRoot(Pow(GetCameraEyePositionX(), 2)+Pow(GetCameraEyePositionY(), 2))
            local real r1=SquareRoot(Pow(GetCameraTargetPositionX(), 2)+Pow(GetCameraTargetPositionY(), 2))
            local real d =GetCameraTargetPositionZ()-GetCameraEyePositionZ()
            set d=SquareRoot((r1-r0)*(r1-r0)+(d*d))
        
            call MoveLocation(core__tempLoc, GetUnitX(localCamera.controller), GetUnitY(localCamera.controller))
            set core__tempLocZ=GetLocationZ(core__tempLoc)
            
            if(localCamera.isLocal) then
                if(localCamera.inheritRotation) then
                    call SetCameraField(CAMERA_FIELD_ROTATION, GetUnitFacing(localCamera.controller),/*
                                                                                */localCamera.panDuration)
                endif
                //In order to counter-weight the smoothing that the camera uses we must find out the
                //offset and correct it using ZOFFSET. On flat-ground, diffz will be 0.00.
                call SetCameraField(CAMERA_FIELD_ZOFFSET, core__tempLocZ-GetCameraTargetPositionZ()/*
                                                */+localCamera.cam__specData.zOffset+(GetCameraEyePositionZ()/*
                                                */-core__tempLocZ)/5, localCamera.panDuration)
                
                //Now we must adjust the target-distance depending on how much the result has varied from it's original
                set core__tempLocZ=localCamera.cam__specData.targetDistance-d
                
                call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,/*
                                                */localCamera.cam__specData.targetDistance+(core__tempLocZ/3),/*
                                                */localCamera.panDuration)
                call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,/*
                                                */localCamera.cam__specData.angleOfAttack-(GetCameraField(CAMERA_FIELD_ZOFFSET)/6),/*
                                                */localCamera.panDuration)
            endif
            
            debug call debug()
        endmethod
    
        //############################################################################################
        private static method onInit takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen(i == 12)
                
                //Initialize camera components:
                set camera[i].controllerPlayer  =Player(i)
                set camera[i].cam__controller   =null
                set camera[i].cam__specData     =cameradata.create()
                set camera[i].panDuration       =DEFAULT_PAN_DURATION
                
                //Setup local camera data:
                set camera[i].isLocal           =false
                
                set i = i + 1
            endloop
            //Finalize local data:
            set localCamera         = camera[GetPlayerId(GetLocalPlayer())]
            set localCamera.isLocal = true
            //Initialize update/refresh loop timer:
            call TimerStart(core__looptmr, core__looptmrRef, true, function thistype.loopFunc)
        endmethod
    endstruct

endlibrary

Tell me what you guys think, as I wanted to add a couple more things before submitting it. There are still quite a few problems, and things that could be done better, but with a reasonable (if it's too close, the camera tends to go beneath the terrain - I'm trying to fix this) set of data, it should work pretty well.

  • Camera eye position is below the terrain.
  • Target of camera is at the top of a hill.
  • Target of camera is not in the line of sight of the camera (terrain obstruction).
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
If you stand at the top of that really big hill you can't see where you're moving at all.

Try it out in my system, it angles the camera up so you still have a clear view of your movement options. Also, this system doesn't solve the problems associated with having the eye position of the camera below the terrain, which will cause the unit's right-click orders to screw up. I hope to solve that problem before I actually release this system.

The goal is to not simply have the camera follow the unit, it's got to be able to allow the unit to easily see the regions nearby. This is extremely easy when the angle of attack is default, since the camera is always above the target. It gets a little bit more complicated the further down you angle your camera. I wanted to develop a system that detects these kinds of gray areas and responds appropriately, giving a result that is pleasing and completely functional.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
YourNameHere said:
You probably didn't even look at the possibilities of my system. Too bad.

I doubt you checked mine out either, but it would have been nice if instead of just a link you actually gave me some instructions on what to do to display some of these amazing possibilities that you say are included.



This is a picture of your camera system in action when moving up a cliff. I bumped up the target distance to 505 so that it wasn't so sensitive. Looking into the sky bears no functionality when it comes to moving a unit.



This is another picture with the camera weight set to 0 (- target weight as was displayed in your configuration). All I see is a big mountain of "possibilities", if that's what you want to call it.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
YourNameHere said:
I'm just too lazy to check your's out. Give me a cool test map and I'll do it. :)

All you have to do is copy and paste the code that I posted. There are no other requirements. Give a test-map some shifty terrain that you think would be appropriate for testing a 3rd-person camera and go at it.

YourNameHere said:
Anyway, look at the so-called "configuration" section of my system. It includes nice things to play around with. Actually just 3 variables - but they can enhance those height problems alot.

They don't solve the underlying problem though, which is the fact that when you're on top of a cliff the only available moving options are way below you. Your camera always seems to try and look "up" (unless it's moving down the side of a cliff, which is hard to do since you can never see the other side of the cliff).
 
Level 11
Joined
Apr 29, 2007
Messages
826
Now seriously, I could also play around with some variables until you get a bad result. It's a matter of configuration - the camera setup depends on what your map got - steep cliffs or just some flat hills. Both cases require a different setup on those variables to get a pretty good result in the end.

edit/ So yeah, I tested your system. I don't think the pictures require any further explanation, do they?






edit2/ Meh okay, I see the problem with my system now. Increase the distance values and everything will just mess up horribly. Sigh. Never tested that D:
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Now seriously, I could also play around with some variables until you get a bad result. It's a matter of configuration

I was using your configuration. I didn't play around with anything but the target distance (because when it's so close to the hero it makes it almost impossible to accurately click). Also to note, even when I was using your exact default configuration definitions I was still having the same problems, not being able to see a damn thing when moving up a cliff.

The screen-shots that you've supplied are all problems in your system as well, except the second screen-shot displays how I solve the problem of not being able to see where you're going when on top of a narrow cliff. The only reason it's like that is because every side of the unit is a down-wards slope, so the camera must be able to see all possible movement options.

YourNameHere said:
steep cliffs or just some flat hills. Both cases require a different setup on those variables to get a pretty good result in the end.

All I did was test your test-map, which had a fairly large cliff in it. The camera did not work as expected, instead I was forced to stare at the cliff or into the sky, neither of which were useful. Please tell me what configuration I need to make it look nice?

First picture you have the camera behind a cliff, and really far away. You were just trying to get bad results. I wasn't. Stop being a douche.

The second picture you have your unit on the very top of a large cliff. The only good option at that point is to have the camera directly above the unit so that you have a clear view of where you can move. I can tell you also let the camera station for a little while because while the unit is moving the camera is never directly above the unit.

The third picture is that problem regarding camera eye position that I referred to in the first post. I've already stated that this is a common problem.

edit/ So yeah, I tested your system. I don't think the pictures require any further explanation, do they?

Well you should have at least given the values for the configurations that you used.

I'm glad you posted the screen shots you did though, it seems that you could only find the 3 most prominent problems with the camera system. The camera eye position being below the terrain, a unit being on top of a cliff, and terrain being in between the camera and the target of the camera.
 
Level 11
Joined
Apr 29, 2007
Messages
826
I was using your configuration. I didn't play around with anything but the target distance (because when it's so close to the hero it makes it almost impossible to accurately click).

All I did was test your test-map, which had a fairly large cliff in it. The camera did not work as expected, instead I was forced to stare at the cliff or into the sky, neither of which were useful. Please tell me what configuration I need to make it look nice?

First picture you have the camera behind a cliff, and really far away. You were just trying to get bad results. I wasn't. Stop being a douche.

I couldn't really experience the same results with my setup, but when I actually tried it myself with a larget distance, I got the same results. I just tried to get some bad points out of your system because I want to show you that both of our systems got good and bad points.

Well you should have at least given the values for the configurations that you used.
I didn't change anything. Just CnP'd the code out of this topic.

I'm glad you posted the screen shots you did though, it seems that you could only find the 3 most prominent problems with the camera system. The camera eye position being below the terrain, a unit being on top of a cliff, and terrain being in between the camera and the target of the camera.
Same here, I'm rewriting my system once again now D:
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
YourNameHere said:
I couldn't really experience the same results with my setup, but when I actually tried it myself with a larget distance, I got the same results. I just tried to get some bad points out of your system because I want to show you that both of our systems got good and bad points.

I opened your test-map hoping that you would have some concrete solutions to the problems that had arisen when I tried to make this camera library. Upon realizing that you had overlooked certain I thought that I would give you a head's up. Then you turned it into a contest and tried to screw mine up.

YourNameHere said:
I didn't change anything. Just CnP'd the code out of this topic.

Well, it's irrelevant but you could have stated that these were the default constants. All of the pictures look as if they had significant changes to their target distance value.

YourNameHere said:
Same here, I'm rewriting my system once again now D

I'm going to post a list of problematic areas so that both of our solutions can perhaps reflect good results.

  • Camera eye position is below the terrain.
  • Target of camera is at the top of a hill.
  • Target of camera is not in the line of sight of the camera (terrain obstruction).

If you think of any more, please share; I'll post this in the first post so that it can be referenced easily by both of us.

Also, could you explain this a little more:

I couldn't really experience the same results with my setup, but when I actually tried it myself with a larget distance, I got the same results.

I'm curious what settings you used to achieve similarly functioning results to mine, since it seems like you just use terrain-height to modify the height of the camera.
 
Level 11
Joined
Apr 29, 2007
Messages
826
I'm curious what settings you used to achieve similarly functioning results to mine, since it seems like you just use terrain-height to modify the height of the camera.

Didn't meant similiar functioning results to your system but to the screenshots you posted. Just changed the distance and z offset a bit and it was completely screwed.

I'm going to post a list of problematic areas so that both of our solutions can perhaps reflect good results.

  • Camera eye position is below the terrain.
  • Target of camera is at the top of a hill.
  • Target of camera is not in the line of sight of the camera (terrain obstruction).

Probably not so relevant, but something that the camera settings won't change with every small hill you walk upon.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
YourNameHere said:
Probably not so relevant, but something that the camera settings won't change with every small hill you walk upon.

I think it's pretty relevant that the camera not hinder the user's ability to control a target.

YourNameHere said:
Just changed the distance and z offset a bit and it was completely screwed.

Obviously the terrain behind your unit will not affect the visibility when the camera is 2 ft. away from the unit. The idea is to produce a result that doesn't cause the system to screw up when the distance is increased from 200 to 2000.
 
Status
Not open for further replies.
Top