Tuesday, January 24, 2012

UIStoryboard Power Drill


I've read some good reasons why Storyboards are not ready for prime time. Some of the articles like Jonathan at toxic software.com simply help me find out that I wasn't doing something wrong, it just wasn't meant for that. But there are some benefits from using a storyboard that I wanted to keep, so I got adamant about finding workarounds. Here's one:




Drill down UITableViews - Standard



1. Provided you've started with a subclassed UITableViewController, or a TableView in a UIViewController that is already the root relationship from a UINavigationController.

2. You link the push outlet from the cell prototype back to the table view controller.
This will automatically provide the control to push-in another of the same ViewController when a cell is clicked.


3. Give the loopback segue an identifier string in the attributes inspector.

4. Instead of implementing tableView:didSelectRowAtIndexPath: in the subclassed viewcontroller you will use
prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

This method will be passed the segue object that is the "tableLoopBack" the sender object for this segue type will be the UITableViewCell.

5. Prepare what data to send to the next instance the the table.


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    
    NSArray* myObjectArray;
    UITableView* myTable;
    
    if ([segue.identifier isEqualToString:@"tableLoopBack"]) {
        SubClassedTable* nextSct = segue.destinationViewController;
        UITableViewCell* myCell = (UITableViewCell*)sender;
        NSIndexPath* idx = [myTable indexPathForCell:myCell];
        nextSct.myObject = [self.myObjectArray objectAtIndex:idx.row];
    }
}

6. In the TableDataSourceDelegate have it decide how to propagate your tables data array based now on the defined and set value of "myObject". This is very different for everyones data types.

An example, using Core Data and have say AMusicLib > Artists > Albums > Songs > Info

The myObject would ideally be of type NSManagedObject* so that you could be any of the above. When the dataDelegates are called like tableview:numberOfRowsInSection: you would want to implement something like the following to handle the many layers.

- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if ([self.myObject isKindOfClass:[Music class]]) {
        Music* myMusicLib = self.myObject;
        return [myMusicLib.artists count];
    } else if ([self.myObject isKindOfClass:[Artist class]]) {
        Artist* myArtist = self.myObject;
        return [myArtist.albums count];
    } else if ([self.myObject isKindOfClass:[Album class]]) {
        Album* myAlbum = self.myObject;
        return [myAlbum.songs count];
    }
}

Now in the implementation of the tableview:cellForRowAtIndexPath: use a similar kindOf conditional so that the cells properties can feed from the difference of the objects at each layer.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"musicCell";
    UITableViewCell* newCell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if ([self.myObject isKindOfClass:[Music class]]) {
        Music* myMusicLib = self.myObject;
        Artist* aArtist = [[myMusicLib.artists allObjects] objectAtIndex:indexPath.row];
        newCell.textLabel.text = aArtist.name;
    } else if ([self.myObject isKindOfClass:[Artist class]]) {
        Artist* myArtist = self.myObject;
        Album* aAlbum = [[myArtist.albums allObjects] objectAtIndex:indexPath.row];
        newCell.textLabel.text = aAlbum.title;
        newCell.imageView.image = [UIImage imageNamed:aAlbum.art];
    } else if ([self.myObject isKindOfClass:[Album class]]) {
        Album* myAlbum = self.myObject;
        Song* aSong = [[myAlbum.songs allObjects] objectAtIndex:indexPath.row];
        newCell.textLabel.text = aSong.name;
        NSDate* aTime = [NSDate dateWithTimeIntervalSince1970:[aSong.length intValue]];
        NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
        [dateFormatter setDateFormat:@"m:ss"];
        newCell.detailTextLabel.text = [dateFormatter stringFromDate:aTime];
    }
    return newCell;
}
And finally because earlier we used a property self.myObjectArray, this property could have been a consistent type simple array, but because I walked you through handing down different managedObjects this myObjectArray getter would also need to return an NSArray from the different managedObject's intended NSSet. Same as just above, just return [myArtist.albums allObjects] or [myAlbum.songs allSongs] and so on for the prepareForSegue can hand off the right object to the next controller.


Drill Down TableViews - Options

The above is simple but can be limiting. The segue always pushes down to another of the same table view. Now you want options, lets go this why, or that.

1. Don't use the push outlet on the cell prototype. This is whats limiting the control of where we go.

