cocos2-x自帶快捷處理方式

1.數學類

cocos2d-x 裏使用最多的數學類型是CCPoint,一個點,本質上也是一個向量,對於向量和向量之間有很多的數學操作要做,oh我知道要幹什麼,也許我知道怎麼求一個值但是不知道怎麼求得高效(或者不知道),怎麼辦我能偷懶嗎?那當然可以。這其實並不是一個懶的標準,因爲有一些方法寫多了也可能確實稍微有那麼點麻煩,所以自然cocos2d提供了一套ccp系列來幫助我們完成很多的工作,也顯示一下庫程序員照顧開發程序員的懶惰精神(當然他們自己也用,他們也很懶)。

那我們首先創建向量

ccp(x, y); // 以座標x,y創建一個向量這個大家都知道。

ccpFromSize(s); // 以size s的width爲x,height爲y創建一個向量

 

有了ccp很多人就覺得自己已經夠懶了,因爲C++是可以用CCPoint()創建臨時變量的,就是喜歡少打幾個字吧。寫個ccp(v1.x + v2.x, v1.y + v2.y)也不長……但是,有沒有稍微再懶一點的?

——這個可以有。

基本的加法、減法、取負、數乘

複製代碼
ccpAdd(v1, v2); // 等價 ccp(v1.x+v2.x, v1.y+v2.y);

ccpSub(v1, v2); // 等價 ccp(v1.x-v2.x, v1.y-v2.y);

ccpNeg(v) // 等價 ccp(-v.x, -v.y);

ccpMult(v, s); //等價 ccp(v.x * s, v.y * s); s是個浮點數嘛
複製代碼

不錯,但是這個寫法不是那麼符合我們原生C++程序員的習慣,向量運算符呢?可惜cocos2d原本是一套objc的API,沒有操作符重載,cocos2d-x也沒有像一些原生的C++數學庫一樣直接重載向量運算符。不過重載一下還是很方便的,我們的項目裏聲明瞭一個數學頭文件,也就幾行代碼就好了:

複製代碼
    inline cocos2d::CCPoint operator + (const cocos2d::CCPoint& v1, const cocos2d::CCPoint v2)
    {
        return ccp(v1.x + v2.x, v1.y + v2.y);
    }

    inline cocos2d::CCPoint operator - (const cocos2d::CCPoint& v1, const cocos2d::CCPoint v2)
    {
        return ccp(v1.x - v2.x, v1.y - v2.y);
    }

    inline cocos2d::CCPoint operator - (const cocos2d::CCPoint& v)
    {
        return ccp(-v.x, -v.y);
    }

    inline cocos2d::CCPoint operator * (const cocos2d::CCPoint& v1, float scale)
    {
        return ccp(v1.x * scale, v1.y * scale);        
    }

    inline cocos2d::CCPoint operator * (float scale, const cocos2d::CCPoint& v1)
    {
        return ccp(v1.x * scale, v1.y * scale);        
    }
    
    inline cocos2d::CCPoint operator / (const cocos2d::CCPoint& v1, float scale)
    {
        return ccp(v1.x / scale, v1.y / scale);        
    }

    inline bool operator == (const cocos2d::CCPoint& v1, const cocos2d::CCPoint& v2)
    {
        return (v1.x == v2.x) && (v1.y == v2.y);
    }

    inline bool operator != (const cocos2d::CCPoint& v1, const cocos2d::CCPoint& v2)
    {
        return (v1.x != v2.x) || (v1.y != v2.y);
    }
複製代碼

順便還重載了等號和不等號,這樣就可以直接用+、-來進行向量加減法,*、 / 進行數乘,==、!=判斷是否相等了。程序員,這樣纔夠懶!

什麼,你說還有 +=、 -=、 /=、 *= 沒重載?哦,改那些必須得修改到cocos2d-x的源代碼了,改完還得重新編譯一遍,略微有點懶得改吧,至少CCPoint還是能用 = 賦值的。我們還是看看cocos2d-x還提供了什麼數學方法吧。

 

取中點!本來也就一 ccpMult(ccpAdd(v1,v2), 0.5f) 的事,開發者說不要,我就是要少打幾個字,好吧庫程序員就給了一個方法

