[CocosCreator 3.0] 请问如何将 3.0 的游戏画面集成到 Native 视图内局部显示

引擎版本:CocosCreator 3.0 preview

从 2.4 版本升级,但之前入口页都是使用 Native 开发的,通过 CCEAGLView 加载游戏场景。
但升级到 3.0 Preview 版本后,没有 CCEAGLView,这部分需要怎么处理呢

预期

  • 部分场景下,游戏页面局部渲染。如底部是 Native 的 Tab 按钮,或者在模型选择中,Cocos 仅用于模型展示,其他部分使用 Native 展示
  • Cocos 游戏页面作为二级页面

示例

  1. 通过上面那种方式,启动后,可以加载游戏页面。但设置的 Rect 无效,加载后仍是全屏展示

  2. 下面这种方式,预期是将 游戏视图插入到 Native 中,但运行后会出现异常,无法渲染

堆栈信息

* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x0000000188b7e0dc libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x0000000188bf7094 libsystem_pthread.dylib`pthread_kill$VARIANT$mp + 380
    frame #2: 0x0000000188ad7ea8 libsystem_c.dylib`abort + 140
    frame #3: 0x00000001881a4788 libc++abi.dylib`abort_message + 132
    frame #4: 0x00000001881a4934 libc++abi.dylib`default_terminate_handler() + 308
    frame #5: 0x00000001881bbe00 libobjc.A.dylib`_objc_terminate() + 124
    frame #6: 0x00000001881b0838 libc++abi.dylib`std::__terminate(void (*)()) + 16
    frame #7: 0x00000001881b01a8 libc++abi.dylib`__cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 32
    frame #8: 0x00000001881b0168 libc++abi.dylib`__cxa_throw + 124
    frame #9: 0x00000001881bbb3c libobjc.A.dylib`objc_exception_throw + 380
    frame #10: 0x0000000188efdab8 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 140
    frame #11: 0x00000001b57cef60 UIKitCore`-[UIResponder doesNotRecognizeSelector:] + 280
    frame #12: 0x0000000188fe6ac4 CoreFoundation`___forwarding___ + 1408
    frame #13: 0x0000000188fe875c CoreFoundation`_CF_forwarding_prep_0 + 92
  * frame #14: 0x00000001004f1e64 NewProject_2-mobile`cc::gfx::CCMTLDevice::initialize(this=0x0000000119e1c4e0, info=0x00000002831bb720) at MTLDevice.mm:56:53
    frame #15: 0x000000010021bec8 NewProject_2-mobile`js_gfx_Device_initialize(s=0x000000016fcdf9e0) at jsb_gfx_auto.cpp:3390:29
    frame #16: 0x000000010021bbf4 NewProject_2-mobile`js_gfx_Device_initializeRegistry(_v8args=0x000000016fcdfb90) at jsb_gfx_auto.cpp:3398:1
    frame #17: 0x00000001006b901c NewProject_2-mobile`v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) + 568
    frame #18: 0x00000001006b85a0 NewProject_2-mobile`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) + 596
    frame #19: 0x00000001006b7c24 NewProject_2-mobile`v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 276
    frame #20: 0x0000000100ee66ec NewProject_2-mobile`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 108
    frame #21: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #22: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #23: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #24: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #25: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #26: 0x0000000100e739a4 NewProject_2-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #27: 0x0000000100e6c184 NewProject_2-mobile`Builtins_ArgumentsAdaptorTrampoline + 228
    frame #28: 0x0000000100ec5140 NewProject_2-mobile`Builtins_PromiseFulfillReactionJob + 64
    frame #29: 0x0000000100e948fc NewProject_2-mobile`Builtins_RunMicrotasks + 572
    frame #30: 0x0000000100e70dcc NewProject_2-mobile`Builtins_JSRunMicrotasksEntry + 172
  1. 作为二级页面展示,从首页 Push 到游戏页面,报错和 2 这种情况类似
