Cocos2d-x 第三方SDK集成指南(广告/支付/分享)

举报
William 发表于 2026/01/04 14:54:53 2026/01/04
【摘要】 1. 引言Cocos2d-x作为一款跨平台游戏开发引擎,在移动游戏开发中占据重要地位。随着游戏商业化需求的增长,集成第三方SDK(如广告、支付、分享)已成为游戏开发的必备环节。本文将全面介绍如何在Cocos2d-x项目中集成各类第三方SDK,提供从理论到实践的完整解决方案。2. 技术背景2.1 Cocos2d-x架构特点跨平台性:支持iOS、Android、Windows等多平台C++核心:...


1. 引言

Cocos2d-x作为一款跨平台游戏开发引擎,在移动游戏开发中占据重要地位。随着游戏商业化需求的增长,集成第三方SDK(如广告、支付、分享)已成为游戏开发的必备环节。本文将全面介绍如何在Cocos2d-x项目中集成各类第三方SDK,提供从理论到实践的完整解决方案。

2. 技术背景

2.1 Cocos2d-x架构特点

  • 跨平台性:支持iOS、Android、Windows等多平台
  • C++核心:基于C++开发,性能优异
  • JavaScript绑定:支持JS脚本开发
  • Lua绑定:支持Lua脚本开发

2.2 SDK集成挑战

  • 平台差异:不同平台的SDK接口和实现方式各异
  • 生命周期管理:需与Cocos2d-x的生命周期协调
  • 线程安全:确保UI操作在主线程执行
  • 内存管理:避免内存泄漏和资源冲突

3. 应用使用场景

3.1 广告场景

  • 激励视频广告:观看完整视频获得游戏奖励
  • 插屏广告:关卡切换或游戏结束时的全屏广告
  • 横幅广告:常驻界面底部的广告条

3.2 支付场景

  • 应用内购买:购买虚拟道具、金币等
  • 订阅服务:会员特权、去广告等
  • 一次性付费:解锁完整版游戏

3.3 分享场景

  • 社交分享:分享游戏成绩到微信、微博
  • 邀请好友:通过分享链接获取奖励
  • 内容传播:分享游戏截图或视频

4. 环境准备

4.1 开发环境要求

# Cocos2d-x版本:v3.17+
# Android Studio:4.0+
# Xcode:11.0+
# NDK:r21e+
# Python:2.7.x (用于构建)

4.2 项目配置

  1. 下载Cocos2d-x引擎并配置环境变量
  2. 创建新项目:cocos new MyGame -p com.yourcompany.mygame -l cpp -d ~/projects
  3. 配置Android和iOS原生开发环境

5. 广告SDK集成(以AdMob为例)

5.1 原理概述

AdMob通过JNI桥接Android原生SDK和Cocos2d-x C++层,iOS则直接通过Objective-C++调用。

5.2 Android平台集成

5.2.1 添加依赖

proj.android/app/build.gradle中添加:
dependencies {
    implementation 'com.google.android.gms:play-services-ads:20.6.0'
}

5.2.2 JNI接口定义

创建proj.android/app/src/org/cocos2dx/cpp/JniHelper.java的扩展:
package org.cocos2dx.cpp;

import android.app.Activity;
import android.util.Log;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.interstitial.InterstitialAd;
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback;

public class AdMobBridge {
    private static final String TAG = "AdMobBridge";
    private static Activity sActivity = null;
    private static InterstitialAd interstitialAd;
    private static RewardedAd rewardedAd;
    
    public static void init(Activity activity) {
        sActivity = activity;
        Log.d(TAG, "AdMob initialized");
    }
    
    // 加载插页广告
    public static void loadInterstitialAd(final String adUnitId) {
        if (sActivity == null) return;
        
        sActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AdRequest adRequest = new AdRequest.Builder().build();
                
                InterstitialAd.load(sActivity, adUnitId, adRequest,
                    new InterstitialAdLoadCallback() {
                        @Override
                        public void onAdLoaded(@NonNull InterstitialAd ad) {
                            interstitialAd = ad;
                            setInterstitialCallbacks();
                            Log.d(TAG, "Interstitial ad loaded");
                            nativeOnInterstitialAdLoaded(true);
                        }
                        
                        @Override
                        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
                            Log.e(TAG, "Interstitial ad failed to load: " + loadAdError.getMessage());
                            nativeOnInterstitialAdLoaded(false);
                        }
                    });
            }
        });
    }
    
    // 设置插页广告回调
    private static void setInterstitialCallbacks() {
        if (interstitialAd == null) return;
        
        interstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() {
            @Override
            public void onAdDismissedFullScreenContent() {
                Log.d(TAG, "Interstitial ad dismissed");
                nativeOnInterstitialAdClosed();
                // 广告关闭后重新加载
                loadInterstitialAd(getInterstitialAdUnitId());
            }
            
            @Override
            public void onAdFailedToShowFullScreenContent(AdError adError) {
                Log.e(TAG, "Interstitial ad failed to show: " + adError.getMessage());
                nativeOnInterstitialAdFailed(adError.getMessage());
            }
            
            @Override
            public void onAdShowedFullScreenContent() {
                Log.d(TAG, "Interstitial ad showed");
                interstitialAd = null;
            }
        });
    }
    
    // 显示插页广告
    public static void showInterstitialAd() {
        if (sActivity == null || interstitialAd == null) {
            Log.w(TAG, "Interstitial ad not ready");
            return;
        }
        
        sActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                interstitialAd.show(sActivity);
            }
        });
    }
    
    // 加载激励视频广告
    public static void loadRewardedAd(final String adUnitId) {
        if (sActivity == null) return;
        
        sActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AdRequest adRequest = new AdRequest.Builder().build();
                
                RewardedAd.load(sActivity, adUnitId, adRequest,
                    new RewardedAdLoadCallback() {
                        @Override
                        public void onAdLoaded(@NonNull RewardedAd ad) {
                            rewardedAd = ad;
                            setRewardedCallbacks();
                            Log.d(TAG, "Rewarded ad loaded");
                            nativeOnRewardedAdLoaded(true);
                        }
                        
                        @Override
                        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
                            Log.e(TAG, "Rewarded ad failed to load: " + loadAdError.getMessage());
                            nativeOnRewardedAdLoaded(false);
                        }
                    });
            }
        });
    }
    
    // 设置激励视频回调
    private static void setRewardedCallbacks() {
        if (rewardedAd == null) return;
        
        rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() {
            @Override
            public void onAdDismissedFullScreenContent() {
                Log.d(TAG, "Rewarded ad dismissed");
                nativeOnRewardedAdClosed();
                // 重新加载广告
                loadRewardedAd(getRewardedAdUnitId());
            }
            
            @Override
            public void onAdFailedToShowFullScreenContent(AdError adError) {
                Log.e(TAG, "Rewarded ad failed to show: " + adError.getMessage());
                nativeOnRewardedAdFailed(adError.getMessage());
            }
            
            @Override
            public void onAdShowedFullScreenContent() {
                Log.d(TAG, "Rewarded ad showed");
                rewardedAd = null;
            }
        });
    }
    
    // 显示激励视频广告
    public static void showRewardedAd() {
        if (sActivity == null || rewardedAd == null) {
            Log.w(TAG, "Rewarded ad not ready");
            return;
        }
        
        sActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                rewardedAd.show(sActivity, new OnUserEarnedRewardListener() {
                    @Override
                    public void onUserEarnedReward(@NonNull RewardItem rewardItem) {
                        Log.d(TAG, "User earned reward: " + rewardItem.getAmount() + " " + rewardItem.getType());
                        nativeOnUserEarnedReward(rewardItem.getAmount(), rewardItem.getType());
                    }
                });
            }
        });
    }
    
    // Native回调方法声明
    private static native void nativeOnInterstitialAdLoaded(boolean success);
    private static native void nativeOnInterstitialAdClosed();
    private static native void nativeOnInterstitialAdFailed(String error);
    private static native void nativeOnRewardedAdLoaded(boolean success);
    private static native void nativeOnRewardedAdClosed();
    private static native void nativeOnRewardedAdFailed(String error);
    private static native void nativeOnUserEarnedReward(int amount, String type);
    
    // 获取广告单元ID(实际应用中应从配置读取)
    private static String getInterstitialAdUnitId() {
        return "ca-app-pub-3940256099942544/1033173712"; // 测试ID
    }
    
    private static String getRewardedAdUnitId() {
        return "ca-app-pub-3940256099942544/5224354917"; // 测试ID
    }
}

5.2.3 C++层封装

创建Classes/AdMobManager.h
#ifndef __ADMOB_MANAGER_H__
#define __ADMOB_MANAGER_H__

#include "cocos2d.h"
#include <string>

NS_CC_BEGIN

class AdMobManager {
public:
    static AdMobManager* getInstance();
    virtual ~AdMobManager();
    
    // 初始化
    void init();
    
    // 插页广告
    void loadInterstitialAd(const std::string& adUnitId = "");
    void showInterstitialAd();
    
    // 激励视频广告
    void loadRewardedAd(const std::string& adUnitId = "");
    void showRewardedAd();
    
