Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

How to make a turn based game with GC : Collaborative Drawing

edited September 2013 in Code Sharing Posts: 437

This is the collaborative drawing based on turns using Game Center network:

These are the simple steps.

1) Create a game in itunesconnect.apple.com, set GameCenter Activated.
2) Create a test account for simulating two players (one in your iPad and the other in the simulator)
3) Export your Codea project and embed the ObjC classes inside the game

PLAY.

Let me explain how Game Center turn based plays works.
You have to authenticate and all that stuff that @Reefwing explained in his tut some time ago.
When you have a valid login then you have to call for find a match with a number of participants with a function like this :

- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers {
    if (!hasGameCenter) return;
 
    self.codeaViewController.paused = YES;
 
    GKMatchRequest *request = [[GKMatchRequest alloc] init];
    request.minPlayers = minPlayers;
    request.maxPlayers = maxPlayers;
 
    GKTurnBasedMatchmakerViewController *mmvc = [[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request];
    mmvc.turnBasedMatchmakerDelegate = self;
    mmvc.showExistingMatches = YES;
 
    [self.codeaViewController presentViewController: mmvc animated:YES completion:nil];
 
}

I'm using only 2 players for the collaborative drawing game but it could be done with more, imagine 12+ players drawing over the same image xD.

This will show a window with the GameCenter Match Finder, you can invite a friend or set it to Auto-Match...when you choose, this is the event called:

-(void)turnBasedMatchmakerViewController: 
(GKTurnBasedMatchmakerViewController *)
viewController didFindMatch:(GKTurnBasedMatch *)
match {
    [self.codeaViewController dismissModalViewControllerAnimated:YES];
    self.currentMatch = match;
    GKTurnBasedParticipant *firstParticipant = [match.participants objectAtIndex:0];
    if (firstParticipant.lastTurnDate == NULL) {
        // It's a new game!
        [matchDelegate enterNewGame:match];
    } else {
        if ([match.currentParticipant.playerID isEqualToString:[GKLocalPlayer localPlayer].playerID]) {
            // It's your turn!
            [matchDelegate takeTurn:match];
        } else {
            // It's not your turn, just display the game state.
            [matchDelegate layoutMatch:match];
        }
    }
}

There is variable called matchDelegate, it handles all the action of a match from @Reefwing GC Addon class.

This is the class prototype:

@interface MatchDelegate : NSObject  {
    GKTurnBasedMatch *currentMatch;
}
 
@property (retain) NSMutableDictionary *playersDict;
@property (retain) GKTurnBasedMatch *currentMatch;
- (void)enterNewGame:(GKTurnBasedMatch *)match;
- (void)layoutMatch:(GKTurnBasedMatch *)match;
- (void)takeTurn:(GKTurnBasedMatch *)match;
- (void)recieveEndGame:(GKTurnBasedMatch *)match;
- (void)sendNotice:(NSString *)notice forMatch:(GKTurnBasedMatch *)match;
- (void)sendTurn;
- (int) getCurrentMatchStatus;
- (void)leaveMatch;
@end

You have to save the *match parameter of the handles coming from the GameCenter network into that MatchDelegate.currentMatch to know everything about the current match, like the current turn, the players, etc.

Register lua calls to read that info from the matches inside the Codea project.

I'm making a loop reading the match status, and change the game status when the match status change, so you can know what you have to do anytime.

Once you have created a game, the first turn starts, you can cancel the turn or finish it, in this case ,you have to use the sendTurn function, you can use a message (NSString) inside match.message and a NSData inside match.matchData, there is a limit for this data, 65536 bytes, that's why the images sent in the video looks ugly, I dont know how to fix it right now without using paths, but this info (vectors,colors,brushes,sizes,etc) could be much more larger that the resized image.

You can know info about the match, so, when it is not your turn you can layout the state of the game, getting the match.matchData and match.message from the currentMatch inside the MatchDelegate variable of your GameCenterAddon class.

When it is your turn, you use this info again to rebuild the game state and continue with your turn, then send it again until you set the turn as the last turn. I'm not using this function because there is no goal in this game and the info can't grow, you are just sending the same image modified.

To send the turn you can do this in the MatchDelegate:

    NSUInteger currentIndex = [currentMatch.participants
                               indexOfObject:currentMatch.currentParticipant];
    GKTurnBasedParticipant *nextParticipant;
    nextParticipant = [currentMatch.participants objectAtIndex:
                       ((currentIndex + 1) % [currentMatch.participants count ])];
 
    [currentMatch endTurnWithNextParticipant:nextParticipant
       matchData:data completionHandler:^(NSError *error) {
           if (error) {
               NSLog(@"%@", error);
           }
     }
    ];
    NSLog(@"Sending Turn, %@, %@", data, nextParticipant); //this is done is background
    currentMatch = nil;

You can see the game is saving a very low quality image, that's because of the limitations of the data size, we'll need to figure out how to compress the image data in a better way without using external services...

You have the full tut here:
http://www.raywenderlich.com/5480/beginning-turn-based-gaming-with-ios-5-part-1

Comments

  • edited September 2013 Posts: 2,820

    Cool. Nice job.

  • Posts: 2,820

    BTW, is there an chance you can share your code for sharing on twitter (and repost the FB code)? Thanks!

  • Posts: 437

    Yes @Zoyt , for Twitter, you need this function in your objc Codea addon Class:

    - (void)sendTwit:(const char*) text withImage:(bool) use_image {
        TWTweetComposeViewController *tweeter = [
           [TWTweetComposeViewController alloc]
           init
        ];
        NSString *message = [NSString stringWithFormat:@"%s",text];
     
        [tweeter setInitialText:message];
        if (use_image){
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
            [tweeter addImage:
             [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/tweet.png",basePath]
             ]
            ];
        }
        [self.codeaViewController presentModalViewController:tweeter animated:YES];
    }

    you have to add a function from lua_register() to use in Codea code, i called it sendTwit("Your message"), and before call this function, save the image of the current buffer like this:

    saveImage("Documents:tweet",theImage)
    

    For Facebook, you need to download the SDK, then set the appid in the plist, add this function to the appdelegate.mm:

    -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
        return [FBSession.activeSession handleOpenURL:url];
    }

    then , you will need two functions for facebook, one to prepare the post and the second to submit it.

    - (void) sendFacebook: (const char*) text withImage:(bool) use_image {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
        NSString *filePath = [NSString stringWithFormat:@"%@/tweet.png",basePath];
        NSData *imageData = [NSData dataWithContentsOfFile:filePath];
     
        NSMutableDictionary* params = [[NSMutableDictionary alloc] init];
        [params setObject:[NSString stringWithFormat:@"%s",text] forKey:@"message"];
        [params setObject:UIImagePNGRepresentation([UIImage imageWithData:imageData])forKey:@"picture"];
        //_shareToFbBtn.enabled = NO; //for not allowing multiple hits
        [FBRequestConnection startWithGraphPath:@"me/photos"
                                     parameters:params
                                     HTTPMethod:@"POST"
                              completionHandler:^(FBRequestConnection *connection,
                                                  id result,
                                                  NSError *error)
         {
             [self showAlert:@"Submit to FB! :-)" result:nil error:nil];
             if (error)
             {
                 //showing an alert for failure
                 //             [self alertWithTitle:@"Facebook" message:@"Unable to share the photo please try later."];
                 NSLog(@"Error uploading %@",error.description);
             }
             else
             {
                 //showing an alert for success
                 //             [UIUtils alertWithTitle:@"Facebook" message:@"Shared the photo successfully"];
     
                 NSLog(@"Sharing ok");
             }
             //         _shareToFbBtn.enabled = YES;
     
         }];
     
    }
     
    - (void) sendFB:(const char *)text withImage:(bool)use_image{
       if (!FBSession.activeSession.isOpen) {
            // if the session is closed, then we open it here, and establish a handler for state changes
            [FBSession openActiveSessionWithPublishPermissions:[[NSArray alloc] initWithObjects:@"publish_stream", nil]
               defaultAudience:FBSessionDefaultAudienceFriends
                  allowLoginUI:YES
             completionHandler:^(FBSession *session,
                                 FBSessionState state, NSError *error) {
                  if (error) {
                      UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error"
                          message:error.localizedDescription
                         delegate:nil
                cancelButtonTitle:@"OK"
                otherButtonTitles:nil];
                      [alertView show];
                      NSLog(@"Error %@",error.description);
                  } else if (session.isOpen) {
                      [self sendFacebook:text withImage:use_image];
     
                  } else {
                      NSLog(@"WTF");
     
                  }
     
     
              }];
        } else {
          [self sendFacebook:text withImage:use_image];
          NSLog(@"Ok, lets go FB 2");
        }
     
    }

    to call this functions from lua , define them like this:

    lua_register(L, "sendFacebook", sendFacebook);
     
    static int sendFacebook(struct lua_State *state)
    {
        [gameCenterAddOnInstance
         sendFB:lua_tostring(state, 1)
         withImage:lua_toboolean(state, 2)
        ];
        return 1;
    }

    I'm doing the casting and all those things there..
    So you got a pretty good idea to add it to your game ;)

  • Posts: 2,820

    @juaxix - Thanks! And I assume I'll have to write my own way to detect if it is available? Thanks!

  • edited September 2013 Posts: 437

    For Facebook and Twitter, it would be this easy:

    #import <FacebookSDK/FacebookSDK.h>
    #import <Twitter/Twitter.h>
    

    and (copy+paste) XD

    that's all,you dont need to check...this code do it

  • Posts: 2,820

    @juaxix - Sorry, I meant to see if the user is signed in, but I forgot that iOS gives you a sign in screen if you're not. Thanks!

  • @juaxix This is an old thread but if you generalize this approach it seems like you figured out how to create interaction between the iOS environment and Codea--which many people have desired!

    If I understand correctly, how this works is that:

    • Inside the Codea project there's a loop that constantly checks a local text file.
    • Once the project is imported into Xcode, that same file gets constantly checked by a loop inside the iOS code.
    • If either environment detects alterations in that text file, it evaluates the changes and reacts accordingly.
    • Thus by updating the common file you can effect communication between the Lua environment and the Swift or Objective-C environment.
    • The implementation given here is for multiplayer play, but the same approach can be generalized to allow either environment to make function calls in the other environment.
    • For example, if someone wanted to their Codea project to display an alert message using iOS's default alert style, they could send the necessary function call and message data to iOS via updates to the common file.

    Do I have that right? It's really uncomplicated and elegant, and seems like a complete solution to the issue.

  • edited September 2017 Posts: 437

    @UberGoober I can't remember very good how I achieved this, but I think you have to use a version of the export of the project in xCode, the thing is I think they changed that so it is very different now, also, the ObjectiveC code should be obsolete, the same goes for the GameCenter ...you should ask the creators of Codea because I don't know how it works now...

Sign In or Register to comment.