cocos android分析

cocos android分析

大家好,又见面了,我是全栈君。

来自:http://xiebaochun.github.io/

cocos2d-x Android环境搭建

cocos2d-x环境搭建比較简单,可是小问题还是不少,我尽量都涵盖的全面一些。

下载软件 

cygwin、NDK(ADT):C++相关 

假设之前没有Android开发环境。还须要Android SDK,Eclipse 

cocos2d-x源代码 

我的环境为ndk r7。cygwin1.7,Android SDK为2.2和3.0.另外,我是通过真机调试,在模拟器上不行,预计还是我T410显卡的问题.

安装cygwin。在cygwin文件进行路径设置 

在cygwin\home\Administrator的.bash_profile中加入例如以下代码

   1: ANDROID_NDK_ROOT=/cygdrive/e/ADT/android-ndk-r7c

   2: export ANDROID_NDK_ROOT

   3: NDK_ROOT=/cygdrive/e/ADT/android-ndk-r7c

   4: export NDK_ROOT

将libgnustl_static.a从NDK中的android-ndk-r7c\sources\cxx-stl\gnu-libstdc++\libs\armeabi拷贝至cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android\obj\local\armeabi。这个从解决方式上看应该是stl的引用不一致导致的问题。但编译中会报错“png.a can not find”,可是path路径确实没什么问题,所以比較坑爹。总之这样就搞定了。我也没怎么深究。

进入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android文件夹下改动例如以下内容到指定路径

   1: NDK_ROOT_LOCAL=/cygdrive/e/ADT/android-ndk-r7c

   2: COCOS2DX_ROOT_LOCAL=/cygdrive/f/cocos2d-1.0.1-x-0.13.0-beta

cygwin中进入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\android文件夹下。运行./build_native.sh,编译C++,JNI接口。供Android Java使用,假设成功。在在libs中生成libhelloworld.so动态库。我们都是为了它做了这么多工作

在Eclipse中导入cocos2d-1.0.1-x-0.13.0-beta\HelloWorld\androidproject。熟悉Android的一看就发现,事实上这本身就是一个Javaproject,我们刚才的操作仅仅是当中jni的部分,供Java以下的调用实现而已

Eclipse中运行Build Project。生成R.java

Run

 

 

Make

ndk的Make是在GNU的Make的基础上的一种封装,以下我们来分析一下./build_native.sh都做了哪些操作。

简单说主要是资源拷贝和代码编译。

资源拷贝在我的cygwin里面发现有问题。拷贝后的文件是错误的,且不能删除我没有深究,自己手动拷贝了一下。和shell一致,非常easy理解,不再深究。

ndk-build编译HelloWorldproject。编译jni目录以下的Android.mk,和makefile基本相似,指定须要编译的文件。include路径,依赖projectcocos2dx_static。进行编译,比如HelloWorld的makefile大致例如以下:

LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)

LOCAL_MODULE := helloworld_shared

LOCAL_MODULE_FILENAME := libhelloworld
LOCAL_SRC_FILES := helloworld/main.cpp \

                   ../../Classes/AppDelegate.cpp \

            ../../Classes/HelloWorldScene.cpp

 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes

LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static

include $(BUILD_SHARED_LIBRARY)

$(call import-module,cocos2dx)
LOCAL_PATH := $(call my-dir):指定当前路径为为LOCAL_PATH

include $(CLEAR_VARS):清空除LOCAL_PATH之外的其它环境变量的干扰

LOCAL_MODULE&LOCAL_MODULE_FILENAME:模块名称&生成库名称

LOCAL_SRC_FILES:编译的C++ Source

LOCAL_WHOLE_STATIC_LIBRARIES:依赖的静态库

BUILD_SHARED_LIBRARY:生成的为共享库。由于Android的动态库都为JNI所用,所以称为共享库。而静态库仅仅为其它C++库所用。

$(call import-module,<name>):通过NDK_MODULE_PATH环境变量引用的模块<name>的文件夹列表。而且将其自己主动包括到Android.mk中

