[{"data":1,"prerenderedAt":399},["ShallowReactive",2],{"blog-page-34":3,"blog-count":398},[4,107,167,289,337],{"id":5,"title":6,"body":7,"date":83,"description":13,"extension":84,"meta":85,"navigation":88,"path":100,"seo":101,"stem":102,"tags":103,"__hash__":106},"blogs\u002F_legacy\u002F2013\u002F2013-01-17-cocos2d-x%e8%af%bb%e5%8f%96phisicaleditor%e8%be%93%e5%87%ba%e6%96%87%e4%bb%b6.md","cocos2d-x读取PhisicalEditor输出文件",{"type":8,"value":9,"toc":79},"minimark",[10,14,17,24,27,38,45,51,56,59,65,68,71],[11,12,13],"p",{},"今天本来想找一个比较方便易用的动画编辑还载入方案，无意中翻到了PhisicalEditor这个软件。",[11,15,16],{},"这个软件和TexturePacker是一个作者出的，功能是用于快速生成用于box2d的碰撞图形。当时下载的时候没有注意过这个软件，因为大多数时候我们都不会用到需要精确到接近像素级的碰撞检测，通常而言采用适当的圆形或者矩形就可以很好的模拟游戏中的大部分碰撞行为了。在实际思考过之后，我发现这个软件还是可以很好的提高开发速度的。只要将它和texturepacker结合使用的话就可以很好的快速生成图形和碰撞了。而且它在需要进行真实物理模拟的游戏中还是很有作用的。",[11,18,19],{},[20,21],"img",{"alt":22,"src":23},"物理模拟","\u002Fwp-content\u002Fuploads\u002F2013\u002F01\u002Funnamed-file-300x233.jpg",[11,25,26],{},"可是在实际开始进行测试的时候遇到了一个问题，cocos2d-x目前的版本号是2.1beta，前不久刚进行过一次大的版本迁移。所以很多能够找到的代码都不能用了，很不幸的是，phisicaleditor提供的cocos2d-x接口就不能用了。于是我便开始改造这个接口，一些大的引擎方面的变更就不提了。主要的还是在核心算法上有很大的变动，因为原本的代码使用的数据结构和目前代码提供的数据结构已经可以说是完全的不同了。虽然有想过使用tinyxml来替代，但是衡量了一下还是决定改用新的数据结构。",[28,29,34],"pre",{"className":30,"code":32,"language":33},[31],"language-text","CCArray *fixtureList = (CCArray *)(bodyData->objectForKey(\"fixtures\"));\n        FixtureDef **nextFixtureDef = &(bodyDef->fixtures);\n        \u002F\u002F\u003CObjectDict *>\n        int con = fixtureList->count();\n        for (int i = 0; i\u003Ccon; i++) {\n            b2FixtureDef basicData;\n            ObjectDict *fixtureData = (ObjectDict *) fixtureList->objectAtIndex(i);\n\n            basicData.filter.categoryBits = static_cast\u003CCCString *>(fixtureData->objectForKey(\"filter_categoryBits\"))->intValue();\n            basicData.filter.maskBits = static_cast\u003CCCString *>(fixtureData->objectForKey(\"filter_maskBits\"))->intValue();\n            basicData.filter.groupIndex = static_cast\u003CCCString *>(fixtureData->objectForKey(\"filter_groupIndex\"))->intValue();\n            basicData.friction = static_cast\u003CCCString *>(fixtureData->objectForKey(\"friction\"))->floatValue();\n            basicData.density = static_cast\u003CCCString *>(fixtureData->objectForKey(\"density\"))->floatValue();\n            basicData.restitution = static_cast\u003CCCString *>(fixtureData->objectForKey(\"restitution\"))->floatValue();\n            basicData.isSensor = (bool)static_cast\u003CCCString *>(fixtureData->objectForKey(\"isSensor\"))->intValue();\n\n            CCString *cb = static_cast\u003CCCString *>(fixtureData->objectForKey(\"userdataCbValue\"));\n\n            int callbackData = 0;\n","text",[35,36,32],"code",{"__ignoreMap":37},"",[11,39,40,41],{},"这个解析函数是通过xml的解析来完成box2d相关属性的读入和初始化，所以其实一直都在重复着类似这样的改动。",[42,43,44],"del",{},"其中比较奇怪的一点是，新的cocos2d-x中的map并没有提供用于遍历的函数。而原来似乎是有着类似于iter的机制的。于是只能采用AllKey()方法获得key的ccarray然后遍历，这样的话效率就会有所下降。使用迭代器的话，遍历的复杂度是n。而这样的话就会变成n*log(n)了，不过一般情况下这个配置文件是不太可能有这么多的节点的，所以实际上影响不会很大。因为好奇所以看了下AllKey()方法的实现，发现是这样的：",[28,46,49],{"className":47,"code":48,"language":33},[31],"~~CCArray* CCDictionary::allKeys()\n{\n int iKeyCount = this->count();\n if (iKeyCount \u003C= 0) return NULL;\n\n CCArray* pArray = CCArray::createWithCapacity(iKeyCount);\n\n CCDictElement *pElement, *tmp;\n if (m_eDictType == kCCDictStr)\n {\n HASH_ITER(hh, m_pElements, pElement, tmp) \n {\n CCString* pOneKey = new CCString(pElement->m_szKey);\n pOneKey->autorelease();\n pArray->addObject(pOneKey);\n }\n }\n else if (m_eDictType == kCCDictInt)\n {\n HASH_ITER(hh, m_pElements, pElement, tmp) \n {\n CCInteger* pOneKey = new CCInteger(pElement->m_iKey);\n pOneKey->autorelease();\n pArray->addObject(pOneKey);\n }\n }\n\n return pArray;\n}~~\n",[35,50,48],{"__ignoreMap":37},[11,52,53],{},[42,54,55],{},"显然的内部还是有迭代机制的，但是似乎不对外开放了。",[11,57,58],{},"更正：当时没仔细研究，ccdic是有遍历的接口的。一般情况下使用类似下面的宏就可以了：",[28,60,63],{"className":61,"code":62,"language":33},[31],"CCDictElement* cde = NULL;\nCCDICT_FOREACH(m_itemlist,cde){\n}\n",[35,64,62],{"__ignoreMap":37},[11,66,67],{},"做完上面的改动之后，运行时会报错，错在ccsprite的gldrawarray上。这时候才能理解到opengl是一个状态机这句话，似乎两个版本之间对opengl状态的修改有所不同，于是在重载了的ondraw中把所有的修改gl的代码一一注释掉了。另外有一个奇怪的地方是，用于b2world调试输出gles-render似乎被从引擎核心里移除了？于是只能从官方的例子中把这个文件给拷了过来。",[11,69,70],{},"总体来看的话其实改动的地方并不多，只是折腾来折腾去的却也花了不少时间。于是把修改后的代码直接传上来，方便有用到的同学就不用再改了。鉴于以前下载别人代码的经验，把整个工程文件打上来了。",[11,72,73],{},[74,75,76],"a",{"href":76,"rel":77},"http:\u002F\u002Fpan.baidu.com\u002Fshare\u002Flink?shareid=201320&uk=1913549955",[78],"nofollow",{"title":37,"searchDepth":80,"depth":81,"links":82},2,3,[],"2013-01-17","md",{"layout":86,"status":87,"published":88,"author":89,"author_login":90,"author_email":91,"author_url":92,"wordpress_id":93,"wordpress_url":94,"date_gmt":95,"excerpt":96},"post","publish",true,{"display_name":90,"login":90,"email":91,"url":92},"chaoshikari","chaoshikari@gmail.com","\u002F",548,"\u002F\u002F?p=548","2013-01-17 15:36:38 +0000",{"type":8,"value":97},[98],[11,99,13],{},"\u002F2013-01-17-cocos2d-x读取phisicaleditor输出文件",{"title":6,"description":13},"_legacy\u002F2013\u002F2013-01-17-cocos2d-x%e8%af%bb%e5%8f%96phisicaleditor%e8%be%93%e5%87%ba%e6%96%87%e4%bb%b6",[104,105],"cocos2d-x","PhisicalEditor","xoAd0yytvCPcz0bZX9_3l14TWi8fh7hLRw8TPUVcBGc",{"id":108,"title":109,"body":110,"date":150,"description":114,"extension":84,"meta":151,"navigation":88,"path":160,"seo":161,"stem":162,"tags":163,"__hash__":166},"blogs\u002F_legacy\u002F2012\u002F2012-12-28-c%e4%b8%ad%e6%96%87%e5%88%86%e5%89%b2.md","C++中文分割",{"type":8,"value":111,"toc":148},[112,115,118,121,124,136,139,145],[11,113,114],{},"其实最近一直都有遇到c++内的中文字符串问题，不过一直都通过各种方法绕过了。",[11,116,117],{},"可是今天遇到了需要将字符串的中文和英文按字符分割的问题，实在是没有取巧的方法了。",[11,119,120],{},"由于从资源文件读取出来的是utf-8编码，而输出时也是用的utf-8编码。如果在中途对字符串进行转化处理的话实在是没有什么必要性。其实一直很好奇，无论是什么格式的编码，windows都是能正常识别的。最重要的是size和length返回的长度不同的话，那么内部就一定有相应的检测机制。于是，开始找字符编码相关的资料。以结论而言是这样的，UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：",[11,122,123],{},"UCS-2编码(16进制) UTF-8 字节流(二进制)",[125,126,127,130,133],"blockquote",{},[11,128,129],{},"0000 - 007F 0xxxxxxx",[11,131,132],{},"0080 - 07FF 110xxxxx 10xxxxxx",[11,134,135],{},"0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx",[11,137,138],{},"至于具体编码对应字符是怎样的其实并不重要了，对于分割utf-8字符而言，只要知道编码规则就好了。于是，接下来的工作就简单了。",[28,140,143],{"className":141,"code":142,"language":33},[31],"sum = strlen(fullline);\nfor(int cur = 0;cur\u003Csum;cur++)\n{       \n                char t = fulline[cur];\n        if((t&0xE0) == 0xE0){   \u002F\u002F3byte\n                lines = lines + t + fulline[cur+1] + fulline[cur+2];\n                cur += 3;\n\n        }else if((t&0xC0) == 0xC0){\u002F\u002F2byte\n                lines = lines + t + fulline[cur+1];\n                cur += 2;\n        }else{\u002F\u002F1byte\n            lines = lines + t;\n            cur++;\n        }\n        printf(\"streaming text:%s\",lines.c_str());\n}\n",[35,144,142],{"__ignoreMap":37},[11,146,147],{},"至于GB2312编码，由于固定是双字节的，分割上就不会有什么问题了。",{"title":37,"searchDepth":80,"depth":81,"links":149},[],"2012-12-28",{"layout":86,"status":87,"published":88,"author":152,"author_login":90,"author_email":91,"author_url":92,"wordpress_id":153,"wordpress_url":154,"date_gmt":155,"excerpt":156},{"display_name":90,"login":90,"email":91,"url":92},543,"\u002F\u002F?p=543","2012-12-28 01:35:34 +0000",{"type":8,"value":157},[158],[11,159,114],{},"\u002F2012-12-28-c中文分割",{"title":109,"description":114},"_legacy\u002F2012\u002F2012-12-28-c%e4%b8%ad%e6%96%87%e5%88%86%e5%89%b2",[164,165],"c++","字符串","PPsXuDCUhh7Y4-mLTuw-OHU0BD7oTm--uAo_WcGSsi0",{"id":168,"title":169,"body":170,"date":274,"description":174,"extension":84,"meta":275,"navigation":88,"path":284,"seo":285,"stem":286,"tags":287,"__hash__":288},"blogs\u002F_legacy\u002F2012\u002F2012-12-22-cocos2d-x%e7%9a%84windows%e6%94%b9%e9%80%a0.md","Cocos2d-x的windows改造",{"type":8,"value":171,"toc":272},[172,175,178,181,187,190,193,196,199,205,208,214,217,223,226,232,235,241,244,250,253,256,262,265],[11,173,174],{},"最近在整cocos2d-x引擎，感觉这个引擎在跨平台特性上还是很不错的。",[11,176,177],{},"而且各个方面都封装的还不错，不用自己再去重复造轮子。可是在windows上使用时还是有些小小的不足，于是自己边查资料开始动手改造之。目前遇到的需要改造的有两个地方，一个是引擎本身没有提供全屏功能，作为一个在windows上的游戏如果不能全屏的话，多少还是有些别扭的，无论是从习惯上还是实际操作中都会遇到问题。另一个地方是CCMenu的事件处理，由于是从移动设备上移植过来的，所以内部并没有传统的onmousemove和onmouseout事件，这样实现出来的菜单显示效果是做在ontouchmove上的，在窗口中使用起来特效出现的时机就会变得很独特：只有在鼠标左键按下时才会触发类似于鼠标移入移出的效果。",[11,179,180],{},"为了避免不必要的麻烦，先说明一下开发环境，win7+vs2010+cocos2d-2.1beta3-x-2.1.0。其实关于全屏的问题，在网上能够找到一些相关的资料，只是上面提供的方法是针对旧版本的，在新版本中不再有效了。原来的方法是这样的，主要改动的是platform下的CCEGLView。",[28,182,185],{"className":183,"code":184,"language":33},[31],"        \u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F FULLSCREEN HACK - BEGIN \n\n        DWORD       dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;                 \u002F\u002F Window Extended Style\n        DWORD       dwStyle = WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX;         \u002F\u002F Window Style\n\n        if (m_bFullScreen)\n        {\n            DEVMODE dmScreenSettings;                               \u002F\u002F Device Mode\n            memset(&dmScreenSettings,0,sizeof(dmScreenSettings));   \u002F\u002F Makes Sure Memory's Cleared\n            dmScreenSettings.dmSize=sizeof(dmScreenSettings);       \u002F\u002F Size Of The Devmode Structure\n            dmScreenSettings.dmPelsWidth    = w;                    \u002F\u002F Selected Screen Width\n            dmScreenSettings.dmPelsHeight   = h;                    \u002F\u002F Selected Screen Height\n            dmScreenSettings.dmBitsPerPel   = 32;                   \u002F\u002F Selected Bits Per Pixel\n            dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;\n\n            \u002F\u002F Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.\n            if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)\n            {\n                \u002F\u002F If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.\n                if (MessageBox(NULL, L\"The Requested Fullscreen Mode Is Not Supported By\\nYour Video Card. Use Windowed Mode Instead?\",L\"cocos2d-x\",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)\n                {\n                    setFullScreen(false);       \u002F\u002F back to windowed mode\n                }\n                else\n                {\n                    \u002F\u002F Pop Up A Message Box Letting User Know The Program Is Closing.\n                    MessageBox(NULL,L\"Program Will Now Close.\",L\"ERROR\",MB_OK|MB_ICONSTOP);\n                    return FALSE;                                   \u002F\u002F Return FALSE\n                }\n            }\n            else    \u002F\u002F yeah! we are in fullscreen\n            {\n                dwExStyle = WS_EX_APPWINDOW;\n                dwStyle=WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;                                       \n\n                \u002F\u002FShowCursor(FALSE);\n\n                RECT rect;\n                rect.left=(long)0;          \u002F\u002F Set Left Value To 0\n                rect.right=(long)w;         \u002F\u002F Set Right Value To Requested Width\n                rect.top=(long)0;           \u002F\u002F Set Top Value To 0\n                rect.bottom=(long)h;        \u002F\u002F Set Bottom Value To Requested Height\n\n                AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle);       \u002F\u002F Adjust Window To True Requested Size\n\n            }\n        }\n\n        \u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F FULLSCREEN HACK - END\n",[35,186,184],{"__ignoreMap":37},[11,188,189],{},"将以上的代码进行替换之后会发现程序并没有进入全屏模式，好在下到了修改过的源码，从这段源码再往下和目前的版本对比的话会发现。函数的调用顺序已经不同了，原来的调用方式是在这一段之后进行窗口创建的。而现在的版本则是先初始化了一个1000x1000的窗口再对其进行resize的。这样的话即便是添加了全屏标志也没有用，因为窗口已经完成了初始化。",[11,191,192],{},"所以只能重新进行修改，其实很简单，在resize的这里再创建一个新的窗口就可以了。为了避免对程序结构的改动，再加上本身对win32 api和opengl之间的机制不是很熟悉，于是google到nehe的opengl教程照着教程边学边对源码进行修改。同时这里交代下没有实现的功能，那就是全屏的运行时切换。nehe的教程中提供了全屏切换的代码，但是代入之后总是无法执行成功。试着改动一些地方也没有办法。跟踪代码的话发现在app初始化完成之后，窗口有关的指针似乎沉入了引擎的深处。虽然试着改了一下，无奈在cocos2d上的功底实在不够，只能留在以后对引擎有更好的把握时再做了。",[11,194,195],{},"全屏里的另一个问题是这句话引起的：\"The Requested Fullscreen Mode Is Not Supported By\\nYour Video Card. Use Windowed Mode Instead?\"。想我大N卡怎么可能会不支持全屏呢？于是仔细的研究了下这个问题。之后看了msdn也有对ChangeDisplaySettings()函数的详细说明。其实这个问题主要出在提供的分辨率并不是显卡和显示设备支持的。通常被支持的分辨率都是我们常见的那几种，像是800x600、1366x768这些设置给显卡就不会出错。回过头来看的话，我们平时玩的游戏其实在分辨率设置的时候也是只提供这些分辨率的，而不会让用户自己去输入分辨率。",[11,197,198],{},"另一个处理显示时遇到的问题是锁定鼠标。虽然到最后并没有用到，但是还是在这里记录一下，在win32 api中，只要调用",[28,200,203],{"className":201,"code":202,"language":33},[31]," CRect rect;\n         this->GetWindowRect(&rect);\n",[35,204,202],{"__ignoreMap":37},[11,206,207],{},"然后使用ClipCursor函数把鼠标控制在这个范围以内,这个函数的功能就是控制鼠标的范围。",[28,209,212],{"className":210,"code":211,"language":33},[31],"ClipCursor(&rect);\n",[35,213,211],{"__ignoreMap":37},[11,215,216],{},"而要释放鼠标控制就调用：",[28,218,221],{"className":219,"code":220,"language":33},[31],"ClipCursor(NULL);\n",[35,222,220],{"__ignoreMap":37},[11,224,225],{},"CCMenu的处理，我一开始天真的以为只要改动CCEGlView里的事件封装机制就可以了。",[28,227,230],{"className":228,"code":229,"language":33},[31],"    case WM_MOUSEMOVE:\n#if(_MSC_VER >= 1600)\n        \u002F\u002F Don't process message generated by Windows Touch\n        if (m_bSupportTouch && (s_pfGetMessageExtraInfoFunction() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) break;\n#endif\n        if (\n            true\n            \u002F\u002FMK_LBUTTON == wParam && m_bCaptured \u002F\u002F改动了这里，去掉了判定，直接传输事件\n            )\n        {\n            POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)};\n            CCPoint pt(point.x, point.y);\n            int id = 0;\n            pt.x \u002F= m_fFrameZoomFactor;\n            pt.y \u002F= m_fFrameZoomFactor;\n            handleTouchesMove(1, &id, &pt.x, &pt.y);\n        }\n        break;\n",[35,231,229],{"__ignoreMap":37},[11,233,234],{},"可是改动之后移动鼠标，Menu依然不动。无奈之下只能跟踪事件的封装和发布过程，才终于找到了解决的方法。于是一路顺着事件机制改过去。之前的改动一直都只限于win32目录，所以可以比较随意的改。再往上走需要修改的是CCEGLViewProtocol.cpp 和CCTouchDispatcher.cpp，为了避免对其他平台的影响，就必须借助CC_TARGET_PLATFORM宏的作用了。",[28,236,239],{"className":237,"code":238,"language":33},[31],"\u002F\u002FCCEGLViewProtocol.cpp --> void CCEGLViewProtocol::handleTouchesMove(int num, int ids[], float xs[], float ys[]) 函数替换对应内容\n#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 )\n            int nUnusedIndex = 0;\n\n            CCTouch* pTouch = new CCTouch();\n            pTouch->setTouchInfo(nUnusedIndex, (x - m_obViewPortRect.origin.x) \u002F m_fScaleX, \n                                     (y - m_obViewPortRect.origin.y) \u002F m_fScaleY);\n\n            \u002F\u002FCCLOG(\"x = %f y = %f\", pTouch->getLocationInView().x, pTouch->getLocationInView().y);\n\n            set.addObject(pTouch);\n\n#else\n        CCInteger* pIndex = (CCInteger*)s_TouchesIntergerDict.objectForKey(id);\n        if (pIndex == NULL) {\n            CCLOG(\"if the index doesn't exist, it is an error\");\n            continue;\n        }\n\n        CCLOGINFO(\"Moving touches with id: %d, x=%f, y=%f\", id, x, y);\n        CCTouch* pTouch = s_pTouches[pIndex->getValue()];\n        if (pTouch)\n        {\n            pTouch->setTouchInfo(pIndex->getValue(), (x - m_obViewPortRect.origin.x) \u002F m_fScaleX, \n                                (y - m_obViewPortRect.origin.y) \u002F m_fScaleY);\n\n            set.addObject(pTouch);\n        }\n        else\n        {\n            \u002F\u002F It is error, should return.\n            CCLOG(\"Moving touches with id: %d error\", id);\n            return;\n        }\n#endif\n",[35,240,238],{"__ignoreMap":37},[11,242,243],{},"可以看到，在这个CCEglView的protocol中，设计了机制保证只有touch begin之后才会有move事件。可以极好的提高在移动设备上的效率，但是却损失了windows上的常用事件。所以我们只要将事件重新进行发送就可以了。",[28,245,248],{"className":246,"code":247,"language":33},[31],"        \u002F\u002FCCTouchDispatcher.cpp  -->   void CCTouchDispatcher::touches(CCSet *pTouches, CCEvent *pEvent, unsigned int uIndex)\n               if (uIndex == CCTOUCHBEGAN)\n                {\n                    bClaimed = pHandler->getDelegate()->ccTouchBegan(pTouch, pEvent);\n\n                    if (bClaimed)\n                    {\n                        pHandler->getClaimedTouches()->addObject(pTouch);\n                    }\n                } else\n                if (pHandler->getClaimedTouches()->containsObject(pTouch))\n                {\n                    \u002F\u002F moved ended canceled\n                    bClaimed = true;\n\n                    switch (sHelper.m_type)\n                    {\n                    case CCTOUCHMOVED:\n                        pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent);\n                        break;\n                    case CCTOUCHENDED:\n                        pHandler->getDelegate()->ccTouchEnded(pTouch, pEvent);\n                        pHandler->getClaimedTouches()->removeObject(pTouch);\n                        break;\n                    case CCTOUCHCANCELLED:\n                        pHandler->getDelegate()->ccTouchCancelled(pTouch, pEvent);\n                        pHandler->getClaimedTouches()->removeObject(pTouch);\n                        break;\n                    }\n                }\n#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 )\n                else{\n                    if(CCTOUCHMOVED == sHelper.m_type){\n                        pHandler->getDelegate()->ccTouchMoved(pTouch, pEvent);\n                    }\n                }\n\n#endif\n",[35,249,247],{"__ignoreMap":37},[11,251,252],{},"可以看到上面实现了touch事件中的claim机制，为了不破坏这些机制，只在最后加上自己的事件转发。",[11,254,255],{},"做了上面的改动之后，本以为能够实现mouse move的事件效果了。可是却依然没有，这时候检查CCMenu的实现，发现仍然有m_eState的保护标志在对事件处理进行控制。为了保证引擎在其他平台上的工作，在工程中便直接继承CCMenu实现自己的MouseMenu了。这样可以方便添加自己的效果，比如播放声音之类的。但是最后却发现犯了一个错误，那就是在menu的touch move中有断言。这样的话所有使用CCMenu的代码都会出错，不过自己还是偷了个懒，没有再去动CCMenu。其实改动起来很简单，像下面的那样处理就差不多了。主要电脑运行速度比较慢，每次改动库内部文件一点，就会触发很多东西的重新生成，实在是太消耗时间了。",[28,257,260],{"className":258,"code":259,"language":33},[31],"\u002F\u002F这不是对CCMenu的修改，而是MouseMenu的一部分，不过其实我们只需要在win32时去掉断言就可以了。\nvoid MouseMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)\n{\n    CC_UNUSED_PARAM(event);\n\n#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 )\n    CCAssert(m_eState == kCCMenuStateTrackingTouch, \"[Menu ccTouchMoved] -- invalid state\");\n#endif\n\n    CCMenuItem *currentItem = this->itemForTouch(touch);\n    if(NULL != currentItem){\n        if (currentItem != m_pSelectedItem ) \n        {\n            SoundManager::sharedSoundManager()->PlayHitSFX();\n            if (m_pSelectedItem)\n            {\n                m_pSelectedItem->unselected();\n            }\n            m_pSelectedItem = currentItem;\n            if (m_pSelectedItem)\n            {\n                m_pSelectedItem->selected();\n            }\n        }\n    }else{\n            if (m_pSelectedItem)\n            {\n                SoundManager::sharedSoundManager()->PlayLaserSFX();\n                m_pSelectedItem->unselected();\n                m_pSelectedItem = NULL;\n            }\n    }\n}\n",[35,261,259],{"__ignoreMap":37},[11,263,264],{},"到了这里终于可以看到熟悉的菜单效果了，而且还可以播放效果音。",[11,266,267,268],{},"所有修改过的代码打包在这里：",[74,269,270],{"href":270,"rel":271},"http:\u002F\u002Fpan.baidu.com\u002Fshare\u002Flink?shareid=153192&uk=1913549955&third=0",[78],{"title":37,"searchDepth":80,"depth":81,"links":273},[],"2012-12-22",{"layout":86,"status":87,"published":88,"author":276,"author_login":90,"author_email":91,"author_url":92,"wordpress_id":277,"wordpress_url":278,"date_gmt":279,"excerpt":280},{"display_name":90,"login":90,"email":91,"url":92},540,"\u002F\u002F?p=540","2012-12-21 17:59:11 +0000",{"type":8,"value":281},[282],[11,283,174],{},"\u002F2012-12-22-cocos2d-x的windows改造",{"title":169,"description":174},"_legacy\u002F2012\u002F2012-12-22-cocos2d-x%e7%9a%84windows%e6%94%b9%e9%80%a0",[],"mGr9xTcCVkfhKamHX6iw2Xh0fS4bWYv9Q8J2l67rjCo",{"id":290,"title":291,"body":292,"date":320,"description":296,"extension":84,"meta":321,"navigation":88,"path":330,"seo":331,"stem":332,"tags":333,"__hash__":336},"blogs\u002F_legacy\u002F2012\u002F2012-12-16-android%e7%ae%80%e5%8d%95%e7%9a%84%e5%b0%86activity%e6%94%b9%e4%b8%bafragment.md","Android简单的将Activity改为Fragment",{"type":8,"value":293,"toc":318},[294,297,300,303,309,312],[11,295,296],{},"开发android有时会遇到这样的问题，那就是需要将Activity改造成Fragment。",[11,298,299],{},"本来Fragment是新的android版本上的东西，不过既然google这么亲切的提供了向下兼容的包。那么总会有这样的需要的时候，好在Activity到Fragment的转化并不复杂，因为本来就是相似的东西。最近就遇到了这的问题，于是来总结一下经验。",[11,301,302],{},"从Activity到Fragment最大的变动在于，在Fragment里面的onCreate()函数内，是没有办法使用findViewById、setContentView这些函数的。因为Fragment本身并没有提供这些方法。于是我们只能通过onCreateView和onViewCreated来实现对显示的控制。",[28,304,307],{"className":305,"code":306,"language":33},[31],"@Override\n\npublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n  return inflater.inflate(R.layout.about_detail, container, false);\n}\n\n@Override\n\npublic void onViewCreated(View view, Bundle savedInstanceState) {\n  super.onViewCreated(view, savedInstanceState);\n  version = (TextView) view.findViewById(R.id.version);\n  loginfo = (TextView) view.findViewById(R.id.loginfo);\n}\n",[35,308,306],{"__ignoreMap":37},[11,310,311],{},"而一些Fragment没有提供的属于Activity的函数我们可以通过getActivity()得到作为parent的Activity来调用，因为Fragment只是作为片段提供给Activity使用的，所以很多功能的实现都被留在了Activity里面。不过无论如何，只要利用Eclipse自带的查错功能可以非常快速的完成这些操作。这种改动一般不会很复杂，只要把Fragment要做的事情交给作为parent的Activity处理就可以了。比如像Intent的处理就可以这样：",[28,313,316],{"className":314,"code":315,"language":33},[31],"Intent i = new Intent();\ni.setClass(getActivity().getApplicationContext(), NewActivity.class);\nstartActivity(i);\ngetActivity().finish();\n",[35,317,315],{"__ignoreMap":37},{"title":37,"searchDepth":80,"depth":81,"links":319},[],"2012-12-16",{"layout":86,"status":87,"published":88,"author":322,"author_login":90,"author_email":91,"author_url":92,"wordpress_id":323,"wordpress_url":324,"date_gmt":325,"excerpt":326},{"display_name":90,"login":90,"email":91,"url":92},526,"\u002F\u002F?p=526","2012-12-16 03:20:01 +0000",{"type":8,"value":327},[328],[11,329,296],{},"\u002F2012-12-16-android简单的将activity改为fragment",{"title":291,"description":296},"_legacy\u002F2012\u002F2012-12-16-android%e7%ae%80%e5%8d%95%e7%9a%84%e5%b0%86activity%e6%94%b9%e4%b8%bafragment",[334,335],"Android","Fragment","zw6kNcWWbibGeTwLMxpCLu4n37LCz9Uchwjkpz8LiDQ",{"id":338,"title":339,"body":340,"date":320,"description":344,"extension":84,"meta":383,"navigation":88,"path":392,"seo":393,"stem":394,"tags":395,"__hash__":397},"blogs\u002F_legacy\u002F2012\u002F2012-12-16-android%e9%85%8d%e7%bd%ae%e7%b1%bb%e7%9a%84%e4%bd%bf%e7%94%a8.md","Android配置类的使用",{"type":8,"value":341,"toc":381},[342,345,348,354,357,363,366,372,375],[11,343,344],{},"Android本身提供了应用保存配置的机制，虽然有时我们会希望自己来控制配置的过程，但是利用系统自带的配置也是一个不错的选择。",[11,346,347],{},"最重要的是，由于是系统自带的功能，所以调用起来比较简单。而不用只是为了一两个变量就自己去写配置文件的读取和写入这些东西。现在写东西比较偷懒，基本每次都是复制下面的代码然后改改就用了。",[28,349,352],{"className":350,"code":351,"language":33},[31],"\u002F\u002F保存配置信息\nSharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);\nSharedPreferences.Editor editor = settings.edit();\neditor.putString(\"preferences\", mPreferences);\neditor.commit();\n\n\u002F\u002F读取配置信息        \nSharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);\nString mPreferences = settings.getString(\"preferences\", DEFULT_PRES);\n",[35,353,351],{"__ignoreMap":37},[11,355,356],{},"关于getSharedPreferences()，第一个参数是配置文件本身的标识，只要提供字符串就可以了。第二参数则是标识配置文件的读写权限的，一般情况下用0就好了，文档对这个参数的描述是这样的：",[28,358,361],{"className":359,"code":360,"language":33},[31],"Operating mode. Use 0 or MODE_PRIVATE for the default operation, MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE to control permissions. The bit MODE_MULTI_PROCESS can also be used if multiple processes are mutating the same SharedPreferences file. MODE_MULTI_PROCESS is always on in apps targetting Gingerbread (Android 2.3) and below, and off by default in later versions.\n",[35,362,360],{"__ignoreMap":37},[11,364,365],{},"操作模式是一个bit flag，具体的值对应关系为",[28,367,370],{"className":368,"code":369,"language":33},[31],"MODE_PRIVATE           ---  0000\nMODE_WORLD_READABLE    ---  0001\nMODE_WORLD_WRITEABLE   ---  0010\nMODE_MULTI_PROCESS     ---  0100\n",[35,371,369],{"__ignoreMap":37},[11,373,374],{},"仔细看一下的话就会发现，正如文档所描述的，只有MODE_MULTI_PROCESS这个标识是API Level11之后才有的。这个设定是为了打开配置文件的多进程同时访问功能，大概是随着android版本和对应设备性能的提升才会出现的设计变更。不过大体上程序都不会异步去访问配置，也很少会有多个程序读取相同的配置文件的情况，所以使用的机会比较少。但是既然是bit flag的话，平时就一直设成4就好了。如果想保险起见的话那就先判定一下就好了。",[28,376,379],{"className":377,"code":378,"language":33},[31],"int __sdkLevel = Build.VERSION.SDK_INT;\nSharedPreferences __sp = $context.getSharedPreferences(SETTING_NAME, (__sdkLevel > Build.VERSION_CODES.FROYO) ? 4 : 0);\n",[35,380,378],{"__ignoreMap":37},{"title":37,"searchDepth":80,"depth":81,"links":382},[],{"layout":86,"status":87,"published":88,"author":384,"author_login":90,"author_email":91,"author_url":92,"wordpress_id":385,"wordpress_url":386,"date_gmt":387,"excerpt":388},{"display_name":90,"login":90,"email":91,"url":92},528,"\u002F\u002F?p=528","2012-12-16 04:45:44 +0000",{"type":8,"value":389},[390],[11,391,344],{},"\u002F2012-12-16-android配置类的使用",{"title":339,"description":344},"_legacy\u002F2012\u002F2012-12-16-android%e9%85%8d%e7%bd%ae%e7%b1%bb%e7%9a%84%e4%bd%bf%e7%94%a8",[334,396],"配置类","QU3_1jY3y2TlcZQGJKdq4u7QyopDtP2t8EQ5KaLpKsI",222,1788763182703]