Swift 多人游戏多次调用当前游戏

问题描述

我正在根据 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")
}

菜单的 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}
}

希望容易理解。

  1. 游戏开始,菜单场景添加当前游戏的观察者
  2. 玩家点击多设备,显示媒人
  3. 玩家从匹配器中选择他们的游戏,我认为这会激活玩家监听器功能
  4. 这会发布到当前游戏的通知中心
  5. 通知中心观察者调用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)
    }

实施后,我不再多次调用通知中心。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...