SpriteBatchNode

添加多个精灵的2种方法

  • 一种方法是依次添加到一个Layer中
  • 一种方法是依次添加到一个SpriteBatchNode中,再把这个SpriteBatchNode添加到Layer中

效率区别

  • 方法1中每个精灵会被单独绘制一次;
  • 方法2中系统只要对精灵集合进行一次渲染;

SpriteBatchNode的使用方法

  • 初始化精灵集合时要加载一张图片资源(png或者pvr文件),这限制集合中的精灵必须从这张图中设置精灵帧。
  • 使用SpriteBatchNode还要注意,因为精灵都存放在集合中,那么这个集合中的精灵都将在同一个z轴上,即同一深度上;

添加迷雾层代码片段示例:

const char* KFogsPlistFileName="fogs.plist";
const char* KFogsPvrFileName="fogs.pvr.ccz";

SpriteFrameCache::getInstance()->addSpriteFramesWithFile(KFogsPlistFileName);
SpriteBatchNode* fogBatchNode = SpriteBatchNode::create(KFogsPvrFileName);

for(…)
{
    Sprite* sprite=Sprite::createWithSpriteFrameName(fogName);
    fogBatchNode->addChild(sprite);
}

layer->addChild(fogBatchNode);

cocos2d-x安卓应用启动调用过程简析

调用org.cocos2dx.cpp.AppActivity

AppActivity是位于proj.android/src下的开发者类(即开发者自定义的类),它继承自org.cocos2dx.lib.Cocos2dxActivity,在项目生成后它没有添加任何代码,纯粹是一个Cocos2dxActivity,也是一个Activity。

AppActivity被调用是因为被配置在AndroidManifest.xml

<application android:label="@string/app_name"
                 android:icon="@drawable/icon">
        <activity android:name="org.cocos2dx.cpp.AppActivity"
                  android:label="@string/app_name"
                  android:screenOrientation="landscape"
                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                  android:configChanges="orientation">

            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                       android:value="cocos2dcpp" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

加载libcocos2dcpp.so

libcocos2dcpp.so在编译后生成到proj.android/libs/armeabi下,从上面代码中可以看到android:value=”cocos2dcpp”这行配置内容,它指示了so的名字。

在AppActivity中加载so的代码如下:

@Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = ai.metaData;
            try {
                String libName = bundle.getString("android.app.lib_name");
                System.loadLibrary(libName);
            } catch (Exception e) {
                 // ERROR
            }
        } catch (PackageManager.NameNotFoundException e) {
             // ERROR
        }

        sContext = this;
        this.mHandler = new Cocos2dxHandler(this);

        this.init();

        Cocos2dxHelper.init(this);
    }

调用cocos_android_app_init

proj.android/jni/hellocpp/main.cpp中的cocos_android_app_init函数被调用。

我们看看this.init();后续代码片段:

public void init() {
        ...
        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
        ...
    }
@Override
    public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) {
        Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);
        this.mLastTickInNanoSeconds = System.nanoTime();
    }

到现在前面都是java代码,上段中的Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);是一句jni调用,java调用c/c++代码。

其代码在cocos2d/cocos/2d/platform/android/javaactivity.cpp中,如下:

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{
    auto director = cocos2d::Director::getInstance();
    auto glview = director->getOpenGLView();
    if (!glview)
    {
        glview = cocos2d::GLView::create("Android app");
        glview->setFrameSize(w, h);
        director->setOpenGLView(glview);

        cocos_android_app_init(env, thiz);

        cocos2d::Application::getInstance()->run();
    }
    else
    {
        cocos2d::GL::invalidateStateCache();
        cocos2d::ShaderCache::getInstance()->reloadDefaultShaders();
        cocos2d::DrawPrimitives::init();
        cocos2d::VolatileTextureMgr::reloadAllTextures();

        cocos2d::EventCustom foregroundEvent(EVENT_COME_TO_FOREGROUND);
        director->getEventDispatcher()->dispatchEvent(&foregroundEvent);
        director->setGLDefaultValues();
    }

}

我们看到了cocos_android_app_init(env, thiz);这句,这就和main.cpp中的cocos_android_app_init连接上了。

