4.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Important rule: never create co-authored commits. Never mention Claude in commits.
Project overview
Cosmic Clash is an open-source, physics-based "vehicle soccer" game (a spiritual successor to Rocket League) built in Godot 4.4, using space ships instead of cars. The project is GDScript/Godot only right now — the "C# backend" mentioned in README.md is planned but not yet started. There is no server-side code; the MVP is local-only play against bots.
Because the gameplay concept (vehicle soccer) can't be copyrighted but specific expression can, all code/art/assets must be original — this is why the project uses Godot instead of Unreal/Unity and ships instead of cars. Keep this in mind when writing code or pulling in assets: don't port or closely mirror Rocket League's actual implementation.
Godot MCP server
This repo vendors godot-mcp as a git submodule at mcp/godot-mcp and registers it in .mcp.json. Prefer the godot-mcp tools over manual file edits or shell commands when the task involves inspecting or modifying the Godot project — reading/editing scenes, nodes, scripts, running the project, or interacting with a live Godot editor/runtime instance. It understands Godot's scene tree and .tscn/.gd structures directly, which is more reliable than hand-parsing them.
Setup after cloning (submodules aren't checked out by default):
git submodule update --init --recursive
cd mcp/godot-mcp
npm install
npm run build
GODOT_PATH (env var in .mcp.json) is left blank to auto-detect the Godot executable; set it explicitly if auto-detection fails on your machine.
Commands
There is no build step, linter, or automated test suite for the GDScript project itself — Godot projects run directly from source.
- Open the project: open
Game/as a project in the Godot 4.4 editor, or rungodot --path Gamefrom the repo root. - Run the game: press Play in the editor, or
godot --path Game Game/scenes/main_menu.tscn. - The
mcp/godot-mcpsubmodule is a separate Node/TypeScript project with its ownnpm install/npm run build(see above) — it is tooling, not part of the game itself.
Architecture
- Scene flow:
main_menu.tscn→ (Play button,main_menu_play_button.gd) →scenes/Game.tscn→ on match timer expiry, back tomain_menu.tscn. The match is a fixed 150-second timer (game.gd). - Game.tscn instances
objects/ship.tscn(player-controlled ship),objects/ball.tscn, andobjects/goal.tscnover a static terrain body. - Ship physics (
scripts/ship.gd, classShip, extendsRigidBody3D): all movement is force/torque-based (_integrate_forces), not kinematic — thrust and rotation inputs are converted to world-space forces/torques relative to the ship's orientation, with manual drag and speed/angular-speed clamping applied each physics tick. Physics formulas are commented inline; seeFLIGHT_MANUAL.mdfor the player-facing explanation of controls and flight model. - Vehicle.gd (
scripts/Vehicle.gd) is a separate, simplerRigidBody3Dbase class used byobjects/vehicle.tscn. It is not currently the base class forShip—ship.gdreimplements similar logic directly. Treat these as two independent, currently-diverging implementations rather than a class hierarchy. - HUD / telemetry pattern:
Shipcomputes flight data (speed, altitude, attitude, heading, thrust, angular velocity, camera mode) once per physics tick and emits it via signals, only when the value changes past a threshold (see the_last_*fields and*_THRESHOLDconstants inship.gd).HUDController(scripts/HUDController.gd, onscenes/HUD.tscn, instanced insideship.tscn) discovers the ship and game manager via Godot groups ("ship","game") rather than direct node paths, connects to their signals, and only updates label text in response — it does no polling. Follow this discovery-by-group + signal-push pattern when adding new instruments or cross-node communication, rather thanget_nodewith hardcoded paths or per-frame polling. - Input actions are defined in
Game/project.godotunder[input](move_forward,turn_left,turbo, etc.) and read viaInput.is_action_pressed(...)— add new controls there rather than hardcoding key/button checks. - Physics engine is Jolt (
Game/project.godot,[physics] 3d/physics_engine="Jolt Physics").