14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ApportableでJavaの機能を使う

Last updated at Posted at 2014-03-14

2015.03.24追記
現在はコマンドラインバージョンのApportableはDLできないです。
新しいバージョンの SpriteBuilder Android Pluginについてまとめました。
http://qiita.com/masanorythm/items/215a7dd0a9ee9365079d

Apportableとは

Objective-Cをクロスコンパイルして
ARMコードに変換しちゃう技術。
x86への変換も目指しているようだ。

cocos2d-xなんかはC++で書いたものを
NDK通してネイティブコードに変換して動いている。
そのC++部分がObjective-Cになったと思えば近い。

有償プランがあり
Androidのネイティブ機能を使った機能を手軽に使えるようになるようだ。
主に課金処理とか
iOSのGameCenterを
AndroidのGoogle Play Game Servicesに対応させたりする
手間を大分減らした形で実装できるようだ。

どうやって使うの?

Apportableの導入は簡単。
公式サイトにメンバー登録をしたら
curlコマンドが出てくる。
curlコマンドをそのままターミナルにコピペして実行すれば
~/.apportable にインストールされる

後はXcodeプロジェクトのルートディレクトリ
(ProjectName.xcodeprojがあるディレクトリ)
apportable load と入力すると
APKが生成されて、実機に転送される。

Java機能が使えるか試してみる

Apportable自体はcocos2d-iphone専用というわけではないが
cocos2d-iphone + SpriteBuilder + ApportableSDK
といった形での使い方が無難だと思う。

UIKitも色々対応しているが
プランによって制限があったり、対応していないものがあったりするので
UIKitは極力避けて実装した方が開発はスムーズ。

けど、Javaの機能を全く使わないというのは
無理な話なので、無償プランでもBridgeKitを使えば好き放題できる。

まずは、SpriteBuilderでプロジェクトを作成する。

SpriteBuilderの使い方は他の記事で少し触れたので
そっちを参照して下さい。

参考

javatest01.png

ルートはこんな感じ。

名前はなんでも良いんだけど
java
java_wrappers
ディレクトリを作っておく。

javaの方にはjavaコードを入れていく予定。
java_wrappersにはObjective-CからJavaを呼び出す為の
橋渡し役のコードを置いていく。

まずはJavaTest.xcodeprojを開いて
最初に呼び出されるシーンのMainScene.mをいじってく。

なにもメソッドが無い状態だと思うんで
適当に追加

MainScene.m
- (void)onEnter
{
    [super onEnter];
    
    NSString *platformString = @"iOS";
    
    // ラベル作る
    CCLabelTTF *label = [CCLabelTTF labelWithString:platformString
                                           fontName:@"Helvetica"
                                           fontSize:32];
    // アウトライン設定
    label.outlineColor = [CCColor blackColor];
    label.outlineWidth = 3.0f;
    
    // 位置設定
    label.positionType = CCPositionTypeNormalized;
    label.position = ccp(0.5f, 0.25f);
    
    // ノードに追加
    [self addChild:label];
}

iOSシミュレータで起動してみて
iOSってラベルが表示されればOK。

なんか実用的じゃない事やってもしょうがないんで
Android側ではアイコン広告のアスタを表示させてみる。

jp.maru.mrd-1.2.0.jar
SDKにある、jarファイル。
先程作ったjavaディレクトリにlibsディレクトリつくってつっこみます。

java/libs/jp.maru.mrd-1.2.0.jar

こんな配置。

ソース用のディレクトリも作っておく。
パッケージ名は適当に変えて下さい。

java/src/com/masanorythm/bridge/

まずはObjective-CからJavaを呼び出す為のクラスを作ります。
java_wrappersに作る。これはAndroidの時だけ使う事になるので
xcodeプロジェクトに追加しないでも良いです。

javatest02.png

AstaBridge.h

#import <BridgeKit/JavaObject.h>
#import <BridgeKit/AndroidActivity.h>

@interface AstaBridge : JavaObject

- (id)initWithActivity:(AndroidActivity *)activity
              iconSize:(int)size
             mediaCode:(NSString *)mediaCode;

- (void)createAdIcon;
- (void)showAdIcon;
- (void)hideAdIcon;

@end

AstaBridge.m