2021-01-19 11:20:27.888861+0800 NewProject_2-mobile[478:52186] -[UILayoutContainerView device]: unrecognized selector sent to instance 0x103e0cd50
2021-01-19 11:20:27.890122+0800 NewProject_2-mobile[478:52186] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILayoutContainerView device]: unrecognized selector sent to instance 0x103e0cd50'
*** First throw call stack:
(0x188fe127c 0x1881bb9f8 0x188efdab8 0x1b57cef60 0x188fe6ac4 0x188fe875c 0x100cbddec 0x1009e7e50 0x1009e7b7c 0x100e84fa4 0x100e84528 0x100e83bac 0x1016b266c 0x10163f924 0x10163f924 0x10163f924 0x10163f924 0x10163f924 0x10163f924 0x101638104 0x1016910c0 0x10166087c 0x10163cd4c 0x106fc8000)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILayoutContainerView device]: unrecognized selector sent to instance 0x103e0cd50'
terminating with uncaught exception of type NSException

排查到相关问题,原因是在启动引擎时,默认使用 root vc 来作为渲染的 View,导致不符合预期。

namespace {
    bool setCanvasCallback(se::Object* global) {
        auto viewLogicalSize = cc::Application::getInstance()->getViewLogicalSize();
        
        CGRect nativeBounds = [[UIScreen mainScreen] nativeBounds];
        int nativeWidth = static_cast<int>(nativeBounds.size.width);
        int nativeHeight = static_cast<int>(nativeBounds.size.height);
        auto rotation = cc::Device::getDeviceRotation();
        bool isLandscape = rotation == cc::Device::Rotation::_90 || rotation == cc::Device::Rotation::_270;
        if (isLandscape) std::swap(nativeWidth, nativeHeight);
        
        char commandBuf[200] = {0};
        // https://stackoverflow.com/questions/5795978/string-format-for-intptr-t-and-uintptr-t/41897226#41897226
        // format intptr_t
        //set window.innerWidth/innerHeight in css pixel units
        sprintf(commandBuf, "window.innerWidth = %d; window.innerHeight = %d; window.nativeWidth = %d; window.nativeHeight = %d; window.windowHandler = 0x%" PRIxPTR ";",
                static_cast<int>(viewLogicalSize.x),
                static_cast<int>(viewLogicalSize.y),
                nativeWidth,
                nativeHeight,
                reinterpret_cast<uintptr_t>(UIApplication.sharedApplication.delegate.window.rootViewController.view));
        
        se::ScriptEngine* se = se::ScriptEngine::getInstance();
        se->evalString(commandBuf);
        return true;
    }
    
#ifndef CC_USE_METAL
    MyTimer* _timer;
#endif
}

新问题,在完善 引擎的 destory 后,重新加载报错

2021-01-19 19:35:00.010596+0800 NewProject_2-mobile[239:4451] Could not load IOSurface for time string. Rendering locally instead.
Initializing V8, version: 8.0.426.16

我在退出引擎时,新增 destory 的方法

void Application::destory() {
#if USE_AUDIO
    AudioEngine::end();
#endif

    EventDispatcher::destroy();
    se::ScriptEngine::destroyInstance();

    Application::_instance = nullptr;
    
#ifndef CC_USE_METAL
    [_timer release];
#endif
}

目前不清楚是不是资源释放的问题,无法在退出引擎后,加载其他的游戏场景

这里评判有误。实际都存在析构防范,在退出游戏时,销毁 Application 然后相关 ScriptEngine 和 v8 都会执行析构函数。

但这里遇到一个问题,再次初始化游戏场景时,以下代码会报错

_isolate = v8::Isolate::New(create_params);

