feat: implement textured line clear effects and refine UI alignment
- **Visual Effects**: Upgraded line clear particles to use the game's block texture instead of simple circles, matching the reference web game's aesthetic. - **Particle Physics**: Tuned particle velocity, gravity, and fade rates for a more dynamic explosion effect. - **Rendering Integration**: Updated [main.cpp](cci:7://file:///d:/Sites/Work/tetris/src/main.cpp:0:0-0:0) and `GameRenderer` to pass the block texture to the effect system and correctly trigger animations upon line completion. - **Menu UI**: Fixed [MenuState](cci:1://file:///d:/Sites/Work/tetris/src/states/MenuState.cpp:19:0-19:55) layout calculations to use fixed logical dimensions (1200x1000), ensuring consistent centering and alignment of the logo, buttons, and settings icon across different window sizes. - **Code Cleanup**: Refactored `PlayingState` to delegate effect triggering to the rendering layer where correct screen coordinates are available.
This commit is contained in:
128
docs/REFACTORING_SOLID_PRINCIPLES.md
Normal file
128
docs/REFACTORING_SOLID_PRINCIPLES.md
Normal file
@ -0,0 +1,128 @@
|
||||
# SOLID Principles Refactoring Plan
|
||||
|
||||
## Current Architecture Issues
|
||||
|
||||
### 1. Single Responsibility Principle (SRP) Violations
|
||||
- `ApplicationManager` handles initialization, coordination, rendering coordination, and asset management
|
||||
- `Game` class mixes game logic with some presentation concerns
|
||||
|
||||
### 2. Open/Closed Principle (OCP) Opportunities
|
||||
- Hard-coded piece types and behaviors
|
||||
- Limited extensibility for new game modes or rule variations
|
||||
|
||||
### 3. Dependency Inversion Principle (DIP) Missing
|
||||
- Concrete dependencies instead of interfaces
|
||||
- Direct instantiation rather than dependency injection
|
||||
|
||||
## Proposed Improvements
|
||||
|
||||
### 1. Extract Interfaces
|
||||
|
||||
```cpp
|
||||
// src/core/interfaces/IRenderer.h
|
||||
class IRenderer {
|
||||
public:
|
||||
virtual ~IRenderer() = default;
|
||||
virtual void clear(uint8_t r, uint8_t g, uint8_t b, uint8_t a) = 0;
|
||||
virtual void present() = 0;
|
||||
virtual SDL_Renderer* getSDLRenderer() = 0;
|
||||
};
|
||||
|
||||
// src/core/interfaces/IAudioSystem.h
|
||||
class IAudioSystem {
|
||||
public:
|
||||
virtual ~IAudioSystem() = default;
|
||||
virtual void playSound(const std::string& name) = 0;
|
||||
virtual void playMusic(const std::string& name) = 0;
|
||||
virtual void setMasterVolume(float volume) = 0;
|
||||
};
|
||||
|
||||
// src/core/interfaces/IAssetLoader.h
|
||||
class IAssetLoader {
|
||||
public:
|
||||
virtual ~IAssetLoader() = default;
|
||||
virtual SDL_Texture* loadTexture(const std::string& path) = 0;
|
||||
virtual void loadFont(const std::string& name, const std::string& path, int size) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Dependency Injection Container
|
||||
|
||||
```cpp
|
||||
// src/core/ServiceContainer.h
|
||||
class ServiceContainer {
|
||||
private:
|
||||
std::unordered_map<std::type_index, std::shared_ptr<void>> services;
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
void registerService(std::shared_ptr<T> service) {
|
||||
services[std::type_index(typeid(T))] = service;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::shared_ptr<T> getService() {
|
||||
auto it = services.find(std::type_index(typeid(T)));
|
||||
if (it != services.end()) {
|
||||
return std::static_pointer_cast<T>(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Break Down ApplicationManager
|
||||
|
||||
```cpp
|
||||
// src/core/ApplicationLifecycle.h
|
||||
class ApplicationLifecycle {
|
||||
public:
|
||||
bool initialize(int argc, char* argv[]);
|
||||
void run();
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
// src/core/SystemCoordinator.h
|
||||
class SystemCoordinator {
|
||||
public:
|
||||
void initializeSystems(ServiceContainer& container);
|
||||
void updateSystems(double deltaTime);
|
||||
void shutdownSystems();
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Strategy Pattern for Game Rules
|
||||
|
||||
```cpp
|
||||
// src/gameplay/interfaces/IGameRules.h
|
||||
class IGameRules {
|
||||
public:
|
||||
virtual ~IGameRules() = default;
|
||||
virtual int calculateScore(int linesCleared, int level) = 0;
|
||||
virtual double getGravitySpeed(int level) = 0;
|
||||
virtual bool shouldLevelUp(int lines) = 0;
|
||||
};
|
||||
|
||||
// src/gameplay/rules/ClassicTetrisRules.h
|
||||
class ClassicTetrisRules : public IGameRules {
|
||||
public:
|
||||
int calculateScore(int linesCleared, int level) override;
|
||||
double getGravitySpeed(int level) override;
|
||||
bool shouldLevelUp(int lines) override;
|
||||
};
|
||||
```
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
1. **Phase 1**: Extract core interfaces (IRenderer, IAudioSystem)
|
||||
2. **Phase 2**: Implement dependency injection container
|
||||
3. **Phase 3**: Break down ApplicationManager responsibilities
|
||||
4. **Phase 4**: Add strategy patterns for game rules
|
||||
5. **Phase 5**: Improve testability with mock implementations
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Testability**: Easy to mock dependencies for unit tests
|
||||
- **Extensibility**: New features without modifying existing code
|
||||
- **Maintainability**: Clear responsibilities and loose coupling
|
||||
- **Flexibility**: Easy to swap implementations (e.g., different renderers)
|
||||
Reference in New Issue
Block a user