Challenge another player to custom built map? - ios

I am working on building a game where a user is able to build their own map to explore. Using GameKit and Game Center is it possible to challenge another user to play that map that was just created?
If so, how does this work so that the other user can see the graphics, data, etc that was created within another users game instance?

It would depend entirely on how your game is designed. GameCenter doesn't really care what sort of data you are sending in a match, as long as it adheres to the limits Game Center Places on messages.
The common factor is that you need to find a way to serialize your custom level into a format that can be sent over Game Center, then write deserialization methods to get the data into the map's format. If your maps are persistent, you can probably just send the file (unless you are using a very inefficient representation) and then use your regular methods for making a map out of the file.
For simplicity, lets say that you're making a turn-based game with a Minecraft-like board, so the only thing that you can edit is the height of each block. You might send a special turn with the JSON-serialized equivalent of
NSArray* board = //Array of arrays of NSNumbers with the heights of each block.
NSArray* turn = #[#"This is the turn that sends the board", board];
//serialize this into a NSData with JSON then send it with endTurnWithMatchData:
Then in your receivedTurnEventForMatch: method, you test for the special string in index zero that turn, or just expect it to be the first turn, and then use it to create the board, and programmatically end your current turn with no action if it is the other player's turn, or let the player receiving the custom map make his first turn.
For more ambitious custom content like images you would have to get the maximum size turn you can currently send and then break up the images into chunks to send.

Related

How to actually start a GKTurnBasedMatch so that I can send an exchange?

I'm trying to send an exchange to another player in a GKTurnBasedMatch. When I send it, my completion handler gets a big error message, with these descriptions:
The requested operation could not be completed because the session is in an invalid state.
Game is not active, session state is Matching
I want the match to start when there are just two players, but to allow a total of 16 players. So naturally I'm setting maxPlayers = 16 and minPlayers = 2. I'd thought that would automatically start the match once two players were seated, but it's not so.
I've tried to do this once the match has two players, :
if match.participants?.count == 2 {
match.status = GKTurnBasedMatchStatus.open
}
But then I'm told that status is read-only. I can't manually set it.
Now, with a regular GKMatch, I officially start the match by calling:
GKMatchmaker.shared().finishMatchmaking(for: match)
But there doesn't seem to be a similar thingy for GKTurnBasedMatch.
How do I actually get the match started, so I can send an exchange between the two players?
Man, the documentation on GKTurnBasedMatch is sparse.
Here's the thing: you don't actually start a GKTurnBasedMatch explicitly. You only end it explicilty.
During gameplay, you just pass the turn from one active player to another active player. The game technically starts as soon as the first player is seated.
The problem I was having is that I had passed the turn when there was no other active player. So I accidentally told Game Center to assign the turn to an empty seat. So when the error message told me
Game is not active, session state is Matching
It meant that the current seat was empty and still looking for a player to fill it. The game apparently goes out of active status any time the current player's status is Matching (which denotes an empty seat).
Why on earth this would affect sending exchanges, whose whole purpose is to enable communication between any players, no matter who the current player is, is beyond me. But there you go.
The solution: make sure that the current turn is held by an actual person, and is not an empty seat. Then you can send exchanges. Otherwise you can't.

GameplayKit - Confusion about sending messages between components