调用堆栈如下

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xdc102a4e1088)
    frame #0: 0x00000001010fc5d4 NewProject_1-mobile`v8::internal::GetRandomMmapAddr() + 24
    frame #1: 0x0000000100de179c NewProject_1-mobile`v8::internal::Heap::SetUp() + 64
    frame #2: 0x0000000100d95100 NewProject_1-mobile`v8::internal::Isolate::Init(v8::internal::ReadOnlyDeserializer*, v8::internal::StartupDeserializer*) + 964
    frame #3: 0x0000000100d95944 NewProject_1-mobile`v8::internal::Isolate::InitWithSnapshot(v8::internal::ReadOnlyDeserializer*, v8::internal::StartupDeserializer*) + 12
    frame #4: 0x00000001010ed75c NewProject_1-mobile`v8::internal::Snapshot::Initialize(v8::internal::Isolate*) + 612
    frame #5: 0x0000000100cbff74 NewProject_1-mobile`v8::Isolate::Initialize(v8::Isolate*, v8::Isolate::CreateParams const&) + 408
    frame #6: 0x0000000100cc007c NewProject_1-mobile`v8::Isolate::New(v8::Isolate::CreateParams const&) + 36
  * frame #7: 0x000000010093eb3c NewProject_1-mobile`se::ScriptEngine::init(this=0x0000000103b3a810) at ScriptEngine.cpp:444:20
    frame #8: 0x00000001009411f0 NewProject_1-mobile`se::ScriptEngine::start(this=0x0000000103b3a810) at ScriptEngine.cpp:627:14
    frame #9: 0x000000010075f4c0 NewProject_1-mobile`Game::init(this=0x0000000282211e60) at Game.cpp:57:9
    frame #10: 0x000000010076c10c NewProject_1-mobile`-[ViewController viewDidLoad](self=0x0000000103b01ea0, _cmd="viewDidLoad") at ViewController.mm:63:15
    frame #11: 0x00000001bc659fc8 UIKitCore`-[UIViewController loadViewIfRequired] + 1012
    frame #12: 0x00000001bc65a3cc UIKitCore`-[UIViewController view] + 28
    frame #13: 0x00000001bc5b74c8 UIKitCore`-[UINavigationController _startCustomTransition:] + 1072
    frame #14: 0x00000001bc5cb4b8 UIKitCore`-[UINavigationController _startDeferredTransitionIfNeeded:] + 708
    frame #15: 0x00000001bc5cc8e0 UIKitCore`-[UINavigationController __viewWillLayoutSubviews] + 164
    frame #16: 0x00000001bc5afaf0 UIKitCore`-[UILayoutContainerView layoutSubviews] + 224
    frame #17: 0x00000001bd09aed0 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1292
    frame #18: 0x00000001948f5a20 QuartzCore`-[CALayer layoutSublayers] + 184
    frame #19: 0x00000001948fa9c8 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 332
    frame #20: 0x000000019485d2d0 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 348
    frame #21: 0x000000019488b330 QuartzCore`CA::Transaction::commit() + 640
    frame #22: 0x000000019488bf20 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92
    frame #23: 0x00000001903d25f8 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
    frame #24: 0x00000001903cd320 CoreFoundation`__CFRunLoopDoObservers + 412
    frame #25: 0x00000001903cd89c CoreFoundation`__CFRunLoopRun + 1228
    frame #26: 0x00000001903cd0b0 CoreFoundation`CFRunLoopRunSpecific + 436
    frame #27: 0x00000001925cd79c GraphicsServices`GSEventRunModal + 104
    frame #28: 0x00000001bcc03978 UIKitCore`UIApplicationMain + 212
    frame #29: 0x000000010076c244 NewProject_1-mobile`main(argc=1, argv=0x000000016f6af758) at main.m:8:18
    frame #30: 0x000000018fe928e0 libdyld.dylib`start + 4

经过调整后,目前 v8 仅初始化一次。主要的原因是v8不支持单进程多实例(多次初始化)

fix crash: the v8 engine will crash on a second initialization
refered by:  https://github.com/cocos-creator/cocos2d-x-lite/issues/2751

参考上述操作

然后出现

问题 2

ScriptEngine._fileOperationDelegate isValid issue

解决方式是在 jsb_global.cpp 中更新

将以下代码移动到 if 块 外部,保证每次可以设置 delegate

se::ScriptEngine::getInstance()->setFileOperationDelegate(delegate);

上部分操作完成后,出现

问题 3

