顯示具有 未使用 標籤的文章。 顯示所有文章
顯示具有 未使用 標籤的文章。 顯示所有文章

2016年9月6日 星期二

0906 如何申請TexturePacker 免費license 跟 製作大小圖

本文主要是討論如何申請 免費license 跟 如何利用TexturePacker產生出大小圖給IOS的retain使用
http://blog.hsin.tw/2013/texturepacker-free-lisence/

最近在研究用 cocos2D 在 iPhone 上寫 APP
買的書有介紹這款 TexturePacker 貼圖集製作程式
雖然是需要買 license 才能使用完整功能 (未付費時 圖片會有浮水印)
但就甘心的有條件提供免費 license
條件也不難
1. 需要有自己的 Blog (就像我這個 Blog 一樣)
2. 必須至少有 5 篇[六個月內]的文章 (這應該沒太強制 只要不是都沒寫文章的空 Blog 我想應該都可以)
3. 必須是 game/software/web 相關的 Blog (但通常需要用的人 Blog 應該都會相關到)
4. 不包含 Facebook, Twitter, Weibo (一定要是 Blog 就是)
就可以到request-free-license 填寫一些資料 等他回 Email 給你 license 囉
cocos2D 有支援retina顯示的圖片
但他區分大圖是用 -hd 而不是 @2x
TexturePacker 有個 AutoSD 功能 只需要大圖
就可以一次生成大圖跟小圖的成品
左側列表 Output 中有個 AutoSD[齒輪]
點齒輪後 Presets選擇 cocos2d hd/sd (通常是第一個 所以不用選) 按下Apply
下一行的Main extension會自動出現 -hd.
接著按Publish
檔名填寫 AAA-hd
即會自動生成AAA-hd.plist AAA-hd.png AAA.plist AAA.png 四個檔案
[UPDATE END]
大圖 : A1.png ~ A5.png
小圖 : A1.png ~ A5.png
作一次 Publish 會生出 .png 跟 .plist 兩隻檔案
HD大圖生成的檔名我是設成 AAA-hd 所以會生出 AAA-hd.png 跟 AAA-hd.plist
SD小圖生成的檔名設為 AAA 所以生出 AAA.png 跟 AAA.plist
需要改的地方在 .plist 檔案裡
SD的檔案不用改
而HD的檔案用Xcode開啟後
找到 metadata 裡的 textureFileName
將 AAA-hd.png 的 -hd 拿掉 變為 AAA.png
而另一個 realTextureFileName 則是維持 AAA-hd.png 不動
如果 .plist 檔案不改的話
他會一直在 log 裡 warning issue #1040 什麼的
就…很討厭
但其實也不會怎樣XD
程式裡只要吃 AAA.png 跟 AAA.plist 就好
他會自動確認有沒有支援 retina (當然config裡要打開)
有支援的話 會自己去找 AAA-hd.png 跟 AAA-hd.plist
就不用自己判別讀哪隻了
以上


2016年5月27日 星期五

0527 Cocos2d-x 3.0坐標系詳解

Cocos2d-x 3.0坐標系詳解

Cocos2d-x坐標系和OpenGL坐標系相同,都是起源於笛卡爾坐標系。

笛卡爾坐標系

笛卡爾坐標系中定義右手系原點在左下角,x向右,y向上,z向外,OpenGL坐標係為笛卡爾右手系。
RightHand

屏幕坐標系和Cocos2d坐標系

標準屏幕坐標系使用和OpenGL不同的坐標系,而Cocos2d則使用和OpenGL相同的坐標系。
iOS, Android, Windows Phone等在開發應用時使用的是標準屏幕坐標系,原點為屏幕左上角,x向右,y向下。
Cocos2d坐標系和OpenGL坐標系一樣,原點為屏幕左下角,x向右,y向上。
UICoordinate
在開發中,我們經常會提到兩個比較抽象的概念-世界坐標系和本地坐標系。這兩個概念可以幫助我們更好的理解節點在Cocos2d坐標系中的位置以及對應關係。