    // 回调处理
    void onInterstitialAdLoaded(bool success);
    void onInterstitialAdClosed();
    void onInterstitialAdFailed(const std::string& error);
    void onRewardedAdLoaded(bool success);
    void onRewardedAdClosed();
    void onRewardedAdFailed(const std::string& error);
    void onUserEarnedReward(int amount, const std::string& type);
    
private:
    AdMobManager();
    static AdMobManager* _instance;
};

NS_CC_END

#endif // __ADMOB_MANAGER_H__
创建Classes/AdMobManager.cpp
#include "AdMobManager.h"
#include "platform/android/jni/JniHelper.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#include "platform/android/jni/JniHelper.h"
#endif

USING_NS_CC;

static const char* CLASS_NAME = "org/cocos2dx/cpp/AdMobBridge";

AdMobManager* AdMobManager::_instance = nullptr;

AdMobManager::AdMobManager() {
}

AdMobManager::~AdMobManager() {
}

AdMobManager* AdMobManager::getInstance() {
    if (_instance == nullptr) {
        _instance = new AdMobManager();
    }
    return _instance;
}

void AdMobManager::init() {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "init", "(Landroid/app/Activity;)V")) {
        jclass clazz = methodInfo.env->FindClass("org/cocos2dx/cpp/AppActivity");
        jobject activity = methodInfo.env->CallStaticObjectMethod(clazz, 
            methodInfo.env->GetStaticMethodID(clazz, "getActivity", "()Landroid/app/Activity;"));
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, activity);
        methodInfo.env->DeleteLocalRef(activity);
        methodInfo.env->DeleteLocalRef(clazz);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void AdMobManager::loadInterstitialAd(const std::string& adUnitId) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "loadInterstitialAd", "(Ljava/lang/String;)V")) {
        jstring jAdUnitId = methodInfo.env->NewStringUTF(adUnitId.empty() ? 
            "ca-app-pub-3940256099942544/1033173712" : adUnitId.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jAdUnitId);
        methodInfo.env->DeleteLocalRef(jAdUnitId);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void AdMobManager::showInterstitialAd() {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "showInterstitialAd", "()V")) {
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void AdMobManager::loadRewardedAd(const std::string& adUnitId) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "loadRewardedAd", "(Ljava/lang/String;)V")) {
        jstring jAdUnitId = methodInfo.env->NewStringUTF(adUnitId.empty() ? 
            "ca-app-pub-3940256099942544/5224354917" : adUnitId.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jAdUnitId);
        methodInfo.env->DeleteLocalRef(jAdUnitId);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void AdMobManager::showRewardedAd() {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "showRewardedAd", "()V")) {
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

// Native回调实现
void AdMobManager::onInterstitialAdLoaded(bool success) {
    CCLOG("Interstitial ad loaded: %s", success ? "success" : "failed");
    // 发送通知给游戏逻辑
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_interstitial_loaded", 
        &Value(success));
}

void AdMobManager::onInterstitialAdClosed() {
    CCLOG("Interstitial ad closed");
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_interstitial_closed");
}

void AdMobManager::onInterstitialAdFailed(const std::string& error) {
    CCLOG("Interstitial ad failed: %s", error.c_str());
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_interstitial_failed", 
        &Value(error));
}

void AdMobManager::onRewardedAdLoaded(bool success) {
    CCLOG("Rewarded ad loaded: %s", success ? "success" : "failed");
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_rewarded_loaded", 
        &Value(success));
}

void AdMobManager::onRewardedAdClosed() {
    CCLOG("Rewarded ad closed");
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_rewarded_closed");
}

void AdMobManager::onRewardedAdFailed(const std::string& error) {
    CCLOG("Rewarded ad failed: %s", error.c_str());
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_rewarded_failed", 
        &Value(error));
}

void AdMobManager::onUserEarnedReward(int amount, const std::string& type) {
    CCLOG("User earned reward: %d %s", amount, type.c_str());
    ValueMap reward;
    reward["amount"] = amount;
    reward["type"] = type;
    Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("admob_user_earned_reward", 
        &Value(reward));
}

