原文:http://galoisplusplus.coding.me/blog/2014/07/30/memory-management-in-cocos2d-x-v3/


cocos2d-x移植自Objective C的cocos2d,其内存管理其实也来自于OC。因而对于写过OC程序的朋友来讲,cocos2d-x的内存管理应该是一目了然的,但对于本渣这枚没接触过OC的C++码农来说,或许直接看cocos2d-x源代码才是最直接快捷的方式。

Node类

我们首先来看Node类的代码,Node是cocos2d-x中极重要的基类,许多常用的SceneLayerMenuItem等都继承自Node。

Node的创建是通过以下的接口,该函数返回一个Node的静态对象指针:

1
2
3
4
5
6
7
8
9
/**  * Allocates and initializes a node.  * @return A initialized node which is marked as "autorelease".  */  * 分配空间并初始化Node  * 返回一个被初始化过且是autorelease的Node对象  */ static Node * create(); 

下面让我们来看这个函数的实现。该函数采用二段式创建的方式——首先用new operator在heap中开辟空间并进行简单的初始化,假如new返回一个合法地址(cocos2d-x没有采用c++的异常处理机制),则接着init函数用于实际初始化Node的成员。只有在这二者都成功后,才把创建的指针设为autorelease(关于autorelease后面会继续解释)并返回。

9 10 11 12 13
Node::create() {  ret = new Node();  if (ret && ret->init())  {  autorelease();  }  else  {  CC_SAFE_DELETE(ret);  }  return ret; } 

对于创建失败的情况,cocos2d-x使用了下面的宏保证该指针被delete且被设为nullptr:

1
#define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0) 

这个二段式的create函数在cocos2d-x中非常常用,因而cocos2d-x用了以下一个叫CREATE_FUNC来表示这个函数以便给继承Node的子类使用:

13 14 15 16 17 18 19 20
 * define a create function for a specific type,such as Layer  * @param \__TYPE__ class type to add create(),117)!important"> */ #define CREATE_FUNC(__TYPE__) \ static __TYPE__* create() \ { \  __TYPE__ *pRet = new __TYPE__(); \  if (pRet && pRet->init()) \  { \  pRet->autorelease(); \  return pRet; \  } \  else \  delete pRet; \  pRet = NULL; \  return NULL; \ } 

这样,继承Node的子类(例如ExampleLayer)只需要在类声明(class declaration)中加入CREATE_FUNC(类名)(例如CREATE_FUNC(ExampleLayer)),再override下init函数即可。

Ref类

在cocos2d-x中,Node类的父类是Ref类,之前我们所看到的autorelease方法实际上就来自于这个父类。

下面我们先来看Ref类的声明,这里为了突出重点,我们忽略script binding的情况:

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
class CC_DLL Ref { public:   * Retains the ownership.  *  * This increases the Ref's reference count.  * @see release,autorelease  * @js NA  */   * 拿到所有权  * 这会增加引用计数  */  void retain();    * Releases the ownership immediately.  * This decrements the Ref's reference count.  * If the reference count reaches 0 after the descrement,this Ref is  * destructed.  * @see retain,117)!important"> * 立即释放所有权  * 这会减少引用计数  * 如果更新后的引用计数为0,该Ref对象会被销毁 release();    * Releases the ownership sometime soon automatically.  * This descrements the Ref's reference count at the end of current  * autorelease pool block.  * @returns The Ref itself.  * @see AutoreleasePool,retain,release  * @lua NA  * 自动释放所有权  */  Ref* autorelease();    * Returns the Ref's current reference count.  * @returns The Ref's reference count.  * 返回该Ref对象的引用计数 unsigned int getReferenceCount() const;  protected:   * Constructor  * The Ref's reference count is 1 after construction.  * 构造函数  * 初始引用计数为1 Ref();   */  virtual ~ * 采用引用计数(reference counting)  * _referenceCount就是计数值  */  // count of references  _referenceCount;   friend AutoreleasePool;   // Memory leak diagnostic data (only included when CC_USE_MEM_LEAK_DETECTION is defined and its value isn't zero)  // 以下函数用于开启内存泄露检测时打印出泄露信息 #if CC_USE_MEM_LEAK_DETECTION public:  static printLeaks(); #endif }; 