2. Do Implement the tableView:didSelectRowAtIndexPath: this is where we will decide what segue we take, Or none.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    if (!sec) {
        Section* aSect = [[self.sectionFetchedController fetchedObjects] objectAtIndex:indexPath.row];
        [self performSegueWithIdentifier:@"tableLoopBack" sender:aSect];
    } else {
        Clip* aClip = [self.clipFetchedController objectAtIndexPath:indexPath];
        [[VideoPlayerController Player] setMoviePlayerForClip:aClip];
    }
}

What we are effectively doing here is getting the option to loop into another table, or stay and do something else like load a video in a modal MPMoviePlayerController. Or even call another segue that goes to detail view.

3. Continue to make use of the prepareForSegue method

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"tableLoopBack"]) {
        VideoListController* vlc = segue.destinationViewController;
        Section* mySec = (Section*)sender;
        vlc.title = mySec.title;
        vlc.sec = mySec;
    }
}

This is so data is still passed to the new instance of this controller for drill down purposes, even if not all cells drill.

4. And now the hack, if in step 1 we do not link the cell to push to the this viewController, we still need to define a segue and name it "tableLoopBack".
Add a UIBarButtonItem to the viewController and set the push outlet on it to loopback, identify that new segue connection as "tableLoopBack" and you have access to it when calling performSegue.

5. Add another UIBarButtonItem, and link it to another ViewController like one that layout details level. Identify that segue as another name like "detailSegue" and performSegue from didSelectRowAtIndexPath: where you can pass down the object full of details right into the sender, which will also be handed through the prepareForSegue, so make sure you hand it down again to the desired property of the new destinationViewController.


Helpful? Vote me up: Stackoverflow.com

Sunday, December 4, 2011

Cut the fat, NSData diet

Fat XML
I'm a long time user of the XML format for transferring data between client and host. It's human readable, can be edited by hand easily in most cases, and it conveys objects and nesting well. So in the past I was often designing a object scheme and custom parsing loops that solved the issues that faced constantly updating applications and site I've been in charge of through out my flash career. The problem I was always concerned with when designing the scheme is how much data was just being taken up by open tags, close tags, and parameter defines. This became exceptionally a problem when working for the company responsible for "MMO crack". I came on board to find that they where converting C objects out to XML. Now I wasn't programming in C yet myself and had lacking experience there, all my objects where far more dynamic in action script. But when I was tasked to parse open some game data I was shocked to see my work be brought to it's knees when getting the users character roster and sub inventory arrays that were compiled into XML Data and downloaded to the Flash client. This may seem to make sense you need to know this information in order to play that game. But for a new character that had no team members, and starter inventory, the XML data surely didn't need to be 2-3mb to tell me I'm a noob. It turned out that when the  object structures are being defined, the play character with some properties, has a max inventory lets say 30 items, and a roster of teammates with a max of say 30, each with their own properties and inventory of 30. So in memory this struct was created with these arrays of array defined. And even though the inventory had maybe two items, and the team roster empty, the space for this storage had been allocated. This left the company's choice XML writer with the task of creating a node tree for every allocation, even if the value was null. Not only was the XML nodes verbose, the property attributes fully spelled out, but every download came in like a template worksheet to be filled in and a later time.

Average JSON
I had always made it a conscious effort to slim down the data that was being sent back and forth in applications I designed, even though this was often not my choice when working at large companies. JSON came along in the midst of all this work and I found it a great way to slim down a lot of the parseable data. A few iOS projects that I started on in 08' I was doing a lot of tracking multiplayer turns moves on a PHP/SQL backend. I was comfortable using JSON and NSDictionaries to convert the data because I could understand how it was being handled on both places. But I did alway prefer to have my classes with their instance variables. I felt like KVO in NSDictionaries was just a long way to get at values in code and made for more lines. I also want to avoid waiting and loading screens, so I separated many requests into small object calls, and take it even further I got on a kick where I would make property names only one or two characters long. If the property name was 'int character_id = 500;' I would hold the value in a temporary NSDictionary to encode the property to {c:500}. Every little bit helps I figure.

