diff --git a/Docs/SVG Source/TimingEventGrouping.svg b/Docs/SVG Source/TimingEventGrouping.svg new file mode 100644 index 0000000000..085c355b3d --- /dev/null +++ b/Docs/SVG Source/TimingEventGrouping.svg @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 0 + + 0.5 + + + + + + + + + + + + + + + Frame 1 + Frame 2 + Frame 3 + Frame 4 + Input Value + Time + + + + Update + + Input + + Update + + Rendering + + Rendering + + Input + + Update + + Rendering + + Input + + Update + + Rendering + + + + + + + Player Loop + + diff --git a/Docs/SVG Source/TimingFixedUpdateFastFPS.svg b/Docs/SVG Source/TimingFixedUpdateFastFPS.svg new file mode 100644 index 0000000000..b724342bb4 --- /dev/null +++ b/Docs/SVG Source/TimingFixedUpdateFastFPS.svg @@ -0,0 +1,1822 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + Time (in Seconds) + + + + Update + + 0 + + + + A + + + + 0.02 + + + Frame + FixedTimestep + PlayerLoop + + + B + + + C + + + D + + + E + + + F + + + G + + + H + + + I + + + J + + + K + + + L + + + M + + + N + + + O + + + P + + + Q + + + + + + + + + Fixed Update A + + Fixed Update B + + Fixed Update C + + Fixed Update D + + Fixed Update E + + Fixed Update F + + Fixed Update G + + Fixed Update H + + Fixed Update I + + Fixed Update J + + Fixed Update K + + Fixed Update L + + Fixed Update M + + Fixed Update N + + + diff --git a/Docs/SVG Source/TimingFixedUpdateSlowFPS.svg b/Docs/SVG Source/TimingFixedUpdateSlowFPS.svg new file mode 100644 index 0000000000..c4c6f8b615 --- /dev/null +++ b/Docs/SVG Source/TimingFixedUpdateSlowFPS.svg @@ -0,0 +1,962 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + Time (in Seconds) + + + + Update + + 0 + + 2 + + 3 + + 4 + + 5 + + 6 + + + + + + + + + + + + + A + B + C + + D + + + + 0.02 + + 0.04 + + 0.06 + + 0.08 + + + Update + + Update + + Fixed Update + + Update + + Update + + Update + + Frame + FixedTimestep + PlayerLoop + + diff --git a/Docs/SVG Source/TimingInputsPerFrame.svg b/Docs/SVG Source/TimingInputsPerFrame.svg new file mode 100644 index 0000000000..6e0d10e0c4 --- /dev/null +++ b/Docs/SVG Source/TimingInputsPerFrame.svg @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 0 + + 0.5 + + + + + + + + + + + + + + + Frame 1 + Frame 2 + Frame 3 + Frame 4 + Input Value + Time + + + + diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/TimingEventGrouping.png b/Packages/com.unity.inputsystem/Documentation~/Images/TimingEventGrouping.png new file mode 100644 index 0000000000..1b574dfb62 Binary files /dev/null and b/Packages/com.unity.inputsystem/Documentation~/Images/TimingEventGrouping.png differ diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/TimingFastFPS.png b/Packages/com.unity.inputsystem/Documentation~/Images/TimingFastFPS.png new file mode 100644 index 0000000000..f106071db9 Binary files /dev/null and b/Packages/com.unity.inputsystem/Documentation~/Images/TimingFastFPS.png differ diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/TimingInputsPerFrame.png b/Packages/com.unity.inputsystem/Documentation~/Images/TimingInputsPerFrame.png new file mode 100644 index 0000000000..4358a0f9d3 Binary files /dev/null and b/Packages/com.unity.inputsystem/Documentation~/Images/TimingInputsPerFrame.png differ diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/TimingSlowFPS.png b/Packages/com.unity.inputsystem/Documentation~/Images/TimingSlowFPS.png new file mode 100644 index 0000000000..c8d216f835 Binary files /dev/null and b/Packages/com.unity.inputsystem/Documentation~/Images/TimingSlowFPS.png differ diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/TimingUnprocessedTime.png b/Packages/com.unity.inputsystem/Documentation~/Images/TimingUnprocessedTime.png new file mode 100644 index 0000000000..20615cd75a Binary files /dev/null and b/Packages/com.unity.inputsystem/Documentation~/Images/TimingUnprocessedTime.png differ diff --git a/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md b/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md index 305e0dfa9f..7ce975228b 100644 --- a/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md +++ b/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md @@ -25,6 +25,13 @@ * [Events](Events.md) * [Layouts](Layouts.md) * [User Management](UserManagement.md) + * [Timing and latency](timing-and-latency.md) + * [Input events queue](timing-input-events-queue.md) + * [Select an input processing mode](timing-select-mode.md) + * [Optimize for dynamic update](timing-optimize-dynamic-update.md) + * [Optimize for fixed update](timing-optimize-fixed-update.md) + * [Avoid missed or duplicate events](timing-missed-duplicate-events.md) + * [Mixed timing scenarios](timing-mixed-scenarios.md) * [Supported Input Devices](SupportedDevices.md) * [Pointers](Pointers.md) * [Touch support](Touch.md) diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-and-latency.md b/Packages/com.unity.inputsystem/Documentation~/timing-and-latency.md new file mode 100644 index 0000000000..2b3d2b8b58 --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-and-latency.md @@ -0,0 +1,21 @@ +--- +uid: timing-latency +--- +# Timing and Latency + +Input Timing refers to the topic of exactly when the Input System receives and processes input from devices. + +Latency is the amount of time between the user providing some input, and the user receiving a response to that input. For example, the time between a button press and your game’s character moving on-screen. In fast-paced input scenarios such as action games, even tiny delays between the user's input and your game responding can be noticeable and affect the feel of your gameplay. + +In addition to the effects of latency, timing can affect one-off discrete events such as when a button press starts or finishes. Checking for these at the wrong time can result in missed or duplicate events. + +To minimize input latency, and to avoid missed or duplicate events, it helps to understand how the Input System processes events in relation to Unity's frame updates, physics updates, and fixed updates. This will help you make decisions about how to read and respond to input in your game or app. + +| **Topic** | **Description** | +| :------------------------------ | :------------------------------- | +| **[Input events queue](timing-input-events-queue.md)** | Understand how and when the Input System receives and processes input from devices. | +| **[Select an input processing mode](timing-select-mode.md)** | How to select an appropriate **Update Mode** which controls when the Input System processes queued input events. | +| **[Optimize for dynamic update](timing-optimize-dynamic-update.md)** | How to optimize input for use in `Update` calls. | +| **[Optimize for fixed update](timing-optimize-fixed-update.md)** | How to optimize input for use in `FixedUpdate` calls. | +| **[Avoid missed or duplicate events](timing-missed-duplicate-events.md)** | How to avoid missing or duplicated discrete input events like when a button was pressed or released. | +| **[Mixed timing scenarios](timing-mixed-scenarios.md)** | How to optimize and avoid problems when using input in both `Update` and `FixedUpdate` calls. | diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-input-events-queue.md b/Packages/com.unity.inputsystem/Documentation~/timing-input-events-queue.md new file mode 100644 index 0000000000..cb05a3a48e --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-input-events-queue.md @@ -0,0 +1,29 @@ +# The input events queue + +The Input System receives information from hardware input devices as a stream of events. These events represent either system events received from the input device, or snapshots in time based on frequent samples from the device. + +The incoming events are stored in a queue, and by default are processed each frame. Input controls which have discrete (on/off) states, such as a button on a gamepad or mouse, generate corresponding single discrete events when they change state. Input controls with a range of motion, for example a stick or trigger on a gamepad that can be gradually moved over a period of time, generate a stream of individual events in rapid succession that approximates the smooth change in value. + +![image alt text](./Images/TimingEventGrouping.png) + +*A diagram showing an example of events coming from a smoothly changing analog input such as a gamepad stick over a period of four frames. In this example, the events are occurring faster than the game’s frame rate, which means multiple events are received each frame.* + +Because a device can cause events at times when the input system can't process them (for example, during the rendering phase of the player loop), these events are placed in an incoming queue, and the input system processes them in batches at frequent intervals. + +## Input Event Queue Processing + +Unity’s player loop repeats at frequent intervals depending on how fast your game or app is running. The player loop repeats once per frame, and performs the Update and FixedUpdate calls. However, the player loop in your game or app usually runs at a rate that's different to the rate of incoming events from input controls, which tend to have their own rate of operation. + +This means that each time an Update cycle occurs while a user is moving an input control, there's likely to be a queue of events representing the gradual change in values that occurred between the last frame and the current frame. This queue is processed at the beginning of the next Update or FixedUpdate, depending on which [**Input Update Mode**](timing-select-mode.md) you're using. + +## Event grouping and processing + +To pass through these incoming input events to your code, the Input System groups and processes them at a specific time within the [Player Loop](https://docs.unity3d.com/Manual/ExecutionOrder.html). This is either just before the next `Update` if the Input Update Mode is set to **Process Events In Dynamic Update**, or just before the next `FixedUpdate` if the Input Mode is set to **Process Events in Fixed Update**. + +The following diagram shows how each batch of events (labeled with letters) is processed at the start of the subsequent frame, when the Input Update mode is set to **Process Events in Dynamic Update**. Events (**a**), (**b**) & (**c**) occurred during frame 1, and so are processed at the start of frame 2, before frame 2’s Update() call. Events (**d**), (**e**), (**f**) & (**g**) occurred during frame 2, and so are processed at the start of frame 3. Events (**h**) & (**i**) occur during frame 3, and so are processed at the start of frame 4. + +![image alt text](./Images/TimingInputsPerFrame.png) + +This means that the exact time that your code receives the events isn't the same as the time the event was received by the input system in Unity. For this reason, when you're using event callbacks (as opposed to polling), the Input System includes time stamps for each event so that you can know when each event was generated. + +For example, event (**d**) in the diagram is received from the device by the input system at time 0.012 s, but the time at which your code receives the event callback is at the start of the next frame, at about 0.03 s. It retains its timestamp of 0.012 s, but it, along with events (**e**), (**f**), and (**g**), are all processed by your code at almost exactly the same time, each marked with their own time stamps. diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-missed-duplicate-events.md b/Packages/com.unity.inputsystem/Documentation~/timing-missed-duplicate-events.md new file mode 100644 index 0000000000..d8ac7992c9 --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-missed-duplicate-events.md @@ -0,0 +1,9 @@ +# Avoid missed or duplicate discrete events + +Discrete events are simple on/off events that occur when a user presses or releases a control such as a gamepad button, key, mouse, or touch press. This is in contrast to continuously changing values like those from gamepad stick movement. You can poll for these types of discrete event by using [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) or [`WasReleasedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasReleasedThisFrame_). However, you can get incorrect results such as missing an event or appearing to receive multiple, if you check for them at the wrong time. + +If your Update Mode is set to **Process in FixedUpdate**, you must ensure that you only use [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) or [`WasReleasedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasReleasedThisFrame_) in **FixedUpdate** calls. Using them in Update might either miss events, or returns true across multiple consecutive frames depending on whether the frame rate is running slower or faster than the fixed time step. + +Conversely, if your Update Mode is set to **process in Dynamic Update**, you must ensure that you only use [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) or [`WasReleasedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasReleasedThisFrame_) in `Update` calls. Using them in `FixedUpdate` might either miss events, or return true across multiple consecutive frames depending on whether the fixed time step is running slower or faster than your game’s frame rate. + +If you find that you're missing events that should have been detected, or are receiving multiple events for what should have been a single press or release of a control, the reason is probably that you either have your Input Update Mode set to the wrong setting, or that you're reading the state of these events in the wrong `Update` or `FixedUpdate` call. diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-mixed-scenarios.md b/Packages/com.unity.inputsystem/Documentation~/timing-mixed-scenarios.md new file mode 100644 index 0000000000..787a48c853 --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-mixed-scenarios.md @@ -0,0 +1,92 @@ +# Mixed timing scenarios with fixed and dynamic input + +There are some situations where you might set the Update Mode **process in Dynamic Update** even when using input code in `FixedUpdate`, to minimize input latency, as described in the [previous section](./timing-optimize-fixed-update.md). + +In this situation, for discrete events you must ensure that you use [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) or [`WasReleasedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasReleasedThisFrame_) in `Update`, and pass through a variable to your `FixedUpdate` code to indicate the event happened. There may still be some latency between the frame in which the event occurred, and the next `FixedUpdate` call. + +For example: + +```c# +using UnityEngine; +using UnityEngine.InputSystem; + +public class ExampleScript : MonoBehaviour +{ + InputAction jumpAction; + bool jumpPressed; + + private void Start() + { + jumpAction = InputSystem.actions.FindAction("Jump"); + } + + private void Update() + { + // read discrete jump pressed event here: + if (jumpAction.WasPressedThisFrame()) + { + // set this variable to true, for use in FixedUpdate + jumpPressed = true; + } + } + + void FixedUpdate() + { + if (jumpPressed) + { + // apply jump physics here + + // set the variable to false so that the jump pressed physics are only applied once + jumpPressed = false; + } + } +} +``` + +## Minimum latency in mixed timing scenarios + +A technique to give the user the feel of absolute minimum latency while still using `FixedUpdate` is to respond as fast as possible in `Update` giving some visual feedback, but also respond to that same input in `FixedUpdate` for your physics system code. For example, you could display the start of a "jump" animation immediately in `Update`, while applying physics to correspond with the "jump" animation in the next available `FixedUpdate` which might come slightly later. + +In this scenario, set your Update Mode **Process events in Dynamic Update** which gives you the fastest response in your `Update` call. However for the reasons mentioned in the previous section, this might mean you miss discrete events if you use methods like [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) in your `FixedUpdate` call. To avoid this problem, use a variable to pass through the pressed/released state of the discrete event from the event handler to your FixedUpdate call, and then clear it once your FixedUpdate code has acted on it. For example: + +```c# +using UnityEngine; +using UnityEngine.InputSystem; + +public class ExampleScript : MonoBehaviour +{ + InputAction jumpAction; + bool jumpPressed; + + private void Start() + { + jumpAction = InputSystem.actions.FindAction("Jump"); + } + + private void Update() + { + // at high FPS, it’s fastest to read actions here: + + // read discrete jump pressed event here: + if (jumpAction.WasPressedThisFrame()) + { + // start jump animation here + + // set this variable to true, for use in FixedUpdate + jumpPressed = true; + + } + } + + void FixedUpdate() + { + if (jumpPressed) + { + // apply jump physics here + + // set the variable to false so that the jump pressed physics are only applied once + jumpPressed = false; + } + } +} +``` diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-optimize-dynamic-update.md b/Packages/com.unity.inputsystem/Documentation~/timing-optimize-dynamic-update.md new file mode 100644 index 0000000000..0e13141018 --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-optimize-dynamic-update.md @@ -0,0 +1,24 @@ +# Optimize for dynamic update (non-physics) scenarios + +If you're not working with the physics system or using `FixedUpdate`, always set the input system to process input in sync with the frame rate and `Update()` calls. This is the default setting, but to check or set this, go to **Project Settings** \> **Input System Package** \> **Input Settings**, and set **Update Mode** to **Process Events in Dynamic Update**. + +You can use either a Polling or Event-driven approach to read and process input each frame. You can find out more about Polling or Event-driven approaches in [Responding To Actions](RespondingToActions.html). Whether you choose polling or event-driven, as long as you have your Update Mode set to **Process Events in Dynamic Update**, you receive the latest events and values at the start of each frame. + +## Polling technique + +Poll input in `Update` and use those values to control your game in `Update`. If there were multiple events after the last frame completed (for example, multiple changing position values of a continuously moving gamepad stick), polling gives you the most recently processed value which is often fine for most scenarios. This approach is often called **sample-and-hold** and is a form of down-sampling, because individual subframe information is discarded. For example, in the scenario shown in the diagram below, polling the input on frame 3 gives the value for event (**g**), while the values for events (**d**) (**e**) and (**f**) are discarded. + +![image alt text](./Images/TimingInputsPerFrame.png) + +Also use `Update` to poll for discrete on/off state changes using API such as [`WasPressedThisFrame`](../api/UnityEngine.InputSystem.InputAction.html#UnityEngine_InputSystem_InputAction_WasPressedThisFrame_) and **WasReleasedThisFrame**. + +> [!NOTE] +> The input system doesn't detect multiple discrete on/off events that happen in a single frame when you use the poll driven approach. Multiple discrete on/off events might happen if your game is running at a low frame rate and a user repeatedly presses a button very rapidly, or if the user is using a type of game controller with a rapid "auto fire" mode. The polling technique also can't detect the order of multiple buttons were pressed on the same frame. Use event-driven input if you require this information. + +### Event-driven technique + +All events that occurred since the last frame are immediately processed before the current frame's `Update`, in the order that they were received. For continuously changing values (for example, multiple changing position values of a continuously moving gamepad stick), you might receive multiple events per frame with backdated timestamps indicating when they occurred between the last frame and the start of the current frame. + +You can read and store input values from the input events in your event handler methods, and use those values to control your game in `Update`. + +For example, in the scenario shown in the previous diagram, at the start of frame 3, you receive events for (**d**), (**e**), (**f**), and (**g**) and can process all of them in your game code. diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-optimize-fixed-update.md b/Packages/com.unity.inputsystem/Documentation~/timing-optimize-fixed-update.md new file mode 100644 index 0000000000..7bcc18522a --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-optimize-fixed-update.md @@ -0,0 +1,112 @@ +# Optimize for fixed-timestep or physics-based scenarios + +If you are working with the physics system or using `FixedUpdate` to control your game in a scenario where a small amount of input latency is acceptable (for example, a few frames), the simplest approach is to set the [input system update mode](./timing-select-mode.md) to **Process Events in Fixed Update**. This means your input code in `FixedUpdate` will operate as expected. + +To get the minimum possible latency from the Input System and minimize lag, set the input system update mode to **Process Events in Dynamic Update**. However in doing this, you must understand how to avoid the problems which can arise when using this strategy. Although it might seem incorrect if you have code in `FixedUpdate`, for most cases, this approach minimizes lag compared with processing events in Fixed Update. The reasons for this are explained in detail on this page. Having a good understanding of how input is processed in each mode allows you to make your own decision about how best to process input for your particular project. + +## Input in Fixed Update mode + +When you set the input system update mode to **Process Events in Fixed Update**, input events are processed in groups according to whether their timestamp falls within the current fixed time step. There might be none, one, or multiple `FixedUpdate` calls processed per frame depending on how fast the game's frame rate is updating compared with the fixed update time step period. + +If your game’s frame rate is running faster than the fixed time step period, you will have either zero or one `FixedUpdate` call per frame, depending whether the previous fixed time step has completed or not. If your game’s frame rate is running slower than the fixed time step period, you will have one or more `FixedUpdate` calls per frame \- as many as are required to catch up with the number of completed fixed time step periods that have elapsed. + +You can learn more about how the player loop processes fixed update time steps in [Time and Framerate Management](https://docs.unity3d.com/Manual/TimeFrameManagement.html). + +It’s important to understand that Fixed Update provides a simulation of code running at fixed time deltas (the fixed time step), however it does not actually run at regular time intervals. Instead, at the start of each frame loop, Unity will run as many Fixed Update steps as is needed to catch-up to the current frame’s time. If a whole fixed time step hasn't completed yet, no Fixed Update steps occur. If more than one whole fixed time step has elapsed, more than one Fixed Update step occurs. This means that on each frame, the `FixedUpdate` method can be called a variable number of times (or even not at all) depending on how much time has elapsed since the last frame and the value set for the Fixed Update time step. + + +### When the frame rate runs faster than fixed time step duration + +![image alt text](./Images/TimingFastFPS.png) + +This diagram shows the frame rate running faster than the fixed update time step rate. Time progresses to the right, each frame is numbered, and shows its `Update` call at the start of the frame in orange. The fixed time step here is 0.02 seconds (50 times per second), and the game is running faster, at about 80 frames per second. In this situation there are some frames with one fixed update call, and some frames with none, depending on whether a full fixed update time step has completed by the time the frame starts. The fixed time step periods are marked with letters A, B, C, D, E, and the frames in which their corresponding fixed update calls occur are marked in green. The fixed update call for time step A occurs at the start of frame 4, the FixedUpdate call for time step B occurs at the start of frame 7, and so on. + +### When the frame rate runs slower than the fixed time step duration + +![image alt text](./Images/TimingSlowFPS.png) + +This diagram shows the opposite scenario, when the fixed update cycle is running faster than the frame rate. The fixed time step here is 0.01 seconds (100 times per second), and the game frame rate is running slower, at about 40 frames per second. In this situation most frames have multiple fixed update calls before each update call, the number depending on how many whole update time steps have elapsed since the previous frame. The fixed update time step periods are marked with letters A, B, C, and so on, and frames in which their corresponding fixed update calls occur are marked in green. The fixed update call for time step A and B occurs at the start of frame 2, the fixed update call for frames C, D & E occur at the start of frame 3, and so on. + + +In both types of situation, whether the frame rate is running either faster or slower than the fixed time step, the start of the frame usually occurs somewhere part-way through a fixed time step interval. This means a portion of the most recent fixed time step period occurs before the frame, and some during the frame. Partially elapsed fixed time step periods like this aren't processed until the frame after they have fully elapsed. + +## Implications for input lag in fixed time step mode + +Because input that occurs during partially elapsed time steps isn't processed until the frame after the time step has fully completed, this has implications for increased input lag in fixed time step mode. There's almost always some amount of unprocessed time left between the end of the last fixed time step, and the start of the next frame. This means it's possible for input events to occur within that unprocessed time. + +An example of an input event occurring during such unprocessed time is shown in this diagram: + +![image alt text](./Images/TimingUnprocessedTime.png) + +This diagram shows the frame rate running faster than the fixed update time step rate. Each frame is numbered (1 to 7), and shows its `Update` call at the start of the frame in orange. The fixed time step here is 0.02s (50 steps per second), and the game is running faster, at about 80 frames per second. In this situation there are some frames with one fixed update call, and some frames with none. + +The diagram shows an input event (shown as a blue dot) that occurs during some unprocessed time during frame 3. Although the event occurs during frame 3, it's not processed in the fixed update input processing step at the start of frame 4 because it didn't occur during fixed time step (**A**). Instead, it occurred during fixed time step (**B**), which is processed at the start of frame 7 \- the first frame to occur after fixed time step (**B**) has completely elapsed. + +This has the counterintuitive effect that the processing of input on frame 4 actually ignores some input that has already occurred on frame 3, because it's only processing events that occurred in the last complete fixed time step: (**A**). + +## Minimize latency when using fixed update code + +To minimize input latency in input code in `FixedUpdate` calls, set the input system update mode to **Process Events in Dynamic Update**, which eliminates the problem of unprocessed time described previously. You can then use an event-driven or polling technique to read your input without missing events that occurred after the last fixed timestep but before the current frame. + +However, the **Process Events in Dynamic Update** mode might introduce the problem of missed or duplicate discrete events, such as attempting to read whether a button was pressed in a given frame. If you use this strategy, you must understand how to [avoid missed or duplicate events](./timing-missed-duplicate-events.md) in mixed timing scenarios requiring fixed and dynamic input. + +### Event-driven input with fixed update code + +For event-driven input, where the [Player Input component](./PlayerInput.md) calls events in your code, you should store the input values in variables which you can then read in your `FixedUpdate` call. For example: + +``` +using UnityEngine; +using UnityEngine.InputSystem; + +public class ExampleScript : MonoBehaviour +{ + Vector2 moveInputValue; + Rigidbody rigidBody; + public float moveForce = 10; + + private void Start() + { + rigidBody = GetComponent(); + } + + public void OnMove(InputAction.CallbackContext context) + { + // in the event callback, we store the input value + moveInputValue = context.ReadValue(); + } + + private void FixedUpdate() + { + // in fixed update, we use the stored value for + // applying physics forces + rigidBody.AddForce(moveInputValue * moveForce); + } +} +``` + +### Polling input with fixed update code + +For code where you're polling input action values, you can read the values directly from `FixedUpdate`: + +``` +using UnityEngine; +using UnityEngine.InputSystem; + +public class ExampleScript : MonoBehaviour +{ + InputAction moveAction; + Rigidbody rigidBody; + public float moveForce = 10; + + private void Start() + { + moveAction = InputSystem.actions.FindAction("move"); + } + + private void FixedUpdate() + { + Vector2 moveInputValue = moveAction.ReadValue(); + rigidBody.AddForce(moveInputValue * moveForce); + } +} +``` diff --git a/Packages/com.unity.inputsystem/Documentation~/timing-select-mode.md b/Packages/com.unity.inputsystem/Documentation~/timing-select-mode.md new file mode 100644 index 0000000000..895fd0af3a --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/timing-select-mode.md @@ -0,0 +1,15 @@ +# Select an appropriate input processing mode + +The Input System **Update Mode** controls when the input system processes queued input events. + +You can find and change the Update Mode by going to **Project Settings** \> **Input System Package** \> **Input Settings** \> **Update Mode**. + +The choice of Update Mode that best suits your project relates to whether you're using Update or FixedUpdate to respond to input events. You should choose this based on the specifics of the game you're making. You can read more about Update and FixedUpdate in [Time and Framerate Management](https://docs.unity3d.com/Manual/TimeFrameManagement.html). + +## When a small amount of latency is not an issue + +In cases where a small amount of input latency (a few frames) isn't an issue, set the update mode to match where you read your input. If your input code is in `Update` (usually non-physics-based scenarios), use **Process Events in Dynamic Update**. If your input code is in `FixedUpdate` (usually physics-based scenarios), use **Process Events in Fixed Update**. + +## When minimum latency is a necessity + +In cases where minimum latency is a necessity, set the update mode to **Process Events in Dynamic Update**, even if you're using code in FixedUpdate to apply physics forces based on input. This strategy comes with some additional issues that you must be aware of. Refer to the section [Optimizing for fixed-timestep scenarios](timing-optimize-fixed-update.md) for more information.