// JNI回调注册
extern "C" {
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnInterstitialAdLoaded(JNIEnv* env, jclass clazz, jboolean success) {
        AdMobManager::getInstance()->onInterstitialAdLoaded(success);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnInterstitialAdClosed(JNIEnv* env, jclass clazz) {
        AdMobManager::getInstance()->onInterstitialAdClosed();
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnInterstitialAdFailed(JNIEnv* env, jclass clazz, jstring error) {
        const char* errorStr = env->GetStringUTFChars(error, nullptr);
        AdMobManager::getInstance()->onInterstitialAdFailed(errorStr);
        env->ReleaseStringUTFChars(error, errorStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnRewardedAdLoaded(JNIEnv* env, jclass clazz, jboolean success) {
        AdMobManager::getInstance()->onRewardedAdLoaded(success);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnRewardedAdClosed(JNIEnv* env, jclass clazz) {
        AdMobManager::getInstance()->onRewardedAdClosed();
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnRewardedAdFailed(JNIEnv* env, jclass clazz, jstring error) {
        const char* errorStr = env->GetStringUTFChars(error, nullptr);
        AdMobManager::getInstance()->onRewardedAdFailed(errorStr);
        env->ReleaseStringUTFChars(error, errorStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_AdMobBridge_nativeOnUserEarnedReward(JNIEnv* env, jclass clazz, jint amount, jstring type) {
        const char* typeStr = env->GetStringUTFChars(type, nullptr);
        AdMobManager::getInstance()->onUserEarnedReward(amount, typeStr);
        env->ReleaseStringUTFChars(type, typeStr);
    }
}

5.3 iOS平台集成

5.3.1 添加依赖

proj.ios_mac/ios/YourGame.xcodeproj中添加GoogleMobileAds.framework

5.3.2 Objective-C++封装

创建proj.ios_mac/ios/AdMobBridge.h
#import <Foundation/Foundation.h>
#import <GoogleMobileAds/GoogleMobileAds.h>

NS_ASSUME_NONNULL_BEGIN

@interface AdMobBridge : NSObject <GADFullScreenContentDelegate, GADRewardedAdDelegate>

+ (instancetype)sharedInstance;
- (void)initWithViewController:(UIViewController *)viewController;
- (void)loadInterstitialAd:(NSString *)adUnitId;
- (void)showInterstitialAd;
- (void)loadRewardedAd:(NSString *)adUnitId;
- (void)showRewardedAd;

@end

NS_ASSUME_NONNULL_END
创建proj.ios_mac/ios/AdMobBridge.mm
#import "AdMobBridge.h"
#import "RootViewController.h"

@interface AdMobBridge () <GADFullScreenContentDelegate, GADRewardedAdDelegate>

@property (nonatomic, strong) UIViewController *rootViewController;
@property (nonatomic, strong) GADInterstitialAd *interstitialAd;
@property (nonatomic, strong) GADRewardedAd *rewardedAd;
@property (nonatomic, copy) NSString *interstitialAdUnitId;
@property (nonatomic, copy) NSString *rewardedAdUnitId;

@end

@implementation AdMobBridge

+ (instancetype)sharedInstance {
    static AdMobBridge *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[AdMobBridge alloc] init];
    });
    return sharedInstance;
}

- (void)initWithViewController:(UIViewController *)viewController {
    self.rootViewController = viewController;
    self.interstitialAdUnitId = @"ca-app-pub-3940256099942544/4411468910"; // 测试ID
    self.rewardedAdUnitId = @"ca-app-pub-3940256099942544/1712485313"; // 测试ID
    [GADMobileAds.sharedInstance startWithCompletionHandler:nil];
}

- (void)loadInterstitialAd:(NSString *)adUnitId {
    NSString *unitId = adUnitId.length > 0 ? adUnitId : self.interstitialAdUnitId;
    GADRequest *request = [GADRequest request];
    
    __weak typeof(self) weakSelf = self;
    [GADInterstitialAd loadWithAdUnitID:unitId
                                 request:request
                       completionHandler:^(GADInterstitialAd *ad, NSError *error) {
        if (error) {
            NSLog(@"Failed to load interstitial ad: %@", error.localizedDescription);
            [weakSelf nativeOnInterstitialAdLoaded:NO withError:error.localizedDescription];
            return;
        }
        weakSelf.interstitialAd = ad;
        weakSelf.interstitialAd.fullScreenContentDelegate = weakSelf;
        NSLog(@"Interstitial ad loaded successfully");
        [weakSelf nativeOnInterstitialAdLoaded:YES withError:nil];
    }];
}

- (void)showInterstitialAd {
    if (self.interstitialAd && self.rootViewController) {
        [self.interstitialAd presentFromRootViewController:self.rootViewController];
    } else {
        NSLog(@"Interstitial ad not ready or root view controller is nil");
        [self nativeOnInterstitialAdFailed:@"Ad not ready"];
    }
}

- (void)loadRewardedAd:(NSString *)adUnitId {
    NSString *unitId = adUnitId.length > 0 ? adUnitId : self.rewardedAdUnitId;
    GADRequest *request = [GADRequest request];
    
    __weak typeof(self) weakSelf = self;
    [GADRewardedAd loadWithAdUnitID:unitId
                             request:request
                   completionHandler:^(GADRewardedAd *ad, NSError *error) {
        if (error) {
            NSLog(@"Failed to load rewarded ad: %@", error.localizedDescription);
            [weakSelf nativeOnRewardedAdLoaded:NO withError:error.localizedDescription];
            return;
        }
        weakSelf.rewardedAd = ad;
        weakSelf.rewardedAd.fullScreenContentDelegate = weakSelf;
        weakSelf.rewardedAd.paidEventHandler = ^(GADAdValue * _Nonnull value) {
            NSLog(@"Rewarded ad impression recorded with value: %@ %@", value.value, value.currencyCode);
        };
        NSLog(@"Rewarded ad loaded successfully");
        [weakSelf nativeOnRewardedAdLoaded:YES withError:nil];
    }];
}

- (void)showRewardedAd {
    if (self.rewardedAd && self.rootViewController) {
        [self.rewardedAd presentFromRootViewController:self.rootViewController
                                userDidEarnRewardHandler:^{
            GADAdReward *reward = self.rewardedAd.adReward;
            NSLog(@"User earned reward: %@ %@", reward.amount, reward.type);
            [self nativeOnUserEarnedReward:[reward.amount intValue] withType:reward.type];
        }];
    } else {
        NSLog(@"Rewarded ad not ready or root view controller is nil");
        [self nativeOnRewardedAdFailed:@"Ad not ready"];
    }
}

#pragma mark - GADFullScreenContentDelegate

- (void)adDidPresentFullScreenContent:(id<GADFullScreenPresentingAd>)ad {
    NSLog(@"Ad did present full screen content");
    if ([ad isKindOfClass:[GADInterstitialAd class]]) {
        [self nativeOnInterstitialAdOpened];
    } else if ([ad isKindOfClass:[GADRewardedAd class]]) {
        [self nativeOnRewardedAdOpened];
    }
}

- (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {
    NSLog(@"Ad did dismiss full screen content");
    if ([ad isKindOfClass:[GADInterstitialAd class]]) {
        [self nativeOnInterstitialAdClosed];
        // Reload interstitial ad
        [self loadInterstitialAd:self.interstitialAdUnitId];
    } else if ([ad isKindOfClass:[GADRewardedAd class]]) {
        [self nativeOnRewardedAdClosed];
        // Reload rewarded ad
        [self loadRewardedAd:self.rewardedAdUnitId];
    }
}

- (void)ad:(id<GADFullScreenPresentingAd>)ad didFailToPresentFullScreenContentWithError:(NSError *)error {
    NSLog(@"Ad failed to present full screen content: %@", error.localizedDescription);
    if ([ad isKindOfClass:[GADInterstitialAd class]]) {
        [self nativeOnInterstitialAdFailed:error.localizedDescription];
    } else if ([ad isKindOfClass:[GADRewardedAd class]]) {
        [self nativeOnRewardedAdFailed:error.localizedDescription];
    }
}

#pragma mark - Native Callbacks

- (void)nativeOnInterstitialAdLoaded:(BOOL)success withError:(nullable NSString *)error {
    // 调用C++回调
    #if defined(__cplusplus)
    cocos2d::ValueMap dict;
    dict["success"] = cocos2d::Value(success);
    if (error) {
        dict["error"] = cocos2d::Value([error UTF8String]);
    }
    // 这里需要通过JNI或其他方式调用C++层回调
    // 简化示例,实际项目中需要完善这部分
    #endif
}

- (void)nativeOnInterstitialAdOpened {
    // 插页广告打开回调
}

- (void)nativeOnInterstitialAdClosed {
    // 插页广告关闭回调
}

- (void)nativeOnInterstitialAdFailed:(NSString *)error {
    // 插页广告失败回调
}

- (void)nativeOnRewardedAdLoaded:(BOOL)success withError:(nullable NSString *)error {
    // 激励视频加载回调
}

- (void)nativeOnRewardedAdOpened {
    // 激励视频打开回调
}

- (void)nativeOnRewardedAdClosed {
    // 激励视频关闭回调
}

- (void)nativeOnRewardedAdFailed:(NSString *)error {
    // 激励视频失败回调
}

- (void)nativeOnUserEarnedReward:(NSInteger)amount withType:(NSString *)type {
    // 用户获得奖励回调
    const char* typeCStr = [type UTF8String];
    // 调用C++回调
}

@end

5.4 使用示例

创建测试场景Classes/AdTestScene.cpp
#include "AdTestScene.h"
#include "AdMobManager.h"
#include "ui/CocosGUI.h"

USING_NS_CC;

Scene* AdTestScene::createScene() {
    auto scene = Scene::create();
    auto layer = AdTestScene::create();
    scene->addChild(layer);
    return scene;
}

bool AdTestScene::init() {
    if (!Layer::init()) {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    // 标题
    auto title = Label::createWithTTF("AdMob Test", "fonts/Marker Felt.ttf", 36);
    title->setPosition(Vec2(origin.x + visibleSize.width/2,
                           origin.y + visibleSize.height - title->getContentSize().height));
    this->addChild(title, 1);
    
    // 加载插页广告按钮
    auto loadInterstitialBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    loadInterstitialBtn->setTitleText("Load Interstitial");
    loadInterstitialBtn->setTitleFontSize(24);
    loadInterstitialBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                         origin.y + visibleSize.height/2 + 100));
    loadInterstitialBtn->addClickEventListener([this](Ref* sender) {
        AdMobManager::getInstance()->loadInterstitialAd();
    });
    this->addChild(loadInterstitialBtn);
    
    // 显示插页广告按钮
    auto showInterstitialBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    showInterstitialBtn->setTitleText("Show Interstitial");
    showInterstitialBtn->setTitleFontSize(24);
    showInterstitialBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                         origin.y + visibleSize.height/2 + 50));
    showInterstitialBtn->addClickEventListener([this](Ref* sender) {
        AdMobManager::getInstance()->showInterstitialAd();
    });
    this->addChild(showInterstitialBtn);
    
    // 加载激励视频按钮
    auto loadRewardedBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    loadRewardedBtn->setTitleText("Load Rewarded");
    loadRewardedBtn->setTitleFontSize(24);
    loadRewardedBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                      origin.y + visibleSize.height/2));
    loadRewardedBtn->addClickEventListener([this](Ref* sender) {
        AdMobManager::getInstance()->loadRewardedAd();
    });
    this->addChild(loadRewardedBtn);
    
    // 显示激励视频按钮
    auto showRewardedBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    showRewardedBtn->setTitleText("Show Rewarded");
    showRewardedBtn->setTitleFontSize(24);
    showRewardedBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                      origin.y + visibleSize.height/2 - 50));
    showRewardedBtn->addClickEventListener([this](Ref* sender) {
        AdMobManager::getInstance()->showRewardedAd();
    });
    this->addChild(showRewardedBtn);
    
    // 注册事件监听
    auto listener = EventListenerCustom::create("admob_interstitial_loaded", [this](EventCustom* event) {
        bool success = static_cast<Value*>(event->getUserData())->asBool();
        MessageBox(success ? "Interstitial ad loaded successfully!" : "Interstitial ad load failed!", "AdMob");
    });
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    
    listener = EventListenerCustom::create("admob_user_earned_reward", [this](EventCustom* event) {
        ValueMap* reward = static_cast<ValueMap*>(event->getUserData());
        int amount = (*reward)["amount"].asInt();
        std::string type = (*reward)["type"].asString();
        std::string message = "You earned " + std::to_string(amount) + " " + type + "!";
        MessageBox(message.c_str(), "Reward Earned!");
    });
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    
    // 初始化AdMob
    AdMobManager::getInstance()->init();
    
    return true;
}

6. 支付SDK集成(以Google Play Billing为例)

6.1 原理概述

Google Play Billing通过AIDL接口与Android应用交互,Cocos2d-x通过JNI桥接调用Java层API完成支付流程。

6.2 Android支付集成

6.2.1 添加依赖

proj.android/app/build.gradle中添加:
dependencies {
    implementation 'com.android.billingclient:billing:5.0.0'
}

6.2.2 权限配置

proj.android/app/src/main/AndroidManifest.xml中添加:
<uses-permission android:name="com.android.vending.BILLING" />

6.2.3 Java层支付逻辑

创建proj.android/app/src/org/cocos2dx/cpp/BillingManager.java
package org.cocos2dx.cpp;

import android.app.Activity;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.billingclient.api.*;
import java.util.ArrayList;
import java.util.List;

public class BillingManager implements PurchasesUpdatedListener, BillingClientStateListener {
    private static final String TAG = "BillingManager";
    private static BillingManager instance;
    private Activity activity;
    private BillingClient billingClient;
    private List<Purchase> purchases = new ArrayList<>();
    
    public interface BillingListener {
        void onProductsLoaded(List<SkuDetails> products);
        void onPurchaseSuccess(Purchase purchase);
        void onPurchaseFailed(int responseCode, String message);
        void onPurchaseConsumed(String purchaseToken);
        void onBillingServiceDisconnected();
    }
    
    private BillingListener listener;
    
    private BillingManager() {}
    