从上面的代码,我们可以初步了解到:Ref采用引用计数(reference counting)的方法来管理某个指针所指向的某个对象,初始创建时计数是1,当计数变为0时该对象被析构;retain方法会增加计数并拿到所有权,而与之对应的,release方法会减少计数;autorelease是把所有权交给友类(friend class)AutoreleasePool,让它来决定何时减少计数,这个类我们后面会继续谈到。

下面我们来看Ref类的实现(deFinition):

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
trackRef(ref); untrackRef(ref); #endif  // 在初始化列表中将计数设为1 Ref::Ref() : _referenceCount(1) // when the Ref is created,the reference count of it is 1 { // 假如开启内存泄露检测,则追踪该对象指针,将该对象指针放入一个列表(list)中 // 后面的代码我们很快就会看到这个list #if CC_USE_MEM_LEAK_DETECTION  trackRef(this); #endif }  Ref::~Ref() { // 假如开启内存泄露检测且引用计数非0,则在追踪列表中找到该对象指针并删除 #if CC_USE_MEM_LEAK_DETECTION  _referenceCount != 0)  untrackRef(#endif }  // retain只是单纯将计数递增 retain() {  // CCASSERT是cocos2d-x对C++的assert所封装的宏  CCASSERT(_referenceCount > 0, "reference count should greater than 0");  ++_referenceCount; }  release() {  // 首先计数递减  "reference count should greater than 0");  --_referenceCount;   // 计数为0,应当析构对象  _referenceCount == 0)  { #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  // 得到一个PoolManager单例的对象  // PoolManager类后面会解释  auto poolManager = PoolManager::getInstance();  // 后面会详细解释这段代码  if (!poolManager->getCurrentPool()->isClearing() && isObjectInPools(this))  {  // 以下的注释很重要,很快会解释到  // Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.  // This happens when 'autorelease/release' were not used in pairs with 'new/retain'.  //  // Wrong usage (1):  // auto obj = Node::create(); // Ref = 1,but it's an autorelease Ref which means it was in the autorelease pool.  // obj->autorelease(); // Wrong: If you wish to invoke autorelease several times,you should retain `obj` first.  // Wrong usage (2):  // auto obj = Node::create();  // obj->release(); // Wrong: obj is an autorelease Ref,it will be released when clearing current pool.  // Correct usage (1):  // |- new Node(); // `new` is the pair of the `autorelease` of next line  // |- autorelease(); // The pair of `new Node`.  // obj->retain();  // obj->autorelease(); // This `autorelease` is the pair of `retain` of prevIoUs line.  // Correct usage (2):  // obj->release(); // This `release` is the pair of `retain` of prevIoUs line.  CCASSERT(false,152)!important">"The reference shouldn't be 0 because it is still in autorelease pool.");  } // 假如开启内存泄露检测,则在追踪列表中找到该对象指针并删除 #endif  // 调用析构函数并释放空间  delete this;  } }  // 把该对象指针交给友类AutoreleasePool(具体来说,是PoolManager单例对象所得到的当前的AutoreleasePool)来管理 autorelease() {  getInstance()->addobject(this);  return this; }  const {  _referenceCount; }  #if CC_USE_MEM_LEAK_DETECTION  // 这里便是存放所追踪的对象指针的列表 std::list<Ref*> __refAllocationList;  printLeaks() {  // Dump Ref object memory leaks  __refAllocationList.empty())  {  log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");  }  "[memory] WARNING: %d Ref objects still active in memory.", (int)size());   // C++的range-for语法  // 打印出每个泄露内存的对象指针的类型和引用计数  for (const auto& ref : __refAllocationList)  {  CC_ASSERT(ref);  const char* type = typeid(*ref).name();  "[memory] LEAK: Ref object '%s' still active with reference count %d.(type ? type : ""), ref->getReferenceCount());  }  } }  // 将对象指针放入列表中 ref) {  ref,152)!important">"Invalid parameter,ref should not be null!");   // Create memory allocation record.  push_back(ref); }  // 在列表中找到该对象指针并删除 ref) {  iter = find(begin(),210)!important">end(),210)!important">iter == end())  {  "[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.name());  return;  }   erase(iter); }  #endif // #if CC_USE_MEM_LEAK_DETECTION 

