396 lines
13 KiB
Markdown
396 lines
13 KiB
Markdown
# 🏗️ SportsBallEngine Architecture
|
|
|
|
This document provides a detailed overview of the engine's architecture, following industry-standard design patterns for cross-platform game development.
|
|
|
|
## Core Principles
|
|
|
|
### 1. Strict Layer Separation
|
|
|
|
The engine is divided into two distinct categories:
|
|
|
|
#### **Platform-Agnostic Layer (Engine Core)**
|
|
- Pure Swift code with **ZERO** platform-specific imports
|
|
- Contains all game logic, physics, and asset management
|
|
- Can be tested and developed independently of any platform
|
|
- Modules: `EngineCore`, `PhysicsEngine`, `AssetLoader`
|
|
|
|
#### **Platform Abstraction Layer (PAL)**
|
|
- Thin wrappers around OS-specific APIs
|
|
- Implements protocols defined by the core layer
|
|
- Only place where platform-specific code exists
|
|
- Modules: `PlatformLinux`, `PlatformWin32`, `VulkanRenderer`, `DX12Renderer`
|
|
|
|
### 2. Protocol-Oriented Design
|
|
|
|
All platform-dependent functionality is accessed through Swift protocols:
|
|
|
|
```swift
|
|
// RendererAPI module (platform-agnostic)
|
|
public protocol Renderer: Sendable {
|
|
func initialize(config: RendererConfig) async throws
|
|
func beginFrame() throws
|
|
func draw(scene: Scene) throws
|
|
func endFrame() throws
|
|
func shutdown() async
|
|
// ... more methods
|
|
}
|
|
|
|
// VulkanRenderer module (platform-specific)
|
|
public final class VulkanRenderer: Renderer {
|
|
// Concrete Vulkan implementation
|
|
}
|
|
|
|
// DX12Renderer module (platform-specific)
|
|
public final class DX12Renderer: Renderer {
|
|
// Concrete DirectX 12 implementation
|
|
}
|
|
```
|
|
|
|
### 3. Dependency Injection
|
|
|
|
Platform implementations are created and injected at startup:
|
|
|
|
```swift
|
|
// main.swift
|
|
let renderer = PlatformFactory.createRenderer() // Creates Vulkan or DX12
|
|
let windowManager = PlatformFactory.createWindowManager()
|
|
let inputHandler = PlatformFactory.createInputHandler()
|
|
let audioEngine = PlatformFactory.createAudioEngine()
|
|
|
|
// Inject into engine core
|
|
let engine = GameEngine(
|
|
renderer: renderer,
|
|
windowManager: windowManager,
|
|
inputHandler: inputHandler,
|
|
audioEngine: audioEngine
|
|
)
|
|
```
|
|
|
|
## Module Dependency Graph
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ main.swift │
|
|
│ (Platform Detection) │
|
|
└────────────────┬────────────────────────────────────────────┘
|
|
│
|
|
│ Creates & Injects
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ EngineCore │
|
|
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
|
│ │ Main Loop │ │ Scene │ │ Systems │ │
|
|
│ │ (Fixed Δt) │ │ Management │ │Coordinator │ │
|
|
│ └────────────┘ └────────────┘ └────────────┘ │
|
|
│ │
|
|
│ Depends on protocols only: │
|
|
│ - Renderer (draw calls) │
|
|
│ - WindowManager (events) │
|
|
│ - InputHandler (keyboard/mouse) │
|
|
│ - AudioEngine (sound) │
|
|
└────────┬───────────────────┬──────────────────────────────┘
|
|
│ │
|
|
│ │
|
|
┌────▼────┐ ┌────▼────┐
|
|
│ Physics │ │ Asset │
|
|
│ Engine │ │ Loader │
|
|
└─────────┘ └─────────┘
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ RendererAPI (Protocols) │
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
│ │ Renderer │ │ Window │ │ Input │ │ Audio │ │
|
|
│ │ Protocol │ │ Protocol │ │ Protocol │ │ Protocol │ │
|
|
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
│ │
|
|
│ Also defines: Scene, Camera, Entity, Transform, etc. │
|
|
└────┬─────────────┬─────────────┬─────────────┬────────────┘
|
|
│ │ │ │
|
|
│ │ │ │
|
|
│ │ │ │
|
|
┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
|
|
│ Vulkan │ │ DX12 │ │ Platform│ │ Platform│
|
|
│Renderer │ │Renderer │ │ Linux │ │ Win32 │
|
|
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
|
│ │ │ │
|
|
│ │ │ │
|
|
┌────▼────────────▼─────────────▼─────────────▼────┐
|
|
│ Operating System APIs │
|
|
│ Vulkan | DirectX 12 | X11 | Wayland | Win32 │
|
|
└───────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### Frame Rendering Flow
|
|
|
|
```
|
|
1. main.swift
|
|
└─> GameEngine.runMainLoop()
|
|
|
|
2. EngineCore
|
|
├─> windowManager.processEvents() [Platform layer]
|
|
├─> inputHandler.pollEvents() [Platform layer]
|
|
│
|
|
├─> fixedUpdate(deltaTime) [Fixed timestep]
|
|
│ ├─> physicsEngine.step() [Core layer]
|
|
│ ├─> audioEngine.update() [Platform layer]
|
|
│ └─> updateGameLogic() [Core layer]
|
|
│
|
|
└─> render(interpolationAlpha)
|
|
├─> renderer.beginFrame() [Platform layer]
|
|
├─> renderer.draw(scene) [Platform layer]
|
|
└─> renderer.endFrame() [Platform layer]
|
|
|
|
3. Renderer (Vulkan or DX12)
|
|
├─> Acquire swapchain image
|
|
├─> Record command buffer
|
|
│ ├─> Bind pipeline
|
|
│ ├─> Set viewport/scissor
|
|
│ └─> For each entity:
|
|
│ ├─> Bind vertex/index buffers
|
|
│ └─> Draw indexed
|
|
├─> Submit command buffer
|
|
└─> Present to window
|
|
```
|
|
|
|
### Asset Loading Flow
|
|
|
|
```
|
|
1. EngineCore
|
|
└─> assetLoader.loadMesh("player.fbx")
|
|
|
|
2. AssetLoader
|
|
├─> Read file from disk
|
|
├─> Parse FBX format
|
|
├─> Extract vertices, indices, bones
|
|
└─> Return MeshData (CPU-side)
|
|
|
|
3. EngineCore
|
|
└─> renderer.loadMesh(vertices, indices)
|
|
|
|
4. Renderer (Vulkan or DX12)
|
|
├─> Allocate GPU buffer
|
|
├─> Upload data to GPU
|
|
└─> Return MeshHandle (GPU resource)
|
|
|
|
5. EngineCore
|
|
└─> Store MeshHandle for rendering
|
|
```
|
|
|
|
## Key Abstractions
|
|
|
|
### Renderer Protocol
|
|
|
|
Handles all graphics API calls:
|
|
- Frame management (begin/end)
|
|
- Scene rendering
|
|
- Resource loading (meshes, textures)
|
|
- Material creation
|
|
|
|
**Implementations**: `VulkanRenderer`, `DX12Renderer`
|
|
|
|
### WindowManager Protocol
|
|
|
|
Handles OS window management:
|
|
- Window creation/destruction
|
|
- Event processing (resize, close, etc.)
|
|
- Native handle retrieval (for surface creation)
|
|
- Fullscreen toggling
|
|
|
|
**Implementations**: `LinuxWindowManager`, `Win32WindowManager`
|
|
|
|
### InputHandler Protocol
|
|
|
|
Handles user input:
|
|
- Keyboard state
|
|
- Mouse state and position
|
|
- Gamepad/controller support
|
|
- Input events
|
|
|
|
**Implementations**: `LinuxInputHandler`, `Win32InputHandler`
|
|
|
|
### AudioEngine Protocol
|
|
|
|
Handles 3D audio:
|
|
- Audio loading and playback
|
|
- 3D spatial audio
|
|
- Listener position (camera)
|
|
- Volume control
|
|
|
|
**Implementations**: `LinuxAudioEngine`, `Win32AudioEngine`
|
|
|
|
## Sports Game Optimizations
|
|
|
|
### Physics System
|
|
|
|
The `PhysicsEngine` is optimized for sports scenarios:
|
|
|
|
```swift
|
|
// Special tracking for sports entities
|
|
private var ballBodies: Set<UUID> = [] // Balls/pucks
|
|
private var playerBodies: Set<UUID> = [] // Players
|
|
|
|
// Custom physics for balls
|
|
if ballBodies.contains(id) {
|
|
// Apply spin, air drag, magnus effect
|
|
let drag = -body.velocity * dragCoefficient
|
|
body.acceleration += drag / body.mass
|
|
}
|
|
```
|
|
|
|
### Animation System (Future)
|
|
|
|
Skeletal animation with state machines:
|
|
- Running → Shooting transition
|
|
- Tackling animations
|
|
- Celebration sequences
|
|
- Injury reactions
|
|
|
|
### Stadium Rendering (Future)
|
|
|
|
Large environment optimizations:
|
|
- LOD (Level of Detail) for distant objects
|
|
- Occlusion culling
|
|
- Crowd rendering (instancing)
|
|
- Particle systems (grass, dust)
|
|
|
|
## Thread Safety
|
|
|
|
The engine uses Swift 6 concurrency features:
|
|
|
|
- `actor` for thread-safe state management
|
|
- `Sendable` protocols for cross-thread data
|
|
- `async/await` for asynchronous operations
|
|
- `@unchecked Sendable` for platform handles
|
|
|
|
```swift
|
|
// EngineCore is an actor - all access is serialized
|
|
public actor GameEngine {
|
|
private let physicsEngine: PhysicsWorld
|
|
private var currentScene: Scene
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Build System
|
|
|
|
Uses Swift Package Manager with modular targets:
|
|
|
|
```swift
|
|
// Package.swift structure
|
|
targets: [
|
|
// Executable
|
|
.executableTarget(name: "SportsBallEngine", dependencies: [...]),
|
|
|
|
// Core modules (platform-agnostic)
|
|
.target(name: "EngineCore", dependencies: ["RendererAPI", ...]),
|
|
.target(name: "RendererAPI", dependencies: []),
|
|
.target(name: "PhysicsEngine", dependencies: []),
|
|
.target(name: "AssetLoader", dependencies: []),
|
|
|
|
// Platform modules (platform-specific)
|
|
.target(name: "VulkanRenderer", dependencies: ["RendererAPI"]),
|
|
.target(name: "DX12Renderer", dependencies: ["RendererAPI"]),
|
|
.target(name: "PlatformLinux", dependencies: ["RendererAPI", "VulkanRenderer"]),
|
|
.target(name: "PlatformWin32", dependencies: ["RendererAPI", "DX12Renderer"]),
|
|
]
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
|
|
- Test core modules in isolation (no platform dependencies)
|
|
- Mock platform implementations using protocols
|
|
- Physics simulation verification
|
|
- Asset parsing validation
|
|
|
|
### Integration Tests
|
|
|
|
- Test platform implementations on target OS
|
|
- Renderer functionality tests
|
|
- Window management tests
|
|
- Input handling tests
|
|
|
|
### Performance Tests
|
|
|
|
- Frame time consistency
|
|
- Physics simulation speed
|
|
- Asset loading performance
|
|
- Memory usage profiling
|
|
|
|
## Extension Points
|
|
|
|
### Adding a New Platform
|
|
|
|
1. Create `Sources/Platform{Name}/`
|
|
2. Implement all protocols:
|
|
- `WindowManager`
|
|
- `InputHandler`
|
|
- `AudioEngine`
|
|
3. Choose or implement renderer
|
|
4. Update `PlatformFactory` in `main.swift`
|
|
|
|
### Adding a New Renderer
|
|
|
|
1. Create `Sources/{API}Renderer/`
|
|
2. Implement `Renderer` protocol
|
|
3. Handle resource loading (meshes, textures)
|
|
4. Implement draw commands
|
|
5. Add to `PlatformFactory`
|
|
|
|
### Adding Game-Specific Features
|
|
|
|
All game logic stays in `EngineCore` or custom modules:
|
|
- Player AI systems
|
|
- Ball physics customization
|
|
- Game rules and scoring
|
|
- Network synchronization
|
|
|
|
**Never** add game logic to platform layers!
|
|
|
|
## Performance Considerations
|
|
|
|
### Fixed Timestep
|
|
|
|
Uses fixed timestep for deterministic physics:
|
|
|
|
```swift
|
|
// Variable framerate for rendering
|
|
// Fixed 60Hz for physics/gameplay
|
|
while accumulator >= fixedTimeStep {
|
|
fixedUpdate(deltaTime: fixedTimeStep)
|
|
accumulator -= fixedTimeStep
|
|
}
|
|
|
|
// Interpolate between physics states for smooth rendering
|
|
render(interpolationAlpha: accumulator / fixedTimeStep)
|
|
```
|
|
|
|
### Memory Management
|
|
|
|
- GPU resources managed through opaque handles
|
|
- Asset caching with LRU eviction (future)
|
|
- Pool allocators for frequent objects (future)
|
|
- Reference counting for shared resources
|
|
|
|
### Multithreading (Future)
|
|
|
|
- Physics on separate thread
|
|
- Async asset loading
|
|
- GPU command recording parallelization
|
|
- Job system for parallel tasks
|
|
|
|
---
|
|
|
|
This architecture ensures:
|
|
- ✅ Clean separation of concerns
|
|
- ✅ Easy platform porting
|
|
- ✅ Testable core logic
|
|
- ✅ Maintainable codebase
|
|
- ✅ Performance optimization opportunities
|
|
- ✅ Sports game-specific features
|
|
|