Development of flying and shooting mobile games like thunder fighters-resource pre-loading and thunder fighters
Most games first appear in a "loading" scenario after startup, this scenario is used to read the images, music, data, and other resources required by the game from the memory card (or disk, flash) into the memory. In this way, when these resources are used later, it can be read directly from the memory to speed up game operation and improve smoothness. Next, we will introduce the resource pre-loading mechanism.
Resource Type
The purpose of pre-loading is to facilitate subsequent reading. Therefore, it usually pre-loads large and complex files, such as the following:
- Single Big image: big background image
- Synthetic image: A large image can be synthesized from multiple images. Here we use TexturePacker to synthesize plist + png files.
- Bone Animation: A bone Animation file created using Cocos Skeletal Animation Editor, ExportJson + plist + png
- Scenario: csd files created using Cocos Studio
- Sound: ogg music file
- Local data: Game archival data (in json format), game configuration data (such as fixed data such as checkpoints, aircraft attributes, and bullet attributes, in the format of sqlite database files)
- Remote Data: As this game is a weak online game, there is not much data stored on the server. Here, only the user logon and time retrieval functions are implemented.
Next, we will introduce the loading methods of different resources one by one.
Large image of Loading Method
The detailed code is as follows:
1 // 1. png to be loaded or jpg 2 m_imageArray.push_back ("BigImg/Bag_Bg.png"); 3 m_imageArray.push_back ("BigImg/BigScreen_Bg.png "); 4 m_imageArray.push_back ("BigImg/Daily_Bg.png"); 5 m_imageArray.push_back ("BigImg/MainUI_Bg.jpg"); 6 7 8 void Preload: asynLoadingImage () 9 {10 // 2. Add the image to the global cache. 11 m_iImageCnt = m_imageArray.size (); 12 for (unsigned I = 0; I <m_imageArray.size (); I ++) 13 {14 ctor: getInstance ()-> getTextureCache ()-> addImageAsync (15 m_imageArray [I], 16 CC_CALLBACK_1 (Preload: asynLoadingImageDone, this, m_imageArray [I]); 17} 18} 19 20 // 3. Callback Function 21 void Preload: asynLoadingImageDone (Texture2D * texture, const std :: string & filename) 22 {23 // notifies the observer of the loading progress 24 this-> yyprogress (++ m_iTmpProgress); 25 m_iImageCnt --; 26 // All loaded 27 if (0 = m_iImageCnt) 28 {29 m_bImageLoaded = true; 30 this-> loadingDone (PreloadType: Image); 31} 32}
Synthetic Diagram
The loading of a compositing image is similar to that of a single image. The difference is that the plist file is loaded one more step in the callback function:
SpriteFrameCache: getInstance ()-> addSpriteFramesWithFile (file. append (". plist"), texture );
1 // plist Image 2 std: vector <std: string> m_plistArray; 3 4 // 1. image to be loaded, excluding the suffix 5 m_plistArray.push_back ("Bag "); 6 m_plistArray.push_back ("Common"); 7 m_plistArray.push_back ("Daily"); 8 9 void Preload: asynLoadingPlist () 10 {11 // 2. Load the image file 12 m_iImagePlistCnt = m_plistArray.size (); 13 for (unsigned I = 0; I <m_plistArray.size (); I ++) 14 {15 ctor:: getInstance ()-> getTextureCache ()-> addImageAsync (16 std: string (m_plistArray [I]). append (". png "), 17 CC_CALLBACK_1 (Preload: asynLoadingPlistDone, this, m_plistArray [I]); 18} 19} 20 21 void Preload: asynLoadingPlistDone (Texture2D * texture, const std:: string & filename) 22 {23 this-> notifyProgress (++ m_iTmpProgress); 24 25 // 3. Load the plist file 26 std: string file = filename; 27 SpriteFrameCache :: getInstance ()-> addSpriteFramesWithFile (file. append (". plist "), texture); 28 m_iImagePlistCnt --; 29 30 if (0 = m_iImagePlistCnt) 31 {32 // All loaded 33 m_bImagePlistLoaded = true; 34 this-> loadingDone (PreloadType: Plist); 35} 36}
Skeleton Animation
Skeleton animation is also similar to the loading method. First, use the addArmatureFileInfoAsync () function to load the image, Graph Information (plist file), and animation information (ExportJson file) of the skeleton animation ), then the callback function asynLoadingArmatureDone () is called ().
1 std::vector<std::string> m_armatureArray; 2 m_armatureArray.push_back("Anim/Anim_Plane_01"); 3 m_armatureArray.push_back("Anim/Anim_Plane_02"); 4 m_armatureArray.push_back("Anim/Anim_Plane_03"); 5 6 void Preload::asynLoadingArmature() 7 { 8 auto p = m_armatureArray[m_iArmatureCnt]; 9 DEBUG_LOG("Preload::asynLoadingArmature: %s", p.c_str());10 ArmatureDataManager::getInstance()->addArmatureFileInfoAsync(11 std::string(p).append("0.png"),12 std::string(p).append("0.plist"),13 std::string(p).append(".ExportJson"),14 this,15 CC_SCHEDULE_SELECTOR(Preload::asynLoadingArmatureDone));16 }17 18 void Preload::asynLoadingArmatureDone(float dt)19 {20 this->notifyProgress(++m_iTmpProgress);21 22 m_iArmatureCnt++;23 if (m_armatureArray.size() == m_iArmatureCnt)24 {25 m_bArmatureLoaded = true;26 this->loadingDone(PreloadType::Armature);27 }28 else29 {30 asynLoadingArmature();31 }32 }
Scenario
The scenario does not have a special asynchronous loading function. You can only generate a node based on the csd file through CSLoader: createNode () and CSLoader: createTimeline (), and then save it to the custom map, you can obtain the data from the map when you want to use the scenario data in the future.
Note that this loading method can run properly in the cocos2dx-3.4 with errors in 3.8 for unknown reasons. However, loading a single scenario file takes a short time and generally does not affect the gaming experience. Therefore, the latest version of this game does not pre-load the scenario file.
1 std: vector <std: string> m_uiArray; 2 std: map <std: string, Node *> m_uiMap; 3 4 // menu 5 m_uiArray.push_back ("Bag. csb "); 6 m_uiArray.push_back (" Daily. csb "); 7 m_uiArray.push_back (" Instruction. csb "); 8 9 void Preload: syncLoadingUI () 10 {11 // CSLoader: createNode cannot be called in a non-main thread; otherwise, OpenGL exception 12 for (auto file: m_uiArray) 13 {14 auto node = Preload: getUI (file); 15 node-> retain (); 16 m_uiMap.insert (std: map <s Td: string, Node * >:: value_type (file, node); 17 18 auto timeLine = CSLoader: createTimeline (file); 19 timeLine-> retain (); 20 m_actionMap.insert (std: map <std: string, cocostudio: timeline: ActionTimeline * >:: value_type (file, timeLine); 21 22 DEBUG_LOG ("Preload :: syncLoadingUI: % s ", file. c_str (); 23 this-> policyprogress (++ m_iTmpProgress); 24} 25 26 m_bUILoaded = true; 27 this-> loadingDone (PreloadType: Ui ); 28} 29 30 Node * Preload: getUI (const std: string & filename) 31 {32 DEBUG_LOG ("Preload: getUI: % s", filename. c_str (); 33 return CSLoader: createNode (filename); 34 35 // The cocos2dx-3.8 does not support the following operations. 3.4 support 36 // auto ui = m_uiMap.find (filename); 37 // if (ui! = M_uiMap.end () 38 // {39 // return ui-> second; 40 //} 41 // else42 // {43 // auto csb = CSLoader :: createNode (filename); 44 // csb-> retain (); 45 // m_uiMap.insert (std: map <std: string, Node * >:: value_type (filename, csb); 46 47 // return csb; 48 //} 49}
Sound
Since cocos provides two types of audio interfaces, audio file pre-loading is also divided into two types.
For old interfaces, You need to distinguish between music and sound files, and the function does not return values;
For new interfaces, the loading results are judged by callback instead of music and audio files.
// The old audio interface CocosDenshion: SimpleAudioEngine: getInstance ()-> preloadBackgroundMusic (filename); CocosDenshion: SimpleAudioEngine: getInstance ()-> preloadEffect (filename ); // new audio interface AudioEngine: preload (filename, [filename] (bool isSuccess) {if (! IsSuccess) {DEBUG_LOG ("Load fail: % s", path. c_str ());}});
Local Data
Local data includes archive data, game configuration data, and other customized data. Here we can use the asynchronous task interface and callback loading result provided by cocos for pre-loading.
1 void Preload::asynLoadingDatabase() 2 { 3 auto loadEnd = [this](void*) 4 { 5 DEBUG_LOG("asynLoadingDatabase OK"); 6 7 m_bOtherLoaded = true; 8 this->loadingDone(PreloadType::Other); 9 };10 11 AsyncTaskPool::getInstance()->enqueue(AsyncTaskPool::TaskType::TASK_IO, loadEnd, (void*)NULL, [this]()12 {13 if (!GlobalData::getInstance()->initialize(this))14 {15 CCLOG("Initialize globla data failed");16 this->notifyError("Initialize globla data failed");17 return;18 }19 20 m_iTmpProgress += PreloadProgress::GlobalData;21 this->notifyProgress(m_iTmpProgress);22 23 if (!GameData::getInstance()->loadData())24 {25 CCLOG("Initialize game data failed");26 this->notifyError("Initialize game data failed");27 return;28 }29 30 m_iTmpProgress += PreloadProgress::GameData;31 this->notifyProgress(m_iTmpProgress);32 33 if (!AchievementMgr::getInstance()->init())34 {35 CCLOG("Initialize achievement data failed");36 this->notifyError("Initialize achievement data failed");37 return;38 }39 40 m_iTmpProgress += PreloadProgress::AchievementMgr;41 this->notifyProgress(m_iTmpProgress);42 43 Sound::preload(this);44 45 m_iTmpProgress += PreloadProgress::Sound;46 this->notifyProgress(m_iTmpProgress);47 });48 }
Remote Data
Remote Data is usually loaded by sending asynchronous http or other tcp requests. The related interfaces vary depending on the network protocol.
Load Interface
1 class PreloadListener2 {3 public: 4 virtual void onStart () = 0; 5 virtual void onProgress (int percent) = 0; 6 virtual void onError (const char * info) = 0; 7 virtual void onWarning (const char * info) = 0; 8 virtual void onEnd (PreloadError errorCode) = 0; 9 };
2. Define the loading interface scenario, inherit from the PreloadListener, and implement the onXXX interface.
1 class LoadingLayer : 2 public Layer, public PreloadListener 3 { 4 public: 5 static Scene* scene(); 6 7 LoadingLayer(); 8 virtual ~LoadingLayer(); 9 10 virtual bool init();11 virtual void update(float dt) override;12 13 CREATE_FUNC(LoadingLayer);14 15 void initUI();16 void ToMainMenu();17 18 virtual void onStart() override;19 virtual void onProgress(int percent) override;20 virtual void onError(const char* info) override;21 virtual void onWarning(const char* info) override;22 virtual void onEnd(PreloadError errorCode) override;23 24 private:25 Node* m_pRootNode;26 27 Sprite* m_pNeedle;28 ui::LoadingBar* m_pLoadingBar;29 ui::Text* m_pTxtErrorInfo;30 31 long m_iBeginTime;32 long m_iEndTime;33 34 int m_iStart;35 };
Pay special attention to the onProgress interface. here we need to implement the logic of pointer rotation:
1 void LoadingLayer::onProgress(int percent)2 {3 float degree = LoadingLayerConstant::NeedleMinDegree +4 (LoadingLayerConstant::NeedleMaxDegree - LoadingLayerConstant::NeedleMinDegree) * percent / 100;5 m_pNeedle->setRotation(degree);6 }
3. Add a function to the loading task to report the loading progress. In this way, when loading an image or any resource file, you can call the notifyProgress function to rotate the pointer on the interface.
1 void Preload: notifyProgress (int progress) 2 {3 // here m_pListener is actually an instance of LoadingLayer 4 if (m_pListener) 5 {6 m_pListener-> onProgress (int) (progress * 100.f/m_iAllProgress); 7} 8}
Download source code
Next, we will analyze the core of the game: Airplane"