#import "AstaBridge.h"

@implementation AstaBridge

+ (void)initializeJava
{
    [super initializeJava];

    [AstaBridge registerConstructorWithSelector:@selector(initWithActivity:iconSize:mediaCode:)
                                      arguments:[AndroidActivity className], [JavaClass intPrimitive], [NSString className], nil];

    [AstaBridge registerInstanceMethod:@"createAdIcon"
                              selector:@selector(createAdIcon)];
    
    [AstaBridge registerInstanceMethod:@"showAdIcon"
                              selector:@selector(showAdIcon)];

    [AstaBridge registerInstanceMethod:@"hideAdIcon"
                              selector:@selector(hideAdIcon)];
}

+ (NSString *)className
{
    return @"com.masanorythm.bridge.Asta";
}

@end

実装ファイル(.m)は多分よくわからん感じですが
ヘッダについてはよくあるObjCコードと大して変わらないと思います。

- (id)initWithActivity:(AndroidActivity *)activity
              iconSize:(int)size
             mediaCode:(NSString *)mediaCode;

アクティビティ、アイコンサイズ、メディアコードを指定して
アスタ表示用のクラスを初期化します。

対応するのは

[AstaBridge registerConstructorWithSelector:@selector(initWithActivity:iconSize:mediaCode:)
                                  arguments:[AndroidActivity className], [JavaClass intPrimitive], [NSString className], nil];

このところです。
initWithActivity:iconSize:mediaCode:を
Java側のコンストラクタとして登録しました。

他の3つはインスタンスメソッドの登録になります。
同じ名前のメソッドをそのまま呼び出しているので簡単になってます。

細かい情報は BridgeKitReference

それじゃ、Javaの方いじりましょう。

java/src/com/masanorythm/bridge/Asta.java
package com.masanorythm.bridge;

import android.app.Activity;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import jp.maru.mrd.IconCell;
import jp.maru.mrd.IconLoader;

public class Asta {
    private static final String TAG = "Asta";
    
    private Activity mActivity;
    private int mIconSize;
    private String mMediaCode;
    private LinearLayout mLayout;
    private IconLoader<Integer> mIconLoader = null;

    public Asta(Activity activity, int size, String mediaCode) {
        mActivity = activity;
        mIconSize = size;
        mMediaCode = mediaCode;
    }

    public void createAdIcon() {

        if (mIconLoader == null) {

            mLayout = new LinearLayout(mActivity);
            mLayout.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
            LayoutParams param = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            mActivity.addContentView(mLayout, param);

            mIconLoader = new IconLoader<Integer>(mMediaCode, mActivity);
            for (int i = 0; i < 4; i++) {
                IconCell icon = new IconCell(mActivity, null);
                icon.setTitleColor(0xffffffff);
                icon.addToIconLoader(mIconLoader);
                LayoutParams iconParam = new LayoutParams(mIconSize, mIconSize);
                mLayout.addView(icon, iconParam);
            }
            mIconLoader.setRefreshInterval(30);
        }
    }

    public void showAdIcon() {
        if (mIconLoader != null) {
            mIconLoader.startLoading();
        }
    }
    
    public void hideAdIcon() {
        if (mIconLoader != null) {
            mIconLoader.stopLoading();
        }
    }
}

はい。。。Java苦手なんでおかしいとこあるかもですが
指定されたサイズのアイコンを4つ作ってるだけです。

それじゃーObjective-Cから呼び出します。

MainScene.m
#import "MainScene.h"

#if defined(ANDROID)
#import "AstaBridge.h"
#endif

@interface MainScene()

#if defined(ANDROID)
@property (nonatomic, readwrite, strong) AstaBridge *astaBridge;
#endif

@end

@implementation MainScene

