Debugging and performance
Correctness and speed require different questions. “Why did a coin count twice?” is a state-and-events question. “Why does collecting it freeze the screen?” is a timing question. Work on one observable problem at a time, and preserve a small reproduction so you know whether a change helped.
Prerequisites: The co-op rounds project, a saved backup, and the test record from lesson 14.
Outcome: Follow a bug from its visible symptom to the owning script, use a breakpoint without confusing editor state with runtime state, and collect a useful performance baseline.
Trace one event from beginning to end
Use the actual co-op object paths, not an invented generic “game manager”:
Workspace > CoopArena > RoundCoinscontains this round's collectible parts.ServerScriptService > RoundServerhandles touches and updates the shared count.ReplicatedStorage > RoundStateexposes Collected, Target, Status, and TimeLeft attributes.- The LocalScript from lesson 12 reads those attributes and updates the player's interface.
Start a two-client test and collect one coin. Observe the server's Collected attribute, then the two client copies, then the label. Ask where the first wrong value appears. If the canonical server value is correct and only one label is stale, rewriting the round logic is unlikely to help.
Read the first relevant Output error, including its script path and stack trace. Later errors may be consequences. A nil-index error means an assumption about an object or value failed; it does not automatically mean WaitForChild belongs everywhere. Confirm the name, parent, creation timing, and execution side first.
Use a breakpoint for a specific question
In RoundServer, set a breakpoint on the executable line that increments collected. Start Test and touch a coin. Inspect the local player, claimed flag, phase, thisRound, and roundId. The code guards against old-round touches and claims a coin before changing shared progress.
Studio supports breakpoints, watches, call-stack inspection, stepping, and conditional/logpoint variants. Insert a breakpoint in the script gutter next to executable code, then resume when you have the answer. See Studio debugging.
Do not leave several accidental breakpoints enabled and interpret the resulting pauses as a performance regression. Similarly, pausing a simulation is not a substitute for understanding asynchronous scripts. Run the decisive test again without debugger interruption before marking it fixed.
A useful hypothesis is narrow: “The callback runs, but returns because the character is dead.” A weak hypothesis is “Touched is broken.” Inspect the guard that would distinguish them. If an occasional race disappears when paused, use a temporary bounded log with round ID and event order instead of adding arbitrary waits.
Create a baseline before optimizing
Pick one repeatable scene: two players, one round start, all eight coins visible, the same camera angle, and the same device. Record the build, client or server side, graphics setting, player count, and observation duration. Compare the same scenario after one change.
Separate three categories:
- Frame time: Does work cause slow individual frames or repeated stutters?
- Memory: Does usage settle, or grow across repeated rounds and respawns?
- Load time: Is the delay only at join, when assets first appear, or throughout play?
Roblox's Developer Console, Performance Stats, Scene Analysis, and MicroProfiler provide different views. Studio measurements include editor overhead; use an actual Roblox client and a representative lower-powered device before making device-support claims. Identify performance issues
For intuition, 60 frames per second gives about 16.7 milliseconds per frame, while 30 gives about 33.3. An average can hide a conspicuous occasional spike. Do not promise a frame-rate target based on one fast Mac.
Investigate the measured bottleneck
MicroProfiler shows how frame time is spent. Capture the moment with the visible slowdown and inspect the expensive region; compare before and after a targeted change. You can label code sections with debug.profilebegin and debug.profileend when a real investigation needs finer attribution. Do not add per-frame print spam as a measurement tool. MicroProfiler
For this small project, useful checks include:
- Does RoundCoins return to the intended count each round, or are old instances accumulating?
- Are event connections and loops tied to objects that have been removed?
- Is a UI being created once per player or repeatedly on every attribute update?
- Are expensive scans repeated every frame when an event could supply the change?
- Is a visual asset disproportionately complex for its tiny screen presence?
These are hypotheses, not universal diagnoses. Eight coins do not justify a spatial-index rewrite by themselves. A decorative model might dominate rendering while your carefully optimized arithmetic saves nothing visible.
Exercise and completion check
In a copy, deliberately rename RoundState in one client lookup. Reproduce the broken display, identify the mismatch, restore the name, and rerun the case. Then observe several complete rounds and respawns without changing anything. Record whether object count and memory settle; do not call a short increase a leak without checking its lifecycle.
For a family-friendly testing role, ask one person to describe the visible symptom while another records the smallest steps. Avoid turning the session into blame about who “broke” the game.
You are done when one bug has an explained cause and verified fix, one baseline is recorded, and every proposed optimization names the measurement it should improve.
Verification: Official debugging and performance references checked 2026-10-03. No Studio profiling or hardware benchmark was performed for this manual; all measurements are exercises for your own build.
Previous: 15 Saving progress · Next: 17 Assets and Toolbox
Related: 04 Events and debugging · 14 Testing multiplayer · Help context