世界坐標系(World Coordinate) VS 本地坐標系(Node Local)

世界坐標系也叫做絕對坐標系,是遊戲開發中建立的概念。因此,「世界」指遊戲世界。cocos2d中的元素是有父子關係的層級結構,我們通過Node的setPosition設定元素的位置使用的是相對與其父節點的本地坐標系而非世界坐標系。最後在繪製屏幕的時候cocos2d會把這些元素的本地坐標映射成世界坐標系坐標。
本地坐標系也叫相對坐標系,是和節點相關聯的坐標系。每個節點都有獨立的坐標系,當節點移動或改變方向時,和該節點關聯的坐標系將隨之移動或改變方向。

錨點(Anchor Point)

將一個節點添加到父節點裡面時,需要設置其在父節點上的位置,本質上是設置節點的錨點在父節點坐標繫上的位置。
  • Anchor Point的兩個參數都在0~1之間。它們表示的並不是像素點,而是乘數因子。(0.5, 0.5)表示Anchor Point位於節點長度乘0.5和寬度乘0.5的地方,即節點的中心
  • 在Cocos2d-x中Layer的Anchor Point為默認值(0, 0),其他Node的默認值為(0.5, 0.5)。
我們用以下代碼為例,使用默認Anchor Point值,將紅色層放在屏幕左下角,綠色層添加到紅色層上:
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);

red->addChild(green);

this->addChild(red, 0);
anchorPoint
我們用以下代碼為例,將紅色層的Anchor Point設為中點放在屏幕中央,綠色層添加到紅色層上,綠色層錨點為右上角:
註:因為Layer比較特殊,它默認忽略錨點,所以要調用ignoreAnchorPointForPosition()接口來改變錨點,關於ignoreAnchorPointForPosition()接口的使用說明,我們將在後面詳細講解。
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
red->ignoreAnchorPointForPosition(false);
red->setAnchorPoint(Point(0.5, 0.5));
red->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
green->ignoreAnchorPointForPosition(false);
green->setAnchorPoint(Point(1, 1));
red->addChild(green);

this->addChild(red, 0);
anchorPoint

忽略錨點(Ignore Anchor Point)

Ignore Anchor Point全稱是ignoreAnchorPointForPosition,作用是將錨點固定在一個地方。
如果設置其值為true,則圖片資源的Anchor Pont固定為左下角,否則即為所設置的位置。
我們用以下代碼為例,將兩個層的ignoreAnchorPointForPosition設為true,並將綠色的層添加到紅色的層上:
auto red = LayerColor::create(Color4B(255, 100, 100, 128), visibleSize.width/2, visibleSize.height/2);
red->ignoreAnchorPointForPosition(true);
red->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

auto green = LayerColor::create(Color4B(100, 255, 100, 128), visibleSize.width/4, visibleSize.height/4);
green->ignoreAnchorPointForPosition(true);

red->addChild(green);

this->addChild(red, 0);
ignore

VertexZ,PositionZ和zOrder

  • VerextZ是OpenGL坐標系中的Z值
  • PositionZ是Cocos2d-x坐標系中Z值
  • zOrder是Cocos2d-x本地坐標系中Z值
在實際開發中我們只需關注zOrder。
可以通過setPositionZ接口來設置PositionZ。
以下是setPositionZ接口的說明:
Sets the 'z' coordinate in the position. It is the OpenGL Z vertex value.
即PositionZ的值即為opengl的z值VertexZ。同樣節點的PositionZ也是決定了該節點的渲染順序,值越大,但是與zOrder不同的區別在於,PositionZ是全局渲染順序即在根節點上的渲染順序,而zOrder則是局部渲染順序,即該節點在其父節點上的渲染順序,與Node的層級有關。用以下事例來說明:
    auto red = LayerColor::create(Color4B(255, 100, 100, 255), visibleSize.width/2, visibleSize.height/2);
    red->ignoreAnchorPointForPosition(false);
    red->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));

    auto green = LayerColor::create(Color4B(100, 255, 100, 255), visibleSize.width/4, visibleSize.height/4);
    green->ignoreAnchorPointForPosition(false);
    green->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2 - 100));
    red->setPositionZ(1);
    green->setPositionZ(0);
    this->addChild(red, 0);
    this->addChild(green, 1);