Thin Binary Data
This year I have been getting more ambitious about using some Multiplayer GameCenter features, wether it be live games, or turn based games. Now the out of the box features of encoding NSDictionaries to a compatible NSData binary I think leave you somewhere in-between the first to parts of this post. Where you can define small objects maps, but this its coded into a XML like string before its coded to binary. Game data needs to be condense to make a quick round trip in peer to peer or to fit in a maximum size allowed for turn based storage. I've come full circle now to better understand C structs and have come to some pros and cons about using them to transmit game data. What really helped me prepare for the use of C structs as NSData was the GKTank sample. Now if your not entirely sure whats going on in this sample like I once was, then hopefully I can help you understand it.
First you must be comfortable using C structs to define your properties. If you are very used to the way NSObjects and Class in Obj-C act for you when you retain and release, you have to be aware of when you assign a struct you are making copies and may be dealing less in pointers. Lets talk about GKTank tankInfo:


typedef struct {
CGPoint tankPreviousPosition;
CGPoint tankPosition;
float tankRotation;
float tankDirection;
} tankInfo;


This struct has four properties, but two are also structs. CGPoint is a C struct that just contains two sub float values. And understanding how this all really boils down in memory helps understand how much data your saving or sending. A float is 4 bytes, this example of struct ultimately consists of 6 floats. 4*6 = 24 bytes to send this struct. Just to compare that point here, the resources out there tell you normally would want to run your NSDictionary through a NSKeyedArchiver which can write to a NSData. An example of this:


    NSDictionary* tank = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithFloat:32.f], @"oldx",
                         [NSNumber numberWithFloat:32.f], @"oldy",
                         [NSNumber numberWithFloat:32.f], @"newx",
                         [NSNumber numberWithFloat:32.f], @"newy",
                         [NSNumber numberWithFloat:32.f], @"rot",
                         [NSNumber numberWithFloat:32.f], @"dir", nil];
    NSMutableData *data = [[NSMutableData alloc]init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
    [archiver encodeObject:tank forKey:@"theTank"];
    [archiver finishEncoding];


Same six float values, for a total of 436 bytes to hold the values that map the key values. So how do we get the struct into a NSData object so the rest of the Objective-C framework can handle it. NSData has a handy way of making a copy of the binary data of the structure provided we can tell NSData how many bytes the object is.


tankInfo mytank = tankInfo();
    mytank.tankPreviousPosition = CGPointMake(32, 32);
    mytank.tankPosition = CGPointMake(32, 32);
    mytank.tankRotation = 32;
    mytank.tankDirection = 32;
    NSData *data = [NSData dataWithBytes:&mytank length:sizeof(tankInfo)];
[myGKSession sendData:data toPeers:peers];



This method Data with Bytes lets us send in the pointer argument of our struct which is giving it the first byte to start from. The length lets the NSData know how far in memory to copy, so we need to know how many bytes our struct is. We did the math earlier and could enter 24 for the length. But C offers a function sizeof() which looks up the definition table of the struct type and returns it for us. Whats great about a struct is when allocated it's properties are lined up in memory so that each of the 6 floats are stacked back to back. How it is aligned is not saved in the memory block with the 6 values but the structs definition itself acts as a parser/de-parser for the memory. Even if the floats are back to back, the definition is the road map to know that the first and second floats are part of the first property, the third and forth floats are the second property, etc. When you get this NSData on the other side it needs to be converted back into a structure so it's easy to access again.


- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer ... {
    tankInfo recievedTank;
    if ([data length]) [data getBytes:&recievedTank length:sizeof(tankInfo)];
}

When we create the instance of tankInfo the memory for 24 bytes has been made available but like is random data or set to null. Like before this now copies the bytes into the pointer location which has already been allocated the line before and the data that just came in can now be accessed and changes by accessing the struct properties and all we sent over the wire was 24 bytes (give or take any gamecenter overhead).

With my mind well wrapped around this C struct concept I can wisely get a lot of game data send over the wire quickly, or stored on Apple servers when making a turn based game. Apple has set a turn based miscellaneous data limit to 4KB. Now for some these seem unreasonable, but those are often the ones I see trying to KeyArchive encode a big Dictionary and it makes sense that they are hitting the limit quick. I've headed into planning my game data storage and for the most part have not come close to needing to worry about how much binary data I need to get my games conveyed. up to 4 players, up to 70 turns, in under 880bytes.

The Con
I know I'm picking a threads here, but now I've come back to the issue I once though obsessive when working for the MMO. I'd love to hear input there from any others that deal in structure binary. I've designed my game data struct to handle these 4 players and the 70 turns possible to complete a game. To define this maximum possible amount of turns I an array of turn structs. This again cause the data to be allocated, and empty early on. An average game will be complete in 60 turns, and as the turns progress the bytes in the turns array are being written to something other Null, but in order to know the size of a matchData struct the turns need to be defined with 70 elements. Only fortunately is this set of struct small and the whole combo comes to 880bytes. But thats 880 on turn 1, 2 .. 54 etc. If later I take on any larger game storages I may come back to talk about how to sub parse the first few bytes in the NSData to help predefine the amount of elements that should be expected, making the struct more variable.


struct matchData {
    unsigned int seed;
    unsigned char playerCount;
    unsigned char turnIndex;
    playerData players[4];
    turnData turns[70];
    
    matchData() : turnIndex(0), playerCount(2) {}
};


Sunday, November 20, 2011

Level building nostalgia

Something about the idea of creating levels and actually creating levels is hard to follow through on. I look forward to trying out some puzzle making when Valve releases the level editor for Portal. Most recently I last spent a bit of my time in the Little Big Planet level creator and had some fun with it's physics system. For me this all steams back to a time ago that I was playing my share of Doom over the local BBS and using ID Software's tools to make more arenas to share with my friends. Then I think even before that, one of the first computers I was lucky to have as a grade school kid, I had a copy of a game called Jetpack. This was a solid game with some very challenging levels and a lot of dying. I spent so much time just mastering and over thinking the creation of one level I couldn't imagine having to create dozens, or a hundred.

I started this time with some good old white board drawings of levels that will help the player progress and get use to the control I want to propose in this game. In addition to my existing placement tools I have in Flash, I'm was going to employ the use of Tiled to handle the nature of tile block maps. This was a great fast start for getting the prototype made and the feel for the game. I must have played that first level design 100 times getting the input right. Then it came time to build a couple more, and quickly I find myself running out of ideas. I need to have my friends and family help, even if they just messed around with the block to create some forms I wouldn't have thought up. I then remember I'll have to get these few people Flash, and Tiled, and teach them how to use the two apps, and no thats not going to work.

Back when I was making Petunk I came to a point where I was going to make a levels in XML code, in game builder, or just some type of layout sheet. Saving myself time I came up with the layout classes in Flash, but at the cost of not making that part of the game. It's got me to wonder how much of the game is the desire to create levels. I wish I had someones numbers here, it would help me decide if thats a sales perk. The developer from jetpack claims it's one of the most popular features, Little Big Planet I'm sure is thriving on user created content. Even Gary from Bouncy Ball Games may have a lot of users that prefer to make levels over playing them. So this time around I'm leaning toward making a easy to use level editor and see what user created content can do for a game. Partially because I want to see if a larger fan following would get onboard earlier to submit levels for the games release like JoyrideLabs.de is doing. Or if serving up downloadable levels helps retain users long after the packaged levels get completed. At this point I'm a long way off but I will try to make a series of this experiment as I hit the milestones of my jetpack version of an iOS game.

Sunday, November 6, 2011

Let's Be Friends. Core Data Relationships

With any good database tutorial, you will learn how to make employees, departments, and managers. Then how to reference the employees to a department and be overseen by which manager. Instead I'm going to talk about a data structure that I made to help me with my next project to help me handle game scenes, characters, and their animations.

The first time I heard of Core Data, I thought I wouldn't need to learn how to use it, I already know how to access SQL and can make use of the SQLite C library. And for a few of my early iOS projects thats what I used. I made lots of classes that handled my common queries and they grew fast as my need to cross reference tables got more complex. This all changed once I watched Dr. Brad Larson's Madison Area Tech course on Core Data. Now that I use Core Data, I find myself being able to do more with table relationships that I would have thought up had I handled the queries myself.

What I want to accomplish is to have multiple Scenes which will be home to Sprites and character Sprites with defined animations. In the past I usually approach this type of design by having a top tier object just contain a list of the child items, the child items would have their settings for position in the scene. I designed this first table relationship to contain multiple scenes, with multiple assets(sprites). For simple still sprites this will still be my basic foundation. I'm only containing the sprite file name reference and the position. The index field is to retain the z-layer of the sprite in the scene. More recently Core Data now offers to track many-to-one collections in a NSOrderedSet, but I've discovered are a few quarks to this still, so I still track the z-depth manually to control the sprite layers. When accessing the 'assets' set I get the ordered array again using:

NSArray* sorters = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]];
NSArray* assets = [m_scene.assets sortedArrayUsingDescriptors:sorters];