这段源代码对使用者最重要的在于release函数中的注释:

  • 当Ref的计数变为0时,它一定不能在AutoreleasePool中。

  • Ref的计数为0且同时在AutoreleasePool中的错误是由new/retain和autorelease/release没有对应引起的(有木有想起C++中new和delete没对应所引起的内存泄露?):

    • autorelease缺乏对应的retain。 例如:
2
obj = create(); // 注意create函数会调用autorelease方法,因此obj已经没有该指针的所有权了 obj->autorelease(); // obj没有所有权,因此无法再把所有权转交给AutoreleasePool,若要调用autorelease方法需要先调用retain拿到所有权 
- release缺乏对应的retain。

例如:

2
release(); // obj没有所有权,因此无法再控制计数(所有权在AutoreleasePool),若要调用release方法需要先调用retain拿到所有权 
  • 正确的用法是在create后调用autorelease或release方法前先用retain拿到所有权: 例如:
9
// 前面我们分析过create函数,它会先用new operator得到对象,再调用autorelease方法 // 这里new和autorelease对应 create();  |- Node();  |- autorelease();  // 这里retain和autorelease对应,autorelease一个已经被autorelease过的对象(例如通过create函数构造的对象)必须先retain retain(); autorelease(); 

又如:

4
create(); // 这里retain和release对应,release一个已经被autorelease过的对象(例如通过create函数构造的对象)必须先retain release(); 

AutoreleasePool类

现在我们来看Ref类的友类AutoreleasePool。 首先来看类声明:

