Swift 多人调用多次呈现游戏
Swift Multiplayer Calls Present Game Multiple Times
我正在根据《九骑士》的 Ray Wenderlich 教程编写几个 Swift 多人游戏。 (https://www.raywenderlich.com/7544-game-center-for-ios-building-a-turn-based-game)
我使用几乎相同的 GameCenterHelper 文件,除了我使用 UIKit 而不是 Sprite Kit 以及以下重要部分:
现在的媒人:
func presentMatchmaker() {
guard GKLocalPlayer.local.isAuthenticated else {return}
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 2
request.inviteMessage = "Would you like to play?"
let vc = GKTurnBasedMatchmakerViewController(matchRequest: request)
vc.turnBasedMatchmakerDelegate = self
currentMatchmakerVC = vc
print(vc)
viewController?.present(vc, animated: true)
}
播放器监听函数:
extension GameCenterHelper: GKLocalPlayerListener {
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
if let vc = currentMatchmakerVC {
currentMatchmakerVC = nil
vc.dismiss(animated: true)
}
guard didBecomeActive else {return}
NotificationCenter.default.post(name: .presentGame, object: match)
}
}
通知中心的以下扩展:
extension Notification.Name {
static let presentGame = Notification.Name(rawValue: "presentGame")
static let authenticationChanged = Notification.Name(rawValue: "authenticationChanged")
}
在菜单的 viewdidload 中我调用如下:
override func viewDidLoad() {
super.viewDidLoad()
createTitleLabel()
createGameImage()
createButtons()
GameCenterHelper.helper.viewController = self
GameCenterHelper.helper.currentMatch = nil
NotificationCenter.default.addObserver(self, selector: #selector(authenticationChanged(_:)), name: .authenticationChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(presentGame(_:)), name: .presentGame, object: nil)
}
点击多设备按钮调用以下内容:
@objc func startMultiDeviceGame() {
multiPlayer = true
GameCenterHelper.helper.presentMatchmaker()
}
通知调用以下内容:
@objc func presentGame(_ notification: Notification) {
// 1
print("present game")
guard let match = notification.object as? GKTurnBasedMatch else {return}
loadAndDisplay(match: match)
}
// MARK: - Helpers
private func loadAndDisplay(match: GKTurnBasedMatch) {
match.loadMatchData { [self] data, error in
if let data = data {
do {
gameModel = try JSONDecoder().decode(GameModel.self, from: data)
} catch {gameModel = GameModel()}
} else {gameModel = GameModel()}
GameCenterHelper.helper.currentMatch = match
print("load and display")
performSegue(withIdentifier: "gameSegue", sender: nil)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare to segue")
if let vc = segue.destination as? GameVC {vc.gameModel = gameModel}
}
希望这很容易理解。
- 游戏开始菜单场景添加当前游戏观察者
- 玩家点击多设备,出现红娘
- 玩家select从媒人那里玩游戏,我认为这激活了玩家监听器功能
- 这会发布到当前游戏的通知中心
- 在prepare segue的帮助下,通知中心观察者调用当前游戏,调用加载和显示
我的问题是,我第一次这样做时效果很好,并且根据该教程中的框架,我无法弄清楚如何在玩家接受后更改(我认为这是另一个问题的问题)轮到他们返回菜单。他们第二次进入当前媒人和 select 游戏时,当前游戏功能被调用两次,第三次轮到他们时没有关闭应用程序,它被调用了 3 次,等等。(我有打印语句在当前的游戏以及加载和显示功能中,它们在第 2 次和第 3 次被连续调用,等等。即使它们只在第一次游戏 select 时被调用来自媒人)
控制台消息
present matchmaker true
<GKTurnBasedMatchmakerViewController: 0x104810000>
present game
present game
present game
load and display
prepare to segue
load and display
prepare to segue
load and display
prepare to segue
2021-03-20 22:32:26.838680-0600 STAX[4997:435032] [Presentation] Attempt to present <STAX.GameVC: 0x103894c00> on <Game.MenuVC: 0x103814800> (from < Game.MenuVC: 0x103814800>) whose view is not in the window hierarchy.
(419.60100000000006, 39.0)
2021-03-20 22:32:26.877943-0600 STAX[4997:435032] [Presentation] Attempt to present <STAX.GameVC: 0x103898e00> on < Game.MenuVC: 0x10501c800> (from < Game.MenuVC: 0x10501c800>) whose view is not in the window hierarchy.
我原以为这是因为我没有删除通知中心观察器,但我在菜单屏幕加载的视图中尝试了以下操作(就在我添加 .presentGame 观察器之前):
NotificationCenter.default.removeObserver(self, name: .presentGame, object: nil)
这并没有解决问题,所以我尝试了以下方法(代替上面的方法):
NotificationCenter.default.removeObserver(self)
那没有用,所以我分别尝试了它们,一次一个在视图中确实消失了游戏视图控制器(我认为这不会起作用,因为 self 指的是菜单 vc, 但我越来越绝望了) 也没有用。
我开始考虑也许我没有添加多个多次调用当前游戏的观察者,因为以下第二次完全不起作用(我只是使用全局变量来跟踪第一次 运行 添加观察者,然后第二次不添加它们):
if addObservers {
NotificationCenter.default.addObserver(self, selector: #selector(authenticationChanged(_:)), name: .authenticationChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(presentGame(_:)), name: .presentGame, object: nil)
addObservers = false
}
因为它正在尝试添加不在视图层次结构中的视图。 (虽然该画面的背景音乐开始播放,但菜单仍然存在,游戏板未显示...)
我不确定我是否错误地删除了通知中心观察器,或者它们是否真的不是问题的根源,所以我决定寻求帮助:)
谢谢!
我明白了。我试图根据以下 link(最底部的答案)从视图控制器的已释放实例中删除通知:
How to avoid adding multiple NSNotification observer?
删除通知的正确方法是在视图中删除通知,如下所示:
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: Notification.Name.presentGame, object: nil)
NotificationCenter.default.removeObserver(self, name: Notification.Name.authenticationChanged, object: nil)
}
实施后,我停止多次调用通知中心。
我正在根据《九骑士》的 Ray Wenderlich 教程编写几个 Swift 多人游戏。 (https://www.raywenderlich.com/7544-game-center-for-ios-building-a-turn-based-game)
我使用几乎相同的 GameCenterHelper 文件,除了我使用 UIKit 而不是 Sprite Kit 以及以下重要部分:
现在的媒人:
func presentMatchmaker() {
guard GKLocalPlayer.local.isAuthenticated else {return}
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 2
request.inviteMessage = "Would you like to play?"
let vc = GKTurnBasedMatchmakerViewController(matchRequest: request)
vc.turnBasedMatchmakerDelegate = self
currentMatchmakerVC = vc
print(vc)
viewController?.present(vc, animated: true)
}
播放器监听函数:
extension GameCenterHelper: GKLocalPlayerListener {
func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) {
if let vc = currentMatchmakerVC {
currentMatchmakerVC = nil
vc.dismiss(animated: true)
}
guard didBecomeActive else {return}
NotificationCenter.default.post(name: .presentGame, object: match)
}
}
通知中心的以下扩展:
extension Notification.Name {
static let presentGame = Notification.Name(rawValue: "presentGame")
static let authenticationChanged = Notification.Name(rawValue: "authenticationChanged")
}
在菜单的 viewdidload 中我调用如下:
override func viewDidLoad() {
super.viewDidLoad()
createTitleLabel()
createGameImage()
createButtons()
GameCenterHelper.helper.viewController = self
GameCenterHelper.helper.currentMatch = nil
NotificationCenter.default.addObserver(self, selector: #selector(authenticationChanged(_:)), name: .authenticationChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(presentGame(_:)), name: .presentGame, object: nil)
}
点击多设备按钮调用以下内容:
@objc func startMultiDeviceGame() {
multiPlayer = true
GameCenterHelper.helper.presentMatchmaker()
}
通知调用以下内容:
@objc func presentGame(_ notification: Notification) {
// 1
print("present game")
guard let match = notification.object as? GKTurnBasedMatch else {return}
loadAndDisplay(match: match)
}
// MARK: - Helpers
private func loadAndDisplay(match: GKTurnBasedMatch) {
match.loadMatchData { [self] data, error in
if let data = data {
do {
gameModel = try JSONDecoder().decode(GameModel.self, from: data)
} catch {gameModel = GameModel()}
} else {gameModel = GameModel()}
GameCenterHelper.helper.currentMatch = match
print("load and display")
performSegue(withIdentifier: "gameSegue", sender: nil)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare to segue")
if let vc = segue.destination as? GameVC {vc.gameModel = gameModel}
}
希望这很容易理解。
- 游戏开始菜单场景添加当前游戏观察者
- 玩家点击多设备,出现红娘
- 玩家select从媒人那里玩游戏,我认为这激活了玩家监听器功能
- 这会发布到当前游戏的通知中心
- 在prepare segue的帮助下,通知中心观察者调用当前游戏,调用加载和显示
我的问题是,我第一次这样做时效果很好,并且根据该教程中的框架,我无法弄清楚如何在玩家接受后更改(我认为这是另一个问题的问题)轮到他们返回菜单。他们第二次进入当前媒人和 select 游戏时,当前游戏功能被调用两次,第三次轮到他们时没有关闭应用程序,它被调用了 3 次,等等。(我有打印语句在当前的游戏以及加载和显示功能中,它们在第 2 次和第 3 次被连续调用,等等。即使它们只在第一次游戏 select 时被调用来自媒人)
控制台消息
present matchmaker true
<GKTurnBasedMatchmakerViewController: 0x104810000>
present game
present game
present game
load and display
prepare to segue
load and display
prepare to segue
load and display
prepare to segue
2021-03-20 22:32:26.838680-0600 STAX[4997:435032] [Presentation] Attempt to present <STAX.GameVC: 0x103894c00> on <Game.MenuVC: 0x103814800> (from < Game.MenuVC: 0x103814800>) whose view is not in the window hierarchy.
(419.60100000000006, 39.0)
2021-03-20 22:32:26.877943-0600 STAX[4997:435032] [Presentation] Attempt to present <STAX.GameVC: 0x103898e00> on < Game.MenuVC: 0x10501c800> (from < Game.MenuVC: 0x10501c800>) whose view is not in the window hierarchy.
我原以为这是因为我没有删除通知中心观察器,但我在菜单屏幕加载的视图中尝试了以下操作(就在我添加 .presentGame 观察器之前):
NotificationCenter.default.removeObserver(self, name: .presentGame, object: nil)
这并没有解决问题,所以我尝试了以下方法(代替上面的方法):
NotificationCenter.default.removeObserver(self)
那没有用,所以我分别尝试了它们,一次一个在视图中确实消失了游戏视图控制器(我认为这不会起作用,因为 self 指的是菜单 vc, 但我越来越绝望了) 也没有用。
我开始考虑也许我没有添加多个多次调用当前游戏的观察者,因为以下第二次完全不起作用(我只是使用全局变量来跟踪第一次 运行 添加观察者,然后第二次不添加它们):
if addObservers {
NotificationCenter.default.addObserver(self, selector: #selector(authenticationChanged(_:)), name: .authenticationChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(presentGame(_:)), name: .presentGame, object: nil)
addObservers = false
}
因为它正在尝试添加不在视图层次结构中的视图。 (虽然该画面的背景音乐开始播放,但菜单仍然存在,游戏板未显示...)
我不确定我是否错误地删除了通知中心观察器,或者它们是否真的不是问题的根源,所以我决定寻求帮助:)
谢谢!
我明白了。我试图根据以下 link(最底部的答案)从视图控制器的已释放实例中删除通知:
How to avoid adding multiple NSNotification observer?
删除通知的正确方法是在视图中删除通知,如下所示:
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: Notification.Name.presentGame, object: nil)
NotificationCenter.default.removeObserver(self, name: Notification.Name.authenticationChanged, object: nil)
}
实施后,我停止多次调用通知中心。