You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpriteKit与Swift:如何获取测试者的关卡完成时间数据?

Great question—collecting playtest data is absolutely crucial to tuning those medal times so they feel fair, rewarding, and aligned with what your actual players can achieve. Let’s break down how to implement this in your SpriteKit/Swift iOS game, with a focus on making it easy for testers to share their data:

1. Track Level Completion Time in Your Code

First, you’ll need to log when a level starts and calculate how long it takes the player to finish. Add this to your level’s SKScene subclass:

private var levelStartTime: Date?
private let currentLevelID = "level_01" // Replace with your actual level identifier

override func didMove(to view: SKView) {
    super.didMove(to: view)
    // Record the exact moment the level loads
    levelStartTime = Date()
}

// Call this function when the player reaches the level's end trigger
func handleLevelCompletion() {
    guard let startTime = levelStartTime else { 
        print("Error: Level start time wasn't recorded")
        return 
    }
    // Calculate time in seconds (rounded to 1 decimal for readability)
    let completionTimeInSeconds = round(Date().timeIntervalSince(startTime) * 10) / 10
    
    // Save this time for later sharing
    saveCompletionTime(forLevel: currentLevelID, time: completionTimeInSeconds)
    
    // Proceed with your existing level completion logic (show medals, load next level, etc.)
}

2. Store Data Locally & Let Testers Share It

You don’t need a backend for testing—just save the data to a file in the app’s documents directory, and add a simple way for testers to share that file with you. Here’s how to implement the save and share functionality:

private func saveCompletionTime(forLevel levelID: String, time: TimeInterval) {
    // Get the path to the app's documents folder
    guard let docsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let dataFileURL = docsDirectory.appendingPathComponent("playtest_level_times.plist")
    
    // Load existing data (if any) or start with an empty dictionary
    var playtestData: [String: [TimeInterval]] = [:]
    if let existingData = try? Data(contentsOf: dataFileURL),
       let existingDict = try? PropertyListSerialization.propertyList(from: existingData, format: nil) as? [String: [TimeInterval]] {
        playtestData = existingDict
    }
    
    // Add the new completion time to the level's entry
    if var levelTimes = playtestData[levelID] {
        levelTimes.append(time)
        playtestData[levelID] = levelTimes
    } else {
        playtestData[levelID] = [time]
    }
    
    // Save the updated data back to the file
    do {
        let data = try PropertyListSerialization.data(fromPropertyList: playtestData, format: .binary, options: 0)
        try data.write(to: dataFileURL)
    } catch {
        print("Failed to save playtest data: \(error.localizedDescription)")
    }
}

// Add a share button to your UI (call this in didMove(to:))
private func setupPlaytestShareButton() {
    let shareButton = UIButton(type: .system)
    shareButton.setTitle("📤 Share Playtest Data", for: .normal)
    shareButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
    shareButton.addTarget(self, action: #selector(sharePlaytestDataTapped), for: .touchUpInside)
    
    // Position the button (adjust frame to fit your game's UI)
    let safeAreaInsets = view?.safeAreaInsets ?? .zero
    shareButton.frame = CGRect(
        x: 20,
        y: safeAreaInsets.top + 20,
        width: (view?.frame.width ?? 375) - 40,
        height: 44
    )
    
    view?.addSubview(shareButton)
}

@objc private func sharePlaytestDataTapped() {
    guard let docsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    let dataFileURL = docsDirectory.appendingPathComponent("playtest_level_times.plist")
    
    guard FileManager.default.fileExists(atPath: dataFileURL.path) else {
        print("No playtest data to share yet")
        // Optional: Show an alert to the tester
        return
    }
    
    // Use UIActivityViewController to let testers share via email, Messages, etc.
    let activityVC = UIActivityViewController(activityItems: [dataFileURL], applicationActivities: nil)
    
    // Present from the root view controller (adjust if your game uses a different setup)
    if let rootVC = view?.window?.rootViewController {
        rootVC.present(activityVC, animated: true)
    }
}

3. Add Context to Make Data More Useful

To get better insights, consider adding a way for testers to share additional context alongside their times:

  • A quick difficulty rating (1-5 scale) for each level
  • A text field for notes (e.g., "Got stuck on the moving platform section")
  • A toggle for their experience level (e.g., "New to platformers" / "Experienced player")

You can append this info to a separate text file or include it in the plist as a nested dictionary.

4. Analyze the Data to Set Medal Times

Once you’ve collected data from a handful of testers, use these guidelines to set your medal thresholds:

  • Bronze: Aim for the 90th percentile (90% of testers finish faster than this time)—this ensures most players feel rewarded for completing the level.
  • Silver: 75th percentile—rewards players who play efficiently without perfect execution.
  • Gold: 30-50th percentile (median or lower)—reserve this for players who master the level’s shortcuts and optimal paths.

Don’t forget to check for outliers (e.g., a tester who finished way faster than everyone else) and adjust if a level’s time distribution is skewed—this might mean the level itself is too easy or hard, not just the medal times.

5. Keep It Simple for Testers

Make sure your testers know:

  • The game automatically tracks their times—no manual input needed.
  • They can share data anytime, but it’s most useful after they’ve played each level a few times.
  • Encourage them to include feedback alongside the data file.

内容的提问来源于stack exchange,提问作者Discoveringmypath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:44:11