ccpMidpoint(v1, v2); // 等價 ccp( (v1.x + v2.x)/2, (v1.y + v2.y)/2 );

 

點乘、叉乘、投影

ccpDot(v1, v2); // 等價 v1.x * v2.x + v1.y * v2.y;

ccpCross(v1, v2); // 等價 v1.x * v2.y - v1.y * v2.x;

ccpProject(v1, v2) // 返回的是向量v1在向量v2上的投影向量

 

喜聞樂見求長度、距離和各自的平方值(在僅需要比較兩個長度大小時使用長度平方,因爲省去了開方這一步,效率要高不少,這就不光是程序員的懶了,懶得要有效率)

複製代碼
ccpLength(v) // 返回向量v的長度,即點v到原點的距離

ccpLengthSQ(v) // 返回向量v的長度的平方,即點v到原點的距離的平方

ccpDistance(v1, v2) // 返回點v1到點v2的距離

ccpDistanceSQ(v1, v2) // 返回點v1到點v2的距離的平方

ccpNormalize(v) // 返回v的標準化向量,就是長度爲1
複製代碼

 

旋轉、逆時針90度、順時針90度(90度的效率當然是更快的。。。同樣懶得有效率)

複製代碼
ccpRotate(v1, v2); // 向量v1旋轉過向量v2的角度並且乘上向量v2的長度。當v2是一個長度爲1的標準向量時就是正常的旋轉了,可以配套地用ccpForAngle

ccpPerp(v); // 等價於 ccp(-v.y, v.x); (因爲opengl座標系是左下角爲原點,所以向量v是逆時針旋轉90度)

ccpRPerp(v); // 等價於 ccp(v.y, -v.x); 順時針旋轉90度
複製代碼

 

上面說到ccpRotate,配套的有向量和弧度的轉換向量,還有一些角度相關的

複製代碼
ccpForAngle(a); // 返回一個角度爲弧度a的標準向量

ccpToAngle(v); // 返回向量v的弧度 

ccpAngle(a, b); // 返回a,b向量指示角度的差的弧度值

ccpRotateByAngle(v, pivot, angle) // 返回向量v以pivot爲旋轉軸點,按逆時針方向旋轉angle弧度
複製代碼

 

線段相交的檢測,哦天哪原來庫程序員把這些事情都幹了!我還在傻傻地想線段相交算法!實在是太勤奮了!

複製代碼
ccpLineIntersect(p1, p2, p3, p4, &s, &t); // 返回p1爲起點p2爲終點線段1所在直線和p3爲起點p4爲終點線段2所在的直線是否相交,如果相交,參數s和t將返回交點在線段1、線段2上的比例
// 得到s和t可以通過 p1 + s * (p2 - p1) 或 p3 + t * (p4 - p3) 求得交點。

ccpSegmentIntersect(A, B C, D) // 返回線段A-B和線段C-D是否相交

ccpIntersectPoint(A, B, C, D) // 返回線段A-B和線段C-D的交點
複製代碼

數學方法沒有列全,更多請直接查頭文件CCPointExtension.h。基本該有的都有了。

 

當然數學不只有向量,還有一些其他的……這些也很經常用到。小懶一下。

 

CC_RADIANS_TO_DEGREES(a);  // 弧度轉角度
CC_DEGREES_TO_RADIANS(a);  // 角度轉弧度

CCRANDOM_0_1();     // 產生0到1之間的隨機浮點數
CCRANDOM_MINUS1_1(); // 產生-1到1之間的隨機浮點數    

 

2.語句宏

常用的,首先第一個,斷言。

CCAssert(cond, msg); // 斷言表達式cond爲真,如果不爲真,則顯示字符串msg信息

 

在這之後,也非常常用的,有遍歷CCARRAY、CCDICTIONARY的宏。

複製代碼
CCArray* _array;
CCObject* _object;     // 用來遍歷數組的臨時變量
CCARRAY_FOREACH(_array, _object) // 正向遍歷
{
    // todo with _object....
}

CCARRAY_FOREACH_REVERSE(_array, _object) // 反向遍歷
{
    // todo with _object....
}

CCDictionary* _dict;
CCDictElement* _elmt; // 遍歷表的臨時變量
CCDICT_FOREACH(_dict, _elmt)
{
  // todo with elmt;
}
複製代碼

