iOS原生应用 集成 cocos2d游戏模块 教程

               iOS原生App新增cocos2d js游戏模块 实现步骤介绍

作者:单永杰 联系方式:qq(405399323)北京多宝灵动科技有限公司 url 欢迎大家使用铃声多多,
儿歌多多,壁纸多多等一些列多多软件,并提出意见和建议。

  1. 用语解释:
    a. iOS原生App:基于iOS原生框架创建的工程,使用iOS基本UI组件和基于原生组件的自定义组件;
    b. js游戏模块:js游戏只是一个模块,作为App的一部分存在于应用中,可以从app进入游戏模块,也可以从游戏
    场景退出,回到原生app场景;游戏模块和原生场景 属于同一个进程;

  2. 需求:
    a. 游戏模块和原生场景属于同一个进程,可以从原生场景进入游戏场景,也可以从游戏场景退出,进入原生界面;
    b. 所有游戏所需资源,包括js游戏文件和资源文件,均为从服务端下载;
    c. 原生场景列出 游戏列表,使用这种方式切换,可以不影响原生App其他功能的情况下,畅玩不同的游戏;

  3. 实现步骤:
    第一部分:创建并编译工程;
    a. 下载 cocos2d js V3.2(勿用之前版本,实现此种需求需修改地方太多)
    b. 按照文档安装 cocos2d:执行 setup.py;
    c. 使用cocos命令,新建工程;
    d. 使用cocos命令,编译iOS平台版本:cocos compile -p ios
    第二部分:实现需求;
    a. 找到iOS工程文件,位于:cocos_game/frameworks/runtime-src/proj.ios_mac
    b. 打开工程,进入我们的正题;

     修改文件列表:
     (1) AppDelegate.cpp
         a. 增加方法:PopView,用于 弹出 游戏场景;
         b. 增加jsb定义,用于和Objective C代码交互;
         代码如下:
         bool PopView( JSContext *cx, uint32_t argc, jsval *vp){
             OcJsInteractive::popGameView();
             return 1;
         } 
    
    
         void register_jsb_kenko_all(JSContext* cx, JSObject* obj){
             JS_DefineFunction(cx, obj, "JS_POP_GAME", PopView, 0, 0);
         }
    
         OcJsInteractive交互定义:
         OcJsInteractive.h文件:
    
         class OcJsInteractive {
         public:
             static void popGameView();
         };
    
         OcJsInteractive.mm文件:
    
         #import "OcJsInteractive.h"
         #import "Notification.h"
    
    
         void OcJsInteractive::popGameView(){
              postNotificationName:NOTI_QUIT_GAME
              object:nil];
         }
    
         c. applicationDidFinishLaunching方法修改:
    
         sc->addRegisterCallback(register_jsb_kenko_all);//添加jsb定义,用于退出游戏场景;
    
         sc->start();
         sc->runScript("script/jsb_boot.js");
         sc->setBasePath("");//设置 js文件和资源文件 路径;
    
     (2) 修改ScriptingCore类, 增加setBasePath方法;
             .h文件:
         void setBasePath(const char* str_search_path);
             .cpp文件
         void ScriptingCore::setBasePath(const char* str_search_path){
             cocos2d::FileUtils::getInstance()->addSearchPath(str_search_path);
         }
     (3) 修改 CCDirector.cpp,参见 https://github.com/cocos2d/cocos2d-x/pull/9746/files
        但是 还有不完善的地方:在 restartDirector()方法结尾,增加代码:
        startAnimation();
        resume();
    
     (4) 增加 MainViewController类,作为启动游戏的界面;
         增加 按钮,代码见附件;
    
         修改 RootViewController类,代码见附录;
    
     (5) 使用 ZipArchive第三方SDK,用于解压缩 下载下来的 游戏包;
    
  4. 注意点:

    1. project.json: 定义了 cocos引擎的一些参数,诸如帧率等;清空 jsFiles列表,因为下载下来的js
      文件是不确定的;
    2. 下载的js包中的 main.js需要修改
      require所需要包含的 js文件;切记需要放在 cc.game.onStart函数内部,此时引擎才成功加载起来,
      可以保证所有js游戏文件的解析;否则会报诸如:cc.Layer XXXX not defined;
  5. 优缺点解析:
    (1)优点就是使用cocos2d js 顺利的解决了开题中的需求
    (2)缺点就是:退出游戏时,引擎并没有退出仍然占用内存资源。最理想的就是:能做到需要时成功加载引擎,
    不需要时,可以干净的清理,毕竟移动设备上的内存资源还是很宝贵的。 cocos2d js还需加油。

    最重要的:谢谢panda大神的帮助,欣赏这种热心帮助技术人员解决问题的态度;谢谢顺哥的指点;

  6. 附录:
    MainViewController代码:

    //
    // MainViewController.m
    // BabyNumber
    //
    // Created by 单永杰 on 15-1-16.
    //
    //