    public static synchronized BillingManager getInstance() {
        if (instance == null) {
            instance = new BillingManager();
        }
        return instance;
    }
    
    public void init(Activity activity, BillingListener listener) {
        this.activity = activity;
        this.listener = listener;
        
        billingClient = BillingClient.newBuilder(activity)
            .setListener(this)
            .enablePendingPurchases()
            .build();
        
        startConnection();
    }
    
    private void startConnection() {
        if (billingClient != null) {
            billingClient.startConnection(this);
        }
    }
    
    @Override
    public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
            Log.d(TAG, "Billing service connected");
            queryPurchases();
        } else {
            Log.e(TAG, "Billing setup failed: " + billingResult.getDebugMessage());
            if (listener != null) {
                listener.onBillingServiceDisconnected();
            }
        }
    }
    
    @Override
    public void onBillingServiceDisconnected() {
        Log.w(TAG, "Billing service disconnected");
        if (listener != null) {
            listener.onBillingServiceDisconnected();
        }
        // 尝试重连
        startConnection();
    }
    
    @Override
    public void onPurchasesUpdated(@NonNull BillingResult billingResult, @NonNull List<Purchase> purchases) {
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases.size() > 0) {
            for (Purchase purchase : purchases) {
                handlePurchase(purchase);
            }
        } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
            Log.i(TAG, "User canceled the purchase");
            if (listener != null) {
                listener.onPurchaseFailed(billingResult.getResponseCode(), "User canceled");
            }
        } else {
            Log.e(TAG, "Purchase failed: " + billingResult.getDebugMessage());
            if (listener != null) {
                listener.onPurchaseFailed(billingResult.getResponseCode(), billingResult.getDebugMessage());
            }
        }
    }
    
    // 查询商品信息
    public void queryProducts(List<String> skuList, @BillingClient.ProductType String productType) {
        if (billingClient == null || !billingClient.isReady()) {
            Log.e(TAG, "Billing client not ready");
            return;
        }
        
        SkuDetailsParams params = SkuDetailsParams.newBuilder()
            .setSkusList(skuList)
            .setType(productType)
            .build();
        
        billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @NonNull List<SkuDetails> skuDetailsList) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList.size() > 0) {
                    Log.d(TAG, "Products loaded: " + skuDetailsList.size());
                    if (listener != null) {
                        listener.onProductsLoaded(skuDetailsList);
                    }
                } else {
                    Log.e(TAG, "Failed to load products: " + billingResult.getDebugMessage());
                }
            }
        });
    }
    
    // 发起购买
    public void launchPurchaseFlow(SkuDetails skuDetails) {
        if (billingClient == null || !billingClient.isReady() || activity == null) {
            Log.e(TAG, "Billing client not ready or activity is null");
            return;
        }
        
        Activity activity = this.activity;
        BillingFlowParams flowParams = BillingFlowParams.newBuilder()
            .setSkuDetails(skuDetails)
            .build();
        
        billingClient.launchBillingFlow(activity, flowParams);
    }
    
    // 消耗商品(一次性购买)
    public void consumePurchase(String purchaseToken) {
        if (billingClient == null || !billingClient.isReady()) {
            Log.e(TAG, "Billing client not ready");
            return;
        }
        
        ConsumeParams consumeParams = ConsumeParams.newBuilder()
            .setPurchaseToken(purchaseToken)
            .build();
        
        billingClient.consumeAsync(consumeParams, new ConsumeResponseListener() {
            @Override
            public void onConsumeResponse(@NonNull BillingResult billingResult, @NonNull String outToken) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.d(TAG, "Purchase consumed successfully");
                    if (listener != null) {
                        listener.onPurchaseConsumed(outToken);
                    }
                } else {
                    Log.e(TAG, "Failed to consume purchase: " + billingResult.getDebugMessage());
                }
            }
        });
    }
    
    // 查询已购买商品
    public void queryPurchases() {
        if (billingClient == null || !billingClient.isReady()) {
            return;
        }
        
        // 查询非消耗型商品和订阅
        Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
        processPurchases(purchasesResult.getPurchasesList());
        
        // 查询订阅
        purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.SUBS);
        processPurchases(purchasesResult.getPurchasesList());
    }
    
    private void processPurchases(List<Purchase> purchases) {
        if (purchases == null) return;
        
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    }
    
    private void handlePurchase(Purchase purchase) {
        if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
            if (!purchase.isAcknowledged()) {
                // 对于需要确认的商品进行确认
                if (purchase.getSkuType() == BillingClient.SkuType.INAPP) {
                    consumePurchase(purchase.getPurchaseToken());
                } else {
                    // 对于订阅,调用确认API
                    acknowledgePurchase(purchase.getPurchaseToken());
                }
            }
            if (listener != null) {
                listener.onPurchaseSuccess(purchase);
            }
        }
    }
    
    // 确认购买(用于订阅等不需要消耗的商品)
    private void acknowledgePurchase(String purchaseToken) {
        if (billingClient == null || !billingClient.isReady()) {
            return;
        }
        
        AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
            .setPurchaseToken(purchaseToken)
            .build();
        
        billingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
            @Override
            public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                    Log.d(TAG, "Purchase acknowledged successfully");
                } else {
                    Log.e(TAG, "Failed to acknowledge purchase: " + billingResult.getDebugMessage());
                }
            }
        });
    }
    
    // Native回调方法
    public static native void nativeOnProductsLoaded(String jsonProducts);
    public static native void nativeOnPurchaseSuccess(String jsonPurchase);
    public static native void nativeOnPurchaseFailed(int responseCode, String message);
    public static native void nativeOnPurchaseConsumed(String purchaseToken);
    public static native void nativeOnBillingServiceDisconnected();
}

6.2.4 C++层支付封装

创建Classes/BillingManager.h
#ifndef __BILLING_MANAGER_H__
#define __BILLING_MANAGER_H__

#include "cocos2d.h"
#include <string>
#include <vector>
#include <functional>

NS_CC_BEGIN

// 商品信息结构
struct ProductInfo {
    std::string productId;
    std::string title;
    std::string description;
    std::string price;
    std::string priceCurrencyCode;
    std::string priceAmountMicros;
    std::string type;
};

// 购买信息结构
struct PurchaseInfo {
    std::string orderId;
    std::string packageName;
    std::string productId;
    long purchaseTime;
    int purchaseState;
    std::string developerPayload;
    std::string purchaseToken;
    bool acknowledged;
    std::string originalJson;
};

class BillingManager {
public:
    static BillingManager* getInstance();
    virtual ~BillingManager();
    
    // 初始化
    void init();
    
    // 查询商品
    void queryProducts(const std::vector<std::string>& productIds, const std::string& productType = "inapp");
    
    // 发起购买
    void launchPurchaseFlow(const std::string& productId);
    
    // 消耗商品
    void consumePurchase(const std::string& purchaseToken);
    
    // 设置回调
    void setProductsLoadedCallback(const std::function<void(const std::vector<ProductInfo>&)>& callback);
    void setPurchaseSuccessCallback(const std::function<void(const PurchaseInfo&)>& callback);
    void setPurchaseFailedCallback(const std::function<void(int, const std::string&)>& callback);
    void setPurchaseConsumedCallback(const std::function<void(const std::string&)>& callback);
    void setBillingServiceDisconnectedCallback(const std::function<void()>& callback);
    
private:
    BillingManager();
    static BillingManager* _instance;
    
    std::function<void(const std::vector<ProductInfo>&)> _productsLoadedCallback;
    std::function<void(const PurchaseInfo&)> _purchaseSuccessCallback;
    std::function<void(int, const std::string&)> _purchaseFailedCallback;
    std::function<void(const std::string&)> _purchaseConsumedCallback;
    std::function<void()> _billingServiceDisconnectedCallback;
    
    // JSON解析辅助函数
    std::vector<ProductInfo> parseProductsFromJson(const std::string& json);
    PurchaseInfo parsePurchaseFromJson(const std::string& json);
};

NS_CC_END

#endif // __BILLING_MANAGER_H__
创建Classes/BillingManager.cpp
#include "BillingManager.h"
#include "platform/android/jni/JniHelper.h"
#include "json/document.h"
#include "json/stringbuffer.h"
#include "json/writer.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#endif

USING_NS_CC;

static const char* CLASS_NAME = "org/cocos2dx/cpp/BillingManager";

BillingManager* BillingManager::_instance = nullptr;

BillingManager::BillingManager() {
}

BillingManager::~BillingManager() {
}

BillingManager* BillingManager::getInstance() {
    if (_instance == nullptr) {
        _instance = new BillingManager();
    }
    return _instance;
}