CCArray和CCDictionary都沒有實現模版,取得的遍歷元素之後還需要強制轉換,假如說,嗯,通常數組裏的元素都是同一類型的,比如這樣

複製代碼
CCArray* _array;
CCObject* _object;     // 用來遍歷數組的臨時變量
CCARRAY_FOREACH(_array, _object) // 正向遍歷
{
    CCSprite* _bullet = (CCSprite*)_object;
    // todo with _bullet....

}
複製代碼

總覺得我好像多定義了一個CCObject* _object,因爲它沒什麼用似的?而且我也懶得多寫一句強制轉換,可以嗎?首先看看CCARRAY_FOREACH怎麼定義的

複製代碼
#define CCARRAY_FOREACH(__array__, __object__)                                                                \
    if ((__array__) && (__array__)->data->num > 0)                                                            \
    for(CCObject** arr = (__array__)->data->arr, **end = (__array__)->data->arr + (__array__)->data->num-1;    \
    arr <= end && (((__object__) = *arr) != NULL/* || true*/);                                                \
    arr++)
複製代碼

看到那句 (__object__) = *arr 了嗎?好,要直接強制轉換,就提供一個類型,在這裏開刀!

複製代碼
#define CCARRAY_TFOREACH(__array__, __object__, __type__)                                                                \
    if ((__array__) && (__array__)->data->num > 0)                                                            \
    for(CCObject** arr = (__array__)->data->arr, **end = (__array__)->data->arr + (__array__)->data->num-1;    \
    arr <= end && (((__object__) = (__type__)*arr) != NULL/* || true*/);                                                \
    arr++)
複製代碼

然後用這個CCARRAY_TFOREACH宏,這樣我們的遍歷就可以做得更懶一點

複製代碼
CCArray* _array;
CCSprite* _bullet;     // 用來遍歷數組的臨時變量
CCARRAY_TFOREACH(_array, _bullet, CCSprite*) // 正向遍歷
{
    // todo with _bullet....

}
複製代碼

舒坦,偷懶改造,完。

 

在定義類型的時候,經常需要定義一些getter setter,有cocos2d從objc帶來的CC_PROPERTY 和 CC_SYNTHESIZE。

複製代碼
    class Ship: public cocos2d::CCNode
    {
        // 定義一個int類的屬性m_energy變量,該變量訪問權限是protected。
        //後面的方法名Energy,即聲明瞭一個int getEnergy() 和一個 void setEnergy(int value)的方法,具體實現需要自己在cpp中定義
        CC_PROPERTY(int, m_energy, Energy); 

        // 基本與上相同,但是get方法傳引用,即聲明瞭一個 int& getEnergy();
        CC_PROPERTY_PASS_BY_REF(int, m_energy, Energy); 

        // 同樣定義變量,但是隻發聲明 get 方法,具體實現需要自己在cpp中定義
        CC_PROPERTY_READONLY(int, m_energy, Energy);
        CC_PROPERTY_READONLY_PASS_BY_REF(int, m_energy, Energy);

        // 同樣定義變量,並且直接定義默認的get/set方法。相似的也有前4類
        CC_SYNTHESIZE(cocos2d::CCObject*, m_weapon, Weapon);
        CC_SYNTHESIZE_PASS_BY_REF(cocos2d::CCObject*, m_weapon, Weapon);
        CC_SYNTHESIZE_READONLY(cocos2d::CCObject*, m_weapon, Weapon);
        CC_SYNTHESIZE_READONLY_PASS_BY_REF(cocos2d::CCObject*, m_weapon, Weapon);

        // 在setWeapon的時候,調用原有m_weapon的release,並且調用新值的的retain。當然已經排除了意外情況(相等或者NULL之類的)。
        CC_SYNTHESIZE_RETAIN(cocos2d::CCObject*, m_weapon, Weapon);
    };
複製代碼

需要注意的是

1.CC_PROPERTY更適用於快速聲明一個值屬性,而CC_SYNTHESIZE更適用於聲明一個對象。因爲CC_SYNTHESIZE提供的默認set沒有任何合法性檢查對於值屬性來說太不實用。

2.這些方法的聲明全部都是virtual的,即便是內聯,聲明爲virtual的方法也不會產生內聯函數,所以不管是CC_PROPERTY還是CC_SYNTHESIZE,他們的效率都是不高的。

