八、添加本机 Java 代码

到目前为止,我们一直只使用编写 Cocos2d-x 游戏框架的编程语言(C++)来创建我们的游戏;尽管如此,Google 编写的 Android API 仅在应用程序的 Java 层可用。在本章中,您将学习如何使用 Java 本机接口 ( JNI )功能将我们的本机 C++ 代码与更高端的 Java 内核进行通信。

本章将涵盖以下主题:

  • 了解安卓平台的 Cocos2d-x 结构
  • 了解 JNI 的能力
  • 将 Java 代码添加到 Cocos2d-x 游戏中
  • 通过插入 Java 代码向游戏中添加广告

了解安卓平台的 Cocos2d-x 结构

第 1 章设置您的开发环境中,当我们正在安装构建 Cocos2d-x 框架所需的所有组件时,我们告诉您下载并安装 Android 原生开发工具包 ( NDK ),它允许我们使用 C++ 语言构建 Android 应用程序,而不是使用已经为其编写了 Android API 的主流 Java 技术核心。

当一个安卓应用程序启动时,它会在其AndroidManisfest.xml文件中寻找一个具有意图过滤器android.intent.action.MAIN的活动定义,然后它会运行 Java 类。下面的列表显示了由 Cocos 新脚本生成的AndroidManifest.xml文件的片段,其中指定了 Android 应用程序启动时要启动的活动:

<activity
   android:name="org.cocos2dx.cpp.AppActivity"
   android:configChanges="orientation"
   android:label="@string/app_name"
   android:screenOrientation="portrait"
   android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

Cocos2d-x 项目创建脚本创建了一个名为AppActivity的 Java 类,它位于proj.android目录下org.cocos2dx.cpp Java 包名下的src文件夹中。这个类没有主体,是从Cocos2dxActivity类扩展而来的,我们可以在下面的代码清单中看到:

package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;

public class AppActivity extends Cocos2dxActivity {
}

Cocos2dxActivity类在其onCreate 方法中加载本机 C++ 框架核心。

了解 JNI 的能力

JNI 在 C++ 代码和 Java 代码之间架起了一座桥梁。Cocos2d-x 框架为我们提供了一个 JNI 助手,这使得集成 C++ 代码和 Java 代码变得更加容易。

JniHelper C++ 类有一个叫getStaticMethodInfo的方法。它接收一个JniMethodInfo对象作为参数,该对象存储调用相应的 Java 代码所需的所有数据、静态方法所在的类名、方法名及其签名。

为了找到 JNI 的方法签名,您可以使用javap命令:例如,如果我们想知道AppActivity类中包含的方法签名是什么,那么我们只需要打开一个控制台窗口,转到您的proj.android\bin\classes目录,并键入以下命令:

SET CLASSPATH=.
javap -s org.cocos2dx.cpp.AppActivity

在这种特殊情况下,您将收到一个响应,为类自动创建的null构造函数的签名如下:

Compiled from "AppActivity.java"
public class org.cocos2dx.cpp.AppActivity extends org.cocos2dx.lib.Cocos2dxActivity {
  public org.cocos2dx.cpp.AppActivity();
    Signature: ()V
}

然后,通过附加到JniMethodInfo实例的属性env,我们可以用该对象包含的一组方法调用 Java 方法,所有这些都从Call…开始。在我们下一节要写的代码中,我们将使用CallStaticVoid方法来调用一个不返回值的静态方法,顾名思义。请注意,如果您想要传递一个 Java 字符串作为参数,那么您将需要调用env属性的NewStringUTF方法,传递const char*,它将返回一个jstring实例,您可以使用该实例传递给一个接收字符串的 Java 方法,如下面的代码清单所示:

JniMethodInfo method;
JniHelper::getStaticMethodInfo(method, CLASS_NAME,"showToast","(Ljava/lang/String;)V");
jstring stringMessage = method.env->NewStringUTF(message);
method.env->CallStaticVoidMethod(method.classID,  method.methodID, stringMessage);