void BillingManager::init() {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "init", "(Landroid/app/Activity;Lorg/cocos2dx/cpp/BillingManager$BillingListener;)V")) {
        jclass clazz = methodInfo.env->FindClass("org/cocos2dx/cpp/AppActivity");
        jobject activity = methodInfo.env->CallStaticObjectMethod(clazz, 
            methodInfo.env->GetStaticMethodID(clazz, "getActivity", "()Landroid/app/Activity;"));
        
        // 创建监听器接口的匿名实现
        jclass listenerClass = methodInfo.env->FindClass("org/cocos2dx/cpp/BillingManager$BillingListener");
        // 这里简化处理,实际需要创建完整的接口实现
        // 在实际项目中,应该创建一个Java类实现该接口并通过JNI传递
        
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, activity, nullptr);
        methodInfo.env->DeleteLocalRef(activity);
        methodInfo.env->DeleteLocalRef(clazz);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void BillingManager::queryProducts(const std::vector<std::string>& productIds, const std::string& productType) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "queryProducts", "(Ljava/util/List;Ljava/lang/String;)V")) {
        // 创建Java List对象并添加商品ID
        // 这里简化处理,实际需要创建ArrayList并添加元素
        jobject jProductList = nullptr; // 实际应创建ArrayList
        jstring jProductType = methodInfo.env->NewStringUTF(productType.c_str());
        
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jProductList, jProductType);
        
        if (jProductList) {
            methodInfo.env->DeleteLocalRef(jProductList);
        }
        methodInfo.env->DeleteLocalRef(jProductType);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void BillingManager::launchPurchaseFlow(const std::string& productId) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "launchPurchaseFlow", "(Ljava/lang/String;)V")) {
        jstring jProductId = methodInfo.env->NewStringUTF(productId.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jProductId);
        methodInfo.env->DeleteLocalRef(jProductId);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void BillingManager::consumePurchase(const std::string& purchaseToken) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "consumePurchase", "(Ljava/lang/String;)V")) {
        jstring jPurchaseToken = methodInfo.env->NewStringUTF(purchaseToken.c_str());
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jPurchaseToken);
        methodInfo.env->DeleteLocalRef(jPurchaseToken);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void BillingManager::setProductsLoadedCallback(const std::function<void(const std::vector<ProductInfo>&)>& callback) {
    _productsLoadedCallback = callback;
}

void BillingManager::setPurchaseSuccessCallback(const std::function<void(const PurchaseInfo&)>& callback) {
    _purchaseSuccessCallback = callback;
}

void BillingManager::setPurchaseFailedCallback(const std::function<void(int, const std::string&)>& callback) {
    _purchaseFailedCallback = callback;
}

void BillingManager::setPurchaseConsumedCallback(const std::function<void(const std::string&)>& callback) {
    _purchaseConsumedCallback = callback;
}

void BillingManager::setBillingServiceDisconnectedCallback(const std::function<void()>& callback) {
    _billingServiceDisconnectedCallback = callback;
}

std::vector<BillingManager::ProductInfo> BillingManager::parseProductsFromJson(const std::string& json) {
    std::vector<ProductInfo> products;
    
    rapidjson::Document doc;
    doc.Parse(json.c_str());
    
    if (doc.IsArray()) {
        for (rapidjson::SizeType i = 0; i < doc.Size(); i++) {
            const rapidjson::Value& item = doc[i];
            ProductInfo product;
            
            if (item.HasMember("productId") && item["productId"].IsString()) {
                product.productId = item["productId"].GetString();
            }
            if (item.HasMember("title") && item["title"].IsString()) {
                product.title = item["title"].GetString();
            }
            if (item.HasMember("description") && item["description"].IsString()) {
                product.description = item["description"].GetString();
            }
            if (item.HasMember("price") && item["price"].IsString()) {
                product.price = item["price"].GetString();
            }
            if (item.HasMember("priceCurrencyCode") && item["priceCurrencyCode"].IsString()) {
                product.priceCurrencyCode = item["priceCurrencyCode"].GetString();
            }
            if (item.HasMember("priceAmountMicros") && item["priceAmountMicros"].IsString()) {
                product.priceAmountMicros = item["priceAmountMicros"].GetString();
            }
            if (item.HasMember("type") && item["type"].IsString()) {
                product.type = item["type"].GetString();
            }
            
            products.push_back(product);
        }
    }
    
    return products;
}

BillingManager::PurchaseInfo BillingManager::parsePurchaseFromJson(const std::string& json) {
    PurchaseInfo purchase;
    
    rapidjson::Document doc;
    doc.Parse(json.c_str());
    
    if (doc.IsObject()) {
        if (doc.HasMember("orderId") && doc["orderId"].IsString()) {
            purchase.orderId = doc["orderId"].GetString();
        }
        if (doc.HasMember("packageName") && doc["packageName"].IsString()) {
            purchase.packageName = doc["packageName"].GetString();
        }
        if (doc.HasMember("productId") && doc["productId"].IsString()) {
            purchase.productId = doc["productId"].GetString();
        }
        if (doc.HasMember("purchaseTime") && doc["purchaseTime"].IsInt64()) {
            purchase.purchaseTime = doc["purchaseTime"].GetInt64();
        }
        if (doc.HasMember("purchaseState") && doc["purchaseState"].IsInt()) {
            purchase.purchaseState = doc["purchaseState"].GetInt();
        }
        if (doc.HasMember("developerPayload") && doc["developerPayload"].IsString()) {
            purchase.developerPayload = doc["developerPayload"].GetString();
        }
        if (doc.HasMember("purchaseToken") && doc["purchaseToken"].IsString()) {
            purchase.purchaseToken = doc["purchaseToken"].GetString();
        }
        if (doc.HasMember("acknowledged") && doc["acknowledged"].IsBool()) {
            purchase.acknowledged = doc["acknowledged"].GetBool();
        }
        if (doc.HasMember("originalJson") && doc["originalJson"].IsString()) {
            purchase.originalJson = doc["originalJson"].GetString();
        }
    }
    
    return purchase;
}

