There is a series of blogs in the COCOS2DX community that completely replicates all the features of the original Flappybird, but the code is more complex and the novice learns a little bit elusive, and here I write a simple version. The demo is as follows:
Create a projectVS2013+COCOS2DX 3.2 created the Win32 project and was not compiled as an executable file for Android, iOS, or WP platform because it was just learning. The final project engineering structure is as follows:
Very simple, only three classes, pre-loaded classes, game main scene class, Application proxy class, novice just like to write a lot of things in as few of the class inside.
Game DesignThe game is structured as follows, and the game contains pre-loaded scenes and main scenes, with backgrounds, birds, pipelines, and various UI interfaces in the main scene.
Development Steps
1, Material collectionExtract some pictures and audio from the APK file, and use Texturepatcher to build a large image and export the plist file.
2, pre-load sceneCreate a new Loadingscene, add a boot image inside, by loading the texture asynchronously and callback to the way all the picture footage, the bird frame animation and audio files are added to the cache, loaded and then jump to the main game scene.
//add load callback function, load texture with async director::getinstance ()->gettexturecache ()->addimageasync ("Game.png", Cc_callback_1 (Loadingscene::loadingcallback, this));
void Loadingscene::loadingcallback (Texture2d *texture) {//Pre-loaded frame cache texture Spriteframecache::getinstance () Addspriteframeswithfile ("game.plist", texture);//pre-load frame animation Auto birdanimation = Animation::create ();birdanimation-> Setdelayperunit (0.2f); Birdanimation->addspriteframe (Spriteframecache::getinstance () Getspriteframebyname ("Bird1.png")); Birdanimation->addspriteframe (Spriteframecache::getinstance () Getspriteframebyname ("Bird2.png")); Birdanimation->addspriteframe (Spriteframecache::getinstance () Getspriteframebyname ("Bird3.png")); Animationcache::getinstance ()->addanimation (birdanimation, "birdanimation"); Add a bird animation to the animation cache//Pre-load Sound simpleaudioengine::getinstance ()->preloadeffect ("Die.mp3"); Simpleaudioengine::getinstance ()->preloadeffect ("Hit.mp3"); Simpleaudioengine::getinstance ()->preloadeffect ("Point.mp3"); Simpleaudioengine::getinstance ()->preloadeffect ("Swooshing.mp3"); Simpleaudioengine::getinstance ()->preloadeffect ("Wing.mp3");//load complete jump to the game scene auto Gamescene = GAmescene::createscene (); Transitionscene *transition = transitionfade::create (0.5f, Gamescene);D irector::getinstance ()->replaceScene ( Transition);}
3, Game home View3.1, background and logo with Picture Wizard can
Add game background Sprite *background = Sprite::createwithspriteframename ("Bg.png"); Background->setposition ( Visibleorigin.x + VISIBLESIZE.WIDTH/2, VISIBLEORIGIN.Y + VISIBLESIZE.HEIGHT/2); This->addchild (backGround);// Logoauto Gamelogo = Sprite::createwithspriteframename ("Bird_logo.png"); Gamelogo->setposition (visibleOrigin.x + VISIBLESIZE.WIDTH/2, VISIBLEORIGIN.Y + visiblesize.height/2+100); Gamelogo->setname ("logo"); This->addChild ( Gamelogo);
The logo will be hidden after the game starts. 3.2, Birdie
Bird Birdsprite = Sprite::create (); Birdsprite->setposition (visibleorigin.x + VISIBLESIZE.WIDTH/3, VISIBLEORIGIN.Y + VISIBLESIZE.HEIGHT/2); This->addchild (birdsprite); auto Birdanim = Animate::create (Animationcache::getinstance ( )->animationbyname ("Birdanimation")); Birdsprite->runaction (Repeatforever::create (BirdAnim) ); Fin Animation Auto up = Moveby::create (0.4f, point (0, 8)), Auto Upback = Up->reverse (), if (gamestatus = = Game_ready) {Swingactio n = repeatforever::create (Sequence::create (UP, Upback, NULL)); Birdsprite->runaction (swingaction); Flick Animation}
In the preparation of the interface in addition to the wing of the movement, there are floating up and down movements.
3.3, the left shift of the floor floor is achieved by using two misplaced floor pictures to move left. Need to use a custom scheduler, pay attention to adjust the speed of movement.
Add two Landland1 = Sprite::createwithspriteframename ("Land.png"); Land1->setanchorpoint (Point::zero); Land1->setposition (Point::zero); This->addchild (Land1, ten); Place the topmost land2 = Sprite::createwithspriteframename ("Land.png"); Land2->setanchorpoint (Point::zero);land2-> SetPosition (Point::zero); This->addchild (Land2, 10);
Size visiblesize = director::getinstance ()->getvisiblesize ();//two pictures loop move land1->setpositionx (land1-> Getpositionx ()-1.0f); Land2->setpositionx (Land1->getpositionx () + land1->getcontentsize (). width-2.0f); if (Land2->getpositionx () <= 0) land1->setposition (Point::zero);
3.4, a group of water pipe from the upper and lower 2 half of the root, with node wrapped up, get a vector container to add two sets of pipes, each appear in the screen of the tube only two groups, when a group disappears in the screen range is reset its horizontal axis, need to calculate the various spacing or height in advance.
Only two tubes appear on the same screen, placed inside the container, bound to a for (int i = 0; i < 2; i++) {Auto visiblesize = director::getinstance ()->getvisiblesize (); Sprite *pipeup = Sprite::createwithspriteframename ("Pipe_up.png"); Sprite *pipedown = Sprite::createwithspriteframename ("Pipe_down.png"); Node *singlepipe = Node::create ();//bind rigid body to upper tube auto Pipeupbody = Physicsbody::createbox (Pipeup->getcontentsize ()); Pipeupbody->setdynamic (False);p Ipeupbody->setcontacttestbitmask (1);p Ipeup->setanchorpoint (Vec2:: Anchor_middle);p ipeup->setphysicsbody (pipeupbody);//To set up a rigid body for two tubes, you can leave the middle of the gap so that the bird through//to the tube bound rigid body auto Pipedownbody = Physicsbody::createbox (Pipedown->getcontentsize ());p ipedownbody->setdynamic (false);p ipedownbody-> Setcontacttestbitmask (1);p ipedown->setanchorpoint (vec2::anchor_middle);p ipedown->setphysicsbody ( Pipedownbody);p ipeup->setposition (0, pipe_height + pipe_space); Singlepipe->addchild (pipeUp);singlePipe-> AddChild (Pipedown); Pipedown default Add to (0,0), merge up and down, at this time singlepipe with the following pipe center as the anchor Point Singlepipe->setposItion (I*pipe_interval + wait_distance, getrandomheight ()); Set the initial height singlepipe->setname ("newpipe"); This->addchild (singlepipe); Add two tubes to the layer pipes.pushback (singlepipe); Two tubes successively added to the container}
Pipe rolling for (auto &singlepipe:pipes) {Singlepipe->setpositionx (Singlepipe->getpositionx ()-1.0f); Singlepipe->getpositionx () <-PIPE_WIDTH/2) {Singlepipe->setpositionx (VISIBLESIZE.WIDTH+PIPE_WIDTH/2); Singlepipe->setpositiony (Getrandomheight ()); Singlepipe->setname ("Newpipe"); Reset one tube at a time, labeled new}}
3.5, joined the physical World COCOS2DX 3.0 after the introduction of the physical engine, the use of the same as box2d and so on. Physical World Initialization
Gamescene->getphysicsworld ()->setgravity (VEC2 (0,-900)); Set gravitational field, the gravitational acceleration can be changed to small point according to feel
Gamelayer->setphysicworld (Gamescene->getphysicsworld ()); Binding the physical world
Bird bound Rigid Body
Bird binding rigid body Auto Birdbody = physicsbody::createcircle (Bird_radius); Think of the bird as a circle, too lazy to get the exact contour line birdbody->setdynamic (true); Set to act Birdbody->setcontacttestbitmask (1), which can be played by the physical site; This must be set to 1 to detect a different object collision birdbody->setgravityenable (false); Whether the setting is affected by gravity, the preparation screen is not affected by gravity birdsprite->setphysicsbody (birdbody); Set up a rigid body for a bird
Floor-bound Rigid body
Set floor rigid body Node *groundnode = node::create (); Auto Groundbody = Physicsbody::createbox (Size (Visiblesize.width, land1-> Getcontentsize ()); Groundbody->setdynamic (false); Groundbody->setcontacttestbitmask (1); groundNode- >setanchorpoint (Vec2::anchor_middle); Rigid bodies in the physical engine allow only node anchors to be set to center Groundnode->setphysicsbody (groundbody); Groundnode->setposition (visibleorigin.x+ Visiblesize.width/2,land1->getcontentsize (). HEIGHT/2); This->addchild (Groundnode);
Pipe set rigid body, the upper and lower halves are set separately, leaving the middle gap
to the upper tube bound rigid body Auto Pipeupbody = Physicsbody::createbox (Pipeup->getcontentsize ());p ipeupbody->setdynamic (false); Pipeupbody->setcontacttestbitmask (1);p ipeup->setanchorpoint (vec2::anchor_middle);p ipeup-> Setphysicsbody (pipeupbody);//For two tubes set up a rigid body, you can leave the middle of the gap so that the bird through//to the tube bound rigid body Auto Pipedownbody = Physicsbody::createbox ( Pipedown->getcontentsize ());p ipedownbody->setdynamic (False);p Ipedownbody->setcontacttestbitmask (1); Pipedown->setanchorpoint (Vec2::anchor_middle);p ipedown->setphysicsbody (pipedownbody);
Collision detection now adds a collision listener to the event dispatcher inside the init layer
Add collision monitoring Auto Contactlistener = Eventlistenerphysicscontact::create (); contactlistener->oncontactbegin = CC_ Callback_1 (Gamescene::oncontactbegin, this); _eventdispatcher->addeventlistenerwithscenegraphpriority ( Contactlistener, this);
Collision monitoring bool Gamescene::oncontactbegin (const physicscontact& Contact) {if (GameStatus = = game_over) // When the game is over, no longer monitor the collision return False;gameover (); return true;}
3.6, Touch detection
Touch monitor bool Gamescene::ontouchbegan (Touch *touch, Event *event)
3.7, control the bird from the preparation mode to the game start mode, touch the screen will give the bird an upward speed, write in the touch detection inside
Birdsprite->getphysicsbody ()->setvelocity (Vec2 (0, 250)); Give an upward initial velocity
The angle of rotation of the bird is related to the longitudinal velocity and is written in update ().
The bird spins auto curvelocity = Birdsprite->getphysicsbody ()->getvelocity (); Birdsprite->setrotation (- CURVELOCITY.Y*0.1-20); Calculates the rotation angle according to the velocity in the vertical direction, minus the counter-clockwise
3.8, start the game after the start of various timers
The game begins void Gamescene::gamestart () {gamestatus = Game_start;score = 0;//Resets the score scorelabel->setstring (String:: Createwithformat ("%d", score)->getcstring ()) This->getchildbyname ("logo")->setvisible (false); Logo disappears scorelabel->setvisible (true); Scoring starts this->scheduleupdate ();//start default update This->schedule (Schedule_selector (Gamescene::scrollland), 0.01f); Start the pipe and floor rolling birdsprite->stopaction (swingaction); The game starts to stop up and down floating birdsprite->getphysicsbody ()->setgravityenable (true); Begin to be subjected to gravity}
3.9, scoring and data store determine and update scores in the default update () function, storing historical scores by default XML
When the game starts, judging the score, this can actually be written elsewhere, such as the tube rolling update function inside or touch monitoring inside if (GameStatus = = Game_start) {for (auto &pipe:pipes) {if (pipe- >getname () = = "Newpipe")//new pipe to determine {if (Pipe->getpositionx () < Birdsprite->getpositionx ()) {score++; Scorelabel->setstring (String::createwithformat ("%d", score)->getcstring ()); Simpleaudioengine::getinstance ()->playeffect ("Point.mp3");p ipe->setname ("passed"); Mark the pipe that has passed out}}}}
4.0, Game Over
Game over void Gamescene::gameover () {gamestatus = game_over;//get historical Data Bestscore = Userdefault::getinstance () Getintegerforkey ("best"); if (Score > Bestscore) {bestscore = score; Update Best Score Userdefault::getinstance ()->setintegerforkey ("good", Bestscore);} Simpleaudioengine::getinstance ()->playeffect ("Hit.mp3");//stop rolling this->unschedule the floor and pipe after the game is over (Schedule_ Selector (Gamescene::scrollland));}
At the end, compare the current score and the historical score to update. 4.1, audio sound files have been added to the cache, where appropriate add the global audio controller to play sound
Simpleaudioengine::getinstance ()->playeffect ("Hit.mp3");
4.2, the scoreboard game is over and slides into the scoreboard and displays the replay button.
Add scoreboard and Replay menu void Gamescene::gamepanelappear () {Size size = Director::getinstance ()->getvisiblesize (); VEC2 origin = Director::getinstance ()->getvisibleorigin ();//Use node to bind Gameoverlogo and scoreboard together node *gameoverpanelnode = Node::create (); Auto Gameoverlabel = Sprite::createwithspriteframename ("gameover.png");gameoverpanelnode-> AddChild (gameoverlabel); auto Panel = sprite::createwithspriteframename ("board. PNG ");//Note here is the uppercase PNG, the original image with what suffix here with what, case-sensitive gameoverlabel->setpositiony (panel->getcontentsize (). height); Set the coordinates gameoverpanelnode->addchild (panel);//Scoreboard add two points auto Curscorettf = Labelttf::create (String:: Createwithformat ("%d", score)->getcstring (), "Arial", "Curscorettf->setposition" (panel->getcontentsize (). width-40, Panel->getcontentsize (). height-45) Curscorettf->setcolor (color3b (255, 0, 0));p anel-> AddChild (CURSCORETTF); auto Bestscorettf = Labelttf::create (String::createwithformat ("%d", Bestscore) Getcstring (), "Arial", Bestscorettf->setposition (PANEL->getcontentsize (). width-40, Panel->getcontentsize (). height-90); Bestscorettf->setcolor (color3b (0, 255, 0)); Panel->addchild (BESTSCORETTF); This->addchild (Gameoverpanelnode); Gameoverpanelnode->setposition ( Origin.x + SIZE.WIDTH/2, ORIGIN.Y + size.height);//Slide into animation gameoverpanelnode->runaction (Moveto::create (0.5f, VEC2 ( Origin.x + SIZE.WIDTH/2, ORIGIN.Y + SIZE.HEIGHT/2)); Simpleaudioengine::getinstance ()->playeffect ("Swooshing.mp3");//Add menu Menuitemimage *restartitem = MenuItemImage :: Create ("Start_btn.png", "Start_btn_pressed.png", This,menu_selector (Gamescene::gameretart)); auto menu = Ccmenu:: Createwithitem (Restartitem); Menu->setposition (origin.x + SIZE.WIDTH/2); This->addchild (menu);} The game restarts void Gamescene::gameretart (Ref *sender) {//re-returns to the initial screen auto Gamescene = Gamescene::createscene ();D irector:: GetInstance ()->replacescene (Gamescene); It's too lazy to add special effects, direct transitions}
:
Source code: Myflappybird
There are a lot of things to be perfected, such as not adding picture numbers, social sharing, etc.
Flappybird of COCOS2DX Instance Development (Starter Edition)