zOrder
雖然green的zOrder大於red的zOder,但是因為red的PositionZ較大,所以red還是在green上面顯示。

觸摸點(Touch position)

所以在處理觸摸事件時需要用重寫以下四個函數:
    virtual bool onTouchBegan(Touch *touch, Event * event);
    virtual void onTouchEnded(Touch *touch, Event * event);
    virtual void onTouchCancelled(Touch *touch, Event * event);
    virtual void onTouchMoved(Touch *touch, Event * event);
在函數中獲取到touch,我們在設計遊戲邏輯時需要用到觸摸點在Cocos2d坐標系中的位置,就需要將touch的坐標轉換成OpenGL坐標系中的點坐標。
Touch position是屏幕坐標系中的點,OpenGL position是Cocos2d-x用到的OpenGL坐標繫上的點坐標。通常我們在開發中會使用兩個接口getLocation()getLocationInView()來進行相應坐標轉換工作。
在開發中一般使用getLocation()獲取觸摸點的GL坐標,而getLocation()內部實現是通過調用Director::getInstance()->convertToGL(_point);返回GL坐標。
此外,關於世界坐標系和本地坐標系的相互轉換,在Node中定義了以下四個常用的坐標變換的相關方法。
    // 把世界坐標轉換到當前節點的本地坐標系中
    Point convertToNodeSpace(const Point& worldPoint) const;

    // 把基於當前節點的本地坐標系下的坐標轉換到世界坐標系中
    Point convertToWorldSpace(const Point& nodePoint) const;

    // 基於Anchor Point把基於當前節點的本地坐標系下的坐標轉換到世界坐標系中
    Point convertToNodeSpaceAR(const Point& worldPoint) const;

    // 基於Anchor Point把世界坐標轉換到當前節點的本地坐標系中
    Point convertToWorldSpaceAR(const Point& nodePoint) const;
下面通過一個例子來說明這四個方法的理解和作用:
    auto *sprite1 = Sprite::create("HelloWorld.png");
    sprite1->setPosition(ccp(20,40));
    sprite1->setAnchorPoint(ccp(0,0));
    this->addChild(sprite1);  //此時添加到的是世界坐標系,也就是OpenGL坐標系

    auto *sprite2 = Sprite::create("HelloWorld.png");
    sprite2->setPosition(ccp(-5,-20));
    sprite2->setAnchorPoint(ccp(1,1));
    this->addChild(sprite2); //此時添加到的是世界坐標系,也就是OpenGL坐標系

    //將 sprite2 這個節點的坐標ccp(-5,-20) 轉換為 sprite1節點 下的本地(節點)坐標系統的 位置坐標
    Point point1 = sprite1->convertToNodeSpace(sprite2->getPosition());

    //將 sprite2 這個節點的坐標ccp(-5,-20) 轉換為 sprite1節點 下的世界坐標系統的 位置坐標
    Point point2 = sprite1->convertToWorldSpace(sprite2->getPosition());

    log("position = (%f,%f)",point1.x,point1.y);
    log("position = (%f,%f)",point2.x,point2.y);
運行結果:

Cocos2d: position = (-25.000000,-60.000000)
Cocos2d: position = (15.000000,20.000000)
convert1
convert2
其中:Point point1 = sprite1->convertToNodeSpace(sprite2->getPosition());
相當於sprite2這個節點添加到(實際沒有添加,只是這樣理解)sprite1這個節點上,那麼就需要使用sprite1這個節點的節點坐標系統,這個節點的節點坐標系統的原點在(20,40),而sprite1的坐標是(-5,-20),那麼經過變換之後,sprite1的坐標就是(-25,-60)。
其中:Point point2 = sprite1->convertToWorldSpace(sprite2->getPosition());
此時的變換是將sprite2的坐標轉換到sprite1的世界坐標系下,而其中世界坐標系是沒有變化的,始終都是和OpenGL等同,只不過sprite2在變換的時候將sprite1作為了」參照「而已。所以變換之後sprite2的坐標為:(15,20)。
convert3

