356 lines
6.2 KiB
Markdown
356 lines
6.2 KiB
Markdown
# 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 under `Assets/GameMain/`.
|
|
- `Assets/GameMain/Scripts/` holds gameplay code organized by domain (`Procedure/`, `Entity/`, `UI/`, `Scene/`, `Sound/`, `Utility/`).
|
|
- `Assets/GameMain/Scenes/` stores Unity scenes (start from `Assets/Launcher.unity`).
|
|
- `Assets/GameMain/Configs/` and `Assets/GameMain/DataTables/` store runtime configuration and data tables.
|
|
- `StreamingAssets/` is for runtime-loaded files that must be preserved on build.
|
|
- `docs/` contains design notes (see `docs/GameDesign.md`).
|
|
- `数据表/` is a top-level data table workspace; keep it in sync with `Assets/GameMain/DataTables/` when exporting.
|
|
|
|
### Build, Test, and Development Commands
|
|
- Open the project with Unity Hub and load `GeometryTD` as 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.sln` or `Assembly-CSharp.csproj` for 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 use `camelCase`.
|
|
- Namespaces follow `GeometryTD.*` by feature area (example: `GeometryTD.Procedure`).
|
|
- Keep Unity `.meta` files 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:
|
|
|
|
1. Is this a boundary input?
|
|
2. Is the value optional by design?
|
|
3. 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.
|