GameCenterManager
提供了一种简单的方法,可以将 Game Center 回合制多人游戏支持添加到应用程序中。
如果您觉得 GameCenterManager
有用,并希望帮助支持其持续开发和维护,请考虑进行小额捐赠,尤其是在商业产品中使用它时。
正是因为有像您这样的贡献者的支持,我才能继续免费构建、发布和维护像 GameCenterManager
这样高质量、文档齐全的 Swift Package。
Swift Package Manager(Xcode 11 及以上版本)
https://github.com/Appracatappra/GameCenterManager.git
粘贴到对话框中。为什么不使用 CocoaPods、Carthage 等?
支持多个依赖管理器会使维护库的复杂性和耗时呈指数级增长。
由于 Swift Package Manager 已集成到 Xcode 11(及更高版本)中,因此它是未来支持的最简单选择。
通过使用 GameCenterManager
和 MultiplayerGameManager
,您可以大大减少支持应用程序中 Game Center 回合制多人游戏所需的样板代码量。
在您的游戏视图启动之前,您需要连接 `GameCenterManage` 事件。 您可以在主应用程序中使用以下代码
import SwiftUI
import SwiftletUtilities
import LogManager
import SwiftUIKit
@main
struct PackageTesterApp: App {
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@Environment(\.scenePhase) private var scenePhase
@Environment(\.colorScheme) var colorScheme
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldScenePhase, newScenePhase in
switch newScenePhase {
case .active:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is active")
case .inactive:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is inactive")
case .background:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is in background")
@unknown default:
Debug.notice(subsystem: "PackageTesterApp", category: "Scene Phase", "App has entered an unexpected scene: \(oldScenePhase), \(newScenePhase)")
}
}
}
}
/// Class the handle the event that would typically be handled by the Application Delegate so they can be handled in SwiftUI.
class AppDelegate: NSObject, UIApplicationDelegate {
/// Handles the app finishing launching
/// - Parameter application: The app that has started.
func applicationDidFinishLaunching(_ application: UIApplication) {
// Register to receive remote notifications
UIApplication.shared.registerForRemoteNotifications()
}
/// Handle the application getting ready to launch
/// - Parameters:
/// - application: The application that is going to launch.
/// - launchOptions: Any options being passed to the application at launch time.
/// - Returns: Returns `True` if the application can launch.
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Wireup Game Center Events
GameCenterManager.shared.gameStateEncoder = {
// TODO: Insert your code here to encode your game state.
return MasterDataStore.shared.gameState.encoded
}
GameCenterManager.shared.gameStateDecoder = { data in
// TODO: Insert your code here to decode your game state. Return `true` if successfully decoded.
if let state = MurderCase(from: data) {
MasterDataStore.shared.gameState = state
return true
} else {
return false
}
}
GameCenterManager.shared.playerTurnEnd = { player, participants in
// TODO: Handle the player's turn ending.
MasterDataStore.shared.gameState.playerTurnEnded(player: player, participants: participants)
}
GameCenterManager.shared.playerQuitInTurn = { player, participants in
// TODO: Handle the player quitting in-turn.
MasterDataStore.shared.gameState.playerQuit()
}
GameCenterManager.shared.playerQuitOutOfTurn = { player in
// TODO: Handle the player quitting out-of-turn.
MasterDataStore.shared.gameState.playerQuit()
}
GameCenterManager.shared.playerWonGame = { player in
// TODO: Handle a player winning the game.
MasterDataStore.shared.gameState.playerWon(player: player)
}
GameCenterManager.shared.playerLostGame = { playerName in
// TODO: Handle the player losing the game.
MasterDataStore.shared.gameState.playerLost(playerName: playerName)
}
GameCenterManager.shared.startNewGame = {
// TODO: Handle a new game starting.
if let match = GameCenterManager.shared.currentMatch {
MasterDataStore.shared.gameState = MurderCase.BuildMurder(numberOfPlayers: match.participants.count, isMultiplayer: true)
}
}
GameCenterManager.shared.changeView = { matchState in
// TODO: Handle a request to switch view based on the match state.
switch matchState {
case .ended:
MasterDataStore.shared.gameState.endOfGameStats()
MasterDataStore.shared.changeView(newView: .gameLobby)
case .open, .matching:
MasterDataStore.shared.gameState.showGameboard()
default:
MasterDataStore.shared.changeView(newView: .gameLobby)
}
}
GameCenterManager.shared.playerMatchEvent = { player in
// TODO: Handle the player receiving a match event (such as another player making a move).
MasterDataStore.shared.gameState.assignPlayerToDetective(teamPlayerId: player.displayName)
MasterDataStore.shared.gameState.setCurrentPlayer()
MultiplayerConversations.startTurn()
}
// Informthe app that the launch has completed successfully.
return true
}
/// Handles the app receiving a remote notification
/// - Parameters:
/// - application: The app receiving the notifications.
/// - userInfo: The info that has been sent to the App.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
}
}
有了这段代码,在 func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
中进行任何样式更改,它们将应用于之后构建的所有视图。
以下 ConnectToGameCenter
辅助视图可以更轻松地连接到游戏中心,从而接管了许多所需的样板代码
import SwiftUI
import SwiftletUtilities
import GameKitUI
import GameKit
import GameCenterManager
import StoreKit
import LogManager
import AppStoreManager
import SoundManager
import SwiftUIKit
struct ConnectToGameCenter: View {
typealias AccessPointvent = () -> Bool
var location:GKAccessPoint.Location = .topTrailing
var shouldDisplayAccessPoint:AccessPointvent? = nil
@State private var checkForGameCenter:Bool = true
var body: some View {
if checkForGameCenter {
GKAuthenticationView(failed: {error in
Debug.info(subsystem: "Game Center", category: "Login", "Failed: \(error.localizedDescription)")
Execute.onMain {
GameCenterManager.shared.isGameCenterEnabled = false
checkForGameCenter = false
}
}, authenticated: {player in
Debug.info(subsystem: "Game Center", category: "Login", "Hello \(player.displayName)")
GKAccessPoint.shared.location = location
// Should we display the access point?
if let test = shouldDisplayAccessPoint {
if test() {
GKAccessPoint.shared.isActive = GKLocalPlayer.local.isAuthenticated
} else {
GKAccessPoint.shared.isActive = false
}
} else {
GKAccessPoint.shared.isActive = GKLocalPlayer.local.isAuthenticated
}
// Has a listener been registered
if GameCenterManager.shared.currentGameManager == nil && GKLocalPlayer.local.isAuthenticated {
GameCenterManager.shared.currentGameManager = MultiplayerGameManager()
GKLocalPlayer.local.register(GameCenterManager.shared.currentGameManager!)
Debug.info(subsystem: "Game Center", category: "Multiplayer Game", "Game Manager registered")
}
Execute.onMain {
GameCenterManager.shared.allowMultiplayer = (GKLocalPlayer.local.isAuthenticated && !GKLocalPlayer.local.isMultiplayerGamingRestricted)
GameCenterManager.shared.isGameCenterEnabled = true
checkForGameCenter = false
}
})
}
}
}
#Preview {
ConnectToGameCenter()
}
在应用程序的第一个视图中使用此代码将玩家连接到 Game Center
import SwiftUI
import SwiftletUtilities
import GameKitUI
import GameKit
import GameCenterManager
import StoreKit
import LogManager
import AppStoreManager
import SoundManager
import SwiftUIKit
import SpeechManager
struct MainMenuLandscape: View {
@ObservedObject var dataStore = MasterDataStore.shared
var body: some View {
ZStack {
...
ConnectToGameCenter() {
// TODO: Switch to the correct view when connected.
return (dataStore.currentView == .menuView)
}
} // End of ZStack
.onDisappear() {
GKAccessPoint.shared.isActive = false
}
}
}
#Preview {
MainMenuLandscape()
}
MultiplayerGameManger
类允许您将游戏状态更改发送到 Game Center,用于您的回合制应用程序。 以下是通过静态调用 MultiplayerGameManger
最常用的功能
isLocalPlayersTurn
- 如果为 true
,则轮到本地玩家。sendStatusUpdate()
- 将任何游戏状态更改发送给所有玩家,例如当前玩家正在移动。endTurn()
- 结束当前玩家的回合。quitInTurn(outcome:GKTurnBasedMatch.Outcome, displayName:String = GKLocalPlayer.local.displayName)
- 处理当前玩家在其回合内退出游戏的情况。quitOutOfTurn(displayName:String = GKLocalPlayer.local.displayName)
- 处理玩家不在其回合内退出游戏的情况。getGameCenterPlayer(for displayName:String)
- 返回具有给定显示名称的玩家。savePlayerScore(of score:Int, for player:GKPlayer, leaderBoards:[String])
- 将玩家的分数保存到给定的 Game Center 排行榜列表中。wonGame(displayName:String)
- 通知 Game Center 该玩家赢得了游戏。lostGame()
- 通知 Game Center 当前玩家输掉了游戏。updateAchievementForGame(for playerName:String, achievementID:String, byAmount:Double = 100.0)
- 按给定的数量更新给定玩家的成就。GraceLanguage Package 包括其所有功能的完整 DocC 文档。