Cocos2d-x的几个注意点

这段时间渐渐地从剑II的项目中退出来,来说研究一些cocos2d-x相关的内容。

Cocos2d-x虽然是使用C++来实现的,但是代码显得很怪异。比如下面的代码:

1
2
3
4
5
6
do
{
CC_BREAK_IF(! CCLayer::init());
CCSprite* bomb1 = CCSprite::create("CloseNormal.png");
addChild(bomb1,1);
}while (0)

这里之所以要用do…while,仅仅只是为了使用CC_BREAK_IF这个宏。非但打乱了C++的语法,而且也是代码复杂化了。

另外一个需要注意的地方是它的对象的生命周期管理,和C++通行的逻辑不同。看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
class CSpriteSheetMgr{
public:
CSpriteSheetMgr();
~CSpriteSheetMgr();
private:
cocos2d::CCDictionary *m_pBatchNodeDic;
};

CSpriteSheetMgr::CSpriteSheetMgr()
{
m_pBatchNodeDic = CCDictionary::create();
}

如果你以为m_pBatchNodeDic的生命周期是跟随类的实例的话,那么就错了。在每一个Message Loop之后,cocos2d-x引擎都会尝试去释放这类对象,可能你还没有用到,他就已经无效了。这个就是cocos2d-x的内存管理模型Reference Count and AutoReleasePool

正确的,需要写成:

1
2
3
4
5
CSpriteSheetMgr::CSpriteSheetMgr()
{
m_pBatchNodeDic = CCDictionary::create();
m_pBatchNodeDic->retain();
}

Dictionary Assignment Breaks the Iteration!

Let’s check the following code first:

dict[btn1] = false;
dict[btn2] = false;
dict[btn3] = false;

for (var key:Object in dict)
{
       dict[key] = true;
}

How many times do you think the loop would be entered in this case?

NOT only 3!

I run the codes in flash player 11.1, it sometimes was 4! Under flash player 11.5, I haven’t encountered any issue.

在flash builder中显示workspace路径

默认的flash builder启动后没有显示workspace的信息,对于在多个workspace中切换的开发人员来说,非常不方便。

flash builder是基于eclipse的,而在eclipse中有一个启动参数 -showLocation ,可以用来在标题栏中显示workspace的路径。

对于windows,有几种在启动时候添加参数的方法,但是对windows 7来说,如果你使用pin功能把flash builder固定在任务栏上,那么最好使用在配置文件中增加启动参数的方法:

打开FlashBuilder.ini,在第一行加入-showLocation。也可以同时在eclipse目录下的eclipse.ini文件中加入该参数。这样,但你启动flash builder的时候,不会在任务栏出现两个图标。

OSX 下提取pkg包中的文件