最后,如果您已经在 C++ 代码中创建了jstring或任何其他 Java 抽象类的实例,那么请确保在将值传递给 Java 核心后删除这些实例,这样我们就不会在内存中有不必要的引用。这可以通过调用位于JniMethodInfo实例的env属性中的DeleteLocalRef方法并传递您想要移除的 Java 抽象引用来实现:

method.env->DeleteLocalRef(stringMessage);

本节中公开的概念将应用于下一节中的代码清单。

在 Cocos2d-x 游戏中添加 Java 代码

现在我们将在这两种技术之间创建一个简单的集成,这将允许我们的 Cocos2d-x C++ 游戏使用 Android Java API 显示祝酒信息。

安卓系统中的祝酒词是一个弹出消息,显示指定的时间,没有在预定义时间之前隐藏的选项。本节末尾的截图显示了敬酒信息的样子。

Cocos2d-x 在一个 Java 活动中运行,因此为了显示原生的 Android toast 消息,我们将创建一个 Java 类,该类将有一个名为showToast的静态方法。这会收到一个字符串,然后它会在祝酒词中显示出来。为了访问 Cocos2d-x 游戏活动,我们将向该类添加一个类型为Activity的静态属性,并在被覆盖的onCreate方法中初始化它。然后,我们将创建一个公共静态方法,允许我们从 Java 代码中的任何地方访问这个实例。经过这些修改后,我们的AppActivity Java 类代码将如下所示:

package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;
import android.app.Activity;
import android.os.Bundle;

public class AppActivity extends Cocos2dxActivity {
   private static Activity instance;
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
       instance = this;
        super.onCreate(savedInstanceState);

    }
    public static Activity getInstance(){
       return instance;
    }
}

现在,让我们在我们的com.packtpub.jni包中创建引用的JniFacade Java 类,它的主体中只有一个接收字符串作为参数的静态 void 方法,然后用接收到的消息在 UI 线程上显示一个 toast,如下所示:

package com.packtpub.jni;

import org.cocos2dx.cpp.AppActivity;
import android.app.Activity;
import android.widget.Toast;

public class JniFacade {
   private static Activity activity = AppActivity.getInstance();

   public static void showToast(final String message) {
      activity.runOnUiThread(new Runnable() {         
         @Override
         public void run() {
            Toast.makeText(activity.getBaseContext(), message, Toast.   LENGTH_SHORT).show();   
         }
      });      
   }
}

现在我们的代码在 Java 端,让我们把JniBridge C++ 类添加到我们的classes文件夹中。

JniBridge.h头文件中,我们将写下以下内容:

#ifndef __JNI_BRIDGE_H__
#define __JNI_BRIDGE_H__
#include "cocos2d.h"

class JniBridge
{
public:
   static void showToast(const char* message);
};

#endif

现在让我们创建实现文件JniBridge.cpp,在这里我们将调用我们的静态 Java 方法showToast,它接收一个字符串作为参数:

#include "JniBridge.h"
#define CLASS_NAME "com/packtpub/jni/JniFacade"
#define METHOD_NAME "showToast"
#define PARAM_CODE "(Ljava/lang/String;)V"

USING_NS_CC;

void JniBridge::showToast(const char* message)
{
   JniMethodInfo method;
   JniHelper::getStaticMethodInfo(method, CLASS_NAME, METHOD_NAME, PARAM_CODE);
   jstring stringMessage = method.env->NewStringUTF(message);
    method.env->CallStaticVoidMethod(method.classID, method.methodID, stringMessage);
    method.env->DeleteLocalRef(stringMessage);
}

正如我们所看到的,这里我们使用了中与 Cocos2d-x 框架捆绑在一起的JniMethodInfo结构和JniHelper类,以便调用showToast方法,并将我们的 C++ 代码中的 C 字符串发送给它,该字符串被转换为 Java 字符串。

现在让我们将我们的JniBridge.h头文件包含在我们的HelloWorldScene.cpp实现文件中,这样我们就可以从我们的主场景类中访问到 Java 代码的桥:

#include "JniBridge.h"

现在在位于HelloWorld.cpp实现文件中的init方法的末尾,我们将调用showToast静态方法,以便使用 Android Java API 来显示本机吐司消息,显示从我们的 C++ 代码发送的文本如下:

JniBridge::showToast("Hello Java");

这将产生以下结果:

Adding Java code to the Cocos2d-x game

正如我们从前面的截图中可以欣赏到的,我们已经实现了从我们的 C++ 游戏逻辑代码中显示原生 Java toast 消息的目标。

通过插入 Java 代码为游戏添加广告

在前面的部分,我们已经使用 JNI 在我们的 C++ 游戏逻辑代码和我们的安卓应用的 Java 层之间创建了一个交互。在这一部分,我们将修改我们的安卓专用代码,以便在我们的安卓游戏中显示谷歌 AdMob 横幅。

AdMob 是一个谷歌平台,允许你通过显示广告将你的应用货币化,它也有分析工具,以及应用内购买的工具。

配置环境

为了显示谷歌 AdMob 横幅,我们需要将Google Play Services库添加到我们的项目中。为了做到这一点,我们首先需要使用安卓软件开发工具包管理器下载它及其依赖项——安卓支持库:

Configuring the environment

成功下载谷歌游戏服务及其依赖项后,您需要将 Android.support.v4 添加到您的项目中,因为谷歌游戏服务库需要它。为此,我们将把位于以下路径上的android-support-v4.jar文件复制到我们的安卓项目中包含的libs文件夹中,然后我们将通过在 Eclipse 的包浏览器中右键单击我们的项目将其添加到我们的构建路径中,然后单击构建路径,然后单击配置构建路径。将显示 Java 构建路径配置窗口,点击添加 JARS… 按钮,在libs文件夹中添加android-support-v4.jar文件。

现在,我们将复制我们刚刚下载的谷歌游戏服务代码。这现在位于我们工作空间路径的<ADT PATH>\sdk\extras\google\google_play_services上。通过右键单击您的 Eclipse Java 项目,然后单击属性,最后选择左侧的资源选项,您可以找到工作空间的路径;在那里你会看到位置信息,如下图截图所示:

Configuring the environment

我们已经建立了依赖关系,所以现在让我们添加谷歌游戏服务库,通过导航到文件 | 导入 | 安卓 | 现有安卓代码到工作区 | 浏览… 。然后,浏览到您从上一步复制谷歌游戏服务的位置。取消选择除google-play-services_lib以外的所有项目,点击完成:

Configuring the environment

现在我们的工作区中有了google-play-services_lib项目,让我们将其配置为我们的 Cocos2d-x 游戏项目的库。为此,我们将再次在包资源管理器上右键单击我们的项目,单击属性,单击左窗格中的安卓部分,然后在屏幕的底部下方,我们将单击添加… 按钮,以便将google-play-services_lib库添加到我们的 Eclipse 项目中,如下图所示:

Configuring the environment

现在我们都准备好了,准备进入下一个部分,我们将使用刚刚添加的库来显示谷歌广告。

现在,我们的 AdMob 横幅将显示在屏幕顶部,我们现在将我们的静音按钮移动到底部,这样它就不会被横幅覆盖。我们将通过改变静音和非静音按钮的位置来实现这一点。我们现在将它的 y 组件设置为屏幕高度减去静音按钮高度的两倍,而不是将屏幕高度减去静音子画面高度的一半作为它的垂直位置,正如我们在initMuteButton方法中的下面一行代码所示:

   _muteItem->setPosition(Vec2(_visibleSize.width -  _muteItem->getContentSize().width/2 ,_visibleSize.height -  _muteItem->getContentSize().height * 2));

修改安卓清单

在这个部分,我们将修改我们的安卓清单,以便将其插入使用谷歌游戏服务库所需的配置中。

我们只需要添加两个片段,其中一个就在打开的应用程序标签旁边,该标签将指示正在使用的谷歌游戏服务的版本,我们可以从下面的代码清单中了解到:

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

我们要添加的第二个片段是AdActivity声明,它将被添加到我们的游戏活动声明的旁边,这样我们的游戏就知道这个位于 Google Play Services 库中的内置活动:

       <activity
            android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation| screenLayout|uiMode|screenSize|smallestScreenSize" />