2016年5月20日 星期五

0520 cocos2d-x 3.0 TOAST 範例

http://www.it165.net/pro/html/201411/25654.html

1、Toast

Android的Toast是一個View視圖,快速為用戶顯示少量的信息。主要用於一些提示和幫助。本文實現了Toast最基本的操作能,
代碼如下
//PacToast.h
#include "cocos2d.h"
#include "cocos-ext.h"
#include "ui/CocosGUI.h"
USING_NS_CC;
USING_NS_CC_EXT;
using namespace ui;
class PacToast : public LayerColor
{
public:
static void makeText(Node* node,const std::string& msg,const float& time);//靜態函數,方便類直接調用
void removeToast(Node* node);

};


//PacToast
#include "PacToast.h"

void PacToast::makeText(cocos2d::Node *node, const std::string &msg, const float &time)
{
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();

auto pLabel = Label::createWithSystemFont(msg.c_str(), "Arial", 30);
pLabel->setColor(Color3B::WHITE);
pLabel->ignoreAnchorPointForPosition(false);
pLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);

auto ly = LayerColor::create(Color4B(130,120,120,255));
ly->ignoreAnchorPointForPosition(false);
ly->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
ly->setContentSize(pLabel->getContentSize() + Size(20,15));

node->addChild(ly);
node->addChild(pLabel);
ly->setPosition(Vec2(visibleSize.width/2,-pLabel->getContentSize().height));
pLabel->setPosition(ly->getPosition());
auto seq1 = Sequence::create(FadeIn::create(time/5), DelayTime::create(time/5*1.5),FadeOut::create(time/5*2.5),CallFuncN::create(ly,callfuncN_selector(PacToast::removeToast)),nullptr);
auto seq2 = Sequence::create(EaseSineIn::create(MoveBy::create(time/5, Vec2(0,200))),DelayTime::create(time/5*2),EaseSineOut::create(MoveBy::create(time/3, Vec2(0,-200))), nullptr);
auto spawn = Spawn::create(seq1, seq2, nullptr);
auto action = Repeat::create(spawn,1);
ly->setOpacity(0);
pLabel->setOpacity(0);
ly->runAction(action);
pLabel->runAction(action->clone());
}

void PacToast::removeToast(Node* node)
{
log("node = %s",node->getDescription().c_str());
this->removeFromParentAndCleanup(true);
}

2、使用代碼



void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
return;
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
 
PacToast::makeText(this, "我是Toast!", 2.5f);
 
#endif

2016年5月19日 星期四

0519 cocos2d-x + Lua接入iOS原生SDK的實現方案

[原創]cocos2d-x + Lua接入iOS原生SDK的實現方案