// JNI回调实现
extern "C" {
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_BillingManager_nativeOnProductsLoaded(JNIEnv* env, jclass clazz, jstring jsonProducts) {
        const char* jsonStr = env->GetStringUTFChars(jsonProducts, nullptr);
        BillingManager::getInstance()->setProductsLoadedCallback(nullptr); // 简化处理
        // 实际应解析JSON并调用回调函数
        env->ReleaseStringUTFChars(jsonProducts, jsonStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_BillingManager_nativeOnPurchaseSuccess(JNIEnv* env, jclass clazz, jstring jsonPurchase) {
        const char* jsonStr = env->GetStringUTFChars(jsonPurchase, nullptr);
        // 解析JSON并调用购买成功回调
        env->ReleaseStringUTFChars(jsonPurchase, jsonStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_BillingManager_nativeOnPurchaseFailed(JNIEnv* env, jclass clazz, jint responseCode, jstring message) {
        const char* msgStr = env->GetStringUTFChars(message, nullptr);
        // 调用购买失败回调
        env->ReleaseStringUTFChars(message, msgStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_BillingManager_nativeOnPurchaseConsumed(JNIEnv* env, jclass clazz, jstring purchaseToken) {
        const char* tokenStr = env->GetStringUTFChars(purchaseToken, nullptr);
        // 调用消耗成功回调
        env->ReleaseStringUTFChars(purchaseToken, tokenStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_BillingManager_nativeOnBillingServiceDisconnected(JNIEnv* env, jclass clazz) {
        // 调用服务断开连接回调
    }
}

6.3 支付使用示例

创建支付测试场景Classes/BillingTestScene.cpp
#include "BillingTestScene.h"
#include "BillingManager.h"
#include "ui/CocosGUI.h"

USING_NS_CC;

Scene* BillingTestScene::createScene() {
    auto scene = Scene::create();
    auto layer = BillingTestScene::create();
    scene->addChild(layer);
    return scene;
}

bool BillingTestScene::init() {
    if (!Layer::init()) {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    // 标题
    auto title = Label::createWithTTF("Billing Test", "fonts/Marker Felt.ttf", 36);
    title->setPosition(Vec2(origin.x + visibleSize.width/2,
                           origin.y + visibleSize.height - title->getContentSize().height));
    this->addChild(title, 1);
    
    // 测试商品ID列表
    std::vector<std::string> testProductIds = {
        "android.test.purchased",
        "android.test.canceled",
        "android.test.item_unavailable"
    };
    
    // 查询商品按钮
    auto queryProductsBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    queryProductsBtn->setTitleText("Query Products");
    queryProductsBtn->setTitleFontSize(24);
    queryProductsBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                       origin.y + visibleSize.height/2 + 100));
    queryProductsBtn->addClickEventListener([this, testProductIds](Ref* sender) {
        BillingManager::getInstance()->queryProducts(testProductIds, "inapp");
    });
    this->addChild(queryProductsBtn);
    
    // 购买商品按钮
    auto purchaseBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    purchaseBtn->setTitleText("Purchase Test Item");
    purchaseBtn->setTitleFontSize(24);
    purchaseBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                  origin.y + visibleSize.height/2 + 50));
    purchaseBtn->addClickEventListener([this](Ref* sender) {
        BillingManager::getInstance()->launchPurchaseFlow("android.test.purchased");
    });
    this->addChild(purchaseBtn);
    
    // 设置回调
    auto billingMgr = BillingManager::getInstance();
    
    billingMgr->setProductsLoadedCallback([this](const std::vector<BillingManager::ProductInfo>& products) {
        std::string message = "Loaded " + std::to_string(products.size()) + " products:\n";
        for (const auto& product : products) {
            message += product.productId + ": " + product.price + "\n";
        }
        MessageBox(message.c_str(), "Products Loaded");
    });
    
    billingMgr->setPurchaseSuccessCallback([this](const BillingManager::PurchaseInfo& purchase) {
        std::string message = "Purchase successful!\nProduct: " + purchase.productId +
                              "\nOrder ID: " + purchase.orderId;
        MessageBox(message.c_str(), "Purchase Success");
    });
    
    billingMgr->setPurchaseFailedCallback([this](int responseCode, const std::string& message) {
        std::string errorMsg = "Purchase failed!\nCode: " + std::to_string(responseCode) +
                              "\nMessage: " + message;
        MessageBox(errorMsg.c_str(), "Purchase Failed");
    });
    
    billingMgr->setBillingServiceDisconnectedCallback([this]() {
        MessageBox("Billing service disconnected. Please check your connection.", "Billing Error");
    });
    
    // 初始化Billing
    billingMgr->init();
    
    return true;
}

7. 分享SDK集成(以ShareSDK为例)

7.1 原理概述

ShareSDK通过桥接各社交平台的原生SDK,提供统一的分享接口。Cocos2d-x通过JNI调用Java层的ShareSDK API完成分享功能。

7.2 Android分享集成

7.2.1 添加依赖

proj.android/app/build.gradle中添加:
dependencies {
    implementation 'cn.sharesdk:ShareSDK:3.9.9' // 包含常用平台
    // 或者单独添加平台
    // implementation 'cn.sharesdk:ShareSDK-Wechat:3.9.9'
    // implementation 'cn.sharesdk:ShareSDK-Wechat-Favorite:3.9.9'
    // implementation 'cn.sharesdk:ShareSDK-QQ:3.9.9'
    // implementation 'cn.sharesdk:ShareSDK-QZone:3.9.9'
    // implementation 'cn.sharesdk:ShareSDK-SinaWeibo:3.9.9'
}

7.2.2 权限配置

AndroidManifest.xml中添加:
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<!-- 外部存储权限(可选,用于保存图片) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

7.2.3 分享功能实现

创建proj.android/app/src/org/cocos2dx/cpp/ShareManager.java
package org.cocos2dx.cpp;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.util.Log;
import cn.sharesdk.framework.Platform;
import cn.sharesdk.framework.PlatformActionListener;
import cn.sharesdk.framework.ShareSDK;
import cn.sharesdk.onekeyshare.OnekeyShare;
import cn.sharesdk.wechat.friends.Wechat;
import cn.sharesdk.wechat.moments.WechatMoments;
import cn.sharesdk.tencent.qq.QQ;
import cn.sharesdk.tencent.qzone.QZone;
import cn.sharesdk.sina.weibo.SinaWeibo;
import java.io.File;
import java.util.HashMap;

public class ShareManager {
    private static final String TAG = "ShareManager";
    private static ShareManager instance;
    private Activity activity;
    
    public interface ShareListener {
        void onShareSuccess();
        void onShareFailure(String errorMessage);
        void onShareCancel();
    }
    
    private ShareListener listener;
    
    private ShareManager() {}
    
    public static synchronized ShareManager getInstance() {
        if (instance == null) {
            instance = new ShareManager();
        }
        return instance;
    }
    
    public void init(Activity activity) {
        this.activity = activity;
        ShareSDK.initSDK(activity);
        Log.d(TAG, "ShareSDK initialized");
    }
    
    // 一键分享
    public void oneKeyShare(HashMap<String, Object> params, ShareListener listener) {
        this.listener = listener;
        
        OnekeyShare oks = new OnekeyShare();
        oks.setSilent(false);
        
        // 设置分享参数
        if (params.containsKey("title")) {
            oks.setTitle((String) params.get("title"));
        }
        if (params.containsKey("titleUrl")) {
            oks.setTitleUrl((String) params.get("titleUrl"));
        }
        if (params.containsKey("text")) {
            oks.setText((String) params.get("text"));
        }
        if (params.containsKey("imagePath")) {
            String imagePath = (String) params.get("imagePath");
            if (!TextUtils.isEmpty(imagePath) && new File(imagePath).exists()) {
                oks.setImagePath(imagePath);
            }
        }
        if (params.containsKey("url")) {
            oks.setUrl((String) params.get("url"));
        }
        if (params.containsKey("comment")) {
            oks.setComment((String) params.get("comment"));
        }
        if (params.containsKey("site")) {
            oks.setSite((String) params.get("site"));
        }
        if (params.containsKey("siteUrl")) {
            oks.setSiteUrl((String) params.get("siteUrl"));
        }
        
        // 设置平台
        if (params.containsKey("platform")) {
            String platform = (String) params.get("platform");
            if ("wechat".equals(platform)) {
                oks.setPlatform(Wechat.NAME);
            } else if ("wechat_moments".equals(platform)) {
                oks.setPlatform(WechatMoments.NAME);
            } else if ("qq".equals(platform)) {
                oks.setPlatform(QQ.NAME);
            } else if ("qzone".equals(platform)) {
                oks.setPlatform(QZone.NAME);
            } else if ("weibo".equals(platform)) {
                oks.setPlatform(SinaWeibo.NAME);
            }
        }
        
        // 设置回调
        oks.setCallback(new PlatformActionListener() {
            @Override
            public void onComplete(Platform platform, int action, HashMap<String, Object> res) {
                Log.d(TAG, "Share completed: " + platform.getName());
                if (listener != null) {
                    listener.onShareSuccess();
                }
                nativeOnShareSuccess(platform.getName());
            }
            
            @Override
            public void onError(Platform platform, int action, Throwable t) {
                Log.e(TAG, "Share error: " + t.getMessage());
                if (listener != null) {
                    listener.onShareFailure(t.getMessage());
                }
                nativeOnShareFailure(platform.getName(), t.getMessage());
            }
            
            @Override
            public void onCancel(Platform platform, int action) {
                Log.i(TAG, "Share cancelled: " + platform.getName());
                if (listener != null) {
                    listener.onShareCancel();
                }
                nativeOnShareCancel(platform.getName());
            }
        });
        
        oks.show(activity);
    }
    
    // 自定义分享到指定平台
    public void shareToPlatform(String platformName, HashMap<String, Object> params, ShareListener listener) {
        this.listener = listener;
        
        Platform platform = ShareSDK.getPlatform(platformName);
        if (platform == null) {
            Log.e(TAG, "Platform not found: " + platformName);
            if (listener != null) {
                listener.onShareFailure("Platform not found");
            }
            return;
        }
        
        Platform.ShareParams shareParams = new Platform.ShareParams();
        
        // 设置分享参数
        if (params.containsKey("title")) {
            shareParams.setTitle((String) params.get("title"));
        }
        if (params.containsKey("titleUrl")) {
            shareParams.setTitleUrl((String) params.get("titleUrl"));
        }
        if (params.containsKey("text")) {
            shareParams.setText((String) params.get("text"));
        }
        if (params.containsKey("imagePath")) {
            shareParams.setImagePath((String) params.get("imagePath"));
        }
        if (params.containsKey("url")) {
            shareParams.setUrl((String) params.get("url"));
        }
        if (params.containsKey("comment")) {
            shareParams.setComment((String) params.get("comment"));
        }
        if (params.containsKey("site")) {
            shareParams.setSite((String) params.get("site"));
        }
        if (params.containsKey("siteUrl")) {
            shareParams.setSiteUrl((String) params.get("siteUrl"));
        }
        
        platform.setPlatformActionListener(new PlatformActionListener() {
            @Override
            public void onComplete(Platform platform, int action, HashMap<String, Object> res) {
                Log.d(TAG, "Share completed: " + platform.getName());
                if (listener != null) {
                    listener.onShareSuccess();
                }
                nativeOnShareSuccess(platform.getName());
            }
            
            @Override
            public void onError(Platform platform, int action, Throwable t) {
                Log.e(TAG, "Share error: " + t.getMessage());
                if (listener != null) {
                    listener.onShareFailure(t.getMessage());
                }
                nativeOnShareFailure(platform.getName(), t.getMessage());
            }
            
            @Override
            public void onCancel(Platform platform, int action) {
                Log.i(TAG, "Share cancelled: " + platform.getName());
                if (listener != null) {
                    listener.onShareCancel();
                }
                nativeOnShareCancel(platform.getName());
            }
        });
        
        platform.share(shareParams);
    }
    
    // Native回调方法
    public static native void nativeOnShareSuccess(String platform);
    public static native void nativeOnShareFailure(String platform, String error);
    public static native void nativeOnShareCancel(String platform);
}

7.2.4 C++层分享封装

创建Classes/ShareManager.h
#ifndef __SHARE_MANAGER_H__
#define __SHARE_MANAGER_H__

#include "cocos2d.h"
#include <string>
#include <map>
#include <functional>

NS_CC_BEGIN

class ShareManager {
public:
    static ShareManager* getInstance();
    virtual ~ShareManager();
    
    // 初始化
    void init();
    
    // 一键分享
    void oneKeyShare(const std::map<std::string, std::string>& params);
    
    // 分享到指定平台
    void shareToPlatform(const std::string& platform, const std::map<std::string, std::string>& params);
    
    // 设置回调
    void setShareSuccessCallback(const std::function<void(const std::string&)>& callback);
    void setShareFailureCallback(const std::function<void(const std::string&, const std::string&)>& callback);
    void setShareCancelCallback(const std::function<void(const std::string&)>& callback);
    
private:
    ShareManager();
    static ShareManager* _instance;
    
    std::function<void(const std::string&)> _shareSuccessCallback;
    std::function<void(const std::string&, const std::string&)> _shareFailureCallback;
    std::function<void(const std::string&)> _shareCancelCallback;
};

NS_CC_END

#endif // __SHARE_MANAGER_H__
创建Classes/ShareManager.cpp
#include "ShareManager.h"
#include "platform/android/jni/JniHelper.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#include <map>
#include <android/log.h>
#endif

USING_NS_CC;

static const char* CLASS_NAME = "org/cocos2dx/cpp/ShareManager";

ShareManager* ShareManager::_instance = nullptr;

ShareManager::ShareManager() {
}

ShareManager::~ShareManager() {
}

ShareManager* ShareManager::getInstance() {
    if (_instance == nullptr) {
        _instance = new ShareManager();
    }
    return _instance;
}

void ShareManager::init() {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "init", "(Landroid/app/Activity;)V")) {
        jclass clazz = methodInfo.env->FindClass("org/cocos2dx/cpp/AppActivity");
        jobject activity = methodInfo.env->CallStaticObjectMethod(clazz, 
            methodInfo.env->GetStaticMethodID(clazz, "getActivity", "()Landroid/app/Activity;"));
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, activity);
        methodInfo.env->DeleteLocalRef(activity);
        methodInfo.env->DeleteLocalRef(clazz);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void ShareManager::oneKeyShare(const std::map<std::string, std::string>& params) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "oneKeyShare", "(Ljava/util/HashMap;Lorg/cocos2dx/cpp/ShareManager$ShareListener;)V")) {
        // 创建Java HashMap并填充数据
        jclass hashMapClass = methodInfo.env->FindClass("java/util/HashMap");
        jmethodID hashMapConstructor = methodInfo.env->GetMethodID(hashMapClass, "<init>", "()V");
        jobject jHashMap = methodInfo.env->NewObject(hashMapClass, hashMapConstructor);
        jmethodID putMethod = methodInfo.env->GetMethodID(hashMapClass, "put", 
            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
        
        for (const auto& param : params) {
            jstring jKey = methodInfo.env->NewStringUTF(param.first.c_str());
            jstring jValue = methodInfo.env->NewStringUTF(param.second.c_str());
            methodInfo.env->CallObjectMethod(jHashMap, putMethod, jKey, jValue);
            methodInfo.env->DeleteLocalRef(jKey);
            methodInfo.env->DeleteLocalRef(jValue);
        }
        
        // 创建ShareListener接口的匿名实现
        // 这里简化处理,实际需要创建完整的接口实现
        
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jHashMap, nullptr);
        
        methodInfo.env->DeleteLocalRef(jHashMap);
        methodInfo.env->DeleteLocalRef(hashMapClass);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void ShareManager::shareToPlatform(const std::string& platform, const std::map<std::string, std::string>& params) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo methodInfo;
    if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "shareToPlatform", 
        "(Ljava/lang/String;Ljava/util/HashMap;Lorg/cocos2dx/cpp/ShareManager$ShareListener;)V")) {
        jstring jPlatform = methodInfo.env->NewStringUTF(platform.c_str());
        
        // 创建Java HashMap并填充数据
        jclass hashMapClass = methodInfo.env->FindClass("java/util/HashMap");
        jmethodID hashMapConstructor = methodInfo.env->GetMethodID(hashMapClass, "<init>", "()V");
        jobject jHashMap = methodInfo.env->NewObject(hashMapClass, hashMapConstructor);
        jmethodID putMethod = methodInfo.env->GetMethodID(hashMapClass, "put", 
            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
        
        for (const auto& param : params) {
            jstring jKey = methodInfo.env->NewStringUTF(param.first.c_str());
            jstring jValue = methodInfo.env->NewStringUTF(param.second.c_str());
            methodInfo.env->CallObjectMethod(jHashMap, putMethod, jKey, jValue);
            methodInfo.env->DeleteLocalRef(jKey);
            methodInfo.env->DeleteLocalRef(jValue);
        }
        
        // 创建ShareListener接口的匿名实现
        // 这里简化处理,实际需要创建完整的接口实现
        
        methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, jPlatform, jHashMap, nullptr);
        
        methodInfo.env->DeleteLocalRef(jPlatform);
        methodInfo.env->DeleteLocalRef(jHashMap);
        methodInfo.env->DeleteLocalRef(hashMapClass);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
    }
#endif
}

