Development of a flying and shooting mobile game similar to thunder fighter-Beginner guide and thunder fighter shooter

Source: Internet
Author: User

Development of a flying and shooting mobile game similar to thunder fighter-Beginner guide and thunder fighter shooter

Reprinted Please note:


Download Windows trial version:




The traditional beginner guide mode is generally to set a global static variable to save the progress of the current beginner guide, and then add a judgment in each position of the project where the novice Guide may appear: if the current beginner's guide step is the same as what I expected, the logic of the Guide part will be executed. The new beginner guide is often in disorder and is often intertwined with the interface logic, otherwise, if else is everywhere in the code to save various temporary state variables.

This article will take the anti-Thunder fighter game as an example to talk about the design of the novice guide.

Game Effect

Next, let's take a look at the typical two types of guidance in the game.

1. Menu Guide



When it is used to start a game for the first time, users are guided to enter the game quickly through two or three steps to experience the pleasure of the game. Therefore, here we take three steps:

A) sister xiong is here to attract the attention of male players and guide them to the main menu;

B) Instruct the player to select the default plane and enter the level selection interface;

C) Instruct the player to select the first level and enter the combat interface;

2. Game Guidance


During the game, when certain conditions are met, the game will be suspended and game items will pop up (Zhongshi shield can turn all the bullets on the screen into gems, and guides players to experience the effects of the items.

The new user guide shows the following results:

A) Highlight the controls to be clicked by the user (for example, buttons), and all other departments reduce the brightness.

B) use an animated arrow to point to the control to facilitate player recognition.

C) display text instructions to inform gamers of the related operation steps or how to use the items

It is easy to darken the interface by overwriting a translucent layer. So how can we make a widget brighter? Easy: remove the control from the parent node and mount it to the translucent layer.

After introducing the principles, let's talk about code design.

Interface call

The menu guide is generally placed in the onEnter function of the interface. In this way, when the player enters the interface, the new guide logic is immediately triggered. The following is a function showing the beauty welcome interface. How about it? There are only a few lines of code, which is very simple.

Void MenuMainUI: onEnter () {Layer: onEnter (); // Guide * pGuide = GuideMgr: getInstance ()-> show (GuideType: WelcomeGirl, this, nullptr, [this] () {GuideMgr: getInstance ()-> show (GuideType: WelcomeClickButtonLevel, this, m_pBtnLevel );});}

In the above Code, we use this function:

Guide* GuideMgr::show(int guideType, Node* parent, Node* widget, const std::function<void()>& callback)


1) guideType is the boot type and has the following values:

Const int WelcomeGirl = 10; // const int WelcomeClickButtonLevel = 20 after you enter the game for the first time; // click the pass mode const int WelcomeClickMissionOne = 30; // click the first level const int GameShield = 140; // shield Guide

2) parent is the parent node, usually the current interface layer. The translucent layer is to be mounted under this node.

3) widgets are the control nodes to be highlighted. For the beauty guide, there is no control that needs to be highlighted, so the nullptr is passed in; for the second guide interface, it is the "Pass mode" button node.

4) callback is a lambda expression. Callback is called when the user clicks anywhere on the guiding interface to make the guiding interface disappear. In this example, we use this parameter to implement continuous appearance of two boot interfaces.

GuideMgr: show () function returns the Guide Object Pointer. What is this? Let's talk about it later.

Let's take a look at the call code of the shield User Guide interface:

void HubLayer::displayGuideShield(){Guide* pGuide = GuideMgr::getInstance()->show(GuideType::GameShield, this->getParent(), m_pBtnShield);if (pGuide){m_pPlaneLayer->pause();}}void HubLayer::menuShieldCallback(Ref* pSender){if (GuideMgr::getInstance()->destory(GuideType::GameShield)){m_pPlaneLayer->resume();}}

The logic is simple. The Boot interface is displayed and the game is paused. After you click the shield button, destroy the boot layer and resume the game. At this time, the bullet on the screen becomes a gem.

It seems quite simple to call the novice Bootstrap interface, and it does not seem to damage the appearance of the original code. Is its internal logic very complicated?

Implementation Principle

The Code is designed as follows:

1) PopupLayer class: displays translucent layers and responds to various touch operations of users.

2) Guide class: displays the main logic guided by the novice

3) GuideMgr class: manages all the beginner Guide classes

4) GuideWelcomeGirl/GuideWelcomeClickButtonLevel/GuideWelcomeClickMissionOne/GuideGameShield class: inherits from the Guide class for various guidance purposes.

