The Unity script needs the Editor and the setup listed below. The separate C# companion practice runs in SharpRune without UnityEngine.
Choose a loop according to the work it owns
Update is associated with rendered frames, FixedUpdate participates in fixed-step physics work, and LateUpdate follows Update in the frame’s ordinary behavior sequence. A rendered frame can contain zero or several fixed steps, so the callback counts need not match. The sample intentionally moves a nonphysics Transform in Update and uses the other callbacks only to observe counts. Putting a task in FixedUpdate does not make it run once per display frame, and multiplying a rate by a guessed universal frame duration does not establish frame-rate-independent behavior.
Read the script’s movement and observation paths separately
LoopProbe increments an Update counter and moves right by unitsPerSecond multiplied by Time.deltaTime. FixedUpdate increments a separate counter. LateUpdate occasionally reports both cumulative counts using unscaledTime to schedule those reports. These are two responsibilities: scaled elapsed-time movement and an unscaled observation interval. The reports are not exact one-second measurements, because execution is observed on eligible callbacks. Keep the object free of a dynamic Rigidbody in this experiment so Transform movement is not competing with a physics simulation that owns the same pose.
Partitioning one second preserves this simple rate calculation
The portable Distance function uses decimal arithmetic to integrate four units per second. The first step sequence is a quarter, another quarter and a half second; the second is two half-second steps. Both total one second, so both results format as 4.00. This validates the arithmetic relationship between rate and elapsed time for the supplied partitions. It does not emulate the Unity Player loop, predict floating-point error in an actual scene, or claim that a real second always contains those chosen callbacks.
Do not mix clock domains or apply time twice
Time.deltaTime in Update follows scaled game time, while the sample’s report scheduling uses an unscaled clock. If an experiment changes timeScale, interpret those paths separately and restore the global setting afterward. Rigidbody AddForce has its own ForceMode integration semantics, so blindly multiplying every physics argument by deltaTime can apply the time factor twice. Likewise, moving by one fixed position unit per Update is a per-callback policy rather than a per-second rate. Name the quantity before choosing the callback or time factor.
Practice: observe changing callback ratios
Run the transform-only scene, note cumulative Update and FixedUpdate counts, then change the fixed timestep in a separate copied experiment. Compare the count relationship without asserting an exact ratio. Next vary the frame-rate target and observe movement over an intentionally chosen interval rather than comparing one frame’s displacement. These tests separate display frequency, physics-step frequency and rate integration. Keep an unchanged baseline scene so project-wide Time settings do not accidentally contaminate another physics lesson whose setup assumes different values.
- Record the timestep and clock used for each observation.
- Use LateUpdate for a separate following-camera experiment only after the movement contract is clear.
Practice: test rate invariants and invalid steps
For an independent C# exercise, add a third partition of one second and predict the same four-unit distance. Then use a step sequence totaling half a second and predict two units. Add a validation rule rejecting negative steps if the function models forward elapsed time. Finally compare the rate-based calculation with adding a constant per iteration, explaining why two partitions with different iteration counts can then produce different distances even when their elapsed totals match.
- Calculate the sum of supplied steps before calculating distance.
- Do not use this exact decimal companion result as a promise of identical real-time Unity coordinates at an arbitrary observation point.
Keep responsibility, time unit and measurement distinct
The correct question is not which callback sounds fastest, but which loop owns the work and which time quantity its inputs represent. The supplied Unity script is a real engine component with visible setup, while the companion isolates one portable arithmetic invariant. Player behavior still requires the configured scene and device. As a controller grows, keep camera follow, input sampling, domain timers and Rigidbody simulation intentionally connected rather than assigning all of them the same callback solely because one small demonstration appeared smooth.
- Both portable rate partitions print 4.00.
- FixedUpdate can occur zero or multiple times per rendered frame.
- A time-based rate, an API-integrated force and a per-callback increment are different quantities.
Unity Editor example
Set up in Unity 6
- Attach LoopProbe.cs to a visible cube without a Rigidbody in a Unity6.0 scene.
- Set unitsPerSecond to 1 and enter Play mode with the Game view visible.
- Observe rightward movement and the periodically reported cumulative callback counts.
- Change Project Settings > Time > Fixed Timestep only in a copied experiment and compare counts.
- For a separate camera exercise, follow the object in LateUpdate rather than mixing camera work into its movement.
LoopProbe.cs
Download Unity script ↗using UnityEngine;
public sealed class LoopProbe : MonoBehaviour
{
[SerializeField] private float unitsPerSecond = 1f;
private int updates, fixedSteps;
private float reportAfter;
private void Update()
{
updates++;
transform.position += Vector3.right * unitsPerSecond * Time.deltaTime;
}
private void FixedUpdate() { fixedSteps++; }
private void LateUpdate()
{
if (Time.unscaledTime < reportAfter) return;
Debug.Log("Update=" + updates + ", FixedUpdate=" + fixedSteps, this);
reportAfter = Time.unscaledTime + 1f;
}
}
What to observe in Unity
The cube moves at an elapsed-time-based rate. Update and FixedUpdate counts can differ; no exact frame count or timer precision is promised.
The console practice below isolates the C# logic. It does not run a Unity scene or emulate Unity engine callbacks.
C# companion practice
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
decimal Distance(decimal[] steps) { decimal position = 0; foreach (decimal dt in steps) position += 4m * dt; return position; }
Console.WriteLine(Distance(new[] { 0.25m, 0.25m, 0.5m }).ToString("0.00", CultureInfo.InvariantCulture));
Console.WriteLine(Distance(new[] { 0.5m, 0.5m }).ToString("0.00", CultureInfo.InvariantCulture));
C# practice output
4.00 4.00
A rate multiplied by elapsed time is different from a fixed increment per callback.