一:Cocos遊戲與平臺交互過程
1:Cocos遊戲與Android平臺交互過程
簡單來說Android平臺的Cocos所有遊戲交互都是通過Jni與遊戲端交互。其中包括Cocos引擎交互和引擎之外的交互,具體交互流程分別舉例:
Cocos引擎交互流程之遊戲啓動過程分析
再此之前,說一下公司的遊戲的依賴情況:
(1).pragon_common:爲基礎庫,無論是純應用類開發,Cocos遊戲或者是Unity3D遊戲都必須包含此依賴,依賴中的內容主要有:Application,BaseActivity,WelcomeActivity,反饋,DBT統計,友盟統計,在線參數,Bugly,工具類函數集合等內容
(2).C2DXPdragonAndroid/C2DXPdragonAndroid_316:爲Cocos遊戲引擎依賴,所有的Cocos遊戲均需要包含此依賴,並且依賴於pragon_common庫;依賴中的主要內容由:Cocos引擎部分的交互文件和遊戲接口代碼兩部分;其中游戲接口包括:分享、廣告、支付、遊戲震動、遊戲拍照、遊戲保存圖片等接口合集
(3).具體遊戲工程:依賴於C2DXPdragonAndroid/C2DXPdragonAndroid_316庫
Cocos遊戲由OpenGL底層進行渲染,在Android端的上層接口爲:GLSurfaceView,而Cocos自定義了一個Cocos2dxGLSurfaceView和渲染器Cocos2dxRenderer兩個類。在Cocos2dxActivity的onCreate函數中,初始化了Cocos引擎入口,
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CocosPlayClient.init(this, false);
onLoadNativeLibraries();
sContext = this;
GameActHelper.initOrientation();
this.mHandler = new Cocos2dxHandler(this);
Cocos2dxHelper.init(this);
//(1)初始化遊戲引擎,並獲取OpenGL屬性
this.mGLContextAttrs = getGLContextAttrs();
this.init();
if (mVideoHelper == null) {
mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
}
if (mAdsViewHelper == null) {
mAdsViewHelper = new Cocos2dxAdsViewHelper(this, mFrameLayout);
}
if(mWebViewHelper == null){
mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
}
}
//(2)初始化遊戲引擎,並獲取OpenGL屬性
private static native int[] getGLContextAttrs();
在Cocos引擎的cocos2d\cocos\platform\android\javaactivity-android.cpp文件中實現getGLContextAttrs函數:
JNIEXPORT jintArray JNICALL Java_org_cocos2dx_lib_Cocos2dxActivity_getGLContextAttrs(JNIEnv* env, jobject thiz)
{
//(1)初始化Cocos引擎,並創建Application
cocos_android_app_init(env, thiz);
cocos2d::Application::getInstance()->initGLContextAttrs();
GLContextAttrs _glContextAttrs = GLView::getGLContextAttrs();
int tmp[6] = {_glContextAttrs.redBits, _glContextAttrs.greenBits, _glContextAttrs.blueBits,
_glContextAttrs.alphaBits, _glContextAttrs.depthBits, _glContextAttrs.stencilBits};
jintArray glContextAttrsJava = env->NewIntArray(6);
env->SetIntArrayRegion(glContextAttrsJava, 0, 6, tmp);
return glContextAttrsJava;
}
cocos_android_app_init函數實現在:當前工程/jni/hellocpp/main.cpp文件中
void cocos_android_app_init (JNIEnv* env, jobject thiz) {
LOGD("cocos_android_app_init");
AppDelegate *pAppDelegate = new AppDelegate();
}
初始化引擎代碼,並獲取到OpenGL屬性後,進行GLSurfaceView的創建,並在初始化完成之後設置GLSurfaceView渲染器,代碼如下:
private void addmGLSurfaceView(){
//(1)創建GLSurfaceView
this.mGLSurfaceView = onCreateView();
//modify by hqm 固定Layout的位置
FrameLayout.LayoutParams layout_params1 =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
layout_params1.gravity = Gravity.TOP|Gravity.LEFT;
layout_params1.leftMargin=0;
layout_params1.topMargin=0;
// ...add to FrameLayout
mFrameLayout.addView(mGLSurfaceView, layout_params1);
// Switch to supported OpenGL (ARGB888) mode on emulator
if (isAndroidEmulator())
this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
//解決clippingnode不能正常顯示問題
//mGLSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);//pjy
//(2)給創建好的GLSurfaceView設置繪製器
mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
mGLSurfaceView.setCocos2dxEditText(edittext);
}
渲染器在onSurfaceCreated函數中,通過JNI調用了Cocos引擎的初始化函數:Cocos2dxRenderer.nativeInit(int widtn, int height),並且傳入了GLSurfaceView的寬高。
@Override
public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
//(1)在onSurfaceCreated函數中調用引擎的初始化函數。
Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);
this.mLastTickInNanoSeconds = System.nanoTime();
mNativeInitCompleted = true;
}
//(2)引擎初始化函數入口
private static native void nativeInit(final int width, final int height);
在Cocos引擎的cocos2d\cocos\platform\android\javaactivity-android.cpp文件中實現nativeInit函數:
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h)
{
auto director = cocos2d::Director::getInstance();
auto glview = director->getOpenGLView();
if (!glview)
{
//(1)Cocos引擎未創建,新建OpenGLView
glview = cocos2d::GLViewImpl::create("Android app");
glview->setFrameSize(w, h);
director->setOpenGLView(glview);
//cocos_android_app_init(env, thiz);
//(2)開始運行Cocos引擎
cocos2d::Application::getInstance()->run();
}
else
{
....
}
}
run函數會調用Application的applicationDidFinishLaunching函數
int Application::run()
{
// Initialize instance and cocos2d.
if (! applicationDidFinishLaunching())
{
return 0;
}
return -1;
}
在applicationDidFinishLaunching函數中,會設置遊戲引擎的Director、openGLView界面大小,設計界面大小,設置完成之後,運行遊戲場景:
bool AppDelegate::applicationDidFinishLaunching() {
//(1)遊戲進入時各種初始化:設置資源搜索路徑,網絡請求獲取參數,設置默認語言等
.....
//(2)獲取Director和OpenGLView,設置界面大小、設計界面大小、顯示幀率
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
glview = GLViewImpl::create("Enclose Cat");
director->setOpenGLView(glview);
}
#ifdef WIN32
//設置win32模擬器大小
glview->setFrameSize(480.0f,800.0f);
#endif
//設置適配分辨率
//設置適配分辨率
if (glview->getFrameSize().width/9 == glview->getFrameSize().height/16)
{
glview->setDesignResolutionSize(720.0f,1280.0f,ResolutionPolicy::EXACT_FIT);
}
else
{
glview->setDesignResolutionSize(720.0f,1280.0f,ResolutionPolicy::SHOW_ALL);
}
director->setDisplayStats(false);
director->setAnimationInterval(1.0 / 60);
//(3)創建遊戲場景,並且通過director運行場景。
auto scene = HelloWorldScene::create();
director->runWithScene(scene);
return true;
}
以上過程便是Cocos遊戲初始化的整體流程。
引擎之外的交互之信息流大圖交互流程
信息流大圖分爲兩部分:主圖+底圖,由於廣告平臺信息流素材差異,導致,部分平臺沒有主圖,部分平臺底圖元素缺失,導致我們的信息流大圖存在多種情況的組合:
(a)主圖+底框
(b)主圖
(c)底框
(d)主圖+底框(無icon)
遊戲暫停界面,會先在遊戲界面創建整個信息流大圖框(因爲這個框要根據每個遊戲色彩進行調整,所以由遊戲進行渲染)
/**********************************************************************************
* 遊戲創建新的廣告位大圖接口,相對於之前的接口修改:
* 1 廣告位大小有調整,之前是610*340,調整後是562*436
* 2 廣告位大圖裏增加了icon,title,download button,直接封裝在了裏面,外部不需要設置大小,只需要設置顏色即可
* 3 增加部分參數,根據不同界面風格設置
* 參數說明:
* ---------------------------------------- ----------------------------------------
* | ---------------------------------- | | ---------------------------------- |
* | | | | | | | |
* | | | | | | | |
* | | Ads Picture or Default picture | | | | Ads Picture or Default picture | |
* | | | | | | | |
* | | | | | | | |
* | ---------------------------------- | | ---------------------------------- |
* | ------ Game Title ---------- | | ---------------------------------- |
* | | icon | | dwonload | | | | Default Cover Image | |
* | ------ Game Content ---------- | | ---------------------------------- |
* ---------------------------------------- ----------------------------------------
* parent : 父節點
* pDefaultImage : 默認圖片
* pos : 位置
* iZorder :層級
* sBgPicPath : 背景圖片
* titleColor : 與上圖中的 Game Title,Game Content共用
* actionBackgroundColor :dwonload Button的背景顏色
* actionColor :dwonload Button 上面的字體顏色
* sCoverPicPath : 對應右邊圖片的Default Cover Image,顯示廣告時填充在廣告底部
* sAdsTitle : 廣告之後更精彩提示語,廣告沒有大圖時,放在Ads Picture位置
* iShowInterstitial : 界面上是否顯示插屏,展示傳1,不顯示傳0
**********************************************************************************/
extern Node* CreateGameOverLargeAdsImageNew(Node* parent, const char* pDefaultImage, Vec2 pos, int iZorder, std::string sBgPicPath, Color3B titleColor,
Color3B actionBackgroundColor, Color3B actionColor, std::string sCoverPicPath, std::string sAdsTitle, int iShowInterstitial = 0);
在CreateGameOverLargeAdsImageNew回調用自定義的遊戲控件:DBTAdsView。DBTAdsView中繼承了Cocos引擎的Widget控件,這樣就可以把廣告調用當做一個遊戲控件來使用,對遊戲開發來說也更爲熟悉。在DBTAdsView中除了Widget控件函數外,還新增了:設置廣告大圖位置、底框位置、設置底框中,標題文字顏色,按鈕背景顏色,按鈕文字顏色、加載信息流廣告等函數。與之對應的,在Android平臺也自定義了Cocos2dxAdsView和Cocos2dxAdsViewHelper兩個java類,用於給遊戲層調用和回調遊戲層;
遊戲交互流程過程第一,創建DBTAdsView類:
Node* CreateGameOverLargeAdsImageNew(Node* parent, const char* pDefaultImage, Vec2 pos, int iZorder, std::string sBgPicPath, Color3B titleColor,
Color3B actionBackgroundColor, Color3B actionColor, std::string sCoverPicPath, std::string sAdsTitle, int iShowInterstitial)
{
...
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
auto adsView = ns_common::AdsView::create();
adsView->setPosition(pos);
adsView->setContentSize(Size(562.0*fScale, 436.0*fScale));
Vec2 picturePoint = Vec2(pos.x, pos.y + 44.0*fScale);
Vec2 footerPoint = Vec2(pos.x, pos.y -148.0*fScale);
//(1)設置大圖尺寸和位置
adsView->setBigAdsPictureRect(Rect(picturePoint.x, picturePoint.y, 608.0f*0.8*fScale, 342.0f*0.8*fScale));
//(2)設置底框尺寸和位置
adsView->setBigAdsFooterRect(Rect(footerPoint.x, footerPoint.y, 493.0*fScale, 91.0*fScale));
//(3)設置底框中,標題文字顏色,按鈕背景顏色,按鈕文字顏色
adsView->setBigAdsViewColor(titleColor, actionBackgroundColor, actionColor);
//(4)加載信息流大圖廣告,返回信息流大圖的樣式:
int isLoad = adsView->loadAds(2, 99);
if (isLoad != 0) {
parent->addChild(adsView, iZorder);
//(4)-(1)大圖+底框模式和大圖模式,需要在底框加一個遮罩,遮住背景框的元素。
if((2 == isLoad) || (3 == isLoad))
{
auto footerBg = Sprite::create(sCoverPicPath);
footerBg->setPosition(footerPoint);
footerBg->setName("BigAdsCoverImg");
footerBg->setContentSize(Size(493.0*fScale, 91.0*fScale));
parent->addChild(footerBg, iZorder);
}
//(4)-(2)底框模式,需要在大圖位置添加“廣告之後更精彩”
if (3 == isLoad)
{
auto adsTitle = Sprite::create(sAdsTitle);
adsTitle->setPosition(pos.x, pos.y + 44.0*fScale);
adsTitle->setName("AdsTitle");
parent->addChild(adsTitle, iZorder);
}
return adsView;
}
#endif
...
return NULL;
}
其中setBigAdsPictureRect、setBigAdsFooterRect兩個個函數分別通過JNI,調用到Cocos2dxAdsViewHelper.java文件中的setAdsWidgetRectNew函數把對應的座標傳入平臺。
/**
* 設置信息流尺寸,新版本
* @param index
* @param name : root整個信息流大框尺寸和位置,picture:大圖尺寸和位置,footer:底框尺寸和位置
* @param left
* @param top
* @param maxWidth
* @param maxHeight
*/
public static void setAdsWidgetRectNew(final int index, String name, final int left, final int top, final int maxWidth, final int maxHeight) {
Message msg = new Message();
msg.what = AdsTaskSetRect;
msg.arg1 = index;
// 設置廣告位擺放尺寸
FeedAdsGameRect gameRect = new FeedAdsGameRect(name, left, top, maxWidth, maxHeight);
msg.obj = gameRect;
mAdsHandler.sendMessage(msg);
}
setBigAdsViewColor函數通過JNI調用Cocos2dxAdsViewHelper.java文件中的setBigAdsViewColor函數把對應的顏色傳入平臺。
/**
* 設置大圖廣告各個元素View的字體顏色和背景顏色
* @param index
* @param name vc_title:標題文字顏色,vbc_action:按鈕背景顏色 , vc_action:按鈕文字顏色
* @param r
* @param g
* @param b
*/
public static void setBigAdsViewColor(final int index, String name, int r, int g, int b) {
r = (r > 255 || r < 0)?255:r;
g = (g > 255 || g < 0)?255:g;
b = (b > 255 || b < 0)?255:b;
UserApp.LogD(TAG, String.format("設置"+name+"顏色{%d,%d,%d}", r,g,b));
Message msg = new Message();
msg.what = AdsTaskSetViewColor;
msg.arg1 = index;
msg.arg2 = Color.rgb(r, g, b);
msg.obj = name;
mAdsHandler.sendMessage(msg);
}
收集到了信息流大圖、底框尺寸和位置以及底框個元素色值後,會保存到Cocos2dxAdsView對象對應的變量當中。
/**
* 添加廣告各元素尺寸
* @param gameRect
*/
public void addGameRect(FeedAdsGameRect gameRect){
gameRectUtils.addRect(gameRect);
//設置根View大小
if(Cocos2dxAdsViewHelper.VNRoot.equals(gameRect.name)){
setAdsViewRect(gameRect.left, gameRect.top, gameRect.width, gameRect.height);
}
}
/**
* 添加廣告元素的顏色
* @param name
* @param color
*/
public void addBigAdsViewColor(String name, int color){
gameColorUtil.addColor(name, color);
}
之後便是,調用Cocos2dxAdsViewHelper.java文件中的load函數加載信息流大圖廣告了。
/**
* 加載信息流廣告,只判斷是否能展示
* @param index
* @param scene
* @param id
* @return
* 廣告渲染方式:0:默認有問題,或者不應該展示的廣告,1:只有主圖,2:主圖+icon/標題 3:icon/標題(沒有主圖)
*/
public static int loadAdsNew(final int index, final int scene, final int id) {
final int canLoad = FeedAdsGameHelper.canLoadAdsDataStatic(scene, id);
if(canLoad == 0){
removeAdsWidget(index);
}
return canLoad;
}
在整個過程開始之前,調用了AdsManager.java中的requestFeedAds函數請求信息流,如果信息流返回失敗,會在遊戲調用load的時候,再次調用requestFeedAds進行重新請求。那麼其實本次load的時候,只是把本地的信息流取出來獲取信息流樣式返回給遊戲。遊戲獲取到load結果之後,會調用Widget控件的show函數,show函數會通過JNI調用Cocos2dxAdsViewHelper.java文件中的show函數展示信息流大圖了
/**
* 展示信息流廣告
* @param index
* @param scene
* @param id
*/
public static void showAds(final int index, final int scene, final int id) {
Message msg = new Message();
msg.what = AdsTaskShowAds;
msg.arg1 = index;
msg.arg2 = scene;
msg.obj = id;
mAdsHandler.sendMessage(msg);
}
調用show的過程,會調用Cocos2dxAdsView對象的loadAds函數進行view的渲染,Android平臺的信息流大圖渲染是通過獲取到的廣告的信息後,根據不同的廣告樣式和廣告平臺機型不同的繪製,具體代碼在FeedAdsGame.java類中的show函數中調用不同的Controller來進行不同的廣告展示。信息流大圖廣告每一個元素的設置,均會在不同的Controller函數中實現。每個Controller中還兼容老版本信息流大圖和新版本信息流大圖的渲染樣式。
/**
* 加載並展示廣告
* @param adsId
* @param parentView
*/
public void showAds(final int adsId, final ViewGroup parentView, FeedAdsGameRectUtils gameRectUtils, FeedAdsGameColorUtil gameColorUtil){
if(info == null){
UserApp.LogE(TAG, "遊戲load返回0時,不能addView");
return;
}
UserApp.LogD(TAG, "即將展示"+info.gameAdsId+"號廣告");
BaseController baseController = null;
if(info.type.equals(FeedAdsType.DATA)){
baseController = new DataController();
}
else if(info.type.equals(FeedAdsType.DATA_VIEW)){
baseController = new DataViewController();
}
else if(info.type.equals(FeedAdsType.DATA_VIEW_ADMOB)){
if(info.company.equalsIgnoreCase("adtiming")){
baseController = new AdtimingController();
}
else if(info.company.equalsIgnoreCase("huawei")){
baseController = new HWController();
}
else{
baseController = new AdmobController();
}
}
baseController.showAds(ctx, parentView, info, gameRectUtils, gameColorUtil);
}
2:Cocos遊戲與IOS平臺交互過程
3:Cocos2dx與Cocos-js發佈App區別與聯繫
(1)區別:開發語言不同c++/js,js通過Js Binding與Cocos2dx進行橋接;Cocos-js底層引擎控件比Cocos2dx引擎少,並且沒有Scene,Button....等元素。
(2)聯繫:cocos-js發佈App時底層還是C++語言實現,js調用廣告,支付,分享,統計等功能,均通過C++調用到平臺層,與Cocos遊戲一致。
二:Unity遊戲與平臺交互過程(以後在完善)
1:Unity遊戲與Android平臺交互過程
2:Unity遊戲與IOS平臺交互過程
三:如何判斷測試反饋的問題是否爲遊戲問題
遊戲一啓動就閃退,未進入GameAct.java等情況必然是非遊戲問題。以上兩點在App啓動時,可以直接判斷,更多的需要通過打印日誌來判斷:
日誌判斷依據:在遊戲調用入口打印日誌,並且日誌中打印參數,有返回值的打印返回值。
不管是閃退,還是接口調用問題,都可以根據當前操作,來判斷遊戲是否調用接口,接口參數是否正確,返回遊戲結果是否正確來定位問題。
四:分享活動回顧
1:Android信息流大圖交互流程是否可以優化,優化點有哪些?請概述優化方案。
2:如果C++遊戲中有一個全局的結構體中,引用了一個未實例化的對象導致App過了開屏界面後,立馬閃退,我們從以上流程分析中哪一段可以大概定位爲遊戲問題還是平臺問題。