Each asset would not be shared between different scenes. If I did reference a asset in two different scenes with different position they would overwrite the position field. My actor charters though are going to have  their own inner animation relationships. I'm starting with an actor that will have a name and a set of animations. They will likely include things like idle, run, jump, climb, fix, talk. Each animation then has a series of frame references to the file names used to comprise the animation, and the rate in which to play the frames. Then for sake of having a starting point I have another single animation relationship to decide which animation will be default when the actor is loaded. In most cases I set the animation field to equal the 'idle' named animation. But it is just a reference to the Animation object so the animation name is not important, and more importantly not defined as a string field. Again I use a index field in the Frame object to control the order of the frames. Because Animation.frames is stored as a NSSet they may come back out of desired order and need to be sorted like above.

A quick note on the power and feature of Core Data that I like the most, is inverse relationship mapping. Thats not to say it's a unique feature, it's a cross reference. But having the Core Data frame work set the relationship field for you in both records by just simply setting the property of one object, just makes storing the relationships all the more easier. Above I have the Animation object that has a relationship set of frames. In the Frame I have a relationship inverse to animation. So during the loop in my program to set these frames, all I need to do is allocate a new Frame, set the name, index, and simply set the animation to equal the Animation instance I created before entering the loop. Instead of requiring me to also access the Animation and add the frame to the NSSet, I'm getting that for free by the Core Data framework and the inverse connection I setup in the model.

