How can I call "participantQuitOutOfTurnWithOutcome:withCompletionHandler:"? - ios

I have a turn based game and am trying to end the game when someone forfeits from Game Center out of turn. I can't figure out what is called when the player out of turn actually presses the "forfeit" button. I want to implement,
participantQuitOutOfTurnWithOutcome:withCompletionHandler:
but don't know where to put it and call it. I have tried to put it here:
-(void)handleTurnEventForMatch:(GKTurnBasedMatch *)match
and
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController playerQuitForMatch:(GKTurnBasedMatch *)match
but neither seem to be called when a player presses the "forfeit" button out of turn. What am I missing?

You'll want to call participantQuitOutOfTurnWithOutcome:withCompletionHandler: whenever you determine the user wants to forfeit—so in your case, when they tap the forfeit button (and possibly after hitting 'Yes' on a confirmation).
In that case, you'll want something like this:
-(void)playerChoseToForfeit {
[self.match participantQuitOutOfTurnWithOutcome:GKTurnBasedMatchOutcomeQuit withCompletionHandler:^(NSError *error) {
//tell the user that they've forfeited (or not, if there's an error!)
}];
}
Basically, it'll be in your own method—not (necessarily) a delegate one.

Five years after you asked this question and Apple still hasn't updated the GKLocalPlayerListener protocol to handle this situation. I am also trying to rely solely on the Game Center MatchMaker view controller (GC MMVC) and I ran into the same problem you did, where if the user forfeits through the GC MMVC when it isn't their turn, then the match is left in a conflicted state: "completed" for the quitter, and "active" for the remaining players.
I found out by checking the matchOutcome of the opponent who quit out of turn after calling match.loadMatchData, that the opponent's outcome was set to .quit.
The reason that Game Center set the quitter's status to .quit but left the game active and set the currentParticipant to the next player was for games with more than two players. Since my game is a two-player only game, I didn't understand why player(wantsToQuitMatch:) wasn't being called.
So every time you call loadMatchData, you should check the matchOutcome of all other players and take appropriate action immediately. In my case, I set the quitter's outcome to .lost, the localParticipant's outcome to .won, and then call match.endMatchInTurn with the updated match data. Then I will display the loaded match to the localParticipant with a message saying the other player quit.
If you don't call match.endMatchInTurn then the remaining player is left hanging with an active match but no active opponents.

Related

'Play on' feature is an in-app purchase? SpriteKit/Swift