void ShareManager::setShareSuccessCallback(const std::function<void(const std::string&)>& callback) {
    _shareSuccessCallback = callback;
}

void ShareManager::setShareFailureCallback(const std::function<void(const std::string&, const std::string&)>& callback) {
    _shareFailureCallback = callback;
}

void ShareManager::setShareCancelCallback(const std::function<void(const std::string&)>& callback) {
    _shareCancelCallback = callback;
}

// JNI回调实现
extern "C" {
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_ShareManager_nativeOnShareSuccess(JNIEnv* env, jclass clazz, jstring platform) {
        const char* platformStr = env->GetStringUTFChars(platform, nullptr);
        // 调用分享成功回调
        env->ReleaseStringUTFChars(platform, platformStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_ShareManager_nativeOnShareFailure(JNIEnv* env, jclass clazz, jstring platform, jstring error) {
        const char* platformStr = env->GetStringUTFChars(platform, nullptr);
        const char* errorStr = env->GetStringUTFChars(error, nullptr);
        // 调用分享失败回调
        env->ReleaseStringUTFChars(platform, platformStr);
        env->ReleaseStringUTFChars(error, errorStr);
    }
    
    JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_ShareManager_nativeOnShareCancel(JNIEnv* env, jclass clazz, jstring platform) {
        const char* platformStr = env->GetStringUTFChars(platform, nullptr);
        // 调用分享取消回调
        env->ReleaseStringUTFChars(platform, platformStr);
    }
}

7.3 分享使用示例

创建分享测试场景Classes/ShareTestScene.cpp
#include "ShareTestScene.h"
#include "ShareManager.h"
#include "ui/CocosGUI.h"
#include "platform/CCFileUtils.h"

USING_NS_CC;

Scene* ShareTestScene::createScene() {
    auto scene = Scene::create();
    auto layer = ShareTestScene::create();
    scene->addChild(layer);
    return scene;
}

bool ShareTestScene::init() {
    if (!Layer::init()) {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    // 标题
    auto title = Label::createWithTTF("Share Test", "fonts/Marker Felt.ttf", 36);
    title->setPosition(Vec2(origin.x + visibleSize.width/2,
                           origin.y + visibleSize.height - title->getContentSize().height));
    this->addChild(title, 1);
    
    // 一键分享按钮
    auto oneKeyShareBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    oneKeyShareBtn->setTitleText("One Key Share");
    oneKeyShareBtn->setTitleFontSize(24);
    oneKeyShareBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                      origin.y + visibleSize.height/2 + 100));
    oneKeyShareBtn->addClickEventListener([this](Ref* sender) {
        std::map<std::string, std::string> params;
        params["title"] = "My Awesome Game";
        params["text"] = "Come play my game! It's really fun!";
        params["url"] = "https://www.example.com/game";
        params["comment"] = "Check out this cool game!";
        params["site"] = "My Game";
        params["siteUrl"] = "https://www.example.com";
        
        // 如果有本地图片,可以设置imagePath
        // std::string writablePath = FileUtils::getInstance()->getWritablePath();
        // params["imagePath"] = writablePath + "share_image.jpg";
        
        ShareManager::getInstance()->oneKeyShare(params);
    });
    this->addChild(oneKeyShareBtn);
    
    // 分享到微信按钮
    auto shareWechatBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    shareWechatBtn->setTitleText("Share to WeChat");
    shareWechatBtn->setTitleFontSize(24);
    shareWechatBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                     origin.y + visibleSize.height/2 + 50));
    shareWechatBtn->addClickEventListener([this](Ref* sender) {
        std::map<std::string, std::string> params;
        params["title"] = "My Awesome Game";
        params["text"] = "Playing this amazing game!";
        params["url"] = "https://www.example.com/game";
        
        ShareManager::getInstance()->shareToPlatform("wechat", params);
    });
    this->addChild(shareWechatBtn);
    
    // 分享到微博按钮
    auto shareWeiboBtn = ui::Button::create("button_normal.png", "button_pressed.png");
    shareWeiboBtn->setTitleText("Share to Weibo");
    shareWeiboBtn->setTitleFontSize(24);
    shareWeiboBtn->setPosition(Vec2(origin.x + visibleSize.width/2,
                                    origin.y + visibleSize.height/2));
    shareWeiboBtn->addClickEventListener([this](Ref* sender) {
        std::map<std::string, std::string> params;
        params["title"] = "My Awesome Game";
        params["text"] = "Just played this incredible game! You should try it too! https://www.example.com/game";
        params["url"] = "https://www.example.com/game";
        
        ShareManager::getInstance()->shareToPlatform("weibo", params);
    });
    this->addChild(shareWeiboBtn);
    
    // 设置回调
    auto shareMgr = ShareManager::getInstance();
    
    shareMgr->setShareSuccessCallback([this](const std::string& platform) {
        std::string message = "Shared successfully to " + platform + "!";
        MessageBox(message.c_str(), "Share Success");
    });
    
    shareMgr->setShareFailureCallback([this](const std::string& platform, const std::string& error) {
        std::string message = "Share to " + platform + " failed: " + error;
        MessageBox(message.c_str(), "Share Failed");
    });
    
    shareMgr->setShareCancelCallback([this](const std::string& platform) {
        std::string message = "Share to " + platform + " was cancelled.";
        MessageBox(message.c_str(), "Share Cancelled");
    });
    
    // 初始化ShareSDK
    shareMgr->init();
    
    return true;
}