今天犯了个错误,本来想把几个文件夹复制到/usr/share/vim/vim73/目录下合并的,谁知osx下却是替换的操作(系统确实有替换的提示,但是windows下的目录,都是合并的操作:-()——它真的替换了,结果原来的文件丢失了。

系统是OSX 10.8,没有从安装盘装回软件的选项,只有自己动手。

1. 翻出安装盘挂载,很明显应该是在Packages目录下的文件;

2. 逐个启动里面的pkg文件,但是并不进行下一步,而是选择 File->Show Files 查看是否所需的包。发现是在BSD.pkg里面;

3. 启动Terminal,运行命令 pkgutil –expand BSD.pkg ~/wxue/bsd,把文件都提取到bsd目录下;

4. 进入~/wxue/bsd 目录,把最大的那个文件(Payload)增加后缀.pax.gz;

5. 接下去就简单了,解压即可。

Python处理多个不同编码的文件

当需要把多个不同编码的文件合并成一个的时候,unicode是一个很好的选择。但是如果把不同的编码转换成unicode,python并没有提供一个很好的方法。

Python内部是使用unicode来编码,但要把不同的编码转换成unicode,首先需要知道需要转换的内容是什么编码的。这种设计用来处理转码特定的编码文件没有问题,但是要写一个通用的工具,将是不可能的任务。

但是对于一些特殊的项目,还是可以做一些事情的。有一个检测文本编码类型的库可以借用:chardet

chardet的使用很简单。它的输入如下:

1
{'confidence': 0.62467997951868914, 'encoding': 'GB2312'}

从它的输出可以看到,这并不是一个非常有效的方法,但是聊胜于无。

在我的案例里面,是这样使用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sf = codecs.open("xxx.txt", "w", "utf-8")

of1 = codecs.open(f1, "r")

line1 = of1.read()
enc = chardet.detect(line1)
print enc
if enc['encoding'] == 'utf-8':
line1 = unicode(line1, 'utf-8')

else:
line1 = line1.decode('gb18030')

sf.write(line1)

对中文需要注意的是, 虽然chardet 能检测出的中文编码是gb2312,但是最好用gb18030来处理它,后者的覆盖会更全一点。

pureMVC对项目并无益处

1. 首先,在稍大一点的项目中,pureMVC的消息传递会成为一个比较严重的瓶颈。

2. 其次, 使用麻烦。先要定义消息,注册消息,创建command,注册command,分发command,处理command等等。绝大多数情况下,程序需要处理的只是command中携带的数据,而非command本身,以上步骤完全是不必要的。

  1. 太过于灵活。一个框架,如果既可以这样使用,也可以那样使用,那么用户基本上不知道该怎么使用。pureMVC的消息传递并非只有一条路径。Message和Notification可以在很多地方发起。当一个框架的使用需要用文档而不是代码本身来规范,这个框架的设计通常都是不知所谓的。

 

这三点是互相影响的。比如,考虑到灵活、强大,必然会导致性能问题,以及使用上的复杂化。 对于框架上的选择,并不推荐使用类似的设计。这种设计模式的框架,和通常的库框架——比如MFC——不同,并不能提供任何功能上的好处,实在是没引入的必要,特别是对那些规模较大的项目。

Flash builder项目多国化设置问题

在配置多国化的时候,在ActionScript Compiler的Additional Compiler argument中是这样设置的:

-locale zh_CN
其他都没有问题,但是ActionScript Build Path中的Output folder怎么都无法设置。 最后查到的问题是,Additional Compiler argument应该是如下设置的:
-locale=zh_CN

ADDED_TO_STAGE的触发时机

Adobe的help文件说,这个event的触发时机是:

The following methods trigger this event: DisplayObjectContainer.addChild(), DisplayObjectContainer.addChildAt().

这个很容易给人误导。设想一下有显示对象A、B、C,他们的关系如下:

1
2
3
B.addChild(C);
A.addChild(B);
stage.addChild(A);

当我们在ADDED_TO_STAGE事件,什么时候会触发呢?是在
B.addChild(C);的时候?还是在
A.addChild(B);的时候?还是在
stage.addChild(A);的时候?

答案是在stage.addChild(A);的时候,ADDED_TO_STAGE事件才会触发。

所以,在程序里,一定要Event.ADDED_TO_STAGE和Event.REMOVED_FROM_STAGE同时出现,这样会减少很多Bug。因为如果Event.ADDED_TO_STAGE没有触发,Event.REMOVED_FROM_STAGE也是铁定不会触发的。

 

有一种错误的使用方法是,把事件注册写在构造函数里面,然后在Event.REMOVED_FROM_STAGE事件中移除注册的事件,其实很可能事件移除是不会被调用的。切记!

Breakpoint acts weirdly

Encountered the break point issue in visual studio 2008, which says

The breakpoint will not currently be hit. The source code is different from the original version
Or, the IDE complains nothing, but the step command jumping between lines.

In my project, I encountered this issue in only a few files .

Well, the root cause is, those files were mixed different line endings!

So, the solution is very easy, save the files in a uniform line ending format, like windows (CR LF).

BitmapData内存测试

测试平台
Windows 7 sp1 x64
Flash builder 4.5.1
IE 9.0 with flash player plug-in 10.3.181.26

测试工具
Profiler

测试内容

  1. removeChild调用是否必要
  2. BitmapData的dispose调用是否必要
  3. Bitmap和BitmapData的对象是否需要设置为null
  4. BitmapData对象赋值语句的影响
     

测试结果

  1. removeChild必须调用
  2. dispose并非必要,只需要把其对象设置为null
  3. 需要释放任然在作用域中的对象,需要设置其为null
  4. 只是增加了引用,所以两个对象的数据是一样的。
    结论

  5. 不需要的显示对象需要及时调用removeChild删除

  6. 不再需要的对象应该手动设为null
  7. 如果容器对象没有设置为null,即使调用了removeChild,其元素也不会被释放
    测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.System;

public class memtest extends Sprite
{

private var bmp:Bitmap;
private var mouseDown:Boolean;
private var bmpData:BitmapData;
private var bitmap:Bitmap;
private var memory:uint;

public function memtest()
{
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(Event.ENTER_FRAME, onFrame);
}

private function onFrame(event:Event):void
{
if (bmpData != null && bmp != null)
{
var seed:Number = Math.floor(Math.random() * 10);
var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE;
bmpData.perlinNoise(100, 100, 6, seed, false, true, channels, false, null);
}
}

private function onMouseDown(event:MouseEvent):void
{
memory = System.totalMemory;

if (!mouseDown)
{
bmpData = new BitmapData(200, 200, false, 0x00CCCCCC);
bmp = new Bitmap(bmpData);
addChild(bmp);

var bmpDataCloned:BitmapData = bmpData;
bitmap = new Bitmap(bmpDataCloned);
bitmap.x = 300;
addChild(bitmap);

mouseDown = true;
}
else
{
removeChild(bmp);
bmp.bitmapData.dispose();
// bmpData.dispose();
// bmpData = null;
bmp = null;

removeChild(bitmap);
// bitmap.bitmapData.dispose();
bitmap = null;

mouseDown = false;
}
}
}
}