Cocos2d-x achieves synchronous playback of multiple genie Animations (1)

Source: Internet
Author: User
2D games often have the role to wear equipment. For example, a weapon is added to the role's hand. In addition, there are also common projection techniques in Combat Games:


Note that the role is in the standing state, and there is an idle animation, and the hand weapon should also be associated with the role. Do we want art to draw another set of materials that add hand animations? The art is obviously no longer needed. Do we have to have a foot and a cloak? Do not die. They will only give you a set of stand animations with pure weapons, so that you can fight on your own.
We need to associate weapons with the role. Naturally, we want to call the ccspawn action method after setting the position and zorder. But there is a big problem, that is, it is very likely that two different animations will not be synchronized independently. To solve this problem, an animation group mechanism must be implemented, that is, to allow a character to be actively painted as an animation group, and weapons to be used as sub-Members of the animation group, the sub-animation is switched only when the active frame is switched. That is, you only need to start it when I touch you.

For example, a robot consists of smoke on its head, highlights on its body, and waist. During an attack, the robot also has its own actions on the attack animation, the smoke will also be switched to the next frame when the robot switches each frame. In this case, the robot acts as the main Member of the animation group, and the smoke animation must act as a sub-animation Member of the animation group.

The principle of synchronous animation is that the update method of ccanimate is to switch the display frame every time it is executed to achieve the animation effect. We need to rewrite this update, let all the members of the animation group also switch the key frame during the active update, so that absolute synchronization can be achieved.
First, implement the animation group member method animatemember, inherited from ccobject

