I am interested in implementing a weapon zoom feature that essentially just magnifies the player camera. I am currently adjusting FOV to create this zoom effect, however, doing so makes use of the zoomed camera very challenging since the sensitivity effectively increases by making the viewport so small. I have tried adjusting the turn sensitivity simultaneously to compensate for this effect but am finding that when turn sensitivity is very low the controls feel weird (it becomes very challenging to move the camera in a straight line and almost seems to curve). Is there a good way to create a high level of magnification while still maintaining an illusion of consistent sensitivity between magnified and unmagnified camera motion?
Are you doing this in third person or first person? First person, keep to FOV while if you’re in third person instead adjust the spring arm.
As for the sensitivity issue, if you stick to FOV you’ll just want to scale sensitivity.
You’ll want the variables CurrentFOV, CurrentSensitivity, MaxFOV (The base FOV your camera is set to), MinFOV (How far you want to zoom in), and BaseSensitivity (1.0f).
Whenever you change the FOV by scrolling or whatever, do your zooming in however you do it (working out CurrentFOV and clamping between MaxFOV and MinFOV then setting the camera FOV to CurrentFOV probably).
Afterwards set CurrentSensitivity to BaseSensitivity * ((Tan(CurrentFOV / 2)) / (Tan(MaxFOV / 2)))
Then when you do your Look input (add ControllerYaw input and add ControllerPitch input) do MouseX x CurrentSensitivity for Yaw and MouseY x CurrentSensitivity for Pitch.
If you want smooth zooming I’d also recommend interpolating it.
Edit: Made some blueprints for you to look at.
This first one is the scaling. I used a scroll well.
Zoom sensitivity I set to three to speed up zooming in. Multiplied by negative one so scrolling up zoomed in and back zoomed out, but that’s personal preference. Lerped and clamped the FOV to make it smooth.
Then, in event tick, you set the actual camera. ’
I did a double clamp here since although TargetFOV shouldn’t be out of bounds, it just catches and prevents errors. You set the current, then set field of view. If you want it to be smoother, lower the interp speed. If you want snappier, increase.
Then, you set current sensitivity. Base Sensitivity times (TanDegree(CurrentFOV / 2) divided by TanDegree(MaxFOV / 2))
Then feed it into your input and that’ll slow down looking around and make it less sensitive.
I will have to give this a try later this week but I think this makes sense. So assuming I don’t want a zoom scroll but rather just a button that toggles zoom, I would only need the second two images. And then since it only needs to be updated when it toggles, I would then just use the set field of view and all subsequent code to be called on the toggle event rather than tick I think?
If you want your zoom to be a toggle button then on button press use a flip flop node to swap between setting Target to either Min or Max. Other than that you’d only need the other two images.
And then since it only needs to be updated when it toggles, I would then just use the set field of view and all subsequent code to be called on the toggle event rather than tick I think?
You’ll only need the tick if you want smooth zooming. If you want to snap straight into view, ignore the tick part and instead do this.
You could make it cleaner that but I’d recommend like this so you can read it fine.
First, thank you so much for your help so far, the blueprints have been extremely helpful and I nearly have exactly what I want.
I have had some time to test a few things and have decided I do like the gradual zoom but would like it to be on a toggle still. I wanted to move away from event tick if possible, so I used a looping timer to update the FOV which gets cleared as soon as current FOV=target FOV. This works great and gives the same results as the zoom setup you have provided (which was exactly what I was looking for). The only problem is that with reference to the sensitivity adjustment, when I try to use FInterp, it gets stuck at a value midway between the current and target sensitivities. Increasing the interp speed gets the value closer to the target, and making it very high (or 0 as in the screenshot) lets it reach the target (but then there is no gradual transition either way). At first, I thought this was because another event was trying to increase the sensitivity at the same rate and it was canceling out, however, I don’t have any other references to the sensitivity multiplier variable that could be doing this. Am I using the interp value incorrectly?
One other question, why do you suggest using (TanDegree(CurrentFOV / 2) divided by TanDegree(MaxFOV / 2)) to determine the new sensitivity rather than doing the current FOV/Max FOV * base sensitivity (wouldn’t this also be a directly proportional change)?
Hey, sorry for the late reply, I was busy yesterday. I’m glad you’re almost at what you’d like!
- A looping timer would work fine. It’s okay to use tick though if you don’t have many ticks running, but best practice would be a timer.
- For FInterp, yes it’s how you’re using the values. Every tick you’re basically doing FInterp(Default, Target) but it should be FInterp(Current, Target)
As for using TAN instead of division, it’s because FOV isn’t visually linear. Going from 90 to 80 does not zoom the same amount as 40 to 30. This is because game cameras use perspective maths, so the real visual zoom amount is based on tan(FOV/2) rather than raw FOV.
If you do currentFOV / maxFOV the sensitivity reduction will be too weak at low FOVs, so you’ll have the camera being too sensitive or not sensitive enough issues again.This way it properly matches your zoom amount and also makes it easier to adjust maximum and minimum FOV without having to alter the entire thing.
Also if you are interpolating your FOV in a timer you could probably merge your sensitivity and interpolation into one timer. I’m assuming your FOV looks something like CurrentFOV = FInterpTo(CurrentFOV, TargetFOV, delta time, InterpSpeed)?
If so you could just go SensMultiplierX = (TanD(CurrentFOV / 2) / TanD(MaxFOV / 2)) x DefaultSensMultiplierX x AimingSensMultiplierX and do the same for Y since you won’t need to double lerp the sensitivity if you’re already lerping the camera.
Could also use a Timeline with values of 0 and 1 at first and last frame, and feed that into lerps between predetermined floats saved as vars:
My timeline there has a value of 0 in a keyframe at 0 seconds and a value of 1 at .5s. Look speed might be slightly off during timeline since FoV isn’t linear, as Hawks mentioned. But if zoom finishes in half a second, who will ever really notice?
Thank you so much, I am extremely happy with how this turned out! I think I do need the second sensitivity lerp maybe just because I have a sensitivity slider so it is possible in some cases that the default X sensitivity and default Y sensitivity will not be the same value, however, I agree that I definitely should put both events on the same timer since there is no real reason to seperate them.
I like this solution as well for if I was going to do a singular zoom magnification, the only problem for me personally is that I intend to have several different zoom magnifications which makes me think it would be easier to do the lerp on looping timer thing as I can then input max and min zoom values depending on context.
If using multiple zoom levels you could store the FOV and sensitivity multiplier floats in array vars, then instead of a single UsingScope? bool to keep track of it, use int vars for current and next zoom. When you press zoom in key, check if current zoom !>= than lastindex of the FoV array, +1 it and set next zoom (or only store current zoom as a var, and send the next index to timeline event input). Timeline lerps could then pull the appropriate min/max floats by getting a copy from array with current/next zoom as the indices to find. When timeline finishes, set current zoom to whatever next was.
Zooming out would be the same, just checking that current zoom >0 first on input event. Would only need to play timeline forward like this as it would always be lerping to the index from the next var (or input pin). Sounds complicated typed out like this, but the blueprint graph itself should be rather easy. Since topic is technically solved, probably best to PM me if you’d like to see an example graph, and I can throw one together after work.






