Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add skills/bullish0x/gamestudio/camera-systemnpx skills add bullish0x/GameStudio --skill camera-systemgit clone --depth 1 https://github.com/bullish0x/GameStudioWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/bullish0x/gamestudio/camera-system)<a href="https://agentmods.dev/skills/bullish0x/gamestudio/camera-system"><img src="https://agentmods.dev/badge/skills/bullish0x/gamestudio/camera-system.svg" alt="Measured on agentmods" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.00020 | $0.03760 |
| Opus 5 | $0.00010 | $0.01880 |
| Sonnet 5 | $0.00004 | $0.00752 |
| Haiku 4.5 | $0.00002 | $0.00376 |
Grade A, and why
camera-system scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured yesterday.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 577 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Camera System
When to Use
Use this skill when:
- Implementing third-person follow camera
- Creating orbit/free-look camera
- Adding camera shake effects
- Building cinematics and cutscenes
- Managing multiple camera views
- Implementing camera transitions
Core Principles
- Smooth Movement: Use damping/lerping for smooth camera
- Configurable: Expose camera settings
- Multiple Modes: Support different camera behaviors
- Collision: Prevent camera clipping through walls
- Transitions: Smooth camera mode switching
- Shake Effects: Screen shake for impact
Camera System Implementation
1. Camera Components
// components/CameraTarget.ts
export class CameraTarget {
// Marks entity as camera target
priority: number = 0;
}
// components/CameraController.ts
export enum CameraMode {
Follow = 'follow',
Orbit = 'orbit',
FirstPerson = 'first-person',
Fixed = 'fixed',
Cinematic = 'cinematic',
}
export class CameraController {
mode: CameraMode = CameraMode.Follow;
// Follow mode settings
followDistance: number = 10;
followHeight: number = 5;
followDamping: number = 5;
lookAtOffset = new Vector3(0, 1, 0);
// Orbit settings
orbitDistance: number = 10;
orbitMinDistance: number = 5;
orbitMaxDistance: number = 20;
orbitSpeed: number = 1;
orbitDamping: number = 5;
orbitAngleX: number = 0; // Pitch
orbitAngleY: number = 0; // Yaw
orbitMinAngleX: number = -Math.PI / 3;
orbitMaxAngleX: number = Math.PI / 3;
// First person settings
firstPersonHeight: number = 1.6;
firstPersonSensitivity: number = 0.002;
// Shake settings
shakeIntensity: number = 0;
shakeDuration: number = 0;
shakeTime: number = 0;
// Collision
collisionEnabled: boolean = true;
collisionRadius: number = 0.5;
constructor(mode: CameraMode = CameraMode.Follow) {
this.mode = mode;
}
}
2. Camera System
// systems/CameraSystem.ts
export class CameraSystem extends UpdateSystem {
priority = 65;
private camera: THREE.Camera;
private target: Entity | null = null;
constructor(camera: THREE.Camera) {
super();
this.camera = camera;
}
update(world: World, deltaTime: number): void {
// Find camera target
this.updateTarget(world);
// Update camera based on mode
const controller = this.getController(world);
if (!controller || !this.target) return;
switch (controller.mode) {
case CameraMode.Follow:
this.updateFollowCamera(controller, deltaTime);
break;
case CameraMode.Orbit:
this.updateOrbitCamera(controller, deltaTime);
break;
case CameraMode.FirstPerson:
this.updateFirstPersonCamera(controller);
break;
case CameraMode.Fixed:
// Fixed camera doesn't move
break;
case CameraMode.Cinematic:
this.updateCinematicCamera(controller, deltaTime);
break;
}
// Apply camera shake
if (controller.shakeTime > 0) {
this.applyCameraShake(controller, deltaTime);
}
// Handle collision
if (controller.collisionEnabled) {
this.handleCameraCollision(world, controller);
}
}
private updateTarget(world: World): void {
const targets = world.query<[Transform, CameraTarget]>([Transform, CameraTarget]);
let highestPriority = -Infinity;
let selectedTarget: Entity | null = null;
targets.iterate((entity, [, target]) => {
if (target.priority > highestPriority) {
highestPriority = target.priority;
selectedTarget = entity;
}
});
this.target = selectedTarget;
}
private getController(world: World): CameraController | null {
const controllers = world.query<[CameraController]>([CameraController]);
const first = controllers.first();
return first?.getComponent(CameraController) ?? null;
}
private updateFollowCamera(controller: CameraController, deltaTime: number): void {
if (!this.target) return;
const targetTransform = this.target.getComponent(Transform);
if (!targetTransform) return;
// Calculate desired position behind target
const forward = new Vector3(0, 0, -1).applyQuaternion(targetTransform.rotation);
const right = new Vector3(1, 0, 0).applyQuaternion(targetTransform.rotation);
const desiredPosition = targetTransform.position.clone()
.add(forward.multiplyScalar(-controller.followDistance))
.add(new Vector3(0, controller.followHeight, 0));
// Smoothly move camera to desired position
const currentPosition = new Vector3(
this.camera.position.x,
this.camera.position.y,
this.camera.position.z
);
const newPosition = new Vector3().lerpVectors(
currentPosition,
desiredPosition,
1 - Math.exp(-controller.followDamping * deltaTime)
);
this.camera.position.set(newPosition.x, newPosition.y, newPosition.z);
// Look at target with offset
const lookAtPoint = targetTransform.position.clone().add(controller.lookAtOffset);
this.camera.lookAt(lookAtPoint);
}
private updateOrbitCamera(controller: CameraController, deltaTime: number): void {
if (!this.target) return;
const targetTransform = this.target.getComponent(Transform);
if (!targetTransform) return;
// Calculate camera position from angles
const x = Math.cos(controller.orbitAngleY) * Math.cos(controller.orbitAngleX);
const y = Math.sin(controller.orbitAngleX);
const z = Math.sin(controller.orbitAngleY) * Math.cos(controller.orbitAngleX);
const offset = new Vector3(x, y, z).multiplyScalar(controller.orbitDistance);
const desiredPosition = targetTransform.position.clone().add(offset);
// Smooth movement
const currentPosition = new Vector3(
this.camera.position.x,
this.camera.position.y,
this.camera.position.z
);
const newPosition = new Vector3().lerpVectors(
currentPosition,
desiredPosition,
1 - Math.exp(-controller.orbitDamping * deltaTime)
);
this.camera.position.set(newPosition.x, newPosition.y, newPosition.z);
this.camera.lookAt(targetTransform.position);
}
private updateFirstPersonCamera(controller: CameraController): void {
if (!this.target) return;
const targetTransform = this.target.getComponent(Transform);
if (!targetTransform) return;
// Position camera at target position + height
const cameraPosition = targetTransform.position.clone()
.add(new Vector3(0, controller.firstPersonHeight, 0));
this.camera.position.copy(cameraPosition);
// Rotate based on target rotation
this.camera.quaternion.copy(targetTransform.rotation);
}
private updateCinematicCamera(controller: CameraController, deltaTime: number): void {
// Cinematic camera is controlled by external timeline/animation system
}
private applyCameraShake(controller: CameraController, deltaTime: number): void {
controller.shakeTime -= deltaTime;
if (controller.shakeTime <= 0) {
controller.shakeTime = 0;
controller.shakeIntensity = 0;
return;
}
// Random shake offset
const shake = new Vector3(
(Math.random() - 0.5) * controller.shakeIntensity,
(Math.random() - 0.5) * controller.shakeIntensity,
(Math.random() - 0.5) * controller.shakeIntensity
);
this.camera.position.add(shake);
}
private handleCameraCollision(world: World, controller: CameraController): void {
if (!this.target) return;
const targetTransform = this.target.getComponent(Transform);
if (!targetTransform) return;
// Raycast from target to camera
const direction = new Vector3()
.subVectors(this.camera.position, targetTransform.position)
.normalize();
// TODO: Implement proper raycast against world geometry
// If collision detected, move camera closer
}
shake(intensity: number, duration: number): void {
const controller = this.getController(world);
if (controller) {
controller.shakeIntensity = intensity;
controller.shakeDuration = duration;
controller.shakeTime = duration;
}
}
setMode(mode: CameraMode): void {
const controller = this.getController(world);
if (controller) {
controller.mode = mode;
}
}
getCamera(): THREE.Camera {
return this.camera;
}
}
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- yesterday First seen · 577 lines · 20 tokens per session scan A 8a06888e67f2
camera-system is a skill published in the GitHub repository bullish0x/GameStudio (10 stars, last pushed 2mo ago), licensed MIT. It adds 20 tokens to every session and 3,760 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-09-03.
Other skills, from other repositories
game-development
Game development with Unity, Unreal Engine, and Godot. Use when building games, implementing game mechanics, physics, AI, or working with game engines.
ar-vr-xr
AR/VR/XR development with Unity XR, WebXR, ARKit, ARCore, Meta Quest SDK, and spatial computing. Use when building augmented reality, virtual reality, mixed reality applications, or spatial experiences.
sdf
SDFormat/SDF model and world authoring, validation, and simulator handoff. Use for .sdf files, SDFormat XML, models, worlds, links, joints, poses, frames, inertials, visual/collision geometry, mesh URIs, sensors, lights, physics, plugins, includes, Gazebo, static SDF review, or simulator-specific metadata. Do not use…
unity-agent-workflows
Use for AI-assisted Unity work that needs live repo discovery, project-derived routing, runtime-owner proof, runtime-visible output hard stops, runtime numeric proof for repeated visible-output failures, state-step guards, multi-agent scope ownership, modular C#/asmdef safety, UI/scene/visual asset gates, data-first…
using-bgs-archive
Use when the user wants to inspect, list, extract, unpack, or repack Bethesda BA2/BSA archives; determine archive format/version/compression; or build archive assets for an MO2 overlay. Triggers - "unpack BA2", "extract BSA", "pack archive", "inspect archive", "bgs-archive".
ai-ml-development
AI and machine learning development with PyTorch, TensorFlow, and LLM integration. Use when building ML models, training pipelines, fine-tuning LLMs, or implementing AI features.