I'm near the end of developing a game, and long story short, the game will be over when an object crashes into one of the obstacles. I've got that part down and the game runs really well by itself, but there is one more step I am looking to add in.
I would like to add in an in-app purchase of a 'play on' feature, that is, starting where the user's game originally ended so they can continue on. I'm okay with in-app purchases on the whole, but I guess what I want to know is how does one make it possible to 'play on' following the in app purchase? I'm just looking for something basic I can build on.
I'm a Stack newbie, as I've only created an account today (but I've been programming for a little while and this site has helped me so many times), so I'm sorry if there has been a duplicate thread made elsewhere. I did look around for an hour or so before deciding to post (and Google was no help).
As a general stack overflow rule you should always post some of your own code or what code you have played around with.
I am actually also looking to integrate a playOn button in my game. Now I haven't actually found the perfect solution for this yet but hopefully this helps you get on the right track.
Step 1:
How have you programmed your game over scenario?
Are you just pausing the scene, are you pausing nodes or are you removing all children from your scene?
The way I pause my scene is by creating a worldNode and than adding all objects that I need paused to the worldNode.
You can read my two answered questions for more detail
Keeping the game paused after app become active?
Sprite moves two places after being paused and then unpaused
This way when I pause my game I dont actually pause the scene which gives me more flexibility adding pauseMenus etc. Furthermore its seems smoother than pausing the skView.
I also call pause when the player died, which means I could resume the enemies/obstacles from where they left of if I call resume.
So my game over method looks like this
func gameOver() {
pause() // call pause method to pause worldNode etc
//show game over screen including playOn button
}
Step 2:
Now in regards to respawning the player, it depends on how he is positioned, how far he can move etc.
If your player is mostly in the same area than you can probably just respawn you player manually once "PlayOn" is pressed and than resume the game as if it was just paused.
So once your playOn button is pressed you can call a method like so
func playOnPressed() {
// Remove current player
// Doesnt have to be called, you could just change the position
player.removeFromParent()
// Add player manually again to scene or just reposition him
...
// Remove obstacle that killed player
// haven't found a great solution for this yet
// You could make the player not receive damage for 5 seconds to make sure you dont die immediately after playOn is pressed
// Call resume method, maybe with delay if needed
resume()
}
If your player position could be all over the screen, as in my game, I have been playing around with a few things so far.
I created a position property to track the player position
playerPosition = CGPoint!
and than in my scene update method I constantly update this method to the
actual position of the player.
override func update(currentTime: CFTimeInterval) {
if gameOver = false {
playerPosition = player.position
}
}
I than have been playing around with the "playOnPressed" method,
func playOnPressed() {
// Remove current player
//Doesnt have to be called, you could just change the positioon
player.removeFromParent()
// Add player manually again to scene or just reposition him
...
player = SKSpriteNode(...
player.position.x = playerPosition.x - 40 // adjust this so it doesnt spawn where he died but maybe a bit further back
player.position.y = playerPosition.y // adjust if needed
// Remove obstacle that killed player
// haven't found a great solution for this yet
// You could make the player not receive damage for 5 seconds to make sure you dont die immediately after playOn is pressed
// Call resume method, with delay if needed
resume()
}
I hope this helps you playing around with your playOn button, if someone has a better way I would also appreciate it immensely.

Handling cancel in GKMatchmakerviewcontroller

I am making a multiplayer feature to a game I made.
Everything is working, except when I am in matchmaking and both players are connected, if one person hits the "Cancel" button the other device gets no notice of the canceling.
On the other device it the words change to say "Disconnected" however none of the delegate methods are called.
How can I handle this?
You should implement the GKMatchmakerViewControllerDelegate protocol.
Unfortunately, there is not a method (that I know of, or could find with almost 3 months of looking into it) that is called when one person disconnects in matchmaking after they have begun to connect.
Therefore, the way I got around this is once the GameViewController is presented it waits one second and then calls a method to check to see if it is connected to someone else.
To do this I have it so once the game begins each player sends the other player a random number (used to determine non-related settings later on - such as who gets to go first). When it calls the method to check to see if it is connected it just checks to see if the random number has been assigned. If so, then it begins the game as normal, if not, it ends the game and pops back to the menu.

How to avoid Game Center authentication repeating its block

Game Kit stores a strong reference to the completion handler that I send to authenticateWithCompletionHandler:, which means that each time the user exits and enters the app, it gets called again. This makes sense, but it causes a problem with a use case I have:
1) I prompt the user to log into Game Center when the app launches.
2) They tap Cancel because they want to play single player for a while. Therefore they are not logged in.
3) At some point, they decide they want to play online, so they tap my "Play Online" button.
4) This ought to show a screen where they can set up online game options, etc, but I notice they have no authenticated player, so…
5) I prompt the user again to log in to Game Center.
6) The user logs in this time, and in the completion handler I show my online game options screen.
Step 6 is the where the problem lies: every time the user leaves and re-enters the app, it will show my game options screen, because my completion handler is repeated. If I take out the code in the completion handler to show the online game options, the user has to tap the button twice - once to login, and once again to show the online options.
What is the smart solution to this?
For reference, a simplified version of my code looks like this:
- (IBAction)playOnlineTapped:(id)sender
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.authenticated) {
[self showOnlineGameOptions];
}
}];
}
So many of the examples I have read assume that the user logs in first time, but I don't think that's always going to be the case.
Thanks in advance for your help!
One solution to this would be to not have the completion handler change your views. This seems like an odd idea because the completion handler is called asynchronously and could happen any time after you request authentication.
Instead, the completion handler could check if you are in the online menu and enable buttons for you. Until then, have these buttons disabled and show a message saying "waiting for Game Center". The key is not to trigger any scene transitions in your completion handler. That would be bad design because you don't know when this block is called.
Another hint. If the user declines to log in to Game Center, your authentication request at some point won't prompt the user. If I remember right, you will receive GKErrorUserDenied immediately. Therefore, you should tell users that they can launch your game from within the Game Center app.

handleInviteFromGameCenter handling for turn based game (iOS6)

The standard handleInviteFromGameCenter event handling (when inviting someone from GC-app for a turn based match) behaves strange under iOS6: when I provide the playerToInvite array to the GKMatchRequest and open the GC turn based match controller, it shows "Waiting..." under that invited player (which it IMHO shouldn't for a turn based game). It never stops. When I add another player manually and press "play", it just starts with me and that newly added player, ignoring the one being passed from playerToInvite array.
Any insights on this behaviour?
Thx, habitoti

Trouble toggling the userInteractionEnabled property in iOS

I am developing a tic tac toe game for iOS and I am using a combination of UIButtons and UIImageViews to allow for user interaction and to display the moves made. My problem is that the buttons continue to accept user input before the cpu makes it's move, which breaks my game logic. I have made several attempts to toggle the userInteractionEnabled property, but I have only been able to turn it off. The engine that gets everything started in the game is my buttonPressed method. I also toggle the userInteractionEnabled property within this method and therein lies my problem: How do I re-enable the property after disabling user interaction? Is there a method that is called in between events that I can overwrite?
I have searched the web and I have searched through the developer documentation provided by Apple and I found information on the touchesBegan and touchesEnded methods. However, from what I understand, those methods need to be explicitly called which brings me back to my original problem of not being able to call those functions without the user's interaction.
If anyone can help, I would greatly appreciate it! I have been racking my brain over this for the past couple of weeks and I am just not seeing a solution.
I'd think that for a game like tic-tac-toe, calculating the countermove should be so fast that it can be done immediately in response to the first button press. If you're doing something complicated to calculate the next move, like kicking off a thread, you might want to reconsider that.
Let's say, though, that your game is something like chess or go, where coming up with a countermove might take a bit longer. Your view controller should have a method to make a move for the current player, let's call it -makeMove:. Your -buttonPressed action should call that method to make a move for the user. In response, -makeMove: should update the state of the game, switch the current player to the next player. If the new current player is the computer, it should then disable the controls and start the process of calculating the next move. Let's imagine that's done by invoking some NSOperation, so that coming up with the next move is an asynchronous task. Once the operation has come up with a move, it should again invoke -makeMove: (by calling -performSelectorOnMainThread:), which will again update the game state and the current player. This time, though, it should see that the new current player is not the computer, and so it should re-enable the controls.

Resources