How to Customize ArtPig Animations

[Section1] [Section2] [Section3] [Section4] [Section5] [Section6]

Section 6: Customize It.

In ArtPig library, points and sizes are based on the model screen you've chosen in ArtPigEditor. Cocos2D-IPhone and Cocos2D-X support letina display by applying scale factor to the coordinate systems. In general, programs are coded based on 460x320(IPhone 3GS), and double scaled images are loaded when it runs on 920x640(IPhone 4). On the other hand, ArtPig does not care of device's scale factor, and it only cares of screen size. With ArtPig, users should set 920x640 as model screen size, and half scaled images are loaded when the program runs on 460x320 screen. Therefore, programming codes should be also based on the larger screen size, 920x640.

It's time to customize the 'pigrun' project. Look at the customized code below, HelloWorldLayer.mm.

HelloWorldLayer.mm

#import "HelloWorldLayer.h"
using namespace artpig;
using namespace std;
// HelloWorldLayer implementation
@implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
    // Try to test with different values of the test variables.
    bool testRepeatRotation = false;
    bool testRepeatMove = false;
    bool testFinishRecovery = false;
    bool testAutoreverse = false;
    bool testAutoreverseDelay = false;
    bool testRandomPosition = false;
    bool testDisableRotation = false;
    bool testDurationChange = false;
        
if( (self=[super init])) {
        /** Get the resource manager with project ID 'pigrun'. The returned 
         resource manager value is managed by APSResourceManager class, and it
         must not be deleted by 'HelloWorldLayer.mm'
         */
APSResourceManager *manager = APSResourceManager::getResourceManagerForProjectId("pigrun");
        if (manager) {
            // Allocate and initialize a new symbol by resource manager.
            _symbol = manager->newSymbol();
            if (_symbol) {
                ////////////////////////////////////////////////////////////
                //                  Customization
                ////////////////////////////////////////////////////////////
                
                APSRotationAction *rotate = dynamic_cast<APSRotationAction *>(_symbol->getResourceForTag("rotate"));
                if (rotate) {
                    if (testRepeatRotation) {
                        // Rotate continuously.
                        rotate->setRepeat(INT32_MAX);
                    }
                    
                    if (testDisableRotation) {
                        rotate->setEnabled(false);
                    }
                }
                
                // Custom bezier move action.
                APSBezierMoveAction *move = dynamic_cast<APSBezierMoveAction *>(_symbol->getResourceForTag("move"));
                if (move) {
                    
                    if (testFinishRecovery) {
                        move->setFinishRecovery(true);
                    }
                    
                    if (testRepeatMove) {
                        move->setRepeat(2);
                    }
                    
                    if (testAutoreverse) {
                        move->setAutoReverse(true);
                        
                        if (testAutoreverseDelay) {
                            move->setAutoReverseDelay(1.f);
                        }
                    }
                    
                    if (testRandomPosition) {
                        time_t t;
                        time(&t);
                        
                        srandom(t);
                        
                        vector<APSAnchor *> *anchors = move->getAnchors();
                        for (int i=1; i < anchors->size(); i++) {
                            APSAnchor *anc = anchors->at(i);
                            CCPoint p = anc->getPosition();
                            
                            p.x += random()%400-200;
                            p.y += random()%200-100;
                            anc->setPosition(p);
                            
                            p = anc->getBackHandle();
                            p.x += random()%400-200;
                            p.y += random()%200-100;
                            anc->setBackHandle(p);
                            
                            p = anc->getFrontHandle();
                            p.x += random()%400-200;
                            p.y += random()%200-100;
                            anc->setFrontHandle(p);
                            
                        }
                    }
                    
                    if (testDurationChange) {
                        move->setDuration(move->getDuration()*2);
                    }
                    
                } // if(move)
                
                ////////////////////////////////////////////////////////////
                
                // Set symbol's scale value to fit into the current screen.
                _symbol->setScale(manager->getScaleModelToDevice());
                
                // Preload all required data so that the animation runs without delay.
                _symbol->preloadData();
                
                // Add symbol's root CCNode object as a child of HelloWorldLayer.
                [self addChild:_symbol->getRootNode()];
                
                // Trigger prologue action groups.
                _symbol->triggerPrologueActionGroups();
                
            } // if(symbol)
        }
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
    if (_symbol) {
        delete _symbol;
    }
    
    APSResourceManager::removeResourceManagerForProjectId("pigrun");
    
// don't forget to call "super dealloc"
[super dealloc];
}
@end



    bool testRepeatRotation = false;
    bool testRepeatMove = false;
    bool testFinishRecovery = false;
    bool testAutoreverse = false;
    bool testAutoreverseDelay = false;
    bool testRandomPosition = false;
    bool testDisableRotation = false;
    bool testDurationChange = false;