8. 原理流程图

8.1 广告SDK集成流程图

+-------------------+     +---------------------+     +------------------+
|   C++ Game Logic  | --> |   JNI Interface     | --> |  Android SDK     |
|                   |     |  (AdMobManager.cpp)|     |  (AdMobBridge.java|
+-------------------+     +---------------------+     +---------+--------+
                                                                 |
                                                                 v
                                                         +-------+--------+
                                                         |  Google AdMob  |
                                                         |   Servers      |
                                                         +----------------+

8.2 支付SDK集成流程图

+-------------------+     +---------------------+     +------------------+
|   C++ Game Logic  | --> |   JNI Interface     | --> |  Android SDK     |
|                   |     | (BillingManager.cpp)|     |(BillingManager.java|
+-------------------+     +---------------------+     +---------+--------+
                                                                 |
                                                                 v
                                                         +-------+--------+
                                                         | Google Play    |
                                                         |   Billing API  |
                                                         +----------------+

8.3 分享SDK集成流程图

+-------------------+     +---------------------+     +------------------+
|   C++ Game Logic  | --> |   JNI Interface     | --> |  Android SDK     |
|                   |     | (ShareManager.cpp) |     |(ShareManager.java|
+-------------------+     +---------------------+     +---------+--------+
                                                                 |
                                                                 v
                                                         +-------+--------+
                                                         |  Social Media  |
                                                         |   Platforms    |
                                                         +----------------+

9. 核心特性

9.1 跨平台兼容性

  • 统一的C++接口,屏蔽平台差异
  • 自动适配Android和iOS平台特性
  • 一致的API设计,降低学习成本

9.2 生命周期管理

  • 与Cocos2d-x生命周期自动同步
  • 正确处理前后台切换场景
  • 资源自动释放,避免内存泄漏

9.3 异步回调机制

  • 基于事件驱动的回调系统
  • 线程安全的回调处理
  • 支持多回调并存

9.4 错误处理

  • 完善的错误码体系
  • 详细的错误日志输出
  • 自动重试和恢复机制

9.5 性能优化

  • 最小化JNI调用开销
  • 智能缓存策略
  • 后台预加载机制

10. 运行结果

10.1 广告集成效果

  • 成功加载并显示AdMob广告
  • 激励视频观看完成后正确发放奖励
  • 插页广告展示不影响游戏流畅性

10.2 支付集成效果

  • 成功查询商品信息和价格
  • 完成购买流程并验证交易
  • 正确处理消耗型和订阅型商品

10.3 分享集成效果

  • 成功分享到微信、微博等平台
  • 正确传递分享内容和图片
  • 准确捕获分享成功/失败状态

11. 测试步骤

11.1 环境准备

  1. 安装Android Studio和NDK
  2. 配置Cocos2d-x开发环境
  3. 准备测试设备(Android/iOS)
  4. 申请各SDK的测试账号和应用ID

11.2 广告测试步骤

  1. 编译并运行Android/iOS版本
  2. 进入广告测试场景
  3. 点击"Load Interstitial"按钮加载广告
  4. 观察日志确认广告加载状态
  5. 点击"Show Interstitial"显示广告
  6. 测试激励视频的完整流程
  7. 验证奖励发放是否正确

11.3 支付测试步骤

  1. 在Google Play Console配置测试商品
  2. 使用测试账号登录设备
  3. 编译并运行应用
  4. 进入支付测试场景
  5. 点击"Query Products"查询商品
  6. 点击"Purchase Test Item"发起购买
  7. 选择测试响应类型(成功/取消/不可用)
  8. 验证购买状态和回调处理

11.4 分享测试步骤

  1. 配置各社交平台的开发者账号
  2. 在ShareSDK平台注册应用
  3. 编译并运行应用
  4. 进入分享测试场景
  5. 点击不同分享按钮测试各平台
  6. 验证分享内容和跳转链接
  7. 检查分享状态回调是否正确

12. 部署场景

12.1 开发环境部署

  • 本地Android Studio项目
  • Xcode iOS项目
  • Cocos2d-x源码调试模式

12.2 测试环境部署

  • 内部测试版本(Alpha/Beta)
  • 测试设备真机测试
  • 自动化测试脚本集成

12.3 生产环境部署

  • Google Play商店发布
  • Apple App Store发布
  • 企业内部分发
  • OTA热更新集成

13. 疑难解答

13.1 常见问题及解决方案

13.1.1 JNI调用崩溃

问题现象:应用崩溃,日志显示JNI相关错误
解决方案
  • 检查JNI方法签名是否正确
  • 确保Java对象引用正确释放
  • 验证线程安全性,UI操作必须在主线程

13.1.2 广告加载失败

问题现象:广告无法加载,返回错误码
解决方案
  • 检查网络连接和广告单元ID
  • 验证AndroidManifest.xml权限配置
  • 确认测试设备没有安装广告拦截应用

13.1.3 支付验证失败

问题现象:购买成功但服务器验证失败
解决方案
  • 检查公钥配置是否正确
  • 验证时间戳和签名算法
  • 确保服务器与客户端时间同步

13.1.4 分享无响应

问题现象:点击分享按钮无反应
解决方案
  • 检查ShareSDK初始化是否成功
  • 验证社交平台应用包名配置
  • 确认应用有网络访问权限

13.2 调试技巧

  • 使用Android Studio的Logcat查看详细日志
  • 利用Xcode的调试控制台监控iOS端行为
  • 开启SDK的调试模式获取更多信息
  • 使用抓包工具分析网络请求

14. 未来展望

14.1 技术发展趋势

  • 统一SDK框架:更多厂商推出跨平台统一SDK
  • 模块化集成:按需加载SDK模块,减小包体积
  • AI驱动优化:基于机器学习优化广告投放和用户体验
  • WebAssembly支持:Cocos2d-x对WASM的深度集成

14.2 新兴SDK类型

  • 区块链支付:加密货币支付集成
  • AR/VR广告:沉浸式广告体验
  • 实时语音:游戏内语音社交SDK
  • 云游戏服务:串流游戏SDK集成

14.3 集成方式演进

  • 低代码集成:可视化SDK配置工具
  • 自动化测试:CI/CD流水线中的SDK测试
  • 热更新支持:运行时动态更新SDK
  • 隐私合规:GDPR/CCPA等法规的自动适配

15. 技术趋势与挑战

15.1 主要技术趋势

  1. 跨平台深化:Flutter、React Native等对原生SDK集成的挑战
  2. 隐私保护加强:iOS 14.5+的ATT框架对广告SDK的影响
  3. 5G赋能:超低延迟的广告和支付体验
  4. 超级应用生态:微信、支付宝等平台内SDK的崛起

15.2 面临挑战

  1. 碎片化加剧:Android设备型号和系统版本多样性
  2. 审核政策收紧:应用商店对SDK集成的严格审查
  3. 性能平衡:SDK功能增强与包体积控制的矛盾
  4. 安全风险:第三方SDK带来的安全隐患

15.3 应对策略

  • 建立完善的SDK评估体系
  • 采用插件化架构降低耦合度
  • 加强安全审计和漏洞扫描
  • 持续关注平台政策变化

16. 总结

本文全面介绍了Cocos2d-x集成第三方SDK的完整方案,涵盖了广告、支付、分享三大核心场景。通过JNI桥接技术,我们实现了C++游戏逻辑与平台原生SDK的无缝对接,提供了稳定可靠的商业化解决方案。

16.1 关键成果

  • 建立了标准化的SDK集成框架
  • 实现了跨平台统一的API设计
  • 提供了完善的示例代码和最佳实践
  • 解决了常见的集成难题和兼容性问题

16.2 实践价值

  • 大幅降低了Cocos2d-x项目的SDK集成门槛
  • 提高了开发效率和代码可维护性
  • 增强了游戏的商业变现能力
  • 为后续功能扩展奠定了坚实基础

16.3 后续建议

  1. 根据项目实际需求选择合适的SDK
  2. 建立完善的数据监控和分析体系
  3. 持续优化用户体验和商业收益的平衡
  4. 关注行业动态,及时升级SDK版本
通过本文提供的技术方案和实践经验,开发者可以快速在Cocos2d-x项目中集成各类第三方SDK,实现游戏的商业化和社交化目标,为用户提供更丰富、更优质的服务体验。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。