Problems with using lissajous curves for sniper scope sway

I like to pick apart games in my spare time and recreate some mechanics from simple to complex which I think would be fun. The game I mainly pick apart is Call of Duty 4: Modern Warfare because of its large modding community and mod tools which make it easy to learn how the game works. The in-game developer console also makes it easy to debug the game on my own as well.

One of the mechanics I am currently trying to pick apart is the sniper scope. One thing I noticed right away is the sway pattern which is a lissajous pattern. I quickly implemented my own lissajous pattern which is the classic figure-8 used in most games and applied it to the camera. Here is the script:

using UnityEngine;  public class ScopeSway : MonoBehaviour {     public int a = 1,                b = 2;      private float size = 1f,                   speed = 0.5f,                   time = 0,                   posX = 0,                   posY = 0;      void Update()     {         time = Time.time * speed;         posX = size * Mathf.Sin((a * time) + (Mathf.PI / 2));         posY = size * Mathf.Sin(b * time);          Vector3 offsetX = transform.up * posX,                 offsetY = transform.right * posY,                 offsetZ = transform.forward * transform.position.z;          transform.localPosition = offsetX + offsetY + offsetZ;     } } 

One thing I want to get out of the way is that if you visualize the figure 8 the height and width are the same size, so instead of making a width and height I merged them into a single size variable which makes a perfect square. The a and b variables modify the frequency which allow you to make more curves instead of a figure-8. Using this source you will be able to plug in your own a and b values from the patterns you see. This is also the source I used when writing this script.. The script doesn’t have any problems, it does exactly what I wrote it to do. However, there is one micro detail and core mechanic I am failing to recreate.

For those that have played the game and have access to it there is one thing you probably have never noticed until you actually spot what is happening. In the game whenever you are looking through the sniper scope you will notice that the scope sways like normal. However, no matter how close or how far whatever it is that you are aiming at the size of the curve pattern is the same. In order to visualize this, take the script I pasted here and put it on a camera and place a cube 100 meters away, also set your cameras FoV to 15 (CoD 4 uses 15 FoV for sniper scopes). The scope will seem like it sways normally but not until you move the cube directly in front of the camera. Now you will notice that the camera sway is huge on targets up close but small for targets far away. In CoD, the sway amount is the same no matter how far or how close you are to what you are aiming at. What exactly is going on here?

After thinking about it I thought that maybe they are changing the size of the pattern based on the distance of where you are pointing. So I decided to add a raycast for up to 100 meters and change the size of the pattern based on the returned hit distance. It failed miserably. It created lots of stuttering and snapping motions which where very visible and hard on the eyes. After that I thought that maybe I can just interpolate between the two points but that won’t work because in CoD it does not look like the view is being interpolated, the sway does not even make a single twitch. If you take my script and aim at the corner of the object and let the sway point off of the object the sway amount goes all the way back up, which does not happen in the game.

I do not know what I happening or how they are achieving this effect. I do know that the sway is programmatically done and is not using an animation because the sway has the same pattern as the gun sway when aiming from the hip. Whenever you scope in and scope out the scope does not start at the same place every time, it starts at a different place which means it is using lissajous curves based on a factor of time.

My question for you guys is, how are they achieving this and what am I doing wrong? I have spent five days now researching and picking apart some open source games but I have not found an solution or even a hint. It seems like this topic isn’t really well covered because it is a mechanic no one notices and we all take for granted, but it is the most important polish for full screen sniper scopes. How do I make the sway pattern the same size and speed no matter how far or how close it is that I am looking at.

A better explanation of what is going on is if you zoom in CoD right in front of a wall the scope will only sway in a box of lets say 20 pixels. And when you zoom to something far out it still only sways in a box of 20 pixels. My script will sway in a box of 20 pixels when aiming at things 100 meters away but when zooming in on things directly in front of the camera the camera will sway in a box of 100 or more pixels. I want my camera to sway in a box of 20 pixels or so no matter how far or how close the object you are aiming at is. It’s almost like they are shifting the cameras viewport and not the physical camera?