HelloWorldLayer.mm is modified to test customizations of 'pigrun' animation. Several local boolean variables are declared to enable or disable for each test.

APSRotationAction *rotate = dynamic_cast<APSRotationAction *>(_symbol->getResourceForTag("rotate"));
tag-rotateaction

getResourceForTag("move") method returns an ArtPig object that is tagged with "rotate" in symbol. You would probably remember that we tagged pig icon's rotate action with "rotate" in ArtPigEditor. dynamic_cast<APSRotationAction *> casts the returned object to APSRotationAction, and if dynamic_cast fails to cast to APSRotationAction, it returns NULL.

    if (testRepeatRotation) {
        // Rotate continuously.
        rotate->setRepeat(INT32_MAX);
    }

setRepeat() method assigns a number of times to repeat the action. Since INT32_MAX is used as a parameter of setRepeat() method, pig icon will rotate almost endlessly. ArtPig action does not have a property to repeat forever, and you may feel INT32_MAX is not big enough. To implement an action that repeat forever, you should add an observer to restart the action when it is finished. We will talk about it in the later section.

    if (testDisableRotation) {
        rotate->setEnabled(false);
    }

rotate->setEnabled(false) disables the rotate action, so if you set testDisableRotation variable true, pig icon will not rotate at all.

APSBezierMoveAction *move = dynamic_cast<APSBezierMoveAction *>(_symbol->getResourceForTag("move"));
tag-moveaction

_symbol->getResourceForTag("move") returns an APSBezierMoveAction object with tagged 'move'. Because the pig icon moves in a curve, this works fine. However, getResourceForTag() will return an APSMoveAction object if the pig moves on a straight line. As a result, you should make sure which class instance it will return.

    if (testFinishRecovery) {
        move->setFinishRecovery(true);
    }

If an action's finishRecovery property is set true, graphic's states are restored at the end of the action. In this case, if you set testFinishRecovery variable true, after 3 seconds of the animation, pig icon is immediately placed back to the original position.

    if (testAutoreverse) {
        move->setAutoReverse(true);
        
        if (testAutoreverseDelay) {
            move->setAutoReverseDelay(1.f);
        }
    }

Action's autoReverse property determines if the action plays in the reverse upon completion. So, if you set testAutoreverse variable true, the pig icon moves in a reversed direction after 3 seconds. Action's autoReverseDelay property specifies delay before reversed action plays. If you set testAutoreverseDelay variable true, the pig icon idles for 1 second after movement, and it moves reversed direction after the delay.

    if (testRandomPosition) {
        time_t t;
        time(&t);
        
        srandom(t);
        
        vector<APSAnchor *> *anchors = move->getAnchors();
        for (int i=1; i < anchors->size(); i++) {
            APSAnchor *anc = anchors->at(i);
            CCPoint p = anc->getPosition();
            
            p.x += random()%400-200;
            p.y += random()%200-100;
            anc->setPosition(p);
            
            p = anc->getBackHandle();
            p.x += random()%400-200;
            p.y += random()%200-100;
            anc->setBackHandle(p);
            
            p = anc->getFrontHandle();
            p.x += random()%400-200;
            p.y += random()%200-100;
            anc->setFrontHandle(p);
            
        }
    }

move->getAnchors() returns a vector list of APSAnchor objects. Each anchor has position, backHandle, and frontHandle properties. In this code, by setting testRandomPosition variable true, anchors are randomized. As you can see, this code does not change the first anchor. For the first anchor, backHandle property is ignored, and position property is reserved for saving graphic's original position. For example, if the move action's finishRecovery property is set true, graphic should go back to the initial position as soon as the move action finishes. The first anchor's position property is used to save the initial position.

bezier-curve