创建AppDelegate

cocos_android_app_init中创建了AppDelegate,后面的事就交给cocos2d-x引擎吧

void cocos_android_app_init (JNIEnv* env, jobject thiz) {
    LOGD("cocos_android_app_init");
    AppDelegate *pAppDelegate = new AppDelegate();
}

 

《知识迷宫》

《知识迷宫》

在迷宫中游戏,在游戏中学习
冲开迷雾,
破除障碍,
展现智慧,
找到终点,
轻松休闲,
趣味连连。


iPhone版 v1.1

km_qr


android版 v1.1

km_qr


继承ScrollView实现左右滑动分页功能

 

#ifndef __PagedScrollView__
#define __PagedScrollView__

#include <iostream>
#include "cocos2d.h"
#include "extensions/cocos-ext.h"

USING_NS_CC;
USING_NS_CC_EXT;

class PagedScrollView : public ScrollView
{
public:
    PagedScrollView();
    ~PagedScrollView();

    static PagedScrollView* create(const Size& size);
    
public://from
    bool onTouchBegan(Touch *touch, Event *event);
    void onTouchMoved(Touch *touch, Event *event);
    void onTouchEnded(Touch *touch, Event *event);
    void onTouchCancelled(Touch *touch, Event *event);
    
private:
    void updateOffset();
    void timer(float dt);
    
private:
    Point offset_;
    clock_t timestamp_;
    bool touchable_;
};

#endif
#include "PagedScrollView.h"

PagedScrollView::PagedScrollView()
{
    timestamp_=0;
    touchable_=true;
}

PagedScrollView::~PagedScrollView()
{
}

PagedScrollView* PagedScrollView::create(const Size& size)
{
    PagedScrollView* pRet = new PagedScrollView();
    if (pRet && pRet->initWithViewSize(size,NULL))
    {
        pRet->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(pRet);
    }
    return pRet;
}

bool PagedScrollView::onTouchBegan(Touch *touch, Event *event)
{
    if(!touchable_)
    {
        return true;
    }
    
    timestamp_=clock()*1000/CLOCKS_PER_SEC;
    return ScrollView::onTouchBegan(touch, event);
}
void PagedScrollView::onTouchMoved(Touch *touch, Event *event)
{
    if(!touchable_)
    {
        return;
    }
    
    ScrollView::onTouchMoved(touch, event);
}
void PagedScrollView::onTouchEnded(Touch *touch, Event *event)
{
    if(!touchable_)
    {
        return;
    }
    
    ScrollView::onTouchEnded(touch, event);

    unscheduleAllSelectors();
    updateOffset();
}
void PagedScrollView::onTouchCancelled(Touch *touch, Event *event)
{
    if(!touchable_)
    {
        return;
    }
    
    ScrollView::onTouchCancelled(touch, event);
}

void PagedScrollView::updateOffset()
{
    const Size& contentSize = getContentSize();
    const Size& viewSize = getViewSize();
    Point offset = getContentOffset();
    if (offset.x == 0 || offset.x == -(contentSize.width - viewSize.width))
    {
        return;
    }
    
    if (offset.x > 0)
    {
        offset.x = 0;
        setContentOffsetInDuration(offset, 0.10);
    }
    else if (offset.x < -(contentSize.width - viewSize.width))
    {
        offset.x = -(contentSize.width - viewSize.width);
        setContentOffsetInDuration(offset, 0.10);
    }
    else
    {
        clock_t timestamp=clock()*1000/CLOCKS_PER_SEC;
        int timediff=timestamp-timestamp_;
        CCLOG("%d",timediff);
        
        if(timediff<=100)
        {
            float divideViewWidth = viewSize.width / 20;
            int diffx=offset.x-offset_.x;
            if(diffx>=divideViewWidth)
            {
                offset_.x+=viewSize.width;
            }
            else if(diffx<=-divideViewWidth)
            {
                offset_.x-=viewSize.width;
            }
        }
        else
        {
            float divideViewWidth = viewSize.width / 2;
            offset_.x = ((int)(offset.x/divideViewWidth))*viewSize.width;
        }
        
        float distance=abs(offset_.x-offset.x);
        float duration=0.6/viewSize.width*distance;
        setContentOffsetInDuration(offset_,duration);
        scheduleOnce(schedule_selector(PagedScrollView::timer), duration);
        touchable_=false;
    }
}