添加 Java 代码

现在我们已经配置了库,并且修改了安卓清单,广告库已经可以使用了。我们要给我们的AppActivity类添加一个 ad 初始化方法,在调用它的超类的实现之后再调用它。

为了下面的例子,我们将使用一个示例 AdMob 标识,您可以用自己的标识替换它。您可以在http://www.google.com/admob找到更多关于如何创建自己的 AdMob ID 的信息。

   private void initAdMob() {
      final String ADMOB_ID = "ca-app-pub-7870675803288590/4907722461";
      final AdView adView;
      final FrameLayout adViewLayout;

      FrameLayout.LayoutParams adParams = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT);
      adParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;      

      AdRequest adRequest = new AdRequest.Builder().
            addTestDevice(AdRequest.DEVICE_ID_EMULATOR).
            addTestDevice("E8B4B73DC4CAD78DFCB44AF69E7B9EC4").build();

      adView = new AdView(this);
      adView.setAdSize(AdSize.SMART_BANNER);
      adView.setAdUnitId(ADMOB_ID);
      adView.setLayoutParams(adParams);
      adView.loadAd(adRequest);
      adViewLayout = new FrameLayout(this);
      adViewLayout.setLayoutParams(adParams);
      adView.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
              adViewLayout.addView(adView);
            }         
      });      
      this.addContentView(adViewLayout, adParams);
   }

与上一节相比,我们没有使用 JNI,因为我们根本没有与 C++ 代码交互;相反,我们正在修改由cocos命令创建的安卓活动,以便添加更多的图形元素来查看模板中定义的 OpenGL E 视图的一面。

我们只是以编程方式创建了一个框架布局,并向其中添加了一个adView实例;最后,我们将这个框架布局作为内容视图添加到游戏活动中,然后我们使用重力布局参数指定了它的期望位置,这就是我们最终能够在屏幕顶部显示谷歌广告的方式。请注意,您可以通过简单地修改布局参数来修改广告的位置,即您希望它显示的位置。

请注意在广告成功加载后,我们已经将adView 添加到框架布局中。通过使用AdListener,如果在广告完成投放前添加了adView实例,则不会显示。

这就是我们的谷歌 AdMob 在将一切联系在一起后的样子:

Adding the Java code

把所有东西放在一起

我们已经完成了在我们的 Cocos2d-x 游戏中嵌入核心 Java 代码的目标。现在我们将一起展示本章中修改过的游戏的所有部分。

这里,我们展示了我们从头开始创建的 C++ JNI 桥(JniBridge.h)的头文件:

#ifndef __JNI_BRIDGE_H__
#define __JNI_BRIDGE_H__
#include "cocos2d.h"

class JniBridge
{
public:
   static void showToast(const char* message);
};

#endif

既然我们已经定义了JniBridge的头,那么让我们编写实现文件(JniBridge.cpp):

#include "JniBridge.h"
#include "platform/android/jni/JniHelper.h"
#define CLASS_NAME "com/packtpub/jni/JniFacade"
#define METHOD_NAME "showToast"
#define PARAM_CODE "(Ljava/lang/String;)V"

USING_NS_CC;

void JniBridge::showToast(const char* message)
{
   JniMethodInfo method;
   JniHelper::getStaticMethodInfo(method, CLASS_NAME, METHOD_ NAME,PARAM_CODE);
   jstring stringMessage = method.env->NewStringUTF(message);
   method.env->CallStaticVoidMethod(method.classID, method.methodID, stringMessage);
   method.env->DeleteLocalRef(stringMessage);
}

现在让我们看看我们的游戏类标题(HelloWorldScene.h)在包含我们的JniBridge之后是什么样子的:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "PauseScene.h"
#include "GameOverScene.h"
#include "JniBridge.h"

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();
    virtual bool init();
    void pauseCallback(cocos2d::Ref* pSender);
    CREATE_FUNC(HelloWorld);