Next we will analyze these classes one by one.

Translucent Layer

Class PopupLayer: public LayerColor {public: PopupLayer ();~ PopupLayer (); virtual bool init (); virtual bool doInit () = 0; // touch Event listening shields virtual bool onTouchBegan (Touch * touch, event * Event) override; virtual void onTouchMoved (Touch * touch, Event * event) override; virtual void onTouchEnded (Touch * touch, Event * event) override; virtual std: function <bool (Touch *, event *)> getTouchBeganFunc () {return CC_CALLBACK_2 (PopupLayer: onTouchBegan, this) ;}; virtual std: function <void (Touch *, Event *)> getTouchMovedFunc () {return CC_CALLBACK_2 (PopupLayer: onTouchMoved, this);} virtual std: function <void (Touch *, Event *)> getTouchEndedFunc () {return CC_CALLBACK_2 (PopupLayer: onTouchEnded, this);} protected: EventListenerTouchOneByOne * m_pListener ;};
PopupLayer::PopupLayer(): m_pListener(nullptr){}PopupLayer::~PopupLayer(){Director::getInstance()->getEventDispatcher()->removeEventListener(m_pListener);}bool PopupLayer::init(){if (!LayerColor::init()){return false;}m_pListener = EventListenerTouchOneByOne::create();m_pListener->setSwallowTouches(true);m_pListener->onTouchBegan = getTouchBeganFunc();m_pListener->onTouchMoved = getTouchMovedFunc();m_pListener->onTouchEnded = getTouchEndedFunc();auto dispatcher = Director::getInstance()->getEventDispatcher();dispatcher->addEventListenerWithSceneGraphPriority(m_pListener, this);setColor(Color3B(0, 0, 0));setOpacity(160);return doInit();}bool PopupLayer::onTouchBegan(Touch *touch, Event *event){return true;}void PopupLayer::onTouchMoved(Touch *touch, Event *event){}void PopupLayer::onTouchEnded(Touch* touch, Event* event){}

In the above Code, a translucent layer is covered by inheriting LayerColor and using setColor (Color3B (0, 0, 0); setOpacity (160, the specific interface initialization and touch response are implemented by the subclass to override doInit/onTouchBegan/onTouchMoved/onTouchEnded.

Beginner guide base class

Code first


# Define DECLARE_GUIDE (name) \ private: \ static bool mInit; \ static int mType; \ public: \ static Guide * create () \ {\ return new name (); \}\ static bool checkCondition (); # define REGISTER_GUIDE (type, name) \ bool name: mInit = GuideMgr: getInstance ()-> registerFunc (type, name :: checkCondition, name: create); \ int name: mType = type; class Guide: public PopupLayer {public: Guide (); public: // display Guide * show (int GuideType, Node * pRoot, Node * pTarget, const std: function <void ()> & callback = nullptr ); // restore targetvoid restore (); // set the text description void setPrompt (const std: string & prompt); // set the response of the player after touching any point on the screen, for example: destroy the current Boot and bring up a new boot virtual void onTouchEnded (Touch * touch, Event * event) override; virtual std: function <void (Touch *, Event *)> getTouchEndedFunc () override {return CC_CALLBACK_2 (Guide: onTouchEnded, this);} virtual void onEnter () override; virtual void onExit () override; protected: // create a GuideBg at the top layer, take the target button from the original node, and add it to the virtual bool doInit () override; protected: int m_iGuideType; std :: function <void ()> m_callback; Node * m_pRoot; Node * m_pTarget; int m_targetLocalZOrder; Vec2 m_targetPos; Node * m_targetParent; ui: Text * m_pTextPrompt; std: string m_prompt; bool m_touchEnable ;};

In this class, the most important thing is the doInit function. Its functions include: remove the target button from the original parent node and mount it to the translucent layer.

M_targetPos = m_pTarget-> getPosition (); m_targetParent = m_pTarget-> getParent (); m_targetLocalZOrder = m_pTarget-> getLocalZOrder (); // Vec2 pos = m_pRoot-> convertToWorldSpace (m_pTarget-> getPosition (); Vec2 pos = m_pTarget-> convertToWorldSpace (Vec2: ZERO ); pos + = m_pTarget-> getAnchorPointInPoints (); // move the target to the current layer m_pRoot-> addChild (this); m_pTarget-> retain (); this-> addChild (m_pTarget); m_pTarget-> release (); m_pTarget-> setPosition (pos );

Add indicator arrow Animation

// M_pTarget center position (converted based on the anchor) float deltaX = (0.5-m_pTarget-> getAnchorPoint (). x) * m_pTarget-> getAnchorPointInPoints (). x/m_pTarget-> getAnchorPoint (). x; float deltaY = (0.5-m_pTarget-> getAnchorPoint (). y) * m_pTarget-> getAnchorPointInPoints (). y/m_pTarget-> getAnchorPoint (). y; // Add the indicator arrow Sprite * pArrow = Sprite: createWithSpriteFrameName ("Guide_Arrow.png"); const Size & targetSize = m_pTarget-> getContentSize (); const Size & arrowSize = pArrow-> getContentSize (); bool bArrowTop = true; Node * pDialogBox = nullptr; Sprite * cursor = nullptr; ui: Text * m_pTextPrompt = nullptr; if (m_prompt.length ()> 0) {pDialogBox = Preload: getInstance ()-> getUI ("Custom"); FIND_UI_CONTROL_RE (ui: Text *, "Text_Content", m_pTextPrompt, pDialogBox); short (Sprite *, "blank", pDialogBoxBg, pDialogBox); m_pTextPrompt-> setTextAreaSize (Size (,110); m_pTextPrompt-> short (false );} if (m_pTarget-> getPositionY () <CONSTANT: DESIGN_RES_HEIGHT/2 + 100) {// The arrow above the control bArrowTop = true; pArrow-> setPosition (m_pTarget-> getPosition (). x + deltaX, m_pTarget-> getPosition (). y + targetSize. height/2 + arrowSize. height/2 + 20 + deltaY); if (pDialogBox) {pDialogBox-> setPosition (CONSTANT: DESIGN_RES_WIDTH/2, CONSTANT: DESIGN_RES_HEIGHT-pDialogBoxBg-> getContentSize (). height/2); m_pTextPrompt-> setString (m_prompt); this-> addChild (pDialogBox) ;}} else {// The Arrow below the control bArrowTop = false; pArrow-> setFlippedY (true); pArrow-> setPosition (m_pTarget-> getPosition (). x + deltaX, m_pTarget-> getPosition (). y-targetSize. height/2-arrowSize. height/2-20 + deltaY); if (pDialogBox) {pDialogBox-> setPosition (CONSTANT: DESIGN_RES_WIDTH/2, pDialogBoxBg-> getContentSize (). height/2); m_pTextPrompt-> setString (m_prompt); this-> addChild (pDialogBox) ;}} this-> addChild (pArrow ); // arrow animation DelayTime * pDelay = DelayTime: create (0.4f); MoveBy * pMove1 = MoveBy: create (0.15f, Vec2 (0,-10.0f )); moveBy * pMove2 = MoveBy: create (0.15f, Vec2 (0, 10.0f); Sequence * paiq = nullptr; if (bArrowTop) {paiq = Sequence: create (pDelay, pMove1, pMove2, nullptr);} else {paiq = Sequence: create (pDelay, pMove2, pMove1, nullptr);} pai* pRepeat = RepeatForever: create (paiq ); pArrow-> runAction (pRepeat); m_touchEnable = false;

Destroy the new boot layer and mount the target button to the original parent node.

// Restore targetvoid Guide: restore () {if (m_pTarget) {m_pTarget-> retain (); this-> getChildren (). eraseObject (m_pTarget); m_targetParent-> addChild (m_pTarget); m_pTarget-> setLocalZOrder (m_targetLocalZOrder); m_pTarget-> setPosition (m_targetPos); m_pTarget-> release ();} this-> removeFromParent ();}

Beginner guide subclass

Here we will briefly introduce the first beauty welcome and the main menu of these two new guide.


//////////////////////////////////////// //// // Introduction to the first entry into the game /////// //////////////////////////////////////// //// // class GuideWelcomeGirl: public Guide {public: DECLARE_GUIDE (GuideWelcomeGirl); public: virtual bool doInit () override ;}; //////////////////////////////////////// //// // enter the game for the first time, click Enter mode ///////////////////////////////////// /// // class GuideWelcomeClickButtonLevel: public Guide {public: DECLARE_GUIDE (GuideWelcomeClickButtonLevel); public: virtual bool doInit () override ;};

//////////////////////////////////////// //// // Introduction to the first entry into the game /////// //////////////////////////////////////// //// // REGISTER_GUIDE (GuideType:: WelcomeGirl, GuideWelcomeGirl); bool GuideWelcomeGirl: checkCondition () {switch (GuideConstant: GuideDisplay) {case GuideTestNoGuide: return false; // used to test case when: return true; // test default: return! GameData: getInstance ()-> checkGuide (mType) & GameData: getInstance ()-> getLevelFinish (Difficulty: Easy) = 0 ;}} bool GuideWelcomeGirl :: doInit () {Guide: setPrompt (GlobalData: getInstance ()-> getParameter (GuideConstant: StringGuideWelcome); if (! Guide: doInit () {return false;} return true ;} //////////////////////////////////////// //// // enter the game for the first time, click Enter mode ///////////////////////////////////// /// // REGISTER_GUIDE (GuideType:: WelcomeClickButtonLevel, GuideWelcomeClickButtonLevel); bool GuideWelcomeClickButtonLevel: checkCondition () {switch (GuideConstant: GuideDisplay) {case GuideTestNoGuide: return false ;// Used to test case GuideTestNecessary: return true; // used to test default: return! GameData: getInstance ()-> checkGuide (mType) & GameData: getInstance ()-> getLevelFinish (Difficulty: Easy) = 0 ;}} bool GuideWelcomeClickButtonLevel :: doInit () {Guide: setPrompt (GlobalData: getInstance ()-> getParameter (GuideConstant: StringGuidePressLevelMode); if (! Guide: doInit () {return false;} return true ;}

Carefully compare the two novice guide codes. The biggest difference lies in the checkCondition function, that is, to determine whether the current condition needs to be displayed. Then, set the actual text description, and then, no more.

Guiding management class

class GuideMgr{public:static GuideMgr* getInstance();bool registerFunc(int type, const std::function<bool()>& checkFunc, const std::function<Guide*()>& createFunc);Guide* show(int GuideType, Node* parent, Node* widget, const std::function<void()>& callback = nullptr);bool destory(int GuideType);void incGuideCnt();void decGuideCnt();bool hasGuide();protected:GuideMgr();private:std::map<int, std::function<bool()>> m_condMap;std::map<int, std::function<Guide*()>> m_createMap;std::map<int, Guide*> m_objectMap;int m_iCnt;static GuideMgr* m_pInstance;};

GuideMgr* GuideMgr::m_pInstance = nullptr;GuideMgr* GuideMgr::getInstance(){if (!m_pInstance){m_pInstance = new GuideMgr();}return m_pInstance;}GuideMgr::GuideMgr() : m_iCnt(0){}bool GuideMgr::registerFunc(int type, const std::function<bool()>& checkFunc, const std::function<Guide*()>& createFunc){m_condMap.insert(std::map<int, std::function<bool()>>::value_type(type, checkFunc));m_createMap.insert(std::map<int, std::function<Guide*()>>::value_type(type, createFunc));return true;}Guide* GuideMgr::show(int guideType, Node* parent, Node* widget, const std::function<void()>& callback){if (hasGuide()){return nullptr;}auto itCond = m_condMap.find(guideType);auto itCreate = m_createMap.find(guideType);if (itCond != m_condMap.end() && itCreate != m_createMap.end()){if (itCond->second()){Guide* pGuide = itCreate->second();pGuide->autorelease();if (pGuide){auto it = m_objectMap.find(guideType);if (it != m_objectMap.end()){m_objectMap.erase(it);}m_objectMap.insert(std::map<int, Guide*>::value_type(guideType, pGuide));return pGuide->show(guideType, parent, widget, callback);}}}return nullptr;}bool GuideMgr::destory(int GuideType){auto it = m_objectMap.find(GuideType);if (it != m_objectMap.end()){it->second->restore();m_objectMap.erase(it);return true;}else{return false;}}void GuideMgr::incGuideCnt(){ ++m_iCnt; }void GuideMgr::decGuideCnt(){ --m_iCnt; if (m_iCnt < 0){m_iCnt = 0;}}bool GuideMgr::hasGuide(){ return m_iCnt > 0; }

There is nothing to say. In a Singleton, two map variables (m_condMap/m_createMap) are used to find the corresponding guiding condition judgment and create functions through the bootstrap type, and then create a Guide, and save it to m_objectMap.


Well, the beginner guide design in this game has been introduced. Of course, this is not the best design. If there is a good design and model, we hope to discuss it together.


Download the relevant code involved in this article: guide.rar

If you have any questions, contact:

Reprinted Please note:

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: 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.