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;
}
[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];
}
This is an interesting read and a handy reference, I was planning to try something like this in the near future.
ReplyDeleteCheers,
hiii... thank u so much. this was very helpful to me. cleared my concepts
ReplyDelete