Animation* ani = [animationRay objectAtIndex:selectedIndex];
for (NSString* file in fileNames) {
    NSEntityDescription* frmEntity = [NSEntityDescription entityForName:@"Frame" inManagedObjectContext:context];
    Frame* frameIn = (Frame*)[[NSManagedObject alloc] initWithEntity:frmEntity insertIntoManagedObjectContext:context];
    frameIn.name = file;
    frameIn.index = [NSNumber numberWithInt:[ani.frames count]];            
    frameIn.animation = ani;
}

Now with Actors setup with many animations, and many frames, I want to add they actors to many scenes. The reason is so I can have a character that is standing out by the boat by the docks, and/or in the kitchen making a sandvich. The things I want to avoid are the position of actor being overwritten by the different locations he will be at in those scenes, and from duplicating the relationship instances I just built for the one actor and his animation tree. I also need the actor to be added to the Scenes' assets set so the Actor can have a index(z-layer) in that scene, which will be a different index than another scene he may be in. If you watched Dr.Larson yet or know about Core Data you'll hear about the Abstract/Parent Entity setting. Using a new entity definition as a parent entity, SceneActor acts like a subclass of SceneAsset and then I can add a extra property to do a non-inversed relationship to a Actor. At this point I also added a non-inverse player relationship to the Scene so that I know which Actor object is defined as the player and to forward the user inputs to when the scene in created. Defining the parent entity and the model object classes that go along with the design this adds one last convenience when looping through the assets set to generate the scene. I can test the class type from the set and let it generate a actor sprite or base class sprite.

[assets enumerateObjectsUsingBlock:^(SceneAsset* obj, NSUInteger idx, BOOL *stop) {        

    NSString* sheetName = [[obj.sheet.file pathComponents] lastObject];
    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:sheetName];
    
    CCSprite* aSprite;
    if ([obj class] == [SceneActor class]) {
        SceneActor* sa = (SceneActor*)obj;
        TTActor* actor = [[TTActor alloc] init:sa];
        aSprite = actor;
    } else {
        aSprite = [CCSprite spriteWithSpriteFrameName:obj.file];
    }
}];

Sunday, October 23, 2011

Making Tools for Making Games

