Minimal C++/Arduino guidelines for this repo:
- Naming: classes/structs
PascalCase, functions/methodslowerCamelCase, variables/memberslowerCamelCase(no trailing underscores); constantsSCREAMING_SNAKE_CASE. - Braces on the same line as the header (
if (...) {), always use braces even for single statements. - Prefer
constand references over pointers whennullptris not expected. - Avoid dynamic allocation; use static/stack buffers sized for the MCU constraints.
- Includes: project/local headers in
"", platform/stdlib in<...>; include only what you need. - Formatting: 2-space indent, keep lines short (< 100 chars), use spaces after commas and around operators.
- Prefer object-centric APIs over exploded argument lists: pass domain objects (e.g.
Paddle) instead ofx/w/ybundles when the callee needs that object’s state. - Object-specific behavior/config belongs to the object class (e.g.
Balllaunch defaults/bounce rules,Paddlemovement params), not toArkanoidGame. - Avoid redundant wrapper/delegate methods and duplicated logic; if a method only forwards state/behavior without adding value, remove it or move logic to the owning class.
- Runtime update methods should update runtime state only; configure immutable/rarely-changing sprite data (
w/h/pixels/scale) in bind/init methods, not every frame. - Sprite rendering internals (scale, anchor, bounds/dirty rect computation) belong to
SpriteLayer/sprite code, not gameplay objects (Ball,Paddle, scenes). - When syncing render state from gameplay objects, expose simple intent-level methods (
setPosition,translate,syncSprite) instead of leaking render math into game code. - Keep naming consistent across engine/game lifecycle methods (
onPhysics,onProcess,delta) and avoid parallel synonyms for the same concept. - If a class already satisfies a project interface (e.g.
IRenderTarget), implement that interface directly instead of creating local adapter structs just to forward the same methods. - Prefer effect names over vague state names in APIs (
invalidate(...)over ambiguous names likesetFull(...)when the method clears and marks redraw state). - Mutable object state should be private by default (especially position/state fields like
x/y); expose behavior methods and explicit accessors (e.g.getPosition()) instead of public field writes. - Reuse common math/value types (e.g.
Vector2for 2D position/size) instead of duplicating one-offPosition/Sizestructs across classes. - If object state change has invariants/side effects (e.g. syncing a bound sprite), the state must be changed through the object API (
setPosition,setX, etc.), not by external direct field mutation. - Do not add central "sync all objects" sweeps to compensate for leaked state mutation. Update dependent state at the point where the owning object state changes.
- Stop generating fucking bloat code.
- Do not cast without a real reason.
- If a cast is truly necessary, prefer the shortest correct form (
std::move, a compactstatic_cast, or a tiny typed helper) instead of verbose conversion scaffolding. - Do not replace one cast style mechanically with another.
- Keep code compact; do not split simple one-line expressions into multiple temporaries unless it materially improves correctness or readability.
- When a struct/class already has sane defaults, initialize only the non-default fields; do not restate
nullptr,0, empty structs, or default enum/flag values without a real reason. - If a value is meant to be tuned from build
DEFINES, do not hide it instatic constexpror file-local constants. Use#ifndef/#defineso it is actually overridable from the build. - Do not introduce new local tuning constants in
.cppfor things the project may want to tweak. Build-time configuration must stay build-time configurable. - Do not hardcode per-type gameplay differences through helper-method
ifchains likeif (kind == ...). Pass a data object/archetype/config into the instance and use that. - Do not bury pathfinding/navigation inside actor classes. Put navigation/path search in a separate module/class and have the actor consume it.
- When future content may come from data files, keep entity code data-driven now; changing content definitions must not require changing gameplay class code.