kinematics

Kinematics

Deterministic segment bodies: a body travels in straight legs and deflects off game-supplied surfaces by a configured rule set (hit-offset steer, capped surface-motion transfer, speed gain with a floor and a ceiling). The wire carries one leg per CONTACT, not a pose per tick, and both tiers evaluate the same line against the shared clock — so there is no interpolation delay and nothing to smooth at a bounce. For pong and its family, air hockey, breakout, arcade football.

Category Spatial Seams 1 Services 1 Options 2 Wire ids 1 Unity types 1
Server
services.AddCrossplayKinematics();
Unity package
com.crossplay.kinematics
Depends on
world

Seams you implement 1

Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayKinematics(); and it wins.

IKinematicSurfaces game SPI

The game's answer to "what can this body bounce off right now?" — the piece's one required SPI.

Example
// An arena game whose bats are world entities it already tracks:
sealed class BatSurfaces : IKinematicSurfaces
{
    public void Collect(string zone, List<KinematicSurface> into)
    {
        foreach (Bat bat in _bats.In(zone))
            into.Add(new KinematicSurface(bat.EntityId, bat.X, bat.Z, bat.HalfLength, bat.HalfThickness, bat.Yaw, bat.SlideSpeed));
    }
}
  • void Collect(string zone, List<KinematicSurface> into)

    Appends the surfaces currently in play in a zone.

Services you call 1

Crossplay implements these. Resolve them from DI and call them from your own systems.

IKinematicBodies

Bodies that travel in straight legs and deflect off game-supplied surfaces by a deterministic rule set — the alternative to a solver for games whose feel IS their rules.

  • event Action<KinematicContactEvent> Contacted

    Raised after each resolved contact — where a game scores, reacts and plays a sound.

  • bool Launch(long entityId, float dirX, float dirZ, float speed = 0)

    Puts a body into play along a direction.

  • bool Park(long entityId)

    Stops a body where it is and tells the zone.

  • bool TryGetSegment(long entityId, out KinematicSegment segment)

    The body's current leg.

Configuration 2

Every tunable lives in an options object — there are no magic numbers to hunt for.

KinematicBodyOptions

How one kind of kinematic body behaves. Keyed by the first byte of an entity's appearance blob — the same "appearance kind" convention every other composition rule in the framework uses — so a game declares its ball, its puck and its grenade as three rows and writes no code to tell them apart.

  • float ContactEpsilon { get; set; }

    How far clear of the contact point the next leg starts.

  • float HitOffsetInfluence { get; set; }

    How hard a hit away from a surface centre steers the body toward that end, as a fraction of body speed. 0 = the surface is a plain mirror.

  • float MaxSpeed { get; set; }

    The ceiling SpeedGainPerHit climbs toward.

  • float MaxSurfaceMotionTransfer { get; set; }

    The most along-axis velocity a surface's motion may impart — the guard that keeps a REPOSITIONED surface (which measures as impossibly fast) inert. See MaxSurfaceMotionTransfer.

  • float MinSeparationFraction { get; set; }

    The least separating component (0..1) an outgoing direction may have along the surface normal, so a graze cannot run the body along the wall forever.

  • float MinSpeed { get; set; }

    Launch speed, and the floor a contact can never take the body below.

  • float Radius { get; set; }

    The body radius, world units. Surfaces are expanded by it, so the body sweeps as a point.

  • float SpeedGainPerHit { get; set; }

    Speed multiplier applied on each contact — the rally accelerator. 1 = no change.

  • float SurfaceMotionInfluence { get; set; }

    How much of a moving surface's along-axis speed is imparted to the body.

KinematicsOptions

Kinematics tuning (defaults here, never inline).

  • Dictionary<byte, KinematicBodyOptions> Bodies { get; set; }

    Body kind, keyed by the first byte of the entity's appearance blob. An entity whose kind is not listed is never adopted, so the piece is inert by default.

  • int MaxContactsPerStep { get; set; }

    The most contacts one body may resolve within a single step, after which the remainder of the step is flown straight.

  • List<string> Zones { get; set; }

    Zones the piece watches. Empty = every zone.

Wire messages 1

The protocol this piece speaks. Ids are allocated per piece so they can never collide.

6901 KinematicSegmentChanged

Server to everyone in the zone: a kinematic body began a new straight leg. Sent on CONTACT, not on a timer — between contacts there is nothing to say, because both tiers can evaluate the same line.

Unity services you inject 1

Crossplay binds these in the client context. Inject and call them from your own MonoBehaviours and presenters.

IKinematicsClient

The Kinematics piece, client side: the current straight leg of every kinematic body, ready to be evaluated at PRESENT time.

  • event Action<KinematicSegmentChanged> SegmentChanged

    Raised whenever a body starts a new leg — a launch, a contact, or a park.

  • bool TryGetSegment(long entityId, out KinematicSegment segment)

    The body's current leg.

  • bool TryGetPositionAt(long entityId, long serverTimeMs, out float x, out float z)

    Where the body is at a given reading of the shared server clock — the whole client-side job.