这样,一个编译环境的include,library,target基本指定。则编译出终于的目标文件,和makefile思路上没什么差别,另外这里须要编译出cocos2dx.a,静态库,是通过cocos2d目录中的make编译而成。这个脚本则要复杂一些,只是思想并无不同。很多其它NDK Make能够參考:《Android Make》

JNI交互
C++接口封装完成后,我们就開始看一下Java代码,了解一下终于实现的流程和效果,Java代码例如以下:

 

Java层的框架也非常easy。这里并没有多Accelerometer和Music、Sound等进行分析,仅仅是对涉及到显示相关的进行分析。Java层面流程例如以下:

 

如上,假设熟悉Android界面开发,能够从基类了解到Java层面是通过Activity、GLSuffaceView来进行的显示。

这里不具体介绍。假设有兴趣。能够看一下《剖析游戏开发用view还是surfaceView》,View类似传统的二维静态界面,数据驱动显示,而SurfaceView则类似三维机制,实时渲染。

由于Cocos2d是OpenGL的。这也好解释。

对于整个框架事实上要说的也非常多,只是我对Java还不太了解。所以有些东西看的不一定透。也难免有一些问题。

Renderer
Renderer类负责每一帧的渲染驱动,调用步骤如图里面的1和2。在2中调用jni里面的nativeRender实现一帧的渲染。而GLSurfaceView则负责UI交互的监听。

这样的机制的优点是在Java中Renderer渲染器是独立线程调用。因此和UI之间没有交互性。这样既保证了用户体验(用户的事件通过GLSurfaceView监听,终于通过Renderer传递至C++层面来响应),也保证了渲染过程的抗干扰,依然通过C++层面进行渲染。

,整个显示过程用到的jni封装主要例如以下:

 private static native void nativeTouchesBegin(int id, float x, float y);

 private static native void nativeTouchesEnd(int id, float x, float y);

private static native void nativeTouchesMove(int[] id, float[] x, float[] y);

private static native void nativeTouchesCancel(int[] id, float[] x, float[] y);

private static native boolean nativeKeyDown(int keyCode);

private static native void nativeRender();

private static native void nativeInit(int w, int h);

private static native void nativeOnPause();

private static native void nativeOnResume();

jni封装

jni的封装主要有两部分,一个是cocos2d自己的JNI封装,这部分封装主要是为了在Java中调用cocos2d的jni接口,一个是HelloWorld中自己的jni接口封装。这一块本来是我比較感兴趣的地方,由于jni封装还是挺繁琐的一件事情。最后发现cocos2d在本质上也没有什么差别,麻烦的还是得封装。

第二点,cocos2d主要是游戏引擎。所以基本全部功能都是由C++层面来实现,一帧的渲染,事件的处理,而Java层主要负责逻辑处理,终于通过jni调用C++接口来实现。第三点来说,cocos2d本身封装的还是非常简洁的,这点我认为做的还是非常优雅的,在设计这块,是以Java的逻辑为根据来进行划分。我认为这个非常可取,尽管cocos2d是C++做起来的。可是并没有为了保证各个平台的一致性而强迫接口的一致。而是在jni层依照SDK在详细平台的应用特点来进行封装,这样减低了实现难度。提高了代码的易用度。牺牲就是应用平台接口的局部不一致性。jni层面主要是事件传递和窗体渲染部分的接口封装,针对游戏开发人员而言,最核心的部分都能够在Windows平台下完毕,然后在Android部分完毕特有事件的传递,渲染部分直接採用cocos2d给出的标准范例实现就可以。大大简化了开发人员自己封装jni的工作。