110
AutoreleasePool {  * @warn Don't create an auto release pool in heap,create it in stack.  * 警告:不要在heap上构造AutoreleasePool对象,要在stack上构造 AutoreleasePool();    * Create an autorelease pool with specific name. This name is useful for debugging. AutoreleasePool(const string &name);    */  ~ * Add a given object to this pool.  * The same object may be added several times to the same pool; When the  * pool is destructed,the object's Ref::release() method will be called  * for each time it was added.  * @param object The object to add to the pool.  * 把指定的对象指针放到AutoreleasePool对象中  * 注意:  * 同一对象的指针可能会被多次加入到同一AutoreleasePool对象中;  * 当该AutoreleasePool对象被析构时,该对象指针被加入多少次,就得调用多少次该对象的release()函数  * 这是因为AutoreleasePool用vector而非set来存放所管理的对象指针,因此不会去重 addobject(Ref *object);    * Clear the autorelease pool.  * Ref::release() will be called for each time the managed object is  * added to the pool.  * 清空AutoreleasePool  * 每个被管理的对象指针被加入多少次,就会调用多少次release()函数 clear();  #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)   * Whether the pool is doing `clear` operation. bool isClearing() const { _isClearing; }; #endif    * Checks whether the pool contains the specified object.  * 检查AutoreleasePool对象是否管理某个对象指针 contains(object) const;    * Dump the objects that are put into autorelease pool. It is used for debugging.  * The result will look like:  * Object pointer address object id reference count dump();  private:   * The underlying array of object managed by the pool.  * Although Array retains the object once when an object is added,proper  * Ref::release() is called outside the array to make sure that the pool  * does not affect the managed object's reference count. So an object can  * be destructed properly by calling Ref::release() even if the object  * is in the pool.  * AutoreleasePool对象将它所管理的对象指针放到下面的vector中  * 尽管每次有对象指针加到该vector中时,该vector实际上retain拿到了所有权,  * 但是Ref::release()会被调用来保证AutoreleasePool不会改变它所管理的对象指针  * 的引用计数。  * 所以,当某个对象指针被放到AutoreleasePool类中管理时,仍然可以通过调用  * Ref::release()函数来析构它 vector<_managedobjectArray;  string _name;   * The flag for checking whether the pool is doing `clear` operation. _isClearing;  从类声明中能解读出的最重要的信息是AutoreleasePool类用STL vector来存放它所管理的Ref所指向的对象。要搞清楚原理还需要继续看它的实现: 
 
77
AutoreleasePool::AutoreleasePool() : _name("") #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) ,210)!important">_isClearing(false) #endif {  _managedobjectArray.reserve(150);  // 每个新创建的AutoreleasePool对象都交由PoolManager单例对象统一管理  push(this); }  name) : _name(name) AutoreleasePool::~AutoreleasePool() {  cclOGINFO("dealLocing AutoreleasePool: %p",22)!important">this);  // 清空该AutoreleasePool  clear();   // 要析构的AutoreleasePool对象不再由PoolManager管理  pop(); }  // 只是单纯调用vector::push_back加入所管理的对象 object) {  object); }  // clear函数就是AutoreleasePool调用release来管理对象的引用计数的地方 clear() { #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)  _isClearing = true; // 调用每个在AutoreleasePool的对象指针的release方法  auto &obj : _managedobjectArray)  {  release();  }  // 清空存放管理对象的vector  clear(); false; // 线性搜索所管理的对象指针的vector,查看所指定的Ref指针是否存在 _managedobjectArray)  {  obj == object)  return true;  }  false; }  dump() {  cclOG("autorelease pool: %s,number of managed object %d_name.c_str(),22)!important">static_cast<int>(size()));  "%20s%20s%20s",152)!important">"Object pointer",152)!important">"Object id",152)!important">"reference count");  CC_UNUSED_ParaM(obj);  "%20p%20uobj,210)!important">getReferenceCount());  } } 

PoolManager类

下面我们来看PoolManager类,在cocos2d-x中,这个类是典型的单例(singleton)工厂类——及有且只有一个PoolManager对象,该Poolmanger有一个存放AutoreleasePool对象指针的stack,该stack是由STL::vector实现的。需要注意的是,cocos2d-x的单例类都不是线程安全的,跟内存管理紧密相关的PoolManager类也不例外,因此在多线程中使用cocos2d-x的接口需要特别注意内存管理的问题。

我们先来看类声明:

44
PoolManager { CC_DEPRECATED_ATTRIBUTE PoolManager* sharedPoolManager() { getInstance(); }  getInstance();   purgePoolManager() { destroyInstance(); }  destroyInstance();    * Get current auto release pool,there is at least one auto release pool that created by engine.  * You can create your own auto release pool at demand,which will be put into auto releae pool stack. AutoreleasePool *getCurrentPool() const;   isObjectInPools(obj) AutoreleasePool;  private:  // singleton类把构造函数和析构函数设为private,避免被调用  PoolManager();  ~PoolManager();   push(pool);  pop();   s_singleInstance;   // 同样用vector来存放所管理AutoreleasePool对象指针的列表  AutoreleasePool*> _releasePoolStack; }; 

再来看类实现:

66
s_singleInstance = nullptr;  getInstance() {  s_singleInstance == nullptr)  {  s_singleInstance = PoolManager();  // Add the first auto release pool  AutoreleasePool("cocos2d autorelease pool");  }  s_singleInstance; }  destroyInstance() {  delete s_singleInstance;  nullptr; }  PoolManager() {  _releasePoolStack.10); }  PoolManager::~"dealLocing PoolManager: %p",22)!important">this);   // 逐个析构所管理的AutoreleasePool对象  while (!AutoreleasePool* pool = back();   pool;  } }  // 加入AutoreleasePool对象指针时用的是stl::vector的push_back函数, // 于是调用back函数就可以得到最新被加入的AutoreleasePool对象指针 back(); }  // 线性搜索每个被管理的AutoreleasePool, // 每个AutoreleasePool对象再用contains函数线性搜索一遍 pool : _releasePoolStack)  {  pool->obj))  pool) {  pool); }  pop() {  CC_ASSERT(!empty());  pop_back(); } 

最后的疑问

想必各位用惯了c++的看官在看完了以上的代码之后,最有疑问的还是神秘的Ref::autorelease函数。我们从AutoreleasePool的源代码看到,事实上被autorelease的对象最后还是通过release函数来减少其引用计数的,只不过release函数不是由使用者来调用,而是AutoreleasePool来调用,调用的地方在AutoreleasePool::clear()函数。那么AutoreleasePool如何个「auto」自动管理内存法儿?AutoreleasePool::clear()会在哪个地方被调用?

谜底隐藏在cocos/base/CCDirector.cpp中:

15
displayLinkDirector::mainLoop() {  _purgeDirectorInNextLoop)  {  _purgeDirectorInNextLoop = false;  purgeDirector();  }  else if (! _invalid)  {  drawScene();   // release the objects  clear();  } } 

这里就不纠缠Director类的实现细节了,上面的代码揭示的事实是:在图像渲染的主循环中,如果当前的图形对象是在当前帧,则调用显示函数,并调用AutoreleasePool::clear()减少这些对象的引用计数。mainLoop是每一帧都会自动调用的,所以下一帧时这些对象都被当前的AutoreleasePool对象release了一次。这也是AutoreleasePool「自动」的来由。

Cocos2d-x V3.x内存管理分析的更多相关文章

  1. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. ios – 仅在异步函数完成执行后运行代码

    所以,例如:如果问题是你不知道要调用什么函数,你可以配置你周围的函数/对象,这样有人可以给你一个函数,然后你在我上面说“调用函数”的地方调用你的函数.例如:

  3. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  4. ios – 使用带有NodeJs HTTPS的certificates.cer

    我为IOS推送通知生成了一个.cer文件,我希望将它与NodeJSHTTPS模块一起使用.我发现HTTPS模块的唯一例子是使用.pem和.sfx文件,而不是.cer:有解决方案吗解决方法.cer文件可以使用两种不同的格式进行编码:PEM和DER.如果您的文件使用PEM格式编码,您可以像使用任何其他.pem文件一样使用它(有关详细信息,请参见Node.jsdocumentation):如果您的文件使

  5. iOS 7,用于断开调用的私有API CTCallDisconnect不起作用

    谢谢!

  6. ios – 监控CBPeripheral状态变化

    我在CoreBluetooth库中找不到任何暴露的东西,我想在CBperipheralstate发生变化时调用一个函数.现在我只有一个switch语句来检查外设状态,但它总是只返回连接或断开连接.我如何进入连接/断开连接的情况?

  7. 处理内存管理和iOS Cordova项目?

    任何人都可以告诉我如何处理基于iOSCordova的项目中的内存管理“收到内存警告”当我在iPhone或iPad上运行时,我在iOSCordova项目中收到此警告.我在我的应用程序中使用CDVlocation进行地理定位.我主要是在加载基于地图的视图时收到此消息.我正在使用基于ARC的Xcode项目任何帮助管理内存警告与cordova“收到内存警告”将不胜感激.谢谢你们解决方法在CDVPlugin.m中尝试这种方式

  8. ios – Xcode游乐场不执行功能

    我创建了一个新的游乐场,我添加了简单的功能,但该功能从未被调用过:你们中的任何人都知道为什么函数没有被调用?我真的很感谢你的帮助解决方法因为你没有调用该函数.只需称呼它:

  9. ios – Swift中没有输入参数的通用函数?

    我有一个通用的Swift函数,如下所示:编译器没有错误,但我不知道如何调用此函数.我试过了:但它不起作用.如何在没有输入参数的情况下在Swift中调用Generic函数?解决方法你需要通过一些调用上下文告诉Swift返回类型是什么:注意,在后一种情况下,只有当someCall采用类似于Any的模糊类型作为其参数时,才需要这样做.相反,someCall被指定为[Int]作为参数,函数本身提供上下文,你可以只写someCall事实上,有时可以非常推断出背景!

  10. xcode – Swift 1.2中的神秘崩溃 – 仅在版本中构建

    我的理论是,Swift的内存管理在优化的版本编译中出现了一些问题,只是在NSArray从Cocoa到达的特定情况下,在我们的代码可以获得它们之前,我们将它们桥接到[AnyObject].这样的NSArray没有正确地穿过桥.但是通过转换到NSArray然后回到特定的[SomeType]Swift数组,问题就解决了.当然,我认为当苹果公司指出这一点时,他们会解决这个问题,然后我们可以停止使用这种解决方法.但直到那时,我的应用程序再次在Release版本中运行.

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部