void PagedScrollView::timer(float dt)
{
    touchable_=true;
}

解决ScrollView中添加ControlButton后的拖动冲突问题

自定义类ControlButtonX继承了ControlButton,把要添中到ScrollView中的ControlButton实例改为ControlButtonX的实例即可。下面贴出ControlButtonX类的.h和.cpp文件源码:

//
//  ControlButtonX.h
//

#ifndef __ControlButtonX__
#define __ControlButtonX__

#include <iostream>
#include "cocos2d.h"
#include "cocos-ext.h"

USING_NS_CC;
USING_NS_CC_EXT;

class ControlButtonX : public ControlButton
{
public:
    ControlButtonX();
    static ControlButton* create(Scale9Sprite* sprite);

public://from
    bool onTouchBegan(Touch *pTouch, Event *pEvent);
    void onTouchMoved(Touch *pTouch, Event *pEvent);
    void onTouchEnded(Touch *pTouch, Event *pEvent);
    void onTouchCancelled(Touch *touch, Event *event);
    
private:
    bool moved_;
};

#endif
//
//  ControlButtonX.cpp
//

#include "ControlButtonX.h"

ControlButtonX::ControlButtonX()
{
}

ControlButton* ControlButtonX::create(Scale9Sprite* sprite)
{
    ControlButtonX *pRet = new ControlButtonX();
    pRet->initWithBackgroundSprite(sprite);
    pRet->autorelease();
    return pRet;
}

bool ControlButtonX::onTouchBegan(Touch *pTouch, Event *pEvent)
{
    moved_=false;
    
    auto parent=getParent();
    if(parent)
    {
        if(auto srollView=dynamic_cast<ScrollView*>(parent->getParent()))
        {
            srollView->onTouchBegan(pTouch,pEvent);
        }
    }

    return ControlButton::onTouchBegan(pTouch,pEvent);
}

void ControlButtonX::onTouchMoved(Touch *pTouch, Event *pEvent)
{
    moved_=true;
    
    ControlButton::onTouchMoved(pTouch,pEvent);
    
    auto parent=getParent();
    if(parent)
    {
        if(auto srollView=dynamic_cast<ScrollView*>(parent->getParent()))
        {
            srollView->onTouchMoved(pTouch,pEvent);
        }
    }
}

void ControlButtonX::onTouchEnded(Touch *pTouch, Event *pEvent)
{
 ScrollView* parentScrollView = nullptr;
 auto parent=getParent();
 if(parent)
 {
 parentScrollView = dynamic_cast<ScrollView*>(parent->getParent());
 }

 if (!parentScrollView)
 {
 ControlButton::onTouchEnded(pTouch, pEvent);
 }
 else
 {
 _isPushed = false;
 setHighlighted(false);

 if(!moved_)
 {
 if (isTouchInside(pTouch))
 {
 sendActionsForControlEvents(Control::EventType::TOUCH_UP_INSIDE);
 }
 else
 {
 sendActionsForControlEvents(Control::EventType::TOUCH_UP_OUTSIDE);
 }
 }
 
 parentScrollView->onTouchEnded(pTouch,pEvent);
 }

 moved_=false;
}

void ControlButtonX::onTouchCancelled(Touch *pTouch, Event *pEvent)
{
    ControlButton::onTouchMoved(pTouch,pEvent);
    
    moved_=false;
}

cocos2d-x 3.0 cocos2d::Vector

cocos2d::Vector<T>模板类型T必须是继承自Ref的类

像Point的数组直接使用std::vector<T>就好。
cocos2d::Vector<T>内部实现使用了std::vector<T>,并增加了内存管理。
使用简例:

cocos2d::Vector<Class1*> objArray;
std::vector<Point> pointArray;

使用前向声明可能出现的问题