I'm diving into GameplayKit with Spritekit and from what I gather, you subclass GKEntity and then start adding GKComponents to that entity. The entity would more or less just be a bag of components that fill some function.
The part I'm confused about is communication between components. How do you keep them decoupled. For example, lets say I have a HealthComponent class, and I add that component to a PlayerEntity, and an EnemyEntity. I also have a HealthBarComponent but I only want a health bar to appear above the player. When the player takes damage, that information needs to be updated in the HealthBarComponent.
So how should that information be sent? I see there is a class called GKComponentSystem in the documentation. I'm not 100% on how that should be used.
Another question is.. when the player's health reaches zero he should regenerate while the enemy should stay dead. When the player runs out of lives, the game ends.
The health system for both an enemy and player would be roughly the same, but the events around death would be totally different for each. I'm not following how to use a component system while keeping the unique behavior of each entity.
some pseudo-code would be great
It looks like this framework works a bit differently than others I've seen, in that systems only work on a single type of component rather than on entities with groups of component types.
In the GamePlay-Kit framework you can either loop through and update your entities manually (which in turns updates each entities components) or create a class which inherits from GKComponentSystem. Then when you update the system it updates all the components you have added to it so long as their class type matches the type you initialized the system with
To handle your health bar issue I would say make a HealthBarComponent which, during its update, retrieves the HealthComponent from its owner entity, reads the health value & renders itself every frame. But you only add a HealthBarComponent to your player entity.
You can retrieve the owner entity from a component and vice versa (see GKComponent.entity & GKEntity.components) so you could update your health bar like this:
/*Note: I'm not an ios developer, this is pseudocode*/
HealthBarComponent.update(){
int healthValue = self.entity.components.get(HealthComponent).value;
//Now render your health bar accordingly or hold onto this for later
}
To handle the player death issue I would think your best bet would be to have two different types of Health components (PlayerHealth & EnemyHealth) with two different corresponding systems. That or have a single Health component but 2 separate 'Death' components. Yeah it seems redundant but I'm having a hard time thinking of a better way inside of this framework. In my experience you either spend your time worrying about keeping everything completely decoupled and reusable or you build a game :)
Components can access their entities via the self.entity property. From there, you can query other components to pass data to via the entity componentForClass property:
guard let moveComponent = entity?.componentForClass(MoveComponent.self) else {
fatalError("A MovementComponent's entity must have a MoveComponent")
}
The iOS Games by Tutorials book has an excellent tutorial that covers GameplayKit entities and components.
Notifications are nice for this kind of problem. There's no direct object coupling at all, and it's very extensible if more than one object ends up needing to know (e.g. your health bar component, a higher-level game object that determines game over, maybe even enemy/ally AI to behave differently when health is low).
You could have a notification named "playerHealthChanged" and have your health-bar component and other objects register independently to respond to that event. (You likely need to let the HealthComponent instance know whether it should post this notification, so only the player posts - perhaps it could use isKind(of:) on its entity, or just have a bool field to enable posting, set to true for the player instance).
I usually put all notification name definitions in a single module so any class can access them:
let kPlayerHealthChangedNotification = Notification.Name("playerHealthChanged")
Here's the way the component would post the notification (you could pass an object other than self if desired):
NotificationCenter.default.post(name: kPlayerHealthChangedNotification, object:self)
Then the objects that care about player health changes can register like so in their init code - create a handler function, than add self as an observer for the notification:
#objc func onPlayerHealthChanged(_ notification:Notification) {
// do whatever is needed; notification.object
// has the object from the post
}
// put this in a setup method - init or similar:
NotificationCenter.default.addObserver(self,
selector: #selector(onPlayerHealthChanged(_:)),
name: kPlayerHealthChangedNotification
object:nil)
Here's the documentation: https://developer.apple.com/documentation/foundation/notifications

Picking the "server" in a Game Center match

I am developing a social game that uses Game Center and had a question about informing one of the player's client that it will be used as the "server". At first I thought to send a data packet to the GKPlayer which was returned with the method:
- (void)chooseBestHostingPlayerWithCompletionHandler:(void (^)(GKPlayer *player))completionHandler
The only issue I can see with using this method is if it is possible for another player in a match to pick someone else who their client thinks would be the best "server". At the moment I am assuming since this method is under GKMatch that it already takes into consideration all current players in the game and each application that runs this match should get the same GKPlayer object returned. Is this true?
All players in the match must call chooseBestHostingPlayerWithCompletionHandler, as per the documentation. If that is done, all players receive the same answer: either a specific GKPlayer or nil.

How do I persist graphic data in iPhone/iPad?

