# πŸ—οΈ 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 = [] // Balls/pucks private var playerBodies: Set = [] // 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