In the previous chapter we followed the complete HelloWorld source code, understand the Cocos2d-x startup process. The Director class runs through the entire Application program. This chapter thoroughly analyzes this class along with XiaoYu.
The habit of reading the source code of ainemo is to analyze the code layer by layer. when reading this class of Director, I encountered many other Cocos2d-x classes, my approach is to first take a rough look at the role of the class, fully understand the Director class, and then analyze other classes according to their importance.
1.1 point analysis CCDirector. h
#ifndef __CCDIRECTOR_H__#define __CCDIRECTOR_H__#include "CCPlatformMacros.h"#include "CCRef.h"#include "ccTypes.h"#include "CCGeometry.h"#include "CCVector.h"#include "CCGL.h"#include "CCLabelAtlas.h"#include "kazmath/mat4.h"NS_CC_BEGIN/** * @addtogroup base_nodes * @{ *//* Forward declarations. */class LabelAtlas;class Scene;class GLView;class DirectorDelegate;class Node;class Scheduler;class ActionManager;class EventDispatcher;class EventCustom;class EventListenerCustom;class TextureCache;class Renderer;#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)class Console;#endif
From the perspective of ccdirector. h including files and referenced classes, we can see what the Director class manages and get a preliminary understanding.
Management includes Label, Scene, GLView, and Node? I don't know what it is. We will analyze it later.) Scheduler (Program scheduling), ActionManager (animation management), EventDispatcher (event management), and EventCuston (also related to events) eventListenerCuston, TextureCache, Renderer, and Console)
This big Manager manages so many things. I will analyze the things one by one in the following chapters. Now, as long as we do not hinder the analysis of the big manager class "Director, you can ignore the specific implementation content of other classes for the moment.
Continue to see the definition of the ctor class.
class CC_DLL Director : public Ref
The Director class inherits the Ref class. For details, refer to the definition of the Ref class and you can see that it is a class used for reference counting. There are also PoolManager, AutoreleasePool, and so on, from the naming can understand the cocos2d-x has its own memory management mechanism, with reference to the number of records to determine whether the object should be released, the corresponding management class to control. Next we will analyze the memory management of the coocs2d-x separately. Here, we only know that the Director class is also controlled by the Unified Memory Manager.
The following describes the public functions of the ctor class.
static const char *EVENT_PROJECTION_CHANGED; static const char* EVENT_AFTER_UPDATE; static const char* EVENT_AFTER_VISIT; static const char* EVENT_AFTER_DRAW;const char *Director::EVENT_PROJECTION_CHANGED = "director_projection_changed";const char *Director::EVENT_AFTER_DRAW = "director_after_draw";const char *Director::EVENT_AFTER_VISIT = "director_after_visit";const char *Director::EVENT_AFTER_UPDATE = "director_after_update";
At first, several Event Director class event types are defined, which are engineering type changes in sequence, draw (rendering) visit (ACCESS) update (update) after the event, you can guess that after draw visit update, director can throw the external capture of the event and then process it yourself.
enum class Projection { /// sets a 2D projection (orthogonal projection) _2D, /// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500. _3D, /// it calls "updateProjection" on the projection delegate. CUSTOM, /// Default projection is 3D projection DEFAULT = _3D, };
This enumeration defines two engineering types: 2D 3D and custom. The default type is 3D game.
/** returns a shared instance of the director */ static Director* getInstance(); /** @deprecated Use getInstance() instead */ CC_DEPRECATED_ATTRIBUTE static Director* sharedDirector() { return Director::getInstance(); } /** * @js ctor */ Director(void); /** * @js NA * @lua NA */ virtual ~Director();
This Code shows that Director is also a singleton creation type. The sharedDirector interface for obtaining instances externally is provided;
virtual bool init();
Init initializes the entire ctor object. This function is very important. We will analyze it separately later.
Many method comments in the code below have already been described in great detail. Here we will simply repeat it, mostly the Get Set method.
/** Get the current running scenario of ctor. director can run only one scenario at a time */inline Scene * getRunningScene () {return _ runningScene ;} /** get the animation frame rate */inline double getAnimationInterval () {return _ animationInterval;}/** sets the animation frame rate. here we can see that this is a pure virtual function, therefore, ctor Ctor is an abstract class and cannot be instantiated. You must inherit this class to implement your own Director. */virtual void setAnimationInterval (double interval) = 0;/** ask if the frame rate is displayed in the lower left corner. We can see that there is an fps in helloworld, this should be the place where fps is displayed */inline bool isDisplayStats () {return _ displayStats;}/** sets whether to display frame rate */inline void setDisplayStats (bool displayStats) in the lower left corner) {_ displayStats = displayStats;}/** returns the time consumed by each frame. For example, if the frame rate is 60 per second, the returned value is 1/60 seconds */inline float getSecondsPerFrame () {return _ secondsPerFrame ;}
/** Obtain the GLView interface for the object that encapsulates OpenGl operations * @ js NA * @ lua NA */inline GLView * getOpenGLView () {return _ openGLView ;} void setOpenGLView (GLView * openGLView );
Texture cache objects
TextureCache* getTextureCache() const;
The following functions are used to control the interval between frames in a game loop. Two member variables are involved.
/* Indicates whether to clear (ignore) _ deltaTime */bool _ nextDeltaTimeZero in the next frame logic;
/* The interval between the last logical frame running and the current time is used to determine whether the next logical frame should be performed. The time of the last frame execution is recorded in the _ lastUpdate variable */float _ deltaTime;
The following two functions are used to determine whether the next _ deltaTime operation is effective. When the entire game is suspended, _ deltaTime will be accumulated continuously and the _ nextDeltaTimeZero variable will be used, if the value of _ deltaTime is 0 next time, the frame will not jump after the pause is restored, but the sequence of the current frame will start.
inline bool isNextDeltaTimeZero() { return _nextDeltaTimeZero; } void setNextDeltaTimeZero(bool nextDeltaTimeZero);
The computed _ deltaTime function is called in each logical loop.
/** Calculate the interval between the last logical frame call time of deltaTime and the current time of deltaTime. If nextDeltaTimeZero is true, deltaTime is 0 */void calculateDeltaTime ();/* interval between the last primary cyclic frame execution and current time _ deltaTime */float getDeltaTime () const;
Continue viewing code
/** Check whether the game is currently suspended. The variable record _ paused */inline bool isPaused () {return _ paused ;} /** how many frames are executed after ctor runs */inline unsigned int getTotalFrames () {return _ totalFrames;}/** set/read the _ projection variable to mark the project type 2d? 3d? @ Since v0.8.2 * @ js NA * @ lua NA */inline Projection getProjection () {return _ projection;} void setProjection (Projection projection ); /** set opengl viewport */void setViewport ();
Below are some coordinate operations
/** You can get the node Node of the notification message. For more information about the Node, see */node * geticationicationnode () const {return _ notificationNode ;} void seticationicationnode (Node * node); // some function annotations for setting and obtaining the window size are described in detail, no translation here/** returns the size of the OpenGL view in points. */const Size & getWinSize () const;/** returns the size of the OpenGL view in pixels. */Size getWinSizeInPixels () const;/** returns visible size of the OpenGL view in points. * the value is equal to getWinSize if don't invoke * GLView: setDesignResolutionSize () */Size getVisibleSize () const;/** returns visible origin of the OpenGL view in points. */Point getVisibleOrigin () const;/** converts a UIKit coordinate to an OpenGL coordinate Useful to convert (multi) touch coordinates to the current layout (portrait or landscape) */Point convertToGL (const Point & point ); /** converts an OpenGL coordinate to a UIKit coordinate conversion Useful to convert node points to window points for CILS such as glScissor */Point convertToUI (const Point & point ); /// XXX: missing description float getZEye () const;
The following are some of the most important methods of Scenario Management.
Let's take a look at some attributes of the Scene scenario.
/* The current execution scenario, which can be known by this variable, the Cocos2d-x can only execute one scenario at a time. */Scene * _ runningScene;/* The next scenario to be executed. This one must be used for */Scene * _ nextScene During scenario switching; /* Whether to clear the Scene flag. When it is true, the old Scene will receive the clear message */bool _ sendCleanupToScene;/* Scene stack */Vector <Scene *> _ scenesStack;
Through these several attributes on the scene can be roughly understood, the Cocos2d-x can only execute one scene at the same time, there is a _ nextScene when switching the scene. There is a tag _ scendCleanupToScene when clearing a scenario, and all the scenarios waiting for execution will have a _ scenesStack
/** Set the scenario to be executed */void runWithScene (Scene * scene);/** Add the new scenario to the execution stack, new scenarios will be executed immediately. When used, avoid too many scenarios in the stack and prevent insufficient device memory, you can call this method to switch between scenarios when a scenario is executed */void pushScene (Scene * scene);/** the last added scenario is displayed from the stack, when using this function, make sure that a scenario is executed and in the stack. The pop-up scenario will be cleared. If the stack is empty, Director will stop */void popScene ();/** by calling 'poptoscenestacklevel (1) 'This method only leaves the root scene for the scenario to be cleaned up in the stack, that is, the first scene left in the stack */void popToRootScene (); /** clean the scenes in the stack by stack level. When level = 0 is cleared completely = 1, it is popToRootScene () if the value exceeds the number of scenes in the stack, it will not process */void popToSceneStackLevel (int level);/** when a scenario is executed, replace the currently running scenario */void replaceScene (Scene * scene);/** stop the current scenario */void end ();/** suspend scenario */void pause (); /** restore scene after pause */void resume ();/** stop animation and all logics */virtual void stopAnimation () = 0; /** start animation loop */virtual void startAnimation () = 0;/** rendering SCENE */void drawScene ();
Below are some memory-controlled
/** Clear the Direct Memory Cache. Check the source code to find out all the memory resources including fonts, textures, and files */void purgeCachedData ();/** set the default value, for details, refer to the code and clearly write */void setdefavaluvalues ();
OpenGl operations
/** Set the default OpenGl value */void setgldefavaluvalues ();/** set whether to enable transparency */void setAlphaBlending (bool on ); /** set whether to enable the deep test */void setDepthTest (bool on );
The logic of all ctor scenarios in the Director Main Loop will be triggered here
virtual void mainLoop() = 0;
There are also some methods. After a simple review, you can know the approximate meaning of the naming. Some of them will be analyzed in detail in the following chapters.
/** Set/get the scaling ratio */void setContentScaleFactor (float scaleFactor); float getContentScaleFactor () const {return _ contentScaleFactor;}/** get the scheduling control object, this sched should be something similar to a fixed period and a bunch of callback methods. We will analyze this stuff later */Scheduler * getScheduler () const {return _ Scheduler ;} /** set the timer */void setScheduler (Scheduler * scheduler);/** obtain and analyze this class separately after setting the Action Manager object */ActionManager * getActionManager () const {return _ actionManager;} void setActionManager (ActionManager * actionManager);/** this class is analyzed separately after the get set operation of the event distributor */EventDispatcher * getEventDispatcher () const {return _ eventDispatcher;} void setEventDispatcher (EventDispatcher * dispatcher);/** analyze this class separately after the Renderer */renderer * getRenderer () const {return _ Renderer ;}
We have dissected the Director class above. There are several methods to focus on.
First look at the method to return the singleton object
Director* Director::getInstance(){ if (!s_SharedDirector) { s_SharedDirector = new DisplayLinkDirector(); s_SharedDirector->init(); } return s_SharedDirector;}
It is worth noting that the class DisplayLinkDirector is returned and the init method is called after the DisplayLinkDirector object is created,
No matter what the DisplayLinkDirector class is, it must be a subclass of Director, because Director is an abstract class.
Let's take a look at the init method. From this method, let's take a look at what Director can do, and how to complete some internal initialization work.
bool Director::init(void){ setDefaultValues(); // scenes _runningScene = nullptr; _nextScene = nullptr; _notificationNode = nullptr; _scenesStack.reserve(15); // FPS _accumDt = 0.0f; _frameRate = 0.0f; _FPSLabel = _drawnBatchesLabel = _drawnVerticesLabel = nullptr; _totalFrames = _frames = 0; _lastUpdate = new struct timeval; // paused ? _paused = false; // purge ? _purgeDirectorInNextLoop = false; _winSizeInPoints = Size::ZERO; _openGLView = nullptr; _contentScaleFactor = 1.0f; // scheduler _scheduler = new Scheduler(); // action manager _actionManager = new ActionManager(); _scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false); _eventDispatcher = new EventDispatcher(); _eventAfterDraw = new EventCustom(EVENT_AFTER_DRAW); _eventAfterDraw->setUserData(this); _eventAfterVisit = new EventCustom(EVENT_AFTER_VISIT); _eventAfterVisit->setUserData(this); _eventAfterUpdate = new EventCustom(EVENT_AFTER_UPDATE); _eventAfterUpdate->setUserData(this); _eventProjectionChanged = new EventCustom(EVENT_PROJECTION_CHANGED); _eventProjectionChanged->setUserData(this); //init TextureCache initTextureCache(); _renderer = new Renderer;#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8) _console = new Console;#endif return true;}
As you can see, the Director manager initializes the ActionManager Action Manager and adds _ actionManager to the timer.
Events such as EventDispatcher EventCustom are initialized.
Texture and Renderer initialized
Next let's take a look at the DisplayLinkDirector class.
This is the Director entity class.
class DisplayLinkDirector : public Director{public: DisplayLinkDirector() : _invalid(false) {} // // Overrides // virtual void mainLoop() override; virtual void setAnimationInterval(double value) override; virtual void startAnimation() override; virtual void stopAnimation() override;protected: bool _invalid;};
This class implements several key virtual functions of ctor.
MainLoop: This is the most important thing. We have repeatedly mentioned that the logical loop refers to this function. All operations, animations, rendering, and timers are driven here.
The main loop of the game repeatedly schedules mainLoop to implement various game actions and animations at a frame by frame ....... MainLoop is used to determine what to execute the current frame, whether to execute the time, and so on.
void DisplayLinkDirector::mainLoop(){ if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector(); } else if (! _invalid) { drawScene(); // release the objects PoolManager::getInstance()->getCurrentPool()->clear(); }}
The code is very simple. You can determine whether to clear mainLoop Based on purgeDirectorInNextLoop.
_ Invalid to determine whether Director should perform a logical Loop
This code is very simple. The main operations are closed to drawScene and we will follow up with drawScene to see what each logical frame has done.
There is also a code PoolManager: getInstance ()-> getCurrentPool ()-> clear (); from the naming point of view, it is to clear the operation, it should be a memory operation, reference objects not needed for each frame recycling should be triggered here. We will go back and discuss this in the memory application chapter.
The following shows the drawScene code.
Void Director: drawScene () {// calculate the time interval between frames. The following uses this interval to determine whether the calculateDeltaTime () operation should be performed (); // skip one flame when _ deltaTime equal to zero. if (_ deltaTime <FLT_EPSILON) {return;} if (_ openGLView) {_ openGLView-> pollInputEvents ();} // if Director does not pause, update the timer, distribute the update message if (! _ Paused) {_ schedcher-> update (_ deltaTime); _ eventDispatcher-> dispatchEvent (_ eventAfterUpdate);} // opengl clears glClear (GL_COLOR_BUFFER_BIT | percent ); /* set the next scenario */if (_ nextScene) {setNextScene ();} kmGLPushMatrix (); // global identity matrix is needed... come on kazmath! KmMat4 identity; kmMat4Identity (& identity); // Rendering scenario if (_ runningScene) {_ runningScene-> visit (_ renderer, identity, false ); // message _ eventDispatcher-> dispatchEvent (_ eventAfterVisit) After rendering in the distribution scenario;} // render the notifications node. The usage of this node is unclear, in the subsequent sections, we will understand if (_ icationicationnode) {_ notificationNode-> visit (_ renderer, identity, false);} if (_ displayStats) // rendering FPS and other frame rate display {showStats () ;}_ renderer-> render (); // The render method of the renderer is called, when analyzing the Render class, let's look back at what we have done. _ eventDispatcher-> dispatchEvent (_ eventAfterDraw); kmGLPopMatrix (); _ totalFrames ++; // swap buffers if (_ openGLView) {_ openGLView-> swapBuffers ();} if (_ displayStats) {calculateMPF ();}}
Till now, we have completely analyzed the Director class and learned which objects are managed by this butler. The following is a summary.
Director manages scenarios, distribution of four events, rendering, Opengl objects, etc.
It controls the logical frames of the game in units of scenarios, and implements changes to different interfaces of the game through scenario switching.
In fact, the mainloop function calls drawscene to implement the rendering logic of each frame.
In the previous chapter, we read that there is a run method in the application, and there is an endless loop in the run method, which is the main loop of the game, calling director> mainLoop in that endless loop is to continuously execute logical frame operations in the main game loop.
In the next section, we start from the basic analysis and look at the memory management mechanism of the ref class cocos2d-x.