相信很多朋友在使用cocos2d-x+lua開發遊戲時都遇到過接入iOS原生SDK的問題,比如常見的接應用內支付SDK,廣告SDK或是一些社交平台SDK等等,我也沒少接過這類SDK。這篇文章主要是對我做過項目中接入iOS原生SDK實現方案的一個總結,在這裡分享給大家,希望對自己和大家的開發工作都有幫助。
在展開正文之前,先做幾點說明:
1.我這裡說的iOS原生SDK是指那些完全用Objective-C語言開發,為原生iOS程序設計的SDK。swift很好很強大,不過我還沒用過,慚愧,不過語言終歸只是表達方式而已,解決問題的思路都是一樣的;
2.這裡假設遊戲的主要邏輯使用lua實現,對於主要邏輯使用C++實現的,用本文的思路一樣可行,並且設計上更簡單,接著往下看就知道了:)
3.本文以quick-cocos2d-x 2.1版本為例進行講解,主要因為這個是我之前做項目用得最多的一個版本,-x新版本變動比較大,但是,還是那句話,解決問題的思路是相同的。
-------------------正式開始的分割線-------------------
好了,我們正式開始。開門見山!
解決這種接入問題,實際上最主要是解決不同語言交互的問題,一旦跨過語言交互的障礙,剩下的事情就so easy!
由於涉及到Lua,C++,Objective-C三種語言,這個問題表面上看起來錯綜複雜,但其實只要冷靜地(=。=)梳理,我們便可以得到正確的思路。
因為我們遊戲的邏輯主要是用Lua實現的(前面已經做過假設),而SDK是用Objective-C實現,所以這裡我們需要解決Lua與Objective-C的交互問題,即最終希望達到的目標是,在Lua層面「調用」Objective-C的代碼(注意這裡的調用是加引號的,間接的調用),而當Objective-C層面收到SDK的回調,再通知Lua。我們知道,Lua並沒有簡單的方法直接和Objective-C交流,但是Lua可以通過Lua Binding和C/C++交流,而我們又知道,C++和Objective-C可以混編,即C++可以直接調用(這裡調用沒引號,是真的直接調用)Objective-C的代碼。想到這裡,思路就很明顯了,我們可以使用C++為Lua和Objective-C的交互充當橋樑,進而實現Lua到Objective-C的交互。
根據上面的分析,我們可以用如下圖表達我們的思路,我們這裡將語言交互的過程分成了4個小部分:
整個語言交互的過程可以總結為:Lua調用Lua Binding的C++接口,C++接口調用混編的Objective-C接口,而Objective-C通過block形式的回調,將結果通知給C++,C++通過Lua的C API將最終結果返回給Lua這樣一趟下來,就完成了Lua與Objective-C的整個交互過程。
簡單的說一下這4部分:
1.Lua Binding
將C/C++接口導出給Lua調用的方法,由於篇幅的原因這裡就不展開了,具體可以參考Lua的文檔,以及網上其他地方的文章。
2.混編
Objective-C的一大優點就是可以和C與C++混編使用,就像同一個語言一樣共存在一個實現文件裡面。具體混編規則也不說了,這裡只提兩個小細節:
一,在XCode下混編的實現文件後綴是.mm,而不能是.cpp或者是.m;
二,混編的實現文件引用頭文件的地方,C++或者C的用#include,而Objective-C用#import,相互沒有影響。
3.Block回調
Block是Objective-C一個非常棒的特性,更棒的是在Block裡面還可以直接寫C++代碼:)具體想瞭解的可以看蘋果官方文檔
其實在最初,我曾經嘗試過使用發送通知的方式來實現Objective-C對C++的回調,即Objective-C收到SDK回調,給C++部分發送附帶回調信息的通知,雖然cocos2d-x中有現成的NotificationCenter來幫助實現,但這種方式的一個顯而易見的弊端是大大增加了C++代碼和Objective-C代碼的耦合度,Objective-C部分也要混編C++調用C++的NotificationCenter發通知,C++部分也要混編Objective-C代碼,調用C++的NotificationCenter收通知,這種結構實在是有夠煩躁的。
相比之下,使用Block回調就乾淨利落太多,Objective-C這邊一切都是純粹的,它並不需要知道自己要被C++調用還是Objective-C調用,也不需要花很多精力在返回回調上,只需要干好自己的本職工作,然後在適當的時候調用Block就一切搞定。
4.Lua C API
Lua C API用於C/C++與Lua的交互,在cocos2d-x中這些C API已經被封裝成了更加易用的C++ Class API。這裡要提到的是,在用這套API調用Lua函數的時候,為了傳參,需要參數入棧的操作,這個入棧的順序影響到了Lua函數接受到參數的順序,不過好在規則很簡單:先入棧的參數排在前或者說是入棧順序和實參順序相同。舉例,如果C++這邊調用Lua函數func時,入棧的順序是A,B,C,那麼就是調用函數func(A,B,C)
-------------------漸入佳境的分割線-------------------
在整個語言交互的過程中,如果認為Lua是頂層,Objective-C是底層,那麼在實際遊戲中交互的過程就是一個自頂向下的過程,然而我們在實現各層級代碼的時候,需要自底向上完成,因為在頂層Lua代碼中的邏輯,是由底層Objective-C SDK的接口與功能決定的。即我們需要先根據SDK中原始的Objective-C的接口,做適合我們遊戲Objective-C封裝代理類,然後根據封裝結果實現C++的bridge接口,最後再實現Lua的對應邏輯
根據以上分析,從層級的角度,設計如下:
除過Lua的邏輯,我們最重要需要實現的兩部分內容:C++ Bridge Class 和 Objective-C SDK Delegate Class。前者是起橋樑作用的接口類,原則上不做任何與遊戲邏輯相關的數據處理,而後者負責封裝原始的SDK接口,接收以及初步處理SDK回調數據。前者的實現依賴於後者的實現,而後者的實現又依賴於SDK。SDK取得的數據最終通過層層傳遞,交給Lua邏輯處理,最終保證對數據處理的遊戲邏輯儘可能多的放到Lua層中。
這樣設計的好處有很多,一方面,頂層的遊戲邏輯變動,不影響下層多語言交互代碼,另一方面,底層的SDK變動,如版本更新甚至更換,不影響上層遊戲邏輯,多層次結構有效地降低了複雜度,隔離了變化,對於頻繁的需求變更,這種結構也可以保證擴展的便利。
-------------------總結的分割線-------------------
綜上所述,解決接入iOS原生SDK的問題,主要需要4步:
1.根據SDK接口與功能實現Objective-C SDK Delegate Class;
2.根據Objective-C SDK Delegate Class實現對應的C++ Bridge Class;
3.根據C++ Bridge Class生成對應的Lua Binding代碼;
4.寫Lua層邏輯。
-------------------最後的分割線-------------------
好了,最後,又到了激動人心的上代碼的環節了:)下面就以某iOS第三方計費SDK為例,來說明下實現接入的步驟。
這個SDK只有一個頭文件GameBilling.h,主要使用到的方法和Protocol如下:(為了避免篇幅過長等原因,把註釋和不必要的代碼都刪掉了)。我用代碼註釋的方式說明了各方法的用途。
複製代碼
 1 // 初始化計費SDK
 2 + (GameBilling *)initializeGameBilling;
 3 
 4 // 告訴SDK遊戲屏幕的Orientation,以便SDK展示正確的UI
 5 -  (void)setDialogOrientationMask:(UIInterfaceOrientationMask)orientationMask;
 6 
 7 // 確認付費,顯示付費UI
 8 - (void)doBillingWithUIAndBillingIndex:(NSString *)billingIndex isRepeated:(BOOL)isRepeated cpParam:(NSString*)cpParam;
 9 
