问题描述
我正在根据 Ray Wenderlich 的九骑士教程编写几款 Swift 多人游戏。 (https://www.raywenderlich.com/7544-game-center-for-ios-building-a-turn-based-game)
我使用几乎相同的 GameCenterHelper 文件,除了我更改为 segue 而不是当前场景,因为我使用 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")
}
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}
}
希望容易理解。
- 游戏开始,菜单场景添加当前游戏的观察者
- 玩家点击多设备,显示媒人
- 玩家从匹配器中选择他们的游戏,我认为这会激活玩家监听器功能
- 这会发布到当前游戏的通知中心
- 通知中心观察者调用present game,它调用load 和display,在prepare segue 的帮助下
我的问题是,我第一次这样做时效果很好,并且根据该教程中的框架,我无法弄清楚在玩家轮到他们之后如何更改(我认为是另一个问题的问题)他们返回到菜单。他们第二次进入当前媒人并选择一个游戏时,当前游戏功能被调用两次,第三次他们在不关闭应用程序的情况下轮到它被调用了3次,依此类推(我在两个现在都有打印语句game 和 load 和 display 函数,它们会在第 2 次背靠背和第 3 次背靠背等情况下被调用,即使它们只在第一次从匹配器中选择游戏时被调用一次)
控制台消息
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,object: nil)
这并没有解决问题,所以我尝试了以下(代替上面的):
NotificationCenter.default.removeObserver(self)
那没有用,所以我尝试了它们,一次一个在视图中确实消失了游戏视图控制器(我认为这行不通,因为 self 指的是菜单 vc,但我得到了绝望),这也不起作用。
我开始想,也许我不会添加多个多次调用当前游戏的观察者,因为以下第二次根本不起作用(我只是使用全局变量来跟踪首先运行添加观察者,然后第二次不添加它们):
if addobservers {
NotificationCenter.default.addobserver(self,object: nil)
addobservers = false
}
因为它试图添加一个不在视图层次结构中的视图。 (虽然该屏幕的背景音乐开始播放,但菜单仍然存在并且没有显示游戏板......)
我不确定我是否错误地删除了通知中心观察者,或者它们是否真的不是问题的根源,所以我决定寻求帮助:)
谢谢!
解决方法
我想通了。我试图通过以下链接(最底部的答案)从视图控制器的释放实例中删除通知:
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)
}
实施后,我不再多次调用通知中心。