堆栈信息

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x200000047)
  * frame #0: 0x00000001014bcd04 NewProject-mobile`cc::gfx::CCMTLCommandBuffer::bindDescriptorSets(this=0x0000000104606900) at MTLCommandBuffer.mm:444:64
    frame #1: 0x00000001014bc5e0 NewProject-mobile`cc::gfx::CCMTLCommandBuffer::draw(this=0x0000000104606900, ia=0x0000000280ee3d00) at MTLCommandBuffer.mm:234:9
    frame #2: 0x000000010121c4ac NewProject-mobile`js_gfx_CommandBuffer_draw(s=0x000000016ed0c880) at jsb_gfx_auto.cpp:6401:15
    frame #3: 0x000000010121c1e4 NewProject-mobile`js_gfx_CommandBuffer_drawRegistry(_v8args=0x000000016ed0ca30) at jsb_gfx_auto.cpp:6407:1
    frame #4: 0x0000000101690b84 NewProject-mobile`v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) + 568
    frame #5: 0x0000000101690108 NewProject-mobile`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) + 596
    frame #6: 0x000000010168f78c NewProject-mobile`v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 276
    frame #7: 0x0000000101ebdccc NewProject-mobile`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 108
    frame #8: 0x0000000101e4af84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #9: 0x0000000101e4af84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #10: 0x0000000101e4af84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #11: 0x0000000101e484c4 NewProject-mobile`Builtins_JSEntryTrampoline + 164
    frame #12: 0x0000000101e4816c NewProject-mobile`Builtins_JSEntry + 172

上述在 Metal 渲染时,问题比较随机。例如重启 APP 后,遇到以下错误

堆栈信息

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3f80000000000000)
  * frame #0: 0x0000000101580e64 NewProject-mobile`cc::gfx::CCMTLCommandBuffer::bindDescriptorSets(this=0x0000000104648d70) at MTLCommandBuffer.mm:453:76
    frame #1: 0x00000001015805e0 NewProject-mobile`cc::gfx::CCMTLCommandBuffer::draw(this=0x0000000104648d70, ia=0x00000002823eca80) at MTLCommandBuffer.mm:234:9
    frame #2: 0x00000001012e04ac NewProject-mobile`js_gfx_CommandBuffer_draw(s=0x000000016ec48880) at jsb_gfx_auto.cpp:6401:15
    frame #3: 0x00000001012e01e4 NewProject-mobile`js_gfx_CommandBuffer_drawRegistry(_v8args=0x000000016ec48a30) at jsb_gfx_auto.cpp:6407:1
    frame #4: 0x0000000101754b84 NewProject-mobile`v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) + 568
    frame #5: 0x0000000101754108 NewProject-mobile`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) + 596
    frame #6: 0x000000010175378c NewProject-mobile`v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 276
    frame #7: 0x0000000101f81ccc NewProject-mobile`Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 108
    frame #8: 0x0000000101f0ef84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #9: 0x0000000101f0ef84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #10: 0x0000000101f0ef84 NewProject-mobile`Builtins_InterpreterEntryTrampoline + 228
    frame #11: 0x0000000101f0c4c4 NewProject-mobile`Builtins_JSEntryTrampoline + 164
    frame #12: 0x0000000101f0c16c NewProject-mobile`Builtins_JSEntry + 172

请教下楼主,你这边最后怎么做到动态调整游戏View大小的啊?

如果你也是用 3.0.0 的话,可以考虑将那个 View 在创建后先持有,在渲染时使用这个 View 就可以了。具体可以参考 cocos native 源码,需要自己维护并修改一部分实现的

这块具体怎么修改方便沟通下嘛,新手 :joy:

建议放在二级页面就可以,你们需求需要显示在屏幕某一部分区域吗?局部显示不建议支持,问题会比较多。

如果你是安卓端的话,直接使用独立 Activity 就可以。

iOS 端的话需要按照上个回复里那样子,将 View 构建以后,传递给引擎去渲染。3.0.0 最新的版本里,是直接取的 window 根视图,所以需要修改支持二级页面传递 View 来使用。具体建议先学习了解一下

请问楼主,cocos引擎支持销毁吗。 销毁了加载新的资源怎么弄,我这边destory就会崩溃