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`).
|
||
- `<60><><EFBFBD>ݱ<EFBFBD>/` 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.
|