3.CC_PROPERTY的get方法都沒有對函數體聲明const修飾符,這意味着對const對象,並不能調用CC_PROPERTY聲明的get方法(我怎麼覺得這是個cocos2d-x的BUG……)。

4.在CC_SYNTHESIZE方法之後直接聲明函數或者變量都會變成public:……注意,嗯。

不好用?跳過去看下定義,自己去定義一個唄……懶得看那就算了。

 

然後還有快捷的CREATE_FUNC,自動生成一個默認的靜態create方法。這實在方便了

複製代碼
class Class: public cocos2d::CCNode
{
public: CREATE_FUNC(Class);
// 自動生成一個不帶參數的 create 靜態方法,返回一個Class*類型指針。自動調用了init和autorelease方法 } //CREATE_FUNC(Class) 等價於與以下 static Class* create() { Class* pRet = new Class(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }
複製代碼

而且這也是建議的C++構造函數和init方法的使用規範,先分配空間之後立刻初始化,並且由初始化結果確定能否返回一個可用的對象。在定義特定參數的create方法時也應當這樣。

 

說到初始化,就不得不說到析構,還有一些析構相關的宏。我要release一堆對象,挨個都得判斷對象是不是NULL?還要把release後的東西賦值NULL?程序員懶得寫這麼多行代碼……

複製代碼
//所謂的safe邏輯都是這樣的,先檢查指針p是否爲NULL,不爲NULL,則執行delete p或者p->release等等。

    CC_SAFE_DELETE(p);         // 當p不爲NULL,delete p 並且將 p 賦爲 NULL
    CC_SAFE_DELETE_ARRAY(p);   // ...delete[] p..
    CC_SAFE_FREE(p);           // ...free p ...

    CC_SAFE_RELEASE(p);        // 當p不爲NULL,p->release()
    CC_SAFE_RELEASE_NULL(p);   // 當p不爲NULL,p->release() 並且將 p 賦爲 NULL
    CC_SAFE_RETAIN(p);         // 當p不爲NULL,p->retain()
複製代碼

 

順便還有交換兩個變量的時候,可以都喜歡懶,寫個 void swap(int& a, int &b)什麼的、再寫void swap(float& a, float& b)什麼的,再寫個 void swap(string& a, string& b)什麼的……總感覺你懶都沒人家庫程序員懶的懶……這裏有個CC_SWAP的宏……

複製代碼
CC_SWAP(x, y, type);

// 等價于于以下
{
     type temp = (x);
     x = y; y = temp;
}
// 至少x 和 y 不是表達式的時候這個宏都能工作正常,也不用擔心temp變量重複
複製代碼

什麼?你說你不服?你說你連type都不想聲明……?你居然這麼懶那你怎麼辦你怎麼能做到這麼懶的啊!你說你用模版?

template <typename t>
inline void swap<typename t>(t& a, t& b);

好吧你贏了……

 

還有cocos2d庫開發人員很喜歡用的CC_BREAK_IF,這個宏有什麼特別的含義嗎?難道其實不就是一行的 if(???) break; ?嗯,就是……沒區別。但是你不覺得CC_BREAK_IF( ??? );懶地比人家高端嗎?現在的IDE都能自動tab出宏耶!還有可以用下面的while(0)循環寫還能代替一些if(???) return false;耶!

複製代碼
bool Class::init()
{
    bool bRet = false;

    do
    {
        // do some initialization 1

        CC_BREAK_IF(cond); // 當表達式cond爲真時候跳出。    

        // do some more initialization 

        bRet = true;
    } while(0);


    return bRet;
}
複製代碼

……積小懶,成大懶啊!可見有一些人,是真的真的很懶很懶……

 

還能更懶一點嗎?答案是肯定的。每當寫一個.h時,cocos2d的庫程序員都要寫一個 namespace cocos2d {...} 吧;每當寫一個cpp的時候,你也總是要用到using namespace吧?。。他們都懶得多打這幾個字母。。

NS_CC_BEGIN    // 這是 namespace cocos2d {
NS_CC_END      // 這是 } !!!!
USING_NS_CC;   // 這是 using namespace cocos2d; 這可以是常用宏。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章