private:
   cocos2d::Director *_director;
   cocos2d::Size visibleSize;   
   cocos2d::Sprite* _sprBomb;
   cocos2d::Sprite* _sprPlayer;   
   cocos2d::Vector<cocos2d::Sprite*> _bombs;
   cocos2d::MenuItemImage* _muteItem;
   cocos2d::MenuItemImage* _unmuteItem;
   int _score;   
   int _musicId;
   void initPhysics();
   bool onCollision(cocos2d::PhysicsContact& contact);
   void setPhysicsBody(cocos2d::Sprite* sprite);
   void initTouch();
   void movePlayerByTouch(cocos2d::Touch* touch,  cocos2d::Event* event);
   bool explodeBombs(cocos2d::Touch* touch,  cocos2d::Event* event);
   void movePlayerIfPossible(float newX);
   void movePlayerByAccelerometer(cocos2d::Acceleration*  acceleration, cocos2d::Event* event);
   void initAccelerometer();
   void initBackButtonListener();
   void onKeyPressed(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event* event);
   void updateScore(float dt);
   void addBombs(float dt);   
   void initAudio();
   void initAudioNewEngine();
   void initMuteButton();
};

#endif // __HELLOWORLD_SCENE_H__

现在我们将向您展示HelloWorldScene.cpp方法在本书最后一章的结尾是什么样子的:

#include "HelloWorldScene.h"
USING_NS_CC;
using namespace CocosDenshion;
using namespace cocos2d::experimental;

// User input handling code …
void HelloWorld::initMuteButton()
{
   _muteItem = MenuItemImage::create("mute.png", "mute.png", CC_CALLBACK_1(HelloWorld::muteCallback, this));

   _muteItem->setPosition(Vec2(_visibleSize.width -  _muteItem->getContentSize().width/2 ,
   _visibleSize.height -  _muteItem->getContentSize().height * 2));

我们更改了静音按钮在代码中的位置,使其不被广告覆盖:

    _unmuteItem = MenuItemImage::create("unmute.png", "unmute.png", CC_CALLBACK_1(HelloWorld::muteCallback, this));

   _unmuteItem->setPosition(Vec2(_visibleSize.width -  _unmuteItem->getContentSize().width/2 ,
   _visibleSize.height -  _unmuteItem->getContentSize().height *2));
   _unmuteItem -> setVisible(false);

   auto menu = Menu::create(_muteItem, _unmuteItem , nullptr);
   menu->setPosition(Vec2::ZERO);
   this->addChild(menu, 2);
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
  if ( !Layer::init() )
  {
     return false;
  }
  _score = 0;
  _director = Director::getInstance();
  visibleSize = _director->getVisibleSize();
  auto origin = _director->getVisibleOrigin();
  auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::pauseCallback, this));
  closeItem->setPosition(Vec2(visibleSize.width -  closeItem->getContentSize().width/2 , closeItem->getContentSize().height/2));

  auto menu = Menu::create(closeItem, nullptr);
  menu->setPosition(Vec2::ZERO);
  this->addChild(menu, 1);
  _sprBomb = Sprite::create("bomb.png");   
  _sprBomb->setPosition(visibleSize.width / 2,  visibleSize.height + _sprBomb->getContentSize().height/2);
  this->addChild(_sprBomb,1);
  auto bg = Sprite::create("background.png");
  bg->setAnchorPoint(Vec2());
  bg->setPosition(0,0);
  this->addChild(bg, -1);
  _sprPlayer = Sprite::create("player.png");   
  _sprPlayer->setPosition(visibleSize.width / 2, visibleSize.height *   0.23);
  setPhysicsBody(_sprPlayer);
  this->addChild(_sprPlayer, 0);
  //Animations
  Vector<SpriteFrame*> frames;
  Size playerSize = _sprPlayer->getContentSize();
  frames.pushBack(SpriteFrame::create("player.png",  Rect(0, 0, playerSize.width, playerSize.height)));
  frames.pushBack(SpriteFrame::create("player2.png",  Rect(0, 0, playerSize.width, playerSize.height)));
  auto animation = Animation::createWithSpriteFrames(frames,0.2f);
  auto animate = Animate::create(animation);
  _sprPlayer->runAction(RepeatForever::create(animate));   
  setPhysicsBody(_sprBomb);   
  initPhysics();   
  _sprBomb->getPhysicsBody()->setVelocity(Vect(0,-100));   
  initTouch();
  initAccelerometer();   
  #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
  setKeepScreenOnJni(true);
  #endif
  initBackButtonListener();
  schedule(schedule_selector(HelloWorld::updateScore), 3.0f);
  schedule(schedule_selector(HelloWorld::addBombs), 8.0f);
  initAudioNewEngine();
  initMuteButton();
  _bombs.pushBack(_sprBomb);
 JniBridge::showToast("Hello Java");
  return true;
}

