/// EngineCore - Platform-agnostic game engine core /// NO PLATFORM-SPECIFIC IMPORTS ALLOWED IN THIS MODULE import Foundation import RendererAPI import PhysicsEngine import AssetLoader // MARK: - Engine Configuration /// Configuration for the game engine public struct EngineConfig: Sendable { public var targetFrameRate: Int public var fixedTimeStep: Double public var maxFrameSkip: Int public init(targetFrameRate: Int = 60, fixedTimeStep: Double = 1.0/60.0, maxFrameSkip: Int = 5) { self.targetFrameRate = targetFrameRate self.fixedTimeStep = fixedTimeStep self.maxFrameSkip = maxFrameSkip } } // MARK: - Game Engine /// Main game engine class - coordinates all subsystems public actor GameEngine { // Platform abstraction layers (injected by platform-specific code) private let renderer: any Renderer private let windowManager: any WindowManager private let inputHandler: any InputHandler private let audioEngine: any AudioEngine // Core systems private let physicsEngine: PhysicsWorld private let assetLoader: AssetManager // Engine state private var isRunning: Bool = false private var config: EngineConfig // Scene management private var currentScene: Scene // Timing private var lastFrameTime: Double = 0 private var accumulator: Double = 0 private var frameCount: UInt64 = 0 // MARK: - Initialization /// Initialize the engine with platform-specific abstractions public init( renderer: any Renderer, windowManager: any WindowManager, inputHandler: any InputHandler, audioEngine: any AudioEngine, config: EngineConfig = EngineConfig() ) { self.renderer = renderer self.windowManager = windowManager self.inputHandler = inputHandler self.audioEngine = audioEngine self.config = config // Initialize core systems (platform-agnostic) self.physicsEngine = PhysicsWorld() self.assetLoader = AssetManager() // Create default scene self.currentScene = Scene( entities: [], camera: Camera(), lights: [] ) print("🎮 SportsBallEngine initialized") } // MARK: - Engine Lifecycle /// Start the engine and run the main loop public func start() async throws { print("🚀 Starting engine...") // Initialize all systems try await initializeSystems() // Load initial scene/assets try await loadInitialScene() // Start the main loop isRunning = true lastFrameTime = getCurrentTime() print("✅ Engine started successfully") // Run the game loop await runMainLoop() } /// Stop the engine and cleanup public func stop() async { print("🛑 Stopping engine...") isRunning = false await shutdownSystems() print("✅ Engine stopped") } // MARK: - System Initialization private func initializeSystems() async throws { print(" → Initializing renderer...") let windowSize = windowManager.getSize() let rendererConfig = RendererConfig( windowHandle: windowManager.getNativeHandle(), width: windowSize.width, height: windowSize.height, vsyncEnabled: true, msaaSamples: 4 ) try await renderer.initialize(config: rendererConfig) print(" ✓ Renderer: \(renderer.info.apiName) \(renderer.info.apiVersion)") print(" ✓ Device: \(renderer.info.deviceName)") print(" → Initializing input system...") try await inputHandler.initialize() print(" → Initializing audio system...") try await audioEngine.initialize(config: AudioConfig()) print(" → Initializing physics engine...") await physicsEngine.initialize() print(" → Initializing asset loader...") await assetLoader.initialize() } private func shutdownSystems() async { await renderer.shutdown() await inputHandler.shutdown() await audioEngine.shutdown() await physicsEngine.shutdown() await assetLoader.shutdown() } // MARK: - Main Game Loop (Carmack-style fixed timestep) private func runMainLoop() async { while isRunning { let currentTime = getCurrentTime() let frameTime = currentTime - lastFrameTime lastFrameTime = currentTime // Cap frame time to prevent spiral of death let clampedFrameTime = min(frameTime, config.fixedTimeStep * Double(config.maxFrameSkip)) accumulator += clampedFrameTime // Process window events let windowEvents = windowManager.processEvents() for event in windowEvents { handleWindowEvent(event) } // Check if window should close if windowManager.shouldClose() { isRunning = false break } // Process input events let inputEvents = inputHandler.pollEvents() handleInputEvents(inputEvents) // Fixed timestep update loop (deterministic physics/gameplay) while accumulator >= config.fixedTimeStep { try? await fixedUpdate(deltaTime: Float(config.fixedTimeStep)) accumulator -= config.fixedTimeStep } // Variable timestep render let alpha = Float(accumulator / config.fixedTimeStep) try? await render(interpolationAlpha: alpha) frameCount += 1 } } // MARK: - Update & Render /// Fixed timestep update for physics and gameplay logic private func fixedUpdate(deltaTime: Float) async throws { // Update physics simulation await physicsEngine.step(deltaTime: deltaTime) // Update audio engine try audioEngine.update(deltaTime: deltaTime) // Update game logic (this would call game-specific code) updateGameLogic(deltaTime: deltaTime) // Sync physics state to scene entities syncPhysicsToScene() } /// Render the current frame private func render(interpolationAlpha: Float) async throws { try renderer.beginFrame() // Interpolate entity positions for smooth rendering let interpolatedScene = interpolateScene(currentScene, alpha: interpolationAlpha) // Draw the scene try renderer.draw(scene: interpolatedScene) try renderer.endFrame() try windowManager.swapBuffers() } // MARK: - Scene Management private func loadInitialScene() async throws { print(" → Loading initial scene...") // Create a simple test scene (stadium environment) let stadium = try await createStadiumScene() currentScene = stadium print(" ✓ Scene loaded: \(currentScene.entities.count) entities") } private func createStadiumScene() async throws -> Scene { // This would load actual assets, for now create a placeholder let camera = Camera( position: SIMD3(0, 10, 20), rotation: SIMD4(0, 0, 0, 1), fieldOfView: 60.0 ) let sunLight = Light( type: .directional, direction: SIMD3(-0.3, -1, -0.3), color: SIMD3(1, 0.95, 0.8), intensity: 1.0 ) return Scene( entities: [], camera: camera, lights: [sunLight] ) } // MARK: - Event Handling private func handleWindowEvent(_ event: WindowEvent) { switch event { case .close: isRunning = false case .resize(let width, let height): print(" ↔ Window resized: \(width)x\(height)") // Notify renderer of resize case .focus(let focused): print(" 👁 Window focus changed: \(focused)") case .minimize: print(" ⬇ Window minimized") case .maximize: print(" ⬆ Window maximized") case .restore: print(" ↕ Window restored") } } private func handleInputEvents(_ events: [InputEvent]) { for event in events { switch event { case .keyEvent(let key, let action): if key == .escape && action == .press { isRunning = false } default: break } } } // MARK: - Game Logic private func updateGameLogic(deltaTime: Float) { // This is where sports game logic would go: // - Player AI // - Ball physics // - Animation state machines // - Collision detection // - Score tracking // etc. } private func syncPhysicsToScene() { // Sync physics simulation results back to scene entities // This would update entity transforms based on physics bodies } private func interpolateScene(_ scene: Scene, alpha: Float) -> Scene { // Interpolate between previous and current physics state for smooth rendering // For now, just return the scene as-is return scene } // MARK: - Public API public func loadScene(_ scene: Scene) { currentScene = scene } public func getFrameCount() -> UInt64 { return frameCount } public func getFPS() -> Double { // Calculate FPS based on frame timing return 60.0 // Placeholder } // MARK: - Utilities private func getCurrentTime() -> Double { return Double(DispatchTime.now().uptimeNanoseconds) / 1_000_000_000.0 } }