I've been creating apps and games for sometime, and each time I seem to make a second app/tool to help in the creation. Defining layouts, levels, settings, data, designs. I could define all this pre-existing data in code, and then muddle through it when I need to change or upgrade it. But often I find myself working with others that haven't devoted themselves to understanding cryptic programing languages and scripts. This prompts them to send me change request that seam so minor I prioritize them low and let them stack up until major code changes have been finished. I try to encourage the co-workers to decipher my xml,json,or sqlite files to change the simple but critical design features themselves, but this just brews difficulties in a project's lifecycle time and again. Even on an off day I'd rather not plot out x and y points in my head only to find that I needed to subtract other offsets and half widths when defining the property. Maybe it's all those years I spend developing in Flash and the authoring stage space that allowed me to place and go.
When I first started using Xcode and InterfaceBuilder, I understood it fairly well because it would help me place and then code. But unlike Flash, it did not strike me as the type of layout app that I should be defining each and every lesson, level, or scene. I combined my experiences with each of these IDEs and leveraged laying out in Flash, while bringing the raw values to the iOS. My co-workers also know their way around the Flash stage so less learning curve there too.

I've used this approach when developing a e-learning engine,  and I'm refining it on my next point-and-click adventure game, but today I will cover how it helped making levels in Petunk.
ActionScript has grown since it limited commands from '98. Today you can access just about every property, which is nice for even breaking down per frame properties. For now I'm only going to go about moving screen elements to get some initial definitions. What I start with is making an ActionScript class that will write out the movieclip properties I'm interested in.


public class Obstacle extends MovieClip {

public function Obstacle() {}

public function getDef():String {
return '{'+
'type = "'+getQualifiedClassName(this)+'";'+
'cord = "'+int(this.x) +','+int(480-this.y)+','+int(this.rotation)+'";'+
'size = "'+this.scaleX+','+this.scaleY+'";'+
'},';
}
}


The design behind this class gets the stage properties I'm interested in so they can be bundled in an iOS app. Each instance of this Obstacle class will define the settings into a simple format that un-translate later in Objective-c. The getQualifiedClassName() is kinda a trick with the library, it will return the subclass name. In this case I define the subclass by the item or image I place in the movieclip at the library level.
This class returns: {type = "goal";cord = "160,417,0";size = "1,1";}

The main timeline needs to aggregate all these instances that laying around the stage, so the movieclip of the FLA file itself needs to be subclassed as well. The main purpose here to let the frame have time to draw the clips that I've laid out, and then read the values back and write in a plist friendly format so I don't have to change the values by hand and make the mistake of missing a bracket or semicolon. The LevelPlist.as file looks like this:

public class LevelPlist extends MovieClip {

public function LevelPlist() {
//Clips aren't on stage yet
this.addEventListener(Event.EXIT_FRAME, onFrame);
}

public function onFrame(evt:Event) {
   //Remove event so this does not call repeatedly
this.removeEventListener(Event.EXIT_FRAME, onFrame);
   var count:int = this.numChildren;
trace("{");
trace("levelId = "+this.currentFrame+";");
trace("obstacles = (");
for(var i:int = 0; i < count; ++i) {
var obj:Obstacle = this.getChildAt(i) as Obstacle;
obj.getDef());
}
trace(");");
trace("}");
}
}
}

With just a little bit of script I can now drag new clips out of the library, place them on stage, move them around on stage, use Flash interface to scale, rotate, and align. Then just simply run a "Test Movie" to cause the script to run and trace string data to the output window. Cut, Paste, Save Plist file and the changes are ready to be bundled in the next build of the Xcode project.

Because the setup of a level is being disconnected from the compiled code, there needs to be a simple way to map the obstacle names back to types that can be interpreted into physics objects. I have a Factory class help me with this, so as I load a level plist file I then loop through the array of obstacles and have a 'else if' stack translate the names to a enum type that helps in switch statements later in the the game loop.


+ (displayType)StringToType:(NSString*)aStr {
    if ([aStr isEqualToString:@"ball"]) return display_ball;
else if ([aStr isEqualToString:@"wall00"]) return obst_wall00;
else if ([aStr isEqualToString:@"wall01"]) return obst_wall01;
else if ([aStr isEqualToString:@"angle"]) return obst_angle;
else if ([aStr isEqualToString:@"bumper"]) return obst_bumper;
    else if ([aStr isEqualToString:@"goal"]) return sens_goal;
return display_null;
}

Then because earlier I saved the X, Y and Rotation into a comma delimited string I use the nsstring separator to make a quick array of the 3 float values I need to use to define position. This next method gives me back my PhysicBase objects which have references to both Box2d objects and the Cocos2d objects.