这就是我们的AppActivity.java类经过所有修改后的样子:

package org.cocos2dx.cpp;

import org.cocos2dx.lib.Cocos2dxActivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.FrameLayout;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;

public class AppActivity extends Cocos2dxActivity {
   private static Activity instance;

   private void initAdMob() {
      final String ADMOB_ID = "ca-app-pub-7870675803288590/4907722461";
      final AdView adView;
      final FrameLayout adViewLayout;

      FrameLayout.LayoutParams adParams = new FrameLayout. LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT);
      adParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;      
      AdRequest adRequest = new AdRequest.Builder().
      addTestDevice(AdRequest.DEVICE_ID_EMULATOR).
      addTestDevice("E8B4B73DC4CAD78DFCB44AF69E7B9EC4").build();

      adView = new AdView(this);
      adView.setAdSize(AdSize.SMART_BANNER);
      adView.setAdUnitId(ADMOB_ID);
      adView.setLayoutParams(adParams);
      adView.loadAd(adRequest);
      adViewLayout = new FrameLayout(this);
      adViewLayout.setLayoutParams(adParams);
      adView.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
            adViewLayout.addView(adView);
            }         
      });      
      this.addContentView(adViewLayout, adParams);
   }

   @Override
   protected void onCreate(final Bundle savedInstanceState) {
      instance = this;      
      super.onCreate(savedInstanceState);
      initAdMob();
   }

   public static Activity getInstance() {
      return instance;
   }
}

这就是我们的JniFacade.java类文件在本章末尾的样子:包com.packtpub.jni

import org.cocos2dx.cpp.AppActivity;
import android.app.Activity;
import android.widget.Toast;

public class JniFacade {
   private static Activity activity = AppActivity.getInstance();

   public static void showToast(final String message) {
      activity.runOnUiThread(new Runnable() {         
         @Override
         public void run() {
            Toast.makeText(activity.getBaseContext(), message, Toast.   LENGTH_SHORT).show();   
         }
      }      
   }
}

这是我们在本章中添加JniBridge.cpp文件后位于proj.android\jniAndroid.mk文件的样子:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

$(call import-add-path,$(LOCAL_PATH)/../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../cocos2d/cocos)

LOCAL_MODULE := cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

LOCAL_SRC_FILES := hellocpp/main.cpp \
         ../../Classes/JniBridge.cpp \
         ../../Classes/AppDelegate.cpp \
         ../../Classes/PauseScene.cpp \
         ../../Classes/GameOverScene.cpp \
                   ../../Classes/HelloWorldScene.cpp

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

LOCAL_STATIC_LIBRARIES := cocos2dx_static

include $(BUILD_SHARED_LIBRARY)

$(call import-module,.)

最后,这就是我们的AndroidManifest.xml文件在本书末尾的样子:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.packt.happybunny"
    android:installLocation="auto"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="9" />

    <uses-feature android:glEsVersion="0x00020000" />

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <meta-data
           android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />

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

        <activity
           android:name="org.cocos2dx.cpp.AppActivity"
           android:configChanges="orientation"
           android:label="@string/app_name"
           android:screenOrientation="portrait"
           android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation| screenLayout|uiMode|screenSize|smallestScreenSize" />
    </application>

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:xlargeScreens="true" />

    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

总结

在本章中,我们已经看到了如何使用 JNI 添加我们的 C++ 游戏逻辑代码和安卓核心 Java 层之间的交互,我们还通过直接修改在调用cocos命令期间创建的 Java Activity类代码,在我们的游戏中显示了谷歌 AdMob 横幅。