I am making an iPad app where the user can create graphic content with images and text. I am storing this in memory in an array of custom UIView subclasses. Each of these view subclasses can have any number of subviews containing images or text.
Now I need to save these in the device. As I explore, there seem to be many ways to do this and would like to know what would be the best for this case.
It looks like you are asking for the architectural design of what will end up being a Drawing app. This means that best it's really dependent on you specific use-cases, and cannot be answered completely unless you provide a quite detailed list of requirement.
But in general, I could try to give you some general tips that will have anyway to be integrated with you own specific nitty-gritty implementation.
This description will make some assumptions regarding the basic use cases that an app like this may need:
The user can create an image using multiple tools to achieve the result. These can be anything, from a brush to a textfield and so on
The information regarding which tools have been used to create the picture and how this tools have influenced the picture current look, can be saved in order to allow the user to later on edit the picture
Said this, the main problem is: how to store your drawing state in order to recover it later?
There are indeed many ways to achieve it, but I believe 2 of them are what would be considered "clean and famous enough".
NSKeyedArchiver
This wouldn't be my favourite (difficult to maintain), but if you have to deal with UIView, it's probably gonna be the quickest.
The NSKeyedArchiver is
.. a concrete subclass of NSCoder, provides a way to encode objects
(and scalar values) into an architecture-independent format that can
be stored in a file.
It implements the Memento design pattern and It's the same pattern described in Pro Objective-C Design Patterns, that, incidentally, presents a case study that has many of the most important use-cases matching yours:
A drawing pad allows scribbling with the user’s finger.
[...]
It allows the user to save a scribble.
It allows the user to open a saved scribble.
[...]
It's an app for having a drawing pad, where you can draw lines with your finger.
Yours looks like a simplified version of this, with images and texts instead of the scribble.
So, what's the pro, in this specific case, of using the NSKeyedArchiver? The UIView already implements the NSCoding protocol, the one needed to archive the object. So, for most of the information you need to store (coordinates, frame size, background color ...), you don't have to do anything but... archiving the object.
For any additional attribute on top of the UIView (for instance: the local path of your image, because archiving an UIImageView is really expensive), you can take a look at this article that explains with proper detail what you have to do in order to take advantage of the NSKeyedArchiver to store your object states.
This all boils down to:
implement the NSCoding protocol for each of the tools your drawing app is gonna provide
keep track of the subviews that the user create (images, text...)
when the user hit "save", loop through them, create an archive, and store them to a sensful path. The first component of the path could be the name of the Drawing, the second one the name of the tool and the third an id for each time the tool has been used. Like
// A mountain image
/<path to you Document dir>/Mountains/Image/1
// A sun
/<path to you Document dir>/Mountains/Image/2
// The text "Mountain is awesome"
/<path to you Document dir>/Mountains/Text/1
Then of course you will have to save the list of Drawing names somewhere, either in a plist file or in a NSUserDefault, so to be able to show them to the user in case they want to restore them for editing.
Core data
This is probably the cleanest and more maintainable way to store you object states, but is gonna be a bit tough and cumbersome, in particular if it's the first time you use core data. I'm not gonna dig into Core Data, but I can give you some guidelines of the whole procedure. Basically:
You create a db schema that represents each of the tools your are gonna let the user use. Like: a table for Image, a table for Text and so on
On each table you put the attributes you need to remember (location, text color for "Text", image URL for "Image" and so on)
You create a table for the Drawing that the user create, with a 1-many relationship to the tool tables. This relations represents the object shown in the drawing.
Initialize you drawing canvas and each component according to what's stored in the db
Every time the user hit "save", create or update the proper db tables in order to reflect the current drawing configuration in the storage.
One of the advantages of this approach is that, if one day you want to change a tool component property or add new ones, you can take advantage of schema migrations in order to deliver backward compatibilities with new updates. So the users will still be able to use their old drawings.
And so on and so forth...
These are two of the zilions of possibilities. You could use also use:
NSUSerDefault to store states, that I suggest to avoid. It's gonna be really hard to maintain
Mix of the two aforementioned techniques
If you plan to deliver >= iOS6 only support, you can check this
etc
The two I described are just what I feel are the usual and most discussed way of doing this. You find them in books, tutorials and they let you quite a lot of flexibility for anything you have to do.
If you need more explanatory links, let me know.
As I mentioned in a comment, you might want to look into iOS's state preservation API's. However, if you want to build your own system to do this it'd be pretty simple using some clever categories and dictionaries. Then you can serialize/deserialize your dictionaries using NSKeyedArchiver and NSKeyedUnarchiver.
eg:
#interface UIButton (MyAppCategory)
- (NSDictionary *)viewProperties;
- (void)configureFromProperties: (NSDictionary *) properties;
#end
#implementation UIButton (MyAppCategory)
- (NSDictionary *)viewProperties {
return #{ #"class" : NSStringFromClass([self class]),
#"frame" : [NSValue valueWithRect:self.frame],
#"titleLabelText" : self.titleLabel.text,
// etc...
};
}
- (void)configureFromProperties: (NSDictionary *) properties {
NSValue * value = properties[#"frame"];
if ([value isKindOfClass:[NSValue class]]) {
self.frame = value.rectValue;
}
NSSString * titleLabelText = properties[#"titleLabelText"];
if ([titleLabelText isKindOfClass:[NSString class]]) {
self.titleLabel.text = titleLabelText;
}
}
#end
// replicate the above pattern for other view objects you need to support
#implementation MyViewFactory
- (UIView)recreateViewFromProperties: (NSDictionary *) properties {
NSString * className = properties[#"class"];
if ([className isKindOfClass:[NSString class]]) {
Class viewClass = NSClassFromString(className);
id viewObject = [[viewClass alloc] init];
if ([viewObject respondsToSelector:#selector(configureFromProperties:)]]) {
[viewObject performSelector:#selector(configureFromProperties:) withObject:properties];
return viewObject;
}
}
return nil;
}
// exercise for the reader: iterate your views and use the viewProperties: method to collect your views' configuration info...
#end
If you want to allow for future session editing and loading etc. I would suggest designing a data structure and create a core data model out of it.
Some structure holding the session metadata e.g. sessionID, creationDate, dictionary of key:imageName value:imageFrame (CGRect wrapped in NSValue, use setObjectForKey).
Loading images for the session would work by calling the keys into an array using e.g.[sessionImageDictionary allKeys], iterating through the keys and asynchronously (NSOperationQueue with maxConcurrentOperationCount) loading the image at some Macro path to e.g. the library directory, and appending the key, which is the imageName.
In the same iteration you can set its frame by calling [sessionImageDictionary valueForKey:[arrayOfKeys objectAtIndex:currentIteration]; Converting the previously stored NSValue back to CGRect.
The datastructure all depends on the amount of features you want, but the good thing is it allows for expansion and with core data as the backing store, you could do things like sync between devices, enable multiple sessions for loading and saving like a "My projects" feature. It will help if lets say the user builds up a library of images (all stored in your apps library directory) and then the user uses the same image in the same session or in multiple sessions, only one copy of the image needs to exist, with zero duplicate write outs to disk and the core data object will have the filename stored in the session.
The most important part would be building a correct Core-Data model and writing an extractor that can accept these custom subclasses, strip out the data to create, populate and save an NSManagedObject to the persistent store.
Your best option is to use UIDocument with NSFileWrapper folder. Then you can store all your files in one folder which is saved automatically when the contents change.
Refer to:http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/DocumentBasedAppPGiOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011149-CH1-SW1

Best alternative for 'long term' storing model-view-controller data in Objective-C/ios

I am planning on creating my app in a 'Model-View-Controller'(MVC)-style, and in the end, for me at least, this means that all data is stored in the controller-class. Let's say I have a class Player, and the player has several objects of class Weapons or Equipment or whatever. The initialization of Controller* stores the player(s), so if I can store/save only the Controller-object over time, even if the app or the device restarts, that would be nice. I did this in Java one, I put in Serialization = 100L;(or something like it) in the top of the file of every object that would be included when saving the Controller-object, and it worked perfectly. Is this possible in ios/cocoa-touch/objective-c?
I have read and used Core Data (not very much), but that is just a database-table, sql?, which would have me extract every piece of information of every object?
For instance, if the object Player* has a member NSString *name;, I would have to save the actual string in Core Data, instead of saving the object of the player? Like, varchar.
If there is any way to store an entire custom object on the device for further use, I would very much like to know what it's called, and where I can read about it/tutorials.
Read up on the NSCoding protocol. You can make your object complient to it, then serialized it and save it to a file. Later you can restore it to the same state by using a decoder. For sure some other posts that cover this topic on SO.

Resources