窗体绑定
窗体绑定我理解的并不太透彻,首先,我觉得CCEGLView_Android仅仅是一个虚的窗体,并没有实质功能。仅仅是为了便于架构理解。

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)

 {

   if (!cocos2d::CCDirector::sharedDirector()->getOpenGLView())

   {

       cocos2d::CCEGLView *view = &cocos2d::CCEGLView::sharedOpenGLView();

      view->setFrameWidthAndHeight(w, h);

      // if you want to run in WVGA with HVGA resource, set it

       // view->create(480, 320);  Please change it to (320, 480) if you're in portrait mode.

       cocos2d::CCDirector::sharedDirector()->setOpenGLView(view);



        AppDelegate *pAppDelegate = new AppDelegate();
       cocos2d::CCApplication::sharedApplication().run();

  }


}

 

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env)

 {

  cocos2d::CCDirector::sharedDirector()->mainLoop();

}
 

函数一是Java层调用onSurfaceCreated时调用函数,用来获取GLView窗体。用来下一步的渲染,而这个View窗体并没有类似Windows下的handle绑定,而接下来函数二是Java中onDrawFrame渲染每一帧时进行调用,终于调用底层的Director渲染,完毕一帧绘制(具体内容可參考《cocos2d-x之HelloWorld范例分析(一)》)。

怎么来理解这样的窗体绑定方式,保证我如今调用的gl函数,就行绘制在窗体呢,通篇没有一个类似的handle从Java传递给JNI,通篇C++层面的View也仅仅是一个仅仅有Width和Height属性的结构体,所以我理解的是GLSurfaceView.Renderer默认在自己的线程中进行了封装,已经自己完毕了和OpenGL的绑定。这个我认为应该是靠谱的吧,并且自己来实时的每一帧渲染,以下的就无论里,你自己愿意调Java的接口也行,自己调gl的渲染也可以。这样也挺好的,都不用我顾虑这个事情了,仅仅要给我高度宽度知道位置信息,我直接渲染。

文字

其它图形图像的绘制,都是和系统无关的。整个的渲染过程,也是跨平台的,一个平台的整合。主要是环境搭建、不同语言之间的消息传递、View的映射这些,前面也都阐述了,仅仅是文字有一定的特殊。在Windows下使用CDC。在Linux是Freetype,在Android下怎样实现?我认为cocos2d实现思路也是不错的:C++通过JNI在Java层绘制。生成一张BitMap给C++,然后贴图完毕。这个长处是简单,缺点就是假设文字太多的话,效率损失还是有的,事实上我认为假设有机会,还是用Freetype来画应该也能够尝试一下。

当然,也新学了一招。C++调用Java的方式,在jni里面也提供了,呵呵。代码在以下贴一下:

bool getBitmapFromJava(const char *text, int nWidth, int nHeight, CCImage::ETextAlign eAlignMask, const char * pFontName, float fontSize)

{

  JniMethodInfo methodInfo;
if (! JniHelper::getStaticMethodInfo(methodInfo, "org/cocos2dx/lib/Cocos2dxBitmap", "createTextBitmap", 

       "(Ljava/lang/String;Ljava/lang/String;IIII)V"))

    {

      CCLOG("%s %d: error to get methodInfo", __FILE__, __LINE__);
      return false;

   }


    jstring jstrText = methodInfo.env->NewStringUTF(text);

    jstring jstrFont = methodInfo.env->NewStringUTF(pFontName);



   methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jstrText, 
    jstrFont, (int)fontSize, eAlignMask, nWidth, nHeight);


    methodInfo.env->DeleteLocalRef(jstrText);
   methodInfo.env->DeleteLocalRef(jstrFont);

   methodInfo.env->DeleteLocalRef(methodInfo.classID);


  return true;

}

static bool getStaticMethodInfo_(cocos2d::JniMethodInfo &methodinfo, const char *className, const char *methodName, const char *paramCode)

