6.2 KiB
Project Philosophy
This project prioritizes clarity, correctness, and maintainability over defensive boilerplate or unnecessary abstraction.
AI-generated code should:
- follow existing architecture
- keep logic simple and explicit
- avoid hiding bugs
- avoid speculative abstractions
If uncertain, prefer simple direct code over generalized frameworks.
Repository Guidelines
Project Structure & Module Organization
Assets/contains all Unity assets and code. Game-specific content lives underAssets/GameMain/.Assets/GameMain/Scripts/holds gameplay code organized by domain (Procedure/,Entity/,UI/,Scene/,Sound/,Utility/).Assets/GameMain/Scenes/stores Unity scenes (start fromAssets/Launcher.unity).Assets/GameMain/Configs/andAssets/GameMain/DataTables/store runtime configuration and data tables.StreamingAssets/is for runtime-loaded files that must be preserved on build.docs/contains design notes (seedocs/GameDesign.md).数据表/is a top-level data table workspace; keep it in sync withAssets/GameMain/DataTables/when exporting.
Build, Test, and Development Commands
- Open the project with Unity Hub and load
GeometryTDas a Unity project. - Play locally via the Unity Editor Play button (no custom CLI runner is defined in this repo).
- Build via Unity:
File > Build Settings...then choose target and build. - IDE support: open
GeometryTD.slnorAssembly-CSharp.csprojfor C# navigation and tooling.
Coding Style & Naming Conventions
- C# uses 4-space indentation and Allman braces (see
Assets/GameMain/Scripts/Procedure/ProcedureLaunch.cs). - Types, methods, and public members use
PascalCase; locals and parameters usecamelCase. - Namespaces follow
GeometryTD.*by feature area (example:GeometryTD.Procedure). - Keep Unity
.metafiles with their assets; do not delete or regenerate them manually.
Code Design Principles
1. Prefer explicit logic over abstraction
Do not introduce abstractions unless they are clearly justified.
Avoid creating layers such as:
Manager
Service
Provider
Repository
Coordinator
Bridge
unless the architecture explicitly requires them.
Simple gameplay logic should remain simple.
Bad example:
EnemyService -> EnemyManager -> EnemyRepository
Good example:
EnemySpawner
Enemy
EnemyAI
2. Do not create abstractions for a single implementation
Avoid introducing interfaces unless multiple implementations are expected.
Bad:
IEnemyService
EnemyService
Good:
EnemySystem
Interfaces are only appropriate when:
- multiple implementations are required
- plugin/mod systems are involved
- testing requires mocking
- architecture explicitly defines extension points
3. Avoid speculative generalization
Do not design systems for hypothetical future use.
Implement only what the current feature requires.
Avoid:
- generic service layers
- premature plugin systems
- unnecessary configuration frameworks
Defensive Programming Policy
Boundary validation only
Validation is appropriate only at true system boundaries, such as:
- user input
- network input
- file/config loading
- inspector/external data
- public API boundaries
Internal gameplay logic should assume that invariants are already satisfied.
Do not add redundant validation inside internal code.
Do not hide errors
Do not silently ignore invalid states.
Avoid code such as:
if (enemy == null)
return;
or
if (config == null)
continue;
unless the behavior is intentionally designed.
Unexpected states should be visible, not hidden.
Prefer fail-fast over silent failure
If a condition should never happen in correct execution:
Use assertions.
Example:
Debug.Assert(config != null);
Do not convert invariant violations into no-op behavior.
Bad:
if (config == null)
return;
Good:
Debug.Assert(config != null);
Do not weaken required dependencies
Required references must remain required.
Do not turn required dependencies into optional ones by adding defensive checks.
Bad:
if (enemy.Config == null)
return;
If a dependency is required by design, assume it exists.
Error Handling
Do not introduce try/catch blocks unless there is a clear recovery strategy.
Avoid:
try
{
DoSomething();
}
catch (Exception e)
{
Debug.LogError(e);
}
Exceptions should generally propagate during development so bugs are visible.
Early Return Policy
Early returns are acceptable for normal control flow simplification.
However, do not use early returns to hide invariant violations or missing required state.
Bad:
if (enemy == null)
return;
Good:
if (!enemy.IsAlive)
return;
Logging
Avoid excessive logging.
Do not log inside per-frame loops unless necessary.
Avoid logs like:
Debug.Log("Updating enemy");
Use logging only when it provides meaningful debugging value.
Utility Classes
Avoid creating generic utility classes such as:
GameUtils
CommonHelper
MathHelper
Prefer placing behavior in the domain object responsible for it.
Example:
Bad:
DamageHelper.CalculateDamage(...)
Good:
enemy.CalculateDamage(...)
Data Structures
Avoid large context objects with many loosely related fields.
Bad:
EffectContext
{
int value;
int count;
float rate;
object source;
object target;
}
Prefer explicit parameters or strongly typed structures.
Code Generation Rules for AI
Before adding any of the following:
- null check
- range check
- fallback default
- try/catch
- abstraction layer
First determine:
- Is this a boundary input?
- Is the value optional by design?
- Is there a real recovery strategy?
If the answer is no, do not add defensive code.
Preferred Coding Style
Prefer:
- simple classes
- explicit logic
- minimal indirection
- clear ownership of behavior
Avoid:
- unnecessary patterns
- speculative architecture
- defensive boilerplate
Guiding Principle
Prefer:
clear failure over hidden corruption
A bug should surface near its source rather than being silently ignored.