Lesson 4: Networked Movement Baseline (ClientNetworkTransform vs Server Authority)

Movement is where players first judge multiplayer quality. If characters jitter, rubber-band, or drift through collisions, trust drops fast even when everything else works.

This lesson helps you choose one reliable movement baseline for your vertical slice and test it with repeatable evidence before adding advanced prediction.

Lesson objective

By the end of this lesson you will have:

  1. A clear movement authority decision for your slice
  2. One working replicated movement path in NGO
  3. Basic interpolation/smoothing configured
  4. A movement jitter test checklist for host + client sessions

Why this matters

You can build matchmaking, scoring, and UI later, but if core movement feels unstable, every playtest conversation turns into "it felt laggy."

A simple, stable baseline now gives you room to scale into reconciliation and anti-cheat later without rewriting everything.

Choose your authority model first

For a vertical slice, pick one:

Option A - Client-owned movement (ClientNetworkTransform)

Best when:

  • You need fast iteration
  • The game is low-stakes co-op or prototype PvP
  • Security constraints are acceptable for early testing

Tradeoff:

  • Easier to move fast, weaker against client-side abuse

Option B - Server-authoritative movement

Best when:

  • Competitive integrity matters
  • You need deterministic collision ownership
  • You can afford more implementation complexity now

Tradeoff:

  • More stable long-term model, higher setup cost for the slice

For most teams at this stage: start with ClientNetworkTransform baseline, then harden high-risk actions server-side.

Step-by-step baseline setup

Step 1 - Wire ownership and spawn assumptions

From Lesson 3 lifecycle:

  • Ensure each player object has clear owner clientId
  • Confirm only one networked player object per connected client
  • Validate movement script runs only for local owner when client-authoritative

Do not debug smoothing before ownership is correct.

Step 2 - Add movement replication component

If using ClientNetworkTransform:

  • Attach to player network object
  • Sync position and rotation for your game needs
  • Disable scale sync unless required

If server-authoritative:

  • Send input to server (RPC or input buffer path)
  • Apply movement on server
  • Replicate resolved transform to clients

Keep one path active. Mixed authority causes jitter that looks like "lag."

Step 3 - Apply interpolation and update settings

Set movement update expectations:

  • Interpolate remote players to reduce visible snapping
  • Keep local player immediately responsive
  • Avoid over-smoothing that adds heavy perceived input delay

Baseline target:

  • Local feel is direct
  • Remote players are stable enough for readable movement intent

Step 4 - Add guardrails for movement writes

Use explicit conditions:

  • Local owner writes input/movement intent
  • Non-owners never write authoritative movement state
  • Server validates impossible speed bursts (even in prototype form)

A tiny speed clamp now prevents extreme outliers from polluting test results.

Step 5 - Run movement jitter checklist

Test this exact loop:

  1. Host + one client join
  2. Both strafe, stop, reverse quickly
  3. Both jump/move near colliders
  4. Disconnect/reconnect client and retest
  5. Repeat with one lower FPS cap

Log:

  • Snaps per minute
  • Average perceived input delay by tester
  • Any collision desync moments

If this fails, fix baseline before adding combat replication.

Example owner-gated movement script

using Unity.Netcode;
using UnityEngine;

public class PlayerMovementNet : NetworkBehaviour
{
    [SerializeField] private float speed = 6f;
    private CharacterController controller;

    private void Awake()
    {
        controller = GetComponent<CharacterController>();
    }

    private void Update()
    {
        if (!IsOwner) return;

        float x = Input.GetAxisRaw("Horizontal");
        float z = Input.GetAxisRaw("Vertical");
        Vector3 move = new Vector3(x, 0f, z).normalized;

        controller.Move(move * speed * Time.deltaTime);
    }
}

This is intentionally simple. The lesson goal is movement baseline stability, not final anti-cheat architecture.

Mini challenge

Create a short movement-baseline-report.md with:

  • Authority model chosen and why
  • Replication component settings
  • Three jitter observations from host/client test
  • One action item before Lesson 5

If another teammate can reproduce your test and match your notes, your baseline is healthy.

Pro tips

  • Record 20-second side-by-side host/client clips for visual comparison across revisions.
  • Separate "movement tuning" commits from "network transport" commits for easier rollback.
  • Keep speed, acceleration, and interpolation values in one config block for fast iteration.

Common mistakes

  • Running movement logic for non-owners
  • Toggling between client and server authority mid-session
  • Treating packet delay symptoms as animation bugs before checking ownership flow

Troubleshooting

"Remote players jitter when changing direction."

Check interpolation settings and confirm only one side writes transform authority.

"Local player feels delayed."

You are likely over-smoothing local motion. Keep local control immediate, smooth only remote views.

"Player snaps after reconnect."

Recheck Lesson 3 spawn/cleanup flow and ensure stale network objects are not reused.

Recap

You now have a networked movement baseline with clear authority boundaries and repeatable jitter checks. This is the minimum reliable base before deeper replication choices.

Next lesson teaser

Lesson 5 covers RPC vs NetworkVariable tradeoffs so you can replicate gameplay state intentionally instead of broadcasting everything by default.

Related links

Bookmark this lesson and rerun the jitter checklist each time you change movement authority or interpolation settings.