{

   jmethodID methodID = 0;

  JNIEnv *pEnv = 0;

  if (! getEnv(&pEnv))

    {

        break;
   }


   jclass classID = getClassID_(className, pEnv);


    methodID = pEnv->GetStaticMethodID(classID, methodName, paramCode);

 

參照里面的凝视,C++驱动Java实现绘制,Java完毕绘制后,调用Java_org_cocos2dx_lib_Cocos2dxBitmap_nativeInitBitmapDC接口,实现内存的拷贝,而s_BmpDC中的m_pData用来保存,进行下一步的纹理贴图,完毕整改流程的传递.

总结

介绍完成,整个过程中,cocos2d使用的技术并不神奇,主要是一个熟悉的过程.最值得称赞的是JNI封装的比較使用,本身做游戏开发,基本全部功能都会在C++中封闭实现,仅仅须要提供一个规范的Java外壳就能够,既跨平台有高效.另外,就是cocos2d对各个平台的语言取舍,哪些用Java方便,哪些用C++ 保持平台一致,都做的还是非常合理的.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115998.html原文链接:https://javaforall.net

(2)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 主宰操作系统的经典算法

    主宰操作系统的经典算法此篇文章带你梳理一下操作系统中都出现过哪些算法进程和线程管理中的算法进程和线程在调度时候出现过很多算法,这些算法的设计背景是当一个计算机是多道程序设计系统时,会频繁的有很多进程或者线程来同时竞争CPU时间片。那么如何选择合适的进程/线程运行是一项艺术。当两个或两个以上的进程/线程处于就绪状态时,就会发生这种情况。如果只有一个CPU可用,那么必须选择接下来哪个进程/线程可以运行。操作系统中有一个叫做调度程序(scheduler)的角色存在,它就是做这件事儿的,调度程序使用的算法叫做调度算

    2022年6月4日
    28
  • 快速搭建自己的直播服务器,完成属于你的直播服务。

    快速搭建自己的直播服务器,完成属于你的直播服务。通过本文您能学到以下内容:1、动手搭建直播服务器,完成一次对移动端支持的直播。2、使用OBS推流。3、使用html5网页播放m3u8流媒体文件。一、动手搭建流媒体服务器这里我们选用开源srs流媒体服务器[https://github.com/ossrs/srs],有兴趣的同学也可选用[AdobeMediaServer5],原理都是一样的。安装方法在[https://…

    2022年6月11日
    129
  • 选择排序(C语言实现)

    选择排序(C语言实现)选择排序(C语言实现)实现原理:给出一组数据,第1轮在待排序记录r[1]-r[n]中选出最小的记录,将它与r[1]交换;第2轮在待排序记录r[2]-r[n]中选出最小的记录,将它与r[2]交换;以此类推,第i趟在待排序记录r[i]~r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。代码初始序列:{49276597761238}第1趟:12与49交换:12{276597764938}第2趟:27不动:1227{6597764938}

    2022年6月25日
    25
  • pycharm编写的文件如何执行_pycharm可以写java吗

    pycharm编写的文件如何执行_pycharm可以写java吗最近在做编译原理课设,准备用Java写个GUI整合一下,因为自己的LL1文法使用python写的,所以需要Java来实现运行python代码,网上给出了主要三种方法,但是使用Jython的方法有局限性且不太方便,于是我将采用最简洁的方法Runtime.getRuntime()运行python代码,下面给出代码Processproc;try{/*注意:这里这种方式和cmd中使用python+文…

    2022年8月27日
    0
  • Mysql主从配置,实现读写分离

    Mysql主从配置,实现读写分离

    2021年11月7日
    34
  • 微信公众号网页开发,获取 调用微信公众号接口后 的返回值,PHP

    微信公众号网页开发,获取 调用微信公众号接口后 的返回值,PHP在微信公众号网页开发中,我们通常会用到许多微信团队提供的接口,我们在运行单个php调用接口的时候,返回值直接出现在php的echo中,我们根据这个值来判断代码正确与否,但是当php文件和微信公众号连接起来的时候,我们无法获取返回值,下面就展示一下如何将返回值的信息写入txt文件中。测试代码段&lt;?php$myfile=fopen("fanhui.txt","a+")ordie("U…

    2022年6月5日
    121

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号