10 // Delegate回調,告訴調用者付費是否成功等信息
11 @protocol GameBillingDelegate<NSObject>
12 @required
13 - (void)onBillingResult:(BillingResultType)resultCode billingIndex:(NSString *)index message:(NSString *)message;
14 @end
複製代碼
以上前兩個方法用於初始化SDK,並且和遊戲的邏輯沒什麼太大關係,所以我們把對他們的調用放在程序開始的位置,不必導出給Lua。第三個方法在用戶確認付費時使用,需要導出給Lua,當用戶在遊戲界面做相應操作時候調用。最後的delegate的回調,我們用前面提到的Objective-C SDK Delegate Class來接收,並作初步處理,再用Block傳給C++ Bridge Class.
好的,那我們先來完成Objective-C SDK Delegate Class。這裡這個Objective-C做成了個簡單的單例來使用,實際可能不需要這麼做。
先完成頭文件,這裡命名為CMGCIAPiOS.h,如下:
複製代碼
 1 #import "GameBilling.h"
 2 
 3 // 聲明Block
 4 typedef void (^BillingResultCallback)(BOOL success, NSString *index,NSString *message); 
 5 
 6 @interface CMGCIAPiOS : NSObject<GameBillingDelegate>
 7 {
 8     GameBilling *_sdk;
 9     NSString *_billingIndex;
10     BillingResultCallback _callback;
11 }
12 
13 +(id)sharedInstance;
14 
15 -(void)setDialogOrientationMask:(UIInterfaceOrientationMask)orientationMask;
16 
17 -(void)doBillingWithUIAndBillingIndex:(NSString *)billingIndex 
18                            isRepeated:(BOOL)isRepeated 
19                               cpParam:(NSString*)cpParam
20                        resultCallback:(BillingResultCallback)callback;
21 
22 @end
複製代碼
應該很清楚,就不多做說明了。
下面是實現文件CMGCIAPiOS.m,如下:
複製代碼
 1 #import "CMGCIAPiOS.h"
 2 
 3 @implementation CMGCIAPiOS
 4 
 5 static CMGCIAPiOS *_sharedInstance = nil;
 6 
 7 + (id)sharedInstance
 8 {
 9     @synchronized(self)
10     {
11         if (_sharedInstance == nil)
12         {
13             _sharedInstance = [[CMGCIAPiOS alloc] init];
14         }
15     }
16     return _sharedInstance;
17 }
18 
19 -(id) init
20 {
21     if( (self = [super init]) ) 
22     {
23         _sdk = [GameBilling initializeGameBilling];
24         _sdk.delegate = self;
25     }
26     return self;
27 }
28 
29 -(void)setDialogOrientationMask:(UIInterfaceOrientationMask)orientationMask
30 {
31     [_sdk setDialogOrientationMask:orientationMask];
32 }
33 
34 - (void)doBillingWithUIAndBillingIndex:(NSString *)billingIndex 
35                             isRepeated:(BOOL)isRepeated 
36                                cpParam:(NSString*)cpParam
37                         resultCallback:(BillingResultCallback)callback
38 {
39 
40     if (_callback != nil)
41     {
42         [_callback release];
43         _callback = nil;
44     }
45 
46     _callback = [callback copy];    // 注意要copy
47 
48     [_sdk doBillingWithUIAndBillingIndex:billingIndex 
49                               isRepeated:isRepeated 
50                                  cpParam:cpParam];
51 }
52 
53 #pragma mark - GameBillingDelegate
54 - (void)onBillingResult:(BillingResultType)resultCode
55            billingIndex:(NSString *)index 
56                 message:(NSString *)message
57 {
58     BOOL b = (resultCode == BillingResultType_PaySuccess || resultCode == BillingResultType_PaySuccess_Activated);
59     NSLog(@"billing = %@ %@ %@", b ? @"yes":@"no", index, message);
60     
61     if (_callback != nil)
62     {
63         _callback(b,index,message);
64 
65         // 調用完成就釋放掉
66         [_callback release];
67         _callback = nil;
68     }
69 }
70 
71 @end
複製代碼
可以看到對提到的幾個方法都做了封裝,並且接收了回調。
下面是C++ Bridge Class部分,頭文件CMGCIAP.h:
複製代碼
 1 #include <iostream>
 2 
 3 class CMGCIAP
 4 {
 5 public:
 6     CMGCIAP();
 7     ~CMGCIAP();
 8     
 9 public:
10     static CMGCIAP *sharedInstance();
11     
12     bool init();
13     
14     void setDoBillingCallbackScriptHandler(int scriptHandler); // for lua callback
15     
16     void doBillingWithUI(const char* billingIndex,
17                          bool isRepeated,
18                          const char* cpParam);
19     
20 private:
21     
22     int m_doBillingCallbackScriptHandler;
23     
24 };
複製代碼
由於用cocos2d-x的tolua工具做Lua Binding的原因,我把設置Lua回調的方法單獨提出來了,如下:
1 void setDoBillingCallbackScriptHandler(int scriptHandler);
更好的做法是把這個scriptHandler放到下面這個函數中,這樣接口就可以和Objective的保持一致了。
1 void doBillingWithUI(const char* billingIndex,
2                      bool isRepeated,
3                      const char* cpParam);
不過也沒關係,獨立設置Lua回調函數也有更靈活的優點。
注意,C++ Bridge Class頭文件一定保持「純潔性」,做純粹的C++文件,不能出現Objective-C的任何代碼,否則就破壞了上面講到的層次結構。
下面是實現文件CMGCIAP.mm:
複製代碼
 1 #include "CMGCIAP.h"
 2 #include "cocos2d.h"
 3 #include "script_support/CCScriptSupport.h"
 4 
 5 #import "CMGCIAPiOS.h"
 6 #import <Foundation/Foundation.h>
 7 #import <UIKit/UIKit.h>
 8 
 9 USING_NS_CC;
10 
11 static CMGCIAP* s_sharedInstance = NULL;
12 
13 CMGCIAP::CMGCIAP()
14 {
15     m_doBillingCallbackScriptHandler = 0;
16 }
17 
18 CMGCIAP::~CMGCIAP()
19 {
20     
21 }
22 
23 CMGCIAP *CMGCIAP::sharedInstance()
24 {
25     if (s_sharedInstance == NULL)
26     {
27         s_sharedInstance = new CMGCIAP();
28     }
29 
30     return s_sharedInstance;
31 }
32 
33 // init方法封裝了對SDK的初始化
34 bool CMGCIAP::init()
35 {
36     // 由於是豎屏的遊戲,所以這裡直接設置好了
37     [[CMGCIAPiOS sharedInstance] setDialogOrientationMask:UIInterfaceOrientationMaskPortrait];
38     
39     return true;
40 }
41 
42 void CMGCIAP::setDoBillingCallbackScriptHandler(int scriptHandler)
43 {
44     m_doBillingCallbackScriptHandler = scriptHandler;
45 }
46 
47 void CMGCIAP::doBillingWithUI(const char* billingIndex,
48                               bool isRepeated,
49                               const char* cpParam)
50 {
51     
52     NSString *billingIndexString = [NSString stringWithUTF8String:billingIndex];
53     
54     NSString *cpParamString = [NSString stringWithUTF8String:cpParam];
55     
56     [[CMGCIAPiOS sharedInstance] doBillingWithUIAndBillingIndex:billingIndexString
57                                                      isRepeated:isRepeated
58                                                         cpParam:cpParamString
59                                                  resultCallback:^(BOOL success, NSString *index,NSString *message){
60      
61         //通過Block將返回結果傳給Lua,Objective-C到C++的無縫連接:)
62      
63         CCLuaStack *stack = CCLuaEngine::defaultEngine()->getLuaStack();
64         stack->clean();
65         stack->pushBoolean(success);
66         stack->pushString([index UTF8String]);
67         stack->pushString([message UTF8String]);
68         stack->executeFunctionByHandler(m_doBillingCallbackScriptHandler, 3);
69      
70      }];
71 }
複製代碼
好了,接下來只需要對C++ Bridge Class做Lua Binding,生成綁定文件,如果用tolua做綁定,綁定配置文件如下:
複製代碼
 1 class CMGCIAP
 2 {
 3     static CMGCIAP *sharedInstance();
 4     
 5     void setDoBillingCallbackScriptHandler(LUA_FUNCTION nHandler);
 6     
 7     void doBillingWithUI(const char* billingIndex,
 8                          bool isRepeated,
 9                          const char* cpParam);
10 }
複製代碼
OK,到這裡主要的編碼工作就完成了,記得要在程序的適當位置做好Lua Binding初始化工作。
如果一切順利,在以上工作完成後,在Lua裡面已經可以直接調用SDK的接口了,接下來的事情就靠你們了:)

呼~~~
文章到此也差不多了,整個思路和方案就是這些,如果有什麼地方不理解或者不明白,歡迎留言討論:)
謝謝收看!

轉載請註明出處,謝謝:)

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...