Carrom is one of those games that looks trivial from the outside and turns into a genuinely interesting engineering problem the moment you try to build it properly. A flat board, a few discs, a striker you flick with your finger — how hard can that be? Pretty hard, actually. Getting a carrom game to feel right in Unity means solving a stack of problems that don't show up in a typical tutorial: precise 2D physics tuning on a frictional surface, fair and readable flick input across wildly different screen sizes, pocket detection that doesn't feel cheap or unfair, turn-based state management, and — if you're building the online variant — real-time multiplayer synchronization for physics objects that are notoriously hard to keep in sync across a network. This article walks through the core systems you need to get right when building a carrom game in Unity, using a working implementation as the reference point throughout. Whether you're building this exact genre or just want to understand how physics-driven board games are architected under the hood, the systems here transfer directly. Why Carrom Is a Deceptively Good Engineering Case Study Before getting into code, it's worth explaining why this genre is such a useful teaching tool for Unity developers. Carrom strips a physics game down to a small, closed system: a flat 2D plane, a fixed set of circular bodies, and a single player-controlled input (the striker flick). There's no complex animation rigging, no pathfinding, no inventory systems. That constraint is exactly what makes it valuable to study — you can focus entirely on getting the physics feel right without any unrelated systems distracting from the core problem. But "simple system" doesn't mean "simple to get right." To make a carrom game feel authentic, you still need to solve: Realistic friction and momentum decay so discs slow down and stop the way they do on a real board A striker flick mechanic that feels precise and skill-based across different screen sizes Fair, consistent pocket/hole detection at the board's corners Turn management, foul detection, and scoring logic Multiplayer state synchronization if you're building an online mode, which is significantly harder than it sounds once physics objects are involved Let's go through each system individually. System 1: Board Physics and Friction Tuning The single most important decision in a carrom game is how your discs move and decelerate. Unlike a lot of mobile physics games where objects bounce indefinitely or come to an abrupt stop, carrom discs need a very specific kind of gradual, natural-feeling deceleration that mimics friction against a wooden board surface. In Unity's 2D physics system, this is primarily controlled through a combination of the Rigidbody2D's linear drag and the PhysicsMaterial2D applied to your disc colliders. Here's a simplified setup: public class CarromDisc : MonoBehaviour { private Rigidbody2D rb; public float minimumVelocityThreshold = 0.05f;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
rb.linearDamping = 0.6f;
rb.angularDamping = 0.8f;
}
void FixedUpdate()
{
// Snap tiny residual velocities to zero to avoid
// discs "creeping" indefinitely at near-imperceptible speeds
if (rb.linearVelocity.magnitude < minimumVelocityThreshold)
{
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
}
}
} Enter fullscreen mode Exit fullscreen mode A few details that separate "technically functional" physics from "feels like real carrom" physics: Tune drag values through playtesting, not theory. There's no universal "correct" drag coefficient — it depends on your disc mass, collider size, and the scale of your board. Start around 0.5–0.8 for linear drag and iterate based on how discs behave after a full-power flick versus a light tap. Snap near-zero velocities to true zero. Without this, floating-point residual velocity can leave discs technically "moving" at imperceptible speeds indefinitely, which can quietly break your turn-end detection logic if you're waiting for all objects to reach a resting state before allowing the next player to move. Use a slightly bouncy PhysicsMaterial2D on the board edges, but not on the discs themselves. Real carrom boards have rigid wooden borders that discs bounce off cleanly, while disc-to-disc collisions should feel more like an elastic but energy-losing impact rather than a perfect bounce. System 2: The Striker Flick Mechanic The striker is the only thing the player directly controls, which means it carries almost the entire weight of how "good" your game feels. Get this wrong and no amount of polish elsewhere will save the experience. For mobile carrom games, drag-and-release flick input is the standard, and for good reason — it maps intuitively to the physical motion of flicking a real striker with your finger. public class StrikerController : MonoBehaviour { public Rigidbody2D strikerBody; public float maxDragDistance = 2.5f; public float forceMultiplier = 12f; public LineRenderer aimLine;
private Vector2 dragStartPos;
private bool isAiming;
void OnDragStart(Vector2 worldPos)
{
dragStartPos = worldPos;
isAiming = true;
aimLine.enabled = true;
}
void OnDragUpdate(Vector2 worldPos)
{
if (!isAiming) return;
Vector2 dragVector = dragStartPos - worldPos;
Vector2 clamped = Vector2.ClampMagnitude(dragVector, maxDragDistance);
UpdateAimLine(strikerBody.position, clamped);
}
void OnDragRelease(Vector2 worldPos)
{
if (!isAiming) return;
Vector2 dragVector = dragStartPos - worldPos;
Vector2 clamped = Vector2.ClampMagnitude(dragVector, maxDragDistance);
strikerBody.AddForce(clamped * forceMultiplier, ForceMode2D.Impulse);
isAiming = false;
aimLine.enabled = false;
}
void UpdateAimLine(Vector2 origin, Vector2 direction)
{
aimLine.SetPosition(0, origin);
aimLine.SetPosition(1, origin + direction);
}
} Enter fullscreen mode Exit fullscreen mode A few implementation details matter more than they seem: Normalize drag input against screen DPI, not raw pixels. A drag distance that feels precise on a small phone screen will feel wildly oversensitive on a tablet if you're working in raw pixel values. Always convert drag distance into world-space units relative to your camera's orthographic size. Constrain the striker to the baseline before release. Real carrom rules restrict the striker's starting position to a line at the player's edge of the board. Enforce this in your input logic, not just visually, or players will find exploits by placing the striker in advantageous positions. Show a power indicator, not just a direction line. Direction alone doesn't communicate force. A simple color gradient or fill-bar tied to drag distance gives players much better control over shot strength, which meaningfully increases the perceived skill ceiling of the game. System 3: Pocket Detection That Feels Fair Pocket (hole) detection sounds trivial — just use a trigger collider at each corner — but naive implementations create frustrating edge cases where discs that visually seem to have fallen in don't register, or discs that clearly missed somehow count as pocketed. public class Pocket : MonoBehaviour { public GameManager gameManager;
void OnTriggerEnter2D(Collider2D other)
{
if (other.TryGetComponent<CarromDisc>(out CarromDisc disc))
{
gameManager.RegisterPocketedDisc(disc);
other.gameObject.SetActive(false);
}
else if (other.CompareTag("Striker"))
{
gameManager.RegisterStrikerFoul();
other.gameObject.SetActive(false);
}
}
} Enter fullscreen mode Exit fullscreen mode The details that actually matter here: Make the trigger collider slightly smaller than the visual pocket graphic. This sounds counterintuitive, but a slightly generous visual pocket paired with a slightly tighter trigger radius prevents "should have missed" complaints, since players tend to judge pocketing visually rather than by exact geometry. Detect the striker separately from regular discs. Pocketing the striker is a foul in standard carrom rules and needs completely different handling — typically a penalty and returning a previously pocketed disc to the board — so don't let it flow through the same code path as scoring a normal disc. Add a brief "settling" delay before finalizing a pocket. A disc that clips the very edge of a pocket trigger and then bounces back out due to physics interactions shouldn't count as pocketed. Waiting a few physics frames, or checking whether the disc's collider is still meaningfully overlapping the trigger, avoids this class of bug. System 4: Turn Management and Foul Rules Carrom has more rule complexity than it first appears — turn order, extra turns for successful pockets, fouls for pocketing the striker or knocking discs off the board entirely, and scoring based on disc color and the queen (the central red disc) rule. Modeling this cleanly requires a proper state machine rather than a tangle of boolean flags. A simplified turn-state structure typically looks like: public enum TurnState { WaitingForInput, StrikerInMotion, ResolvingPhysics, EvaluatingTurnResult, SwitchingTurn } The key architectural decision is not resolving scoring or fouls until every physics object on the board has returned to rest. This means your GameManager needs a reliable way to detect "all objects are stationary" before transitioning out of the ResolvingPhysics state — typically by checking the velocity magnitude of every active Rigidbody2D on the board each fixed update and only proceeding once all of them fall below a small threshold for several consecutive frames (a single frame isn't reliable enough, since physics can produce brief false negatives). Getting this state machine right up front saves an enormous amount of debugging time later, since almost every scoring bug and turn-order bug in a physics-based board game traces back to evaluating game state before physics has actually finished settling. System 5: Building the Online Multiplayer Layer If you're building an online carrom mode rather than a purely local pass-and-play game, you're now dealing with one of the genuinely hard problems in real-time multiplayer development: keeping physics simulations synchronized across clients with different hardware, frame rates, and network latency. The approach that works reliably for turn-based physics games like carrom is to avoid synchronizing continuous physics state entirely, and instead treat each turn as a discrete, deterministic event: The active player's client captures the striker's flick vector (direction and force) locally. That input is sent to the server (or host, in a peer-to-peer setup) as a single compact message — just two floats for direction and one for force magnitude. Every connected client, including the one that made the shot, simulates the resulting physics locally using that same input, rather than trying to stream continuous position updates for every disc on the board. Once physics settles, each client independently calculates the resulting board state (which discs pocketed, foul status), and the server reconciles these results to confirm consensus before advancing the turn. This approach dramatically reduces bandwidth compared to streaming live Rigidbody2D transforms every frame, and it sidesteps a lot of the jitter and desync issues you'd otherwise fight with naive real-time physics replication. The trade-off is that your physics simulation needs to be reasonably deterministic across devices — meaning fixed timestep settings, physics material values, and floating-point precision behavior need to be consistent, which is worth testing explicitly across different device tiers rather than assuming it "just works." If you want to see this entire system — physics tuning, striker mechanics, pocket detection, turn logic, and online multiplayer synchronization — already implemented and working end to end rather than building each piece from scratch, the carrom online Unity game source code is built around exactly this architecture, giving you a tested reference implementation you can study, reskin, or extend directly. System 6: Performance Considerations for Mobile Carrom games tend to run well on most devices since the physics workload is relatively light compared to something like a physics-heavy destruction game, but there are still a few mobile-specific details worth handling deliberately: Use a fixed timestep tuned for your board scale , since physics behavior — especially collision response between discs — can vary subtly between devices running at different frame rates if your Time.fixedDeltaTime isn't set deliberately rather than left at Unity's default. Pool pocketed disc objects instead of destroying and reinstantiating them , particularly if your game supports rematches or multiple rounds in a single session, since repeated instantiation of physics objects is a common source of frame hitches on budget Android hardware. Disable Rigidbody2D sleep thresholds carefully. Unity automatically puts slow-moving rigidbodies to "sleep" to save performance, which is generally good, but overly aggressive sleep thresholds can cause discs to stop slightly earlier than expected, subtly changing shot outcomes. Tune this value explicitly rather than relying on defaults. Applying These Principles Beyond Carrom While this article uses carrom as the working example, the underlying systems — friction-tuned physics, precise drag-based input, fair trigger-zone detection, state-machine-driven turn logic, and deterministic input-based multiplayer synchronization — apply directly to a wide range of physics-driven board and table games, from pool and air hockey to more abstract tabletop adaptations. For a broader look at how these same purchasing and evaluation principles apply across the wider Unity source code market — not just carrom, but genre selection, budget tiers, and monetization setup — this complete 2026 buyer's guide to Unity source code is a solid companion resource if you're deciding what to build or buy next after finishing a project like this one. Final Thoughts Carrom is a great example of a game that's easy to prototype badly and genuinely difficult to get exactly right. The gap between a mediocre implementation and one that feels authentic isn't found in flashy features — it's in the accumulation of small, deliberate decisions: friction values tuned through actual playtesting, input normalized properly across device sizes, pocket detection that matches player intuition rather than raw geometry, and a turn state machine that waits for physics to genuinely settle before evaluating results. If you're building a physics-driven board game — carrom or otherwise — treat every system covered here as a checklist rather than a nice-to-have. The physics might look simple on the surface, but getting each piece right is exactly what separates a forgettable prototype from a table game people actually want to keep playing.