# Ifndef _ animationmember _ # DEFINE _ animationmember _ # include "cocos2d. H" class animationmember: Public cocos2d: ccobject {public: animationmember ();~ Animationmember (); static animationmember * Runtime (cocos2d: ccanimation * animation, cocos2d: ccsprite * target); bool Runtime (cocos2d: ccanimation * animation, cocos2d :: ccsprite * target); // use the animation and playback object to initialize void start (); // start playing the animation void stop (); // stop playing the animation void setframe (INT frameindex); // set the play animation object (_ target) to a certain frame in the animation protected: cocos2d: ccspriteframe * _ origframe; // initial frame cocos2d: ccanimation * _ animation; // animated cocos2d: ccsprite * _ target; // who is playing the animation private:}; # endif
This is the header file, including the initial frame, animation, and target key methods. Check the initialization implementation.
AnimationMember* AnimationMember::memberWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target){AnimationMember* pRet = new AnimationMember();if (pRet && pRet->initWithAnimation(animation, target)){return pRet;}else{delete pRet;pRet = NULL;return pRet;}}bool AnimationMember::initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target){bool bRet = false;do {//CC_BREAK_IF(!)this->_animation = animation;this->_target = target;this->_animation->retain();this->_target->retain();_origFrame = NULL;bRet = true;} while (0);return bRet;}
It is very simple to assign values to several key information during initialization.
Let's look at the start and stop functions.
Void animationmember: Start () {_ origframe = _ target-> displayframe (); // get the currently displayed frame as the initial frame} void animationmember: Stop () {bool brestore = _ animation-> getrestoreoriginalframe (); // whether to restore the first frame after playback. If (brestore) {_ target-> setdisplayframe (_ origframe ); // restore the first frame }}
The Start and Stop functions only set the initial frame, which is irrelevant to playing. Don't worry. Let's look down.
Setframe function:
Void animationmember: setframe (INT frameindex) {ccarray * frames = _ animation-> getframes (); int ncount = frames-> count (); If (frameindex> = ncount) {cclog ("animationmember setframe frameindex is greater than framecount"); return;} // obtain the index frame from the animation ccanimationframe * frame = (ccanimationframe *) (frames-> objectatindex (frameindex); ccspriteframe * spriteframe = frame-> getspriteframe (); _ target-> setdisplayframe (spriteframe );}
Setframe is to get the frame from the animation and set it to the currently displayed frame. This method will be used later.
Other constructor and destructor
AnimationMember::AnimationMember(){_target = NULL;_origFrame = NULL;_animation = NULL;}AnimationMember::~AnimationMember(){CC_SAFE_RELEASE_NULL(_animation);CC_SAFE_RELEASE_NULL(_target);}

Let's look at the animation group animategroup class. The animation group is used to play the animation, so it inherits from the ccanimate class. Therefore, it has all the functions of ccanimate. the header file is as follows:

# Ifndef _ animategroup _ # DEFINE _ animategroup _ # include "cocos2d. H" class animategroup: Public cocos2d: ccanimate {public: animategroup ();~ Animategroup (); // use an array to initialize the function static animategroup * actionwithanimation (cocos2d: ccanimation * animation, cocos2d: ccarray * members); bool evaluate (cocos2d :: ccanimation * animation, cocos2d: ccarray * members); // use animations and arrays to initialize static animategroup * actionwithanimation (cocos2d: ccanimation * animation, int membercount); bool initwithanimation (cocos2d: ccanimation * animation, int membercount); // initialize void startwithtarget (cocos2d: ccnode * pTARGET) with animation and number of groups ); void stop (); // all animations stop void Update (float DT); cocos2d: ccarray * _ members; // animation member protected:}; # endif
We can see that its important member variable is the _ members animation member array. In addition, there is an update method. Let's take a look at the initialization implementation.
Constructor:
AnimateGroup::AnimateGroup(){_members = NULL;}AnimateGroup::~AnimateGroup(){CC_SAFE_RELEASE_NULL(_members);}
Initialize the function based on the array:
AnimateGroup* AnimateGroup::actionWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members){AnimateGroup* pRet = new AnimateGroup();if (pRet && pRet->initWithAnimation(animation, members)){return pRet;}else{delete pRet;pRet = NULL;return pRet;}}bool AnimateGroup::initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members){bool bRet = false;do {CC_BREAK_IF(!CCAnimate::initWithAnimation(animation));this->_members = members;this->_members->retain();bRet = true;} while (0);return bRet;}
It can be seen that member _ members is directly transmitted. The following is another initialization function.
AnimateGroup* AnimateGroup::actionWithAnimation(cocos2d::CCAnimation *animation,int memberCount){AnimateGroup* pRet = new AnimateGroup();if (pRet && pRet->initWithAnimation(animation, memberCount)){return pRet;}else{delete pRet;pRet = NULL;return pRet;}}bool AnimateGroup::initWithAnimation(cocos2d::CCAnimation *animation, int memberCount){bool bRet = false;do {CC_BREAK_IF(!CCAnimate::initWithAnimation(animation));this->_members = CCArray::createWithCapacity(memberCount);this->_members->retain();bRet = true;} while (0);return bRet;}
Here, an empty array with the specified capacity is created.
Let's take a look at the important functions to start and stop playing.
void AnimateGroup::startWithTarget(CCNode *pTarget){CCAnimate::startWithTarget(pTarget);AnimationMember* aniMember = NULL;CCObject *member = NULL;CCARRAY_FOREACH(this->_members, member){aniMember = (AnimationMember*)member;aniMember->start();}}void AnimateGroup::stop(){CCAnimate::stop();AnimationMember *aniMember = NULL;CCObject* member = NULL;CCARRAY_FOREACH(_members, member){aniMember = (AnimationMember *)member;aniMember->stop();}}
It can be seen that both start playing and stop playing start by calling the base class method, and then call the start and stop methods of each sub-member in turn, since both start and stop playback are completed in the base class, the task that the sub-member must do is to set the currently displayed frame.
Maybe the students still don't understand that the active painting is ccanimate, and the Child animation will be ccanimate and put it into _ members in the future, so the start function of the animatemember class does not call ccanimate :: the Start and Stop Functions, how can we start playing the sub-animation? Yes, this is a problem. If we want to allow the sub-animation to play simultaneously with the animation, we can no longer call the ccanimate start method to start playing the animation, because this will cause an issue of non-synchronization, we need to use the original method to directly set the frame image, and call the setframe of the sub-animation through the update of the animategroup. as follows:
void AnimateGroup::update(float dt){CCAnimate::update(dt);int frameIndex = MAX(0, m_nNextFrame - 1);AnimationMember *aniMember = NULL;CCObject* member = NULL;CCARRAY_FOREACH(_members, member){aniMember = (AnimationMember *)member;aniMember->setFrame(frameIndex);}}
If you check the update source code implementation of ccanimate, you will find that it also achieves the animation effect by setting the switching frame in update, so we are also doing the same way, and every animation member is rotated in update, let it switch the frame. Note that m_nnextframe is the protected member variable in ccanimate, indicating the next frame index to be played.
In this way, our animation linkage class will be implemented. Once again, we find that the principle is not complex, that is, to allow the sub-animation to switch to the next animation in the update, how can we use it?

Because the source project is relatively ambitious, it is impossible to paste all the code. I am a cainiao and often laughed at by so-called experts. But I believe that as long as I understand the principle, even cainiao like me can use their IQ easily:
Well, it's not a lot of nonsense. It's quite troublesome to set up an animation. For clarity, write a method: animategroupwithactionword.
Assume that we have a machine human, inherited from ccsprite, which has a variety of leisurely, attack, and walking actions. Because of the smoke on its head, every animation should be animategroup. The method is as follows:
Animategroup * ROBOT: const char * actionkeyword, int framecount, float delay {// create a basic animation ccanimation * baseanimation = This-> animationwithprefix (ccstring:: createwithformat ("robot_base _ % s", actionkeyword)-> getcstring (), 0, framecount, delay); // belt animation animationmember * beltmember = This-> animationmemberwithprefix (ccstring:: createwithformat ("robot_belt _ % s", actionkeyword)-> getcstring (), 0, framecount, delay, _ belt ); // animation * smokemember = This-> animationmemberwithprefix (ccstring: createwithformat ("robot_smoke _ % s", actionkeyword)-> getcstring (), 0, framecount, delay, _ smoke); <span style = "white-space: pre "> </span> // Add the belt animation and smoke animation to the ccarray * animationmembers = ccarray: Create (); animationmembers-> addobject (beltmember ); animationmembers-> addobject (smokemember); <span style = "white-space: pre"> </span> // generates an animation group return animategroup: actionwithanimation (baseanimation, animationmembers );}
The annotation is clearly written, that is, three basic animations are generated, and the animation of the robot is used as an active drawing (actionwithanimation is input as the first parameter, which is very important ), the other two are inserted into _ members as sub-members. Wait, what is the nasty animationmemberwithprefix? The Code is as follows:
AnimationMember* ActionSprite::animationMemberWithPrefix(const char* prefix, int startFrameIdx, int frameCount, float delay, cocos2d::CCSprite* target){CCAnimation* animation = this->animationWithPrefix(prefix, startFrameIdx, frameCount, delay);return AnimationMember::memberWithAnimation(animation, target);}
Suspect? Why is there another layer of animationwithprefix encapsulation? No way. The source project code is written in this way. I also use it. This method is the real animation encapsulation, the frame name prefix is extracted from plist, and the frame name is spelled out in the code based on the Start index number and end index number, for example, "robot_idle_00.png", "robot_idle_01.png ", an animation is generated based on the delay of each frame. The Code is as follows:
Ccanimation * actionsprite: const char * prefix, int startframeidx, int framecount, float delay) {int idxcount = framecount + startframeidx; // total number of frames ccarray * frames = ccarray :: createwithcapacity (framecount); ccspriteframe * frame; For (INT I = startframeidx; I <idxcount; I ++) {frame = ccspriteframecache: trim ()-> spriteframebyname (ccstring :: createwithformat ("s_s_10902d.png", prefix, I)-> getcstring (); frames-> addobject (FRAME);} return ccanimation: createwithspriteframes (frames, delay );}
This code you'll be familiar with is the standard Animation Generation step of the cocos2d-x, not much to explain, it returns ccanimation.
It is easy to use it.
For example, the robot's standing Animation:
// Idle animation animategroup * idleanimationgroup = This-> groups ("idle", 5, 1.0f/12.0f); this-> _ idleaction = ccrepeatforever: Create (idleanimationgroup) this-> idleaction-> retain ();
In this robot class, a special member variable is _ idleaction, which is used to store the standing animation and is directly used for playback:
Robot-> runaction (robot-> _ idleaction );
It can be seen that the operation is normal
Summary: although it seems troublesome and cumbersome to use it, I have never believed that simple is a superficial form of beauty. To implement complicated functions, it is useless to simply think about simplicity. However, the basic principle is really not complex. In fact, a lot of code is basically generating frame animations. The main steps of an animation group are to create active and sub-animations, initialize the animation group with active paintings, and add the Sub-animations to the _ members array of the animation group, then it can be played like a normal ccanimate.

The robot example is completed. I believe that readers can use their own projects after reading them, but don't think it's done. This example is not representative, because the smoke on the head of the machine itself belongs to the machine human, and we often encounter the role wear equipment, as well as the projection technology in the fighting game, the sub-object and the primary object are not contained in a class, but two independent objects with their own positions and orientations. In this case, we need to consider the relationship between positions and orientations, otherwise, the animation is linked, but the position is right. This is explained in the next section.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.