在头文件中使用Vector的成员的类型的前向声明,可能会出现在编译时报static_assert(std::is_convertible<T, Ref*>::value, “Invalid Type for cocos2d::Vector<T>!”);等错误。
修改为在头文件中包含其头文件就好了。

若从使用CCArray的代码重构到cocos2d::Vector<T>,还可能出现下列问题

不要使用==或!=做比较

否则编译时会报错 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/c++/v1/utility:405:49: Invalid operands to binary expression (‘const cocos2d::Value’ and ‘const cocos2d::Value’)

调用back()等函数前要先做尺寸判断

 if(categoryArray_.size()>0)
 {
 Category* category=(Category*)categoryArray_.back();
 ...
 }

http://dev.bunnyhero.org/2014/01/cocos2d-x-30-beta-the-new-vector-class/
https://github.com/cocos2d/cocos2d-x/blob/develop/docs/RELEASE_NOTES.md#new-eventdispatcher

cocos2d-x 3.0 创建工程

新建工程

https://github.com/chukong/cocos-docs/blob/master/catalog/zh.md

执行以下命令

cd cocos2d-x
./setup.py
source ~/.bash_profile
cocos new KnowledgeMaze -p game.knowledge.maze -l cpp -d ~/x_workspace/

参数解释

  • new后面跟工程名称
  • -p指定android工程使用的包名
  • -l指定编程语言 cpp或者lua
  • -d指定创建到的目录
  • -m指定编译模式 debug或release
  • setup.py 需要指定sdk,ndk,ant 目录,到http://ant.apache.org/bindownload.cgi下载ant,需要指定目录为ant下的bin,下载最新的sdk,ndk。

.bash_profile的内容示例

export NDK_ROOT="/Users/gzty1/adt-bundle-mac-x86_64/ndk"

# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x
export COCOS_CONSOLE_ROOT=/Users/gzty1/cocos2d-x/tools/cocos2d-console/bin
export PATH=$COCOS_CONSOLE_ROOT:$PATH

# Add environment variable ANDROID_SDK_ROOT for cocos2d-x
export ANDROID_SDK_ROOT=/Users/gzty1/adt-bundle-mac-x86_64/sdk
export PATH=$ANDROID_SDK_ROOT:$PATH
export PATH=$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$PATH

# Add environment variable ANT_ROOT for cocos2d-x
export ANT_ROOT=/Users/gzty1/adt-bundle-mac-x86_64/ant/bin
export PATH=$ANT_ROOT:$PATH

编译并运行工程

执行以下命令

cocos run -s  ~/x_workspace/KnowledgeMaze -p ios

参数解释

  • run改为compile 只编译
  • -s指定工程根目录
  • -p指定平台 ios android win32 mac linux

编译并运行android工程

把cocos2d-x/cocos/2d/platform/android/java/src下的org目录拷贝到proj.android/src,注意不要把原来的org的内容覆盖了。也可以导入cocos2dx/platform/android/java这个工程生成一个jar文件添加到项目的libs文件夹下。

把Resources下的文件拷贝到proj.android/assets;
修改Android.mk示例:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/HelloWorldScene.cpp \
                   这里列出每个cpp

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes/Common \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes/Knowledge \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes/data \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes/regex \

LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static
LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocos_extension_static


include $(BUILD_SHARED_LIBRARY)

$(call import-module,2d)
$(call import-module,audio/android)
$(call import-module,Box2D)
$(call import-module,extensions)

执行以下命令

cocos run -s ~/x_workspace/KnowledgeMaze -p android

如果是在windows中,先要进入D:\cocos2d-x\tools\cocos2d-console\bin目录,找到cocos命令

cocos run -s d:\c_workspace\KnowledgeGame -p android
  • 可能出现一直Waiting for device的问题,解决方法是在开发者选项中,选择USB配置,切换到Audio Source尝试。
  • 可能出现在手机上显示Waiting for debugger,解决方法是在开发者选项中,选择调试应用,切换到无。

命令行编译android项目时如果报错: UNEXPECTED TOP-LEVEL Multiple dex files …