+ (PhysicBase*)CreateObject:(NSDictionary*)objData inMapSpace:(CGSize)mapsize {
displayType dType = [ObjectFactory StringToType:[objData objectForKey:@"type"]];
    physicType pType = [ObjectFactory DisplayToPhysicType:dType];
PhysicBase* base = [[PhysicBase alloc] initWithDisplay:dType];
    base->m_basetype = pType;
    NSArray* values = [[objData objectForKey:@"cord"] componentsSeparatedByString:@","];
base.position = ccp([[values objectAtIndex:0] floatValue], [[values objectAtIndex:1] floatValue]);
base.rotation = [[values objectAtIndex:2] floatValue];
[base autorelease];
return base;
}


So what does this really solve?
Well I mainly found myself editing plist files and changing the x or y value, only to find that I miss calculated or want to nudge it a bit more, and then a bit more. Changing a value like x = 124 was not a instance change and gave me know way of "Eye balling it" for spacing until I compiled and ran the project. Seeing the placement drawn as you move and rotate something is clearer to see than just numbers.
Why not make a level builder on the iPhone?
As nice of a framework UIKit and Cocos2d are they do not have out of the box, move, scale, and rotate tools, I could spend time making them. Then there is the confined workspace of the 320x480. Software like Photoshop and Flash are great for comping out designs. (And if I could export propertied from Photoshop, I would.) So why not levels, Flash already has move, scale, and rotate.  Has a stage area and extra workspace area this zoom controls already hooked up.


Sunday, September 25, 2011

Use GL, Thats a Wrap!


Let GL Repeat Itself
As I'm starting to make improvements to Petunk, I've started to test the game play for maps larger than the screen bounds. And with larger maps means tracking the active ball on screen, and scrolling the scene to follow. The floor texture (green paper) would then need to be expanded past the screen bounds so that the camera seems to scroll along as new obstacles slide into view.
Often this style of scrolling ground texture I see done with tiles. And I like tiles too, but I did not want this green paper to looked tiled so I need another option. With Petunk I made the switch to using Cocos2d and for a lot of things its great. But earlier in my studies of GL I knew of a few texture settings that included texture pixel wrapping. The idea if your not familiar is that when a texture is used it can, clamp (stretch the last edge pixel), or repeat (loop back to the other side and continue mapping). I didn't find the settings for repeating textures in Cocos2d (if they are there) so I messed around with some classes until I could change the settings I was looking for.

But First Lets Run With Scissors
I needed to make a texture worth repeating, so your going to also get to learn a little Photoshop here.
I started with a rough edge piece of green craft paper (as per the style of Petunk) and want to get a 512x512 wrapping texture from it. The same could be done for many designs, and Photoshop make it easy to make sure your edge is ready to loop.
I duplicated the layer and over lapped the paper so it looked like paper on paper. Picked an ideal area of the file to be my target 512x512 by cropping it to that selection size. The crop alone will not produce a texture with a matching edge, but the file now sliced to 512x512 the Filter>Other>Offset.. tool becomes helpful. Using the offset dialog I set both a horizontal and vertical offset to give me enough room from the edge and selected to have it wrap around.
This will map the image to the offset and make the edges I didn't see before stand out so I can paint, blend, blur, and stamp until the seams go away. (Not the paper layer seam, that I want, but I did need to adjust it so it aligns back up with it self). The result is I get a wrappable texture, that GL can repeat on its triangles.

Thank You Open Source
With the repeatable texture we can now place it in the scene as a CCSprite and even butt a bunch up next to each other, but then thats just how tiling works. Instead we need to get access to the UVs of a sprite so we can modify them to go past 1 or more. The thing with UVs is each time the value is greater than a whole number it represents another wrap in the texture. Great for even smaller textures files on a large area of triangles. The get the effect of a sprite the wraps rather than moves I made a new class extension of a CCSprite.


#import "cocos2d.h"

@interface CCRepeatingSprite : CCSprite {

}

@end

No real meat here because this sets up my plan to override a few methods. When the sprite is created I need different texture settings established, I need a different triangle size than that of the image file it's going to reference, and I need it not to move in the scene.

#import "CCRepeatingSprite.h"

