# The player character (character.* kits) The body the player controls: walking, running, jumping, the camera that follows it, and the contract other kits use to borrow it (vehicles, climbing, attacks, dashes). Readable offline: `gripforge docs character`, or the MCP tool `gripforge_docs` (topic `character`). ## What it is for A game installs ONE controlled-character kit for its view, and optionally the add-ons. Every controlled-character kit owns the entity tagged `player` and provides `character.control`, so interaction, vehicles, scenes, saves and combat work whichever one is installed. | Kit | What it adds | | --- | --- | | `character.third_person` | a kinematic capsule that walks, runs and jumps from input actions, a spring-arm chase camera, optional free look, shoulder aim for shooters, double jump | | `character.first_person` | the same capsule seen through its eyes: pointer and right stick turn the head, the body faces where the eyes look | | `character.top_down` | the view from above: isometric follow camera, world-relative movement, aim from the pointer, click to move, and a motor other kits use to lock, push or turn the body | | `character.platformer` | camera-relative platforming: accelerated movement, variable jump height, coyote time, jump buffering, checkpoint respawn | | `character.roster` | game-defined characters and roles, team selection and locking, ability slots and bounded resources (reuses `combat.abilities`) | | `character.forms` | transformations (small, large, robot...) with a capability profile, collision size, duration and a bounded stack of previous forms; a Rust core | Install what the game needs; `gripforge kit api ` lists every setting of a kit. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "character.third_person" } gripforge_gamekit_configure { project_id: "gkp_…", id: "character.third_person", config: { "walkSpeed": 4, "runSpeed": 7, "airJumps": 1, "cameraDistance": 6 } } ``` The controlled-character kits require `input.actions`, `world.ground` (the platformer: `world.platforms`), the host physics (`physics.character`, `physics.port`) and `game.engine`. Controls come from the input kits (see the Controls guide): the actions are `move_x` / `move_y`, `sprint`, `jump`, and `look_x` / `look_y` when free look is on. ## Settings and defaults ### character.third_person | Key | Default | Meaning | | --- | --- | --- | | `walkSpeed` / `runSpeed` | 4 / 7 | m/s | | `jumpImpulse` | 5 | take-off speed | | `airJumps` / `airJumpImpulse` | 0 / 5 | extra jumps before landing (1: double jump); also usable after walking off an edge | | `cameraDistance` / `cameraHeight` | 5 / 2 | metres | | `turnSmoothing` | 0.15 | 0 snaps, 1 turns very slowly | | `freeLook` | `false` | the pointer and the right stick turn the camera; it no longer recentres behind the heading | | `shoulder` | 0 | camera offset to the right of the character, metres (0 to 1.5) | | `aimDistance` | 0 | with `freeLook`: camera distance while `aim` is held (0: `cameraDistance`) | | `combatDistance` / `combatSeconds` | 0 / 4 | camera distance while the player fights (0: off), and how long after the last blow it holds | | `lookSensitivity` / `stickLookSpeed` / `pitchLimit` | 0.0024 / 2.4 / 85 | free look comfort (radians per pixel, radians per second, degrees) | | `lockStrafe` / `lockCameraSpeed` | `true` / 6 | movement and camera blend while a `combat.ranged` target is locked | | `actionLayers` | `{}` | animation layers by action family (attack, shoot, reload, block, hit...): mask, weight, fade and how much the movement slows | A shooter turns the shoulder aim on together: `{ "freeLook": true, "shoulder": 0.6, "aimDistance": 3 }`, with `combat.ranged` `aimFromView: true`. A melee game pulls the camera back while the player fights, so wind-ups and threats on the sides stay in view: `{ "cameraDistance": 4.5, "combatDistance": 6.5, "combatSeconds": 4 }`. The player fights while a blow it dealt, took or swung is less than `combatSeconds` old, or while a lock-on of `combat.lockon` holds; `camera.distance` follows, and `aimDistance` still wins while `aim` is held. Without `combatDistance` nothing changes. ### character.first_person `walkSpeed` 4, `runSpeed` 7, `jumpImpulse` 5, `eyeHeight` 1.62 m, `fov` 72 degrees, `lookSensitivity`, `stickLookSpeed`, `pitchLimit` 85. The player's own model is not drawn through its eyes; it comes back in a vehicle. ### character.top_down | Key | Default | Meaning | | --- | --- | --- | | `moveSpeed` / `acceleration` / `turnSpeed` | 7 / 60 / 18 | m/s, m/s squared, rad/s | | `aim` | `auto` | the pointer when the host publishes `aim.point`, else the nearest enemy in front, else the heading | | `aimAssistRange` / `aimAssistCone` | 7 / 100 | metres / degrees | | `cameraYaw` / `cameraPitch` / `cameraDistance` | 45 / 55 / 22 | degrees, degrees, metres | | `cameraProjection` | `perspective` | or `orthographic` (then `cameraViewHeight` is the visible world height) | | `cameraFov`, `cameraTargetOffset`, `cameraSmoothing` | 0, `[0,0,0]`, 0.125 | lens, framed point, follow softness (seconds) | | `clickArrive` / `clickReach` | 0.3 / 1.6 | click to move: stop distance, and reach before attacking or using the clicked entity | | `clickPath` / `clickBlocked` / `clickRepath` | `navigation` / `straight` / 0.5 | route with `ai.navigation` when the game has it, what to do when the point is unreachable, replan delay | ### character.platformer `moveSpeed` 6.8, `acceleration` 42, `jumpImpulse` 9.5, `gravity` 23, `coyoteTime` 0.12 s, `jumpBuffer` 0.14 s, `airControl` 0.68, `jumpCut` 0.48 (upward speed kept when the jump key is released early), `fallGravity` 1.25, `cameraDistance` 9, `cameraHeight` 5, capsule `radius` 0.35 and `height` 1.8. With `world.platforms` the kit also keeps the course records and settings of the campaign. ### character.roster and character.forms `character.roster`: `unique` (a character is taken once per game), `autoPlayer`, `maxMembers`, `title`, and the collection `roster_characters` (`{ id, name, slots: [{ id, ability, label, action }] }`, up to 12 slots). `character.forms`: `baseForm` (default `base`), `changeCooldown` 0.5 s, `maxDepth` 4 (1 to 8) and 1 to 16 `forms`, each `{ id, duration, radius, height, speedScale, damageScale, capabilities[] }` (`duration` 0 is permanent; the default set is a base form and a ten-second small form). ## Services other kits use - `character.control`: `player()`, `position()`, `yaw()`, `lookYaw()` / `pitch()` (where the camera looks: the aim of a shooter), `speed()`, `detach` / `attach`, `teleport`, `hold(owner)`. In the Rust player also `claim(owner, priority)`, `release(owner, velocity)`, `beginMove(owner)`, `setVelocity(owner, velocity)`, `velocity()` so grapples, grinds and boosts can borrow the capsule by priority. - `camera.third_person`, `camera.first_person`, `camera.top_down` (the pose the renderer reads) and `camera.look` (free look: `add(dx, dy)`, `yaw()`, `pitch()`). - `character.motor` (top down): lock, push or turn the body for attacks, dashes and staggers. - `character.platformer`: `grounded()`, `velocity()`, `launch(vy)` (a spring), `push(dx, dz)` (a conveyor). Events: `player.moved` (10 Hz while moving: `{ id, x, y, z, yaw, speed }`), `player.jumped` (`aerial` is true for an extra jump), `player.action` (an action started and how it slows the movement), `player.pushed`. Variables: `player.speed`, `camera.yaw`, `camera.pitch`, `camera.shoulder`. The character listens to `vehicle.entered` / `vehicle.exited`: the capsule is detached while driving and returns beside the door. ## Hooks and modding The names and payloads are the same in every character kit, so one tap works whichever character a game plays. | Hook | Payload | What a tap may do | | --- | --- | --- | | `character.jump` | `{ entity, speed, aerial }` | change `speed` (0 to 30), cancel the jump | | `character.respawn` | `{ entity, x, y, z, reason }` | change the position, cancel; asked at a scene entry (`scene`) or on the scene's player instance (`instance`), not on a load or while driving | | `character.action` | `{ entity, anim, movement, moveScale }` | change how an action slows the movement (`free`, `slow`, `lock`, `root`) | | `character.push` | top down | accept or change a push | | `roster.select`, `roster.lock`, `roster.close`, `roster.use`, `roster.grant` | roster | pick another character, refuse a lock, cancel the end of selection, swap the ability of a slot, change a granted amount | ```json { "id": "no_jump_indoors", "hook": "character.jump", "if": [{ "in_zone": "z_house" }], "cancel": true, "reason": "low_ceiling" } ``` Moddable keys: the speeds, jump values, camera keys and `actionLayers`. The comfort of the person playing (`lookSensitivity`, `stickLookSpeed`) is not moddable. In `game.engine` content a scene entry places the player (position and heading), and an instance of kind `player` is the player. ## Engines The movement rules are the same Rust implementation on the web and in the native player. `character.third_person`, `character.top_down` and `character.platformer` are also delivered to Godot and Unity as files (a character body with a seed scene and a generated options file) and to Unreal through the shared kit library; `character.first_person`, `character.roster` and `character.forms` run on the web and in the native player. On the engines that do not run the hooks yet, the behaviour is the one without any tap. ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | The player does not move | no `input.actions`, or the action is not mapped to the device: check the Controls guide | | No double jump | `airJumps` is 0 (the default); set it to 1 | | The camera keeps recentring behind the character | `freeLook` is off; turn it on for pointer-driven views | | A second shooter-style setting does nothing | `aimDistance` only works with `freeLook`; `shoulder` works without | | A form change is refused | the cooldown is running, the stack is full (`maxDepth`) or the engine reported the destination blocked |