#import “MainViewController.h”
#import “AppController.h”
#import “cocos2d.h”
#import “platform/ios/CCEAGLView-ios.h”
#import “ScriptingCore.h”
#import “RootViewController.h”
#import “ZipArchive.h”

static RootViewController* _gameView = nil;

@interface MainViewController (){

}

@end

@implementation MainViewController

  • (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
    self = ;
    if (self) {
    // Custom initialization
    }
    return self;
    }

  • (void)viewDidLoad
    {
    ;
    // Do any additional setup after loading the view.

    UIButton* btn_game = ;
    btn_game.frame = CGRectMake(50, 50, 100, 50);
    ];
    ;
    [btn_game addTarget:self action:@selector(onBtnClick) forControlEvents:
    (UIControlEventTouchUpInside)];
    ;

    _gameView = nil;
    }

  • (void)didReceiveMemoryWarning
    {
    ;
    // Dispose of any resources that can be recreated.
    }

  • (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
    interfaceOrientation {
    return UIInterfaceOrientationIsLandscape( interfaceOrientation );
    }

// For ios6, use supportedInterfaceOrientations & shouldAutorotate instead

  • (NSUInteger) supportedInterfaceOrientations{
    #ifdef __IPHONE_6_0
    return UIInterfaceOrientationMaskAllButUpsideDown;
    #endif
    }

  • (BOOL) shouldAutorotate {
    return YES;
    }

//fix not hide status on ios7

  • (BOOL)prefersStatusBarHidden
    {
    return YES;
    }

  • (void)viewDidUnload {
    ;
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    }

  • (void)dealloc {
    ;
    _gameView = nil;
    ;
    }

  • (void)onBtnClick{
    /* 解压缩相关代码 /
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSURL
    url_game = ;
    NSError* error = nil;
    NSData* data = ;
    if (!error) {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
    NSUserDomainMask, YES);
    NSString *path = ;
    NSString zipPath = ;
    ;
    if (!error) {
    ZipArchive
    zip_archive = init];
    ;
    if () {
    ;

                  dispatch_sync(dispatch_get_main_queue(), ^{
                      bool first_run = false;
    
                      if(!cocos2d::Director::getInstance()->getOpenGLView()){
                          _gameView =  initWithNibName:nil
                           bundle:nil];
                          _gameView.wantsFullScreenLayout = YES;
                          CCEAGLView *__glView = [CCEAGLView viewWithFrame: 
                          CGRectMake(0, 0, 1136, 640)
                                      pixelFormat: kEAGLColorFormatRGBA8
                                       depthFormat: GL_DEPTH24_STENCIL8_OES //_OES
                                                        preserveBackbuffer: NO
                                                                sharegroup: nil
                                                             multiSampling: NO
                                                           numberOfSamples: 0 ];
    
                          ;
                          [__glView setFrame:CGRectMake(0, 0, 
                          .bounds.size.height, 
                          .bounds.size.width)];
                          _gameView.view = __glView;
                          cocos2d::GLView *glview = cocos2d::GLViewImpl::
                          createWithEAGLView((__bridge void*)__glView);
                          cocos2d::Director::getInstance()->setOpenGLView(glview);
                          first_run = true;
                      }
                      ScriptingCore::getInstance()->setBasePath();
                      [self presentViewController:_gameView animated:YES completion:^{
                          if (first_run) {
                              cocos2d::Application::getInstance()->run();
                          }else {
                              ScriptingCore::getInstance()->setBasePath
                              ();
                              ScriptingCore::getInstance()->reset();
    
                          }
                      }];
                  });
    
              }
          }
      }
    

    });
    ];
    }

@end

RootViewController 代码:

/****************************************************************************
Copyright © 2010-2011 cocos2d-x.org
Copyright © 2010 Ricardo Quesada

http://www.cocos2d-x.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/

#import “RootViewController.h”
#import “cocos2d.h”
#import “platform/ios/CCEAGLView-ios.h”
#import “Notification.h”
#import “ScriptingCore.h”

@implementation RootViewController

//Implement viewDidLoad to do additional setup after loading the view, typically
// from a nib.

  • (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
    self = ;
    if (self) {
    // Custom initialization
    NSLog(@“game view created”);
    addObserver:self selector:@selector
    (quitGame) name:NOTI_QUIT_GAME object:nil];
    }
    return self;
    }

  • (void)viewDidLoad
    {
    ;
    // Do any additional setup after loading the view.
    setStatusBarHidden:YES];

    if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
    if ( respondsToSelector:@selector(setOrientation:)]) {
    SEL selector = NSSelectorFromString(@“setOrientation:”);
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
    ];
    ;
    ];
    int val = UIInterfaceOrientationLandscapeRight;
    ;
    ;
    }
    }

    cocos2d::GLView *glview = cocos2d::Director::getInstance()->getOpenGLView();
    if (glview)
    {
    cocos2d::CCEGLView eaglview = (cocos2d::CCEGLView) glview->getEAGLView();

      if (eaglview)
      {
          CGSize s = .bounds.size;
          cocos2d::Application::getInstance()->applicationScreenSizeChanged((int)
           s.width, (int) s.height);
      }
    

    }
    }
    #pragma mark
    #pragma mark ----- hide status bar & view scape left delegate

  • (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
    toInterfaceOrientation{
    return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);;
    }

  • (BOOL) shouldAutorotate{
    return NO;
    }
    //

  • (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationLandscapeRight;
    }
    //

  • (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscape;
    }

  • (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)
    fromInterfaceOrientation {
    ;

    cocos2d::GLView *glview = cocos2d::Director::getInstance()->getOpenGLView();
    if (glview)
    {
    cocos2d::CCEGLView eaglview = (cocos2d::CCEGLView) glview->getEAGLView();

      if (eaglview)
      {
          CGSize s = CGSizeMake(eaglview->getFrameSize().width, 
          eaglview->getFrameSize().height);
          cocos2d::Application::getInstance()->applicationScreenSizeChanged
          ((int) s.width, (int) s.height);
      }
    

    }
    }

//fix not hide status on ios7

  • (BOOL)prefersStatusBarHidden
    {
    return YES;
    }

  • (void)quitGame{
    setStatusBarHidden:NO];
    // removeObserver:self];

    [self dismissViewControllerAnimated:YES completion:^{
    cocos2d::Director::getInstance()->pause();
    cocos2d::Director::getInstance()->stopAnimation();
    NSLog(@“dismiss game view”);
    }];
    }

  • (void)dealloc {
    ;
    }

@end

1赞

支持~~不错啊

这方法怎么看起来如此复杂?

iOS原生直接用webview打开html游戏不行吗

能不能给个示例程序?

可以是可以,但是效率会很低。
你可以自己测试一下。

lz这里给出的方法是JSB,JS绑定到C++核心库。效率问题基本不存在。

另外,这种方法和OC交互起来很方便。相比webview的单一入口,要灵活很多。

那么现在能够在游戏退出时清理掉引擎占用的内存了吗?

您好,请问内存释放的问题解决了吗

不错,试一下

入口是游戏的。

Hi there!

Sorry for writting in english, but I dont know any chinese.

I was trying to use this tutorial, which is great by the way, but I got an issue when I use JS_DefineFunction(cx, obj, “JS_POP_GAME”, PopView, 0, 0);, I am getting an error saying: “No matching function for call to JS_DefineFunctions”

Any advice???

Thanks in advance for all your help.

您好,我有编译时的问题。它告诉我下面的问题:

Undefined symbols for architecture arm64: (未定义的符号体系结构arm64)
“ScriptingCore::setBasePath(char*)”, referenced from:

它的任何建议或帮助?

谢谢。

退出游戏会闪退啊, 怎么解决的??

mark
mark