@implementation CCRepeatingSprite

+ (id)spriteWithFile:(NSString *)filename {
CCRepeatingSprite* sprite = [[[self alloc] initWithFile:filename] autorelease];
sprite.anchorPoint = CGPointZero;
ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
[sprite.texture setTexParameters:&params];
return sprite;
}

Overriding the spriteWithFile gives me the first best opportunity to change the texture parameters which normally get set to clamp to edge. In this class method I've reset the parameters for this texture reference to instead repeat. Next it gets tricky because we need to mess with the 3D verticies directly.

- (void)setContentSize:(CGSize)size {

[super setContentSize:size];

quad_.bl.vertices = (ccVertex3F) { 0, 0, 0 };

quad_.br.vertices = (ccVertex3F) { size.width, 0, 0 };
quad_.tl.vertices = (ccVertex3F) { 0, size.height, 0 };
quad_.tr.vertices = (ccVertex3F) { size.width, size.height, 0 };
}

Along with other various important property settings the content size of a CCSprite defines where the vertices of the Gl triangle set gets positioned in the view. The verts are 3d points, but in most cases for cocos2d the 3rd value is always 0 so as to make it not so much 3rd dimensional. I've adjusted the points of this Square triangle set to position it's right and top bounds to the size I send in. (I intended this for background wrapping so I did not properly change the left and bottom points so it could optionally start somewhere other than the bottom left coordinate). In my case I set the aSprite.ContentSize = ccp(320,480) which means it will place the vertices at the 4 corners of the iPhone screen.

Playing with fire... I mean UVs


- (void)setPosition:(CGPoint)pos {

CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_;

if(!tex) return;
    
    static float left = 0.f;
static float right = contentSize_.width / tex.pixelsWide;
static float top = contentSize_.height / tex.pixelsHigh;
static float bottom = 0.f;
CGPoint texOffset = ccp(pos.x / contentSize_.width, pos.y / contentSize_.height);
quad_.bl.texCoords.u = left + texOffset.x;
quad_.bl.texCoords.v = bottom + texOffset.y;
quad_.br.texCoords.u = right + texOffset.x;
quad_.br.texCoords.v = bottom + texOffset.y;
quad_.tl.texCoords.u = left + texOffset.x;
quad_.tl.texCoords.v = top + texOffset.y;
quad_.tr.texCoords.u = right + texOffset.x;
quad_.tr.texCoords.v = top + texOffset.y;
}

Now we go even deeper into the properties behind the 2d goodness that is cocos2d. We are going to change the UV values of each of the 4 vertices so they map to a different place in the texture file. If you don't already know, a texture file is mapped by a precent of its size in memory, which we can perceive as a 0 through 1 value (0% - 100%)
First thing here is I get a reference to the texture this instance is using because there is valuable data in the texture class. Then I set a few properties that define the percent of the texture is used within the content size so as not to get any stretching or shrinking from the texture when its drawn on to triangle smaller or bigger than it. These static left,right,top,bottom will be my baseline or origin of the textures UVs. Next I take in to account the value passed in by the position method and calculate and UV offset. Why a UV offset is because I going to move UV points and not the vertices position in this override. I get the difference in pixels converted to percentage so I can add it to my static baselines and change each pair of UVs of the 4 vert points in the CCSprite so the sprite is not moving, but where it draws from memory moves. Because we set the texture parameters to gl_repeat in the case the U or V value overtakes 100% or the whole number of 1, say 1.2 then the GL maps the memory back around to 20% or 0.2 of the texture file and no seam is ever seen due to our awesome Photoshoping work.


You Want Some More
This week Mystery Coconut made a great post about using core graphics to do parallax scrolling. And if your a Cocos2d fan you may already be using Cocos2d parallax layering class. Well this mod can fit right in because it's a CCSprite and it takes a position change. Lets say you have this neato background that you want to loop in the back, and a platform layer in the foreground. This sample works the same way, we take this tetris pattern that wraps well and save it as a 256x256. Using this CCRepeatingSprite class to load it. We can then set its content size to 480x320, and then layer it into the parallax class that Cocos2d provides or just take say half the value of the platform layer. m_repeatingBG.position = ccpMult(viewPoint, 0.5f);

Next thing we have is a background that will wrap with out needing to worry about repositioning a tile or repeating asset.