- (void)onEnter
{
    [super onEnter];
    
    NSString *platformString = @"iOS";
    
#if defined(ANDROID)
    platformString = @"Android";
#endif
    
    // ラベル作る
    CCLabelTTF *label = [CCLabelTTF labelWithString:platformString
                                           fontName:@"Helvetica"
                                           fontSize:32];
    // アウトライン設定
    label.outlineColor = [CCColor blackColor];
    label.outlineWidth = 3.0f;
    
    // 位置設定
    label.positionType = CCPositionTypeNormalized;
    label.position = ccp(0.5f, 0.25f);
    
    // ノードに追加
    [self addChild:label];
    
    // アスタアイコン作成
    [self createAdIcon];

    // トグルボタン作成
    CCSpriteFrame *normalFrame = [CCSpriteFrame frameWithImageNamed:@"ccbResources/ccbButtonNormal.png"];
    CCSpriteFrame *hiliteFrame = [CCSpriteFrame frameWithImageNamed:@"ccbResources/ccbButtonHighlighted.png"];
    CCButton *toggleButton = [CCButton buttonWithTitle:@"Show"];
    toggleButton.togglesSelectedState = YES;
    toggleButton.zoomWhenHighlighted = NO;
    toggleButton.preferredSize = CGSizeMake(80, 30);
    [toggleButton setBackgroundSpriteFrame:normalFrame forState:CCControlStateNormal];
    [toggleButton setBackgroundSpriteFrame:hiliteFrame forState:CCControlStateHighlighted];
    [toggleButton setTarget:self selector:@selector(touchToggleButton:)];
    toggleButton.positionType = CCPositionTypeNormalized;
    toggleButton.position = ccp(0.5f, 0.15f);
    [self addChild:toggleButton];
}

- (void)touchToggleButton:(id)sender
{
    if ([sender isKindOfClass:[CCButton class]]) {
        CCButton *button = (CCButton *)sender;
        if (button.selected) {
#if defined(ANDROID)
            [self.astaBridge showAdIcon];
#endif
            [button.label setString:@"Hide"];
        } else {
#if defined(ANDROID)
            [self.astaBridge hideAdIcon];
#endif
            [button.label setString:@"Show"];
        }
    }
}

- (void)createAdIcon
{
#if defined(ANDROID)
    self.astaBridge = [[AstaBridge alloc] initWithActivity:[AndroidActivity currentActivity]
                                                  iconSize:(int)(75 * [CCDirector sharedDirector].contentScaleFactor)
                                                 mediaCode:@"__TEST__"];
    [self.astaBridge createAdIcon];
#endif
}

@end
#if defined(ANDROID)
#endif

この部分はAndroidのみの実行になります。

or 

#ifdef ANDROID
#ifdef APPORTABLE

#if defined(APPORTABLE)

これでも同じ動きです。
Apportableが他のプラットフォームに対応したりを考えると

#if defined(ANDROID)

#elif defined(WINDOWS)

#endif

こういう風にできるんで、definedがスマートかもしれないです。

アイコンサイズにcontentScaleFactorをかける事で
iOSとAndroidで同じ見え方になります。

このままだとjavaファイルなどがコンパイル時に読み込まれてないので
動きません。
JavaTest.approj/configuration.json をいじります。
中身はシンプルなjsonになってるんで簡単です。

いじるのはadd_paramsの中です。

configuration.json
  "add_params": {
    "pchs": [],

    "header_paths": [
        "java_wrappers"
    ],
    "flags": [],

    "defines": {},

    "deps": [],

    "sources": [
        "java_wrappers/AstaBridge.m"
    ],

    "sources_glob":[],

    "assets": [],

    "infoplists": [],

    "java_sources": [
        "java/src/com/masanorythm/bridge/Asta.java"
    ],

    "java_sourcepaths": [
        "java/src"
    ],

    "java_libs": [
        "java/libs/jp.maru.mrd-1.2.0.jar"
    ],

    "java_res_dirs": [],

    "libs":[],

    "link_flags":[],

    "modules": [],
  },

今回xcodeプロジェクトにjava_wrappersは入れてないので
ヘッダーが見つけられるようにheader_pathsにいれときます
同じ理由でsourcesに実装ファイルも。

上記2つは面倒な場合プロジェクトに入れちゃって
ワーニングやら解除しちゃうのも良いかもしれません。

後はjava系のファイル
java_sources java_sourcepaths java_libs
にそれぞれ追加しただけです。

これでxcodeprojがある場所で
apportable load を実行してみて下さい。
間違いがなければ動くはず。

ss.png

実際使う場合は
もう一段階上にクラスをおいて
iOS、Androidへの処理をわけた方が使いやすいでしょう。

14
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?