可能是因为在用Eclipse IDE开发为了解决找不到java代码时的编译错误,把cocos2d/cocos/2d/platform/android/java/src/org/cocos2dx/下的lib代码文件夹拷贝到了proj.android/src/org/cocos2dx下,而proj.android/project.properties文件中有android.library.reference.1=../cocos2d/cocos/2d/platform/android/java这行指示会引用这个库,所以最后用命令行编译时会把dex files多了的错误。

如果在使用eclipse编译时找不到cocos2d-x的java代码,把项目目录下的cocos2d\cocos\2d\platform\android\java目录导入为单独库,然后在项目属性\android\library中把它添加进来,不要勾选Is Library。

运行示例代码

示例代码工程在\cocos2d-x\build中,找到对应的工程文件,打开即可。

在Visual Studio 2012中编译使用HttpClient

添加现有项,\cocos2d-x\cocos\network\proj.win32\libNetwork.vcxproj,然后在主工程的通用属性中添加新引用libNetwork,然后在属性\链接器\输入\附加依赖项中输入libcurl_imp.lib

 cocos2d-x3.3visual studio项目中添加cocos2d-x库

在解决方案中添加现有项目,\cocos\2d\ibcocos2d.vcxproj,然后在主工程中添加引用,添加新引用,勾选要添加的引用,确定。

 

ControlButton

不要多次addTargetWithActionForControlEvents

注意这个函数是add,而不是set

ControlButton* NodeUtil::createOrUpdateControlButton(ControlButton* controlButton,const char* normalBgSpriteFrameName,const char* selectedBgSpriteFrameName,float scale,const Size& contentSize,Ref* target, Control::Handler action)
{
    Scale9Sprite* btnDown = Scale9Sprite::createWithSpriteFrameName(normalBgSpriteFrameName);
    Scale9Sprite* btnNormal = Scale9Sprite::createWithSpriteFrameName(selectedBgSpriteFrameName);
    
    btnNormal->setScale(scale);
    btnDown->setScale(scale);
    
    if(!controlButton)
    {
        controlButton = ControlButton::create(btnNormal);
    }
    else
    {
        controlButton->setBackgroundSpriteForState(btnNormal, Control::State::NORMAL);
    }
    controlButton->setBackgroundSpriteForState(btnDown, Control::State::SELECTED);
    
    Size tempSize=contentSize;
    if(tempSize.equals(Size::ZERO))
    {
        tempSize=btnDown->getContentSize();
    }
    
    controlButton->setPreferredSize(tempSize);
    
    if (target && action)
    {
        controlButton->removeTargetWithActionForControlEvents(target, action, Control::EventType::TOUCH_UP_INSIDE);
        controlButton->addTargetWithActionForControlEvents(target, action, Control::EventType::TOUCH_UP_INSIDE);
    }

    return controlButton;
}

cocos2d-x android平台下在c++代码中调用java代码

JniHelper

cocos2d-x中封装好了一个JniHelper类,可以帮助在c++代码中调用java代码。
#include “platform/android/jni/JniHelper.h”

参考 http://blog.csdn.net/yuechuzhao/article/details/9283847

通过JniHelper获取java函数信息体

static bool getStaticMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramsAndReturntype);
static bool getMethodInfo(JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramsAndReturytype;

示例:

static const char* JavaPackgeClassName="game/knowledge/maze/AppActivity";
JniMethodInfo methodInfo;
bool isHave = JniHelper::getStaticMethodInfo(methodInfo,JavaPackgeClassName,"fun", "(Ljava/lang/String;Ljava/lang/String;)V");

java类型简写对照

boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
Object Ljava/lang/Object;
Array [Ljava/lang/Object;

使用JniMethodInfo中的信息调用java函数

示例

//void fun(const char* title, const char* message)

jstring jtitle = methodInfo.env->NewStringUTF(title);
jstring jmessage = methodInfo.env->NewStringUTF(message);

methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jtitle, jmessage);

c++类型和jni类型对应关系

下面列表出部分
void jvoid
bool jboolean
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
boolean[] jbooleanArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray

NDK-JNI实战教程(二) JNI官方中文资料
http://www.cnblogs.com/jycboy/archive/2016/04/15/5396876.html