Published on

[Dev Log] The KCC Dilemma: Why I Scrapped Two Weeks of Code

Authors
  • Name
    Logan Kim
    Twitter

The Limits of Simplicity and the Swamp of Over-Engineering

Handling Character Control (CC) in a networked environment is an entirely different beast from controlling my own physical body.

Photon Fusion provides a module for this called KCC (Kinematic Character Controller), which is divided into two types: 'Simple KCC' and 'Advanced KCC'. Since the game I am building is an isometric, quarter-view hack-and-slash MMORPG that doesn't require highly complex Z-axis (vertical) physics, I naturally assumed Simple KCC would be sufficient.

That was pure arrogance and a complete miscalculation.

1. Wide Area Networks (WAN) and the Limits of Simple KCC

The primary target market for my service is North America. As everyone knows, the US is physically massive.

If a well-funded studio uses clouds like AWS or GCP, they would simply provision regions on both the East and West coasts. But a solo developer with strictly limited resources is bound by budget constraints. Even as a compromise, if I set up a single central region in the US, players connecting from either coast will inevitably experience pings of 200ms or higher.

In such high-latency environments, Client Prediction, along with Extrapolation and Interpolation to compensate for network delays, are absolute necessities for smooth movement.

The problem was that the compensation options provided by Simple KCC were despairingly limited, and the performance itself was poor.

2. Abandoning Sunk Costs and Entering a New Hell

A virtue of an engineer is knowing how to quickly admit when something doesn't work.

I scrapped the entire two weeks' worth of Simple KCC integration code I had written and made the decision to migrate the whole logic to Advanced KCC.

However, the moment I made the switch, I was greeted by integration hell with Unity's NavMeshAgent. Using NavMesh pathfinding to control monster or character movement is the most fundamental approach in an MMORPG. But synchronizing these path coordinates flawlessly under a State Authority-based Advanced KCC environment was an agonizing process.

After weeks of grinding, I finished the path integration and began tuning for poor network conditions (pings over 250ms). Fortunately, Advanced KCC offered an overwhelming number of options for extrapolation and interpolation. The catch was that while the options existed, absolutely no one told you how to configure them.

3. Dynamic Tuning for Extreme Ping

Through countless tests and failures, I began finding my own optimal values. Below is a snippet of the legacy code I wrote to dynamically control compensation intensity based on poor network latency (RTT). (Though this code was eventually discarded for other reasons, it remains a trace of that relentless struggle.)

private void InitializeKCC(KCC kcc)
    {
        Debug.Log("[KCC] InitializeKCC called!");

        if (Object.HasInputAuthority)
        {
            // 🎮 Local Player: Maintain maximum quality
            kcc.Settings.InputAuthorityBehavior = EKCCAuthorityBehavior.PredictFixed_InterpolateRender;
        }
        else
        {
            // 👥 Remote Player: Prioritize computation efficiency synced to server tick (30Hz)
            kcc.Settings.InputAuthorityBehavior = EKCCAuthorityBehavior.PredictFixed_InterpolateRender;

            // ⭐ Settings for interpolation and jitter suppression
            kcc.Settings.PredictionCorrectionSpeed = 5.0f;  // Very slow prediction correction (Default 30 -> 5)
            kcc.Settings.AntiJitterDistance = new Vector2(0.15f, 0.15f);  // Expanded jitter tolerance (Default 0.025 -> 0.15)
            kcc.Settings.CompressNetworkPosition = false;  // Maintain precision
        }

        // Common settings
        kcc.Settings.ProxyInterpolationMode = EKCCInterpolationMode.Full;
        kcc.Settings.StateAuthorityBehavior = EKCCAuthorityBehavior.PredictFixed_InterpolateRender;
    }

    public override void FixedUpdateNetwork()
    {
        // Measure the player's Round Trip Time (RTT)
        double rttMs = Runner != null ? Runner.GetPlayerRtt(Object.InputAuthority) : 0f;
        _lastRttMs = rttMs;

        // Dynamic Jitter control based on network conditions
        if (rttMs <= 0.050f)
        {
            // Good Ping (under 50ms)
            _kcc.Settings.AntiJitterDistance = new Vector2(0.1f, 0.05f);
        }
        else if (rttMs <= 0.150f)
        {
            // Normal Ping (under 150ms)
            _kcc.Settings.AntiJitterDistance = new Vector2(0.12f, 0.06f);
        }
        else
        {
            // Bad Ping (over 150ms)
            _kcc.Settings.AntiJitterDistance = new Vector2(0.2f, 0.12f);
        }

        _kcc.Settings.PredictionCorrectionSpeed = _moveSpeed * 2.5f;
    }

4. The Trap of Advanced KCC: The Curse of Bloat

Just when I thought I had gained the level of control I wanted, I realized I had fallen into another trap.

Advanced KCC is a multi-purpose module designed to cover not just MMORPGs, but also FPS games that require highly precise hit registration and physics. Because of this, it was riddled with over-engineered features I had absolutely no use for—like separate friction calculations for different ground types, or micro-interpolations for climbing stairs.

As a result, the character's network payload itself became incredibly heavy. An MMORPG must be able to handle hundreds of players connecting simultaneously in a single scene. Synchronizing hundreds of characters carrying this bloated data? Thinking about the server bandwidth and processing costs made my head spin.

Even so, at this point, my stubbornness took over. I still had the lingering delusion that I could somehow carve down this heavy tool, optimize it, and make it work.