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.
Related
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.
On Mac, one can always get the location of the mouse "outside the event stream" (ie, even if you've not subscribed to any delegate methods for mouseUp: et al) by calling [NSEvent mouseLocation].
Is there any way on iOS to get current touch events without listening to touchesBegan:?
I ask because there is at least one situation in which touchesBegan is not called: at app launch, if a touch is already in progress, touchesBegan is never called. In fact, neither are any of the touch-related UIEvent methods called as near as I can tell (nor UIApplication's / UIWindow's sendEvent:, apparently).
I would like to vary the behavior of my app slightly based on whether a touch is in progress at launch. Is there any way to detect an in-progress touch at app launch?
This cannot be done. The simple reason: The touch events don't belong to your app. Each touch belongs to some UI element (or responder). As you already know, this element gets the began, moved, ended, cancelled messages.
This is even true within a properly programmed app: All events regarding one touch are delivered to the very same object. After all, how would another object know what to do with that event, and how should the first object properly finish its expected behavior?
While you can (or could, but probably shouldn't) find a work around within your app, there's just no way for cross-app-touch passings.
And on the Mac you may query the mouse position, but in normal application flow there'll always be a mouse down before you get a mouse up event.
To be honest, I don't see any reason why this would be needed anyway... oh wait... I could split my app icon into several areas... not sure if it would already break privacy laws, though, if you get to know where the user has his icon on screen.
I think you could simply "extend" the application launch. When I had time consuming tasks during my application launch, I used to show the same splash screen with a UIActivityIndicator while the action was being carried out.
You could simply create a NSTimer, wait for about 2 seconds and during this time, check for touches, while the splash screen will still be showing.
To do this, in applicationDidFinishLaunch, push a ViewController that looks exactaly like the splash screen and check for touches in this ViewController. After those 2 seconds, proceed with normal initialisation. This behaviour also helps if you have time consuming tasks during initialisation.
I know, it`s a workaround, but my guess, is that it is not possible to check for touches because application will be working on the main thread and the touches also processes on the main thread. This could happen also because there are no ViewControllers or UIWindow initialised and ready to listen to touches.
Hope it helps.
You might try handling the hitTest:withEvent: instead.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
Since according to apple doc "Returns the farthest descendant of the receiver in the view hierarchy (including itself) that contains a specified point."
I have a question regarding sending and receiving data through Game Center when playing a multi-player game on iOS.
In my game after I matchmake two people, the game is taken to the Game Screen where the user can start playing.
Because the two devices do not load the game screen at the same time (for network reasons), the viewDidLoad function in my first device sometimes will already have completed by the time the second device has loaded the screen.
What this means is say for example in the viewDidLoad I send through my local player alias to my slower device, as the slower device is still loading by the time the faster device is doing so, when the screen loads, my faster device receives the slower devices player alias, but not vice versa.
Therefore, is there any easy way for one device to detect when the other device has loaded, and push through any relevant code from there?
First of all, you should only send messages once the connection has been established. You can detect this in the match: player: didChangeState: method of your delegate by waiting for the GKPlayerStateConnected state. If you send any message after this event occurred, it should arrive.
Otherwise, there may be concerns with your design. Your delegate should be able to interpret and use any message that comes in. If you are exchanging player aliases and really need to know them when starting the game, you should wait for the alias to come in as a trigger, or come up with an even more elaborate handshake protocol.
So, one simple recipe would be to
Send the alias shortly after you receive the GKPlayerStateConnected message.
Transition to your game view after receiving the alias of your peer.
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
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.