我对 Swift 很陌生,最近开始开发自己的应用程序。我一直在尝试实现 Game Center 多人游戏。我项目的当前结构是(标准游戏中心结构):
- GameViewController - UIViewController, GKGameCenterControllerDelegate
- GameKitHelper - NSObject、GKGameCenterControllerDelegate、GKMatchmakerViewControllerDelegate、GKMatchDelegate
- GameKitHelperDelegate - 协议(matchStarted、matchEnded、match)
- MultiplayerNetwork - GameKitHelperDelegate(用于发送和处理消息)
我面临的问题是在多人游戏的早期阶段。在 GameViewController 上,我有以下代码来触发多人游戏:
func playMultiGame(){
var gameScene = GameSceneMultiPlayer(size:CGSize(width: 2048, height: 1536))
let networkingEngine = MultiplayerNetworking()
networkingEngine.delegate = gameScene
gameScene.networkingEngine = networkingEngine
GameKitHelper.SharedGameKitHelperInstance.findMatchWithMinPlayers(minPlayers: 2, maxPlayers: 2, viewController: self, delegate: networkingEngine)
let skView = self.view as! SKView
gameScene.scaleMode = .fill
skView.presentScene(gameScene)
}
上面触发的 findMatchWithMinPlayers 方法如下:
func findMatchWithMinPlayers(minPlayers:Int, maxPlayers:Int, viewController:UIViewController, delegate:GameKitHelperDelegate) {
if(!_enableGameCenter) {
return;
}
_matchStarted = false
self._match = nil
_delegate = delegate
viewController.dismiss(animated: false, completion: nil)
//GKmatch request
let request = GKMatchRequest()
request.minPlayers = minPlayers
request.maxPlayers = maxPlayers
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
viewController.present(mmvc!, animated: true, completion: nil)
}
上面的方法在 GameKitHelper 中,它有以下方法:
class GameKitHelper : NSObject, GKGameCenterControllerDelegate, GKMatchmakerViewControllerDelegate, GKMatchDelegate {
var _enableGameCenter : Bool
var _matchStarted : Bool
var _match : GKMatch!
var _delegate : GameKitHelperDelegate?
var authenticationViewController: UIViewController?
var lastError : NSError?
var playersDict : NSMutableDictionary?
class var SharedGameKitHelperIntance:GameKitHelper {
return _GameKitHelperSharedInstace
}
override init() {
self._enableGameCenter = true
self._matchStarted = false
super.init()
}
func authenticateLocalPlayer() {
[...]
}
func setAuthenticationViewController(authViewController:UIViewController!) {
[...]
}
func findMatchWithMinPlayers(minPlayers:Int, maxPlayers:Int, viewController:UIViewController, delegate:GameKitHelperDelegate) {
[...]
}
func lookupPlayers() {
[...]
}
/* Implementing delegate GKMatchmakerViewControllerDelegate methods */
func matchmakerViewControllerWasCancelled(_ viewController:GKMatchmakerViewController) {
viewController.dismiss(animated: true, completion: nil)
print("canceling multiplayer view")
_delegate?.matchEnded()
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController,
didFailWithError error: Error) {
viewController.dismiss(animated: true, completion: nil)
NSLog("Error finding match: %@", error.localizedDescription)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController,
didFind match: GKMatch!) {
viewController.dismiss(animated: true, completion: nil)
match.delegate = self
_match = match
if(!_matchStarted && match.expectedPlayerCount==0) {
NSLog("Ready to start match")
self.lookupPlayers()
}
}
/* Implementing delegate GKMatchDelegate methods */
func match(match: GKMatch!, didReceiveData data: NSData!, fromPlayer playerID: NSString!) {
if(_match != match) {
return
}
_delegate?.match(match: match, didReceiveData: data, fromPlayer: playerID)
}
func match(match: GKMatch!, player: String!, didChangeState state: GKPlayerConnectionState) {
if(_match != match) {
return
}
switch(state) {
case GKPlayerConnectionState.stateConnected:
if(!_matchStarted && match.expectedPlayerCount == 0) {
NSLog("Ready to start match!")
self.lookupPlayers()
}
case GKPlayerConnectionState.stateDisconnected:
NSLog("Player disconnected!")
_matchStarted = false
_delegate?.matchEnded()
default:
break
}
}
func match(match: GKMatch!, connectionWithPlayerFailed:String!, withError error:NSError!) {
if(_match != match) {
return
}
NSLog("Failed to connect to player with error: %@", error.localizedDescription)
_matchStarted = false
_delegate?.matchEnded()
}
func match(match: GKMatch!, didFailWithError error: NSError!) {
if(_match != match) {
return
}
NSLog("Match failed with error: %@", error.localizedDescription)
_matchStarted = false
_delegate?.matchEnded()
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController)
{
gameCenterViewController.dismiss(animated: true, completion: nil)
}
一旦两个用户配对并且游戏准备好开始,只要 GKMatchmakerViewController 被关闭,应用程序就会在执行此操作的 matchmakerViewController 方法中崩溃:
func matchmakerViewController(_ viewController: GKMatchmakerViewController,
didFind match: GKMatch!) {
viewController.dismiss(animated: true, completion: nil)
_match.delegate = self
_match = match
if(!_matchStarted && match.expectedPlayerCount==0) {
NSLog("Ready to start match")
self.lookupPlayers()
}
}
通过异常断点,我发现导致上述问题的确切行如下:
_match.delegate = self
我得到的错误如下:
2017-07-12 19:15:47.472473+0100 xxx[2653:492023] -[xxx.GameKitHelper match:didReceiveData:fromPlayer:]: 无法识别的选择器发送到实例 0x17047a600 2017-07-12 19:15:47.507642+0100 xxx [2653:492023] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[xxx.GameKitHelper 匹配:didReceiveData:fromPlayer:]:无法识别的选择器发送到实例 0x17047a600”
任何想法可能导致此问题?我什么都试过了。