Unity与iOS交互(2)——接入SDK

这篇具有很好参考价值的文章主要介绍了Unity与iOS交互(2)——接入SDK。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【前言】

接入Android和iOS SDK有很多相同的地方,建议先看下Android SDK如何接入。

 【UnityAppController详解】

 整个程序的入口在MainApp文件下的main.mm文件中,先加载了unityframework,然后调用runUIApplicationMain。源码如下:(这些源码在Xcode工程里都有)

#include <UnityFramework/UnityFramework.h>

UnityFramework* UnityFrameworkLoad()
{
    NSString* bundlePath = nil;
    bundlePath = [[NSBundle mainBundle] bundlePath];
    bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"];//获取完整的UnityFramework的路径

    NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
    if ([bundle isLoaded] == false) [bundle load];//调用bunlde加载接口加载,bundle表示一个包含代码、资源和其他文件的目录或者是一个.framework文件

    //UnityFramework类的头文件是UnityFramework.h,实现文件在Classes文件夹中的main.mm
    UnityFramework* ufw = [bundle.principalClass getInstance];//获取bundle主类示例,主类是一个UnityFramework,bundle 只有一个主类,该类通常是应用程序的控制器类
    if (![ufw appController])  //调用appController方法获取,第一次获取的为空                  
    {
        // unity is not initialized
        [ufw setExecuteHeader: &_mh_execute_header];//设置头信息,初始化unity引擎
    }
    return ufw;
}

int main(int argc, char* argv[])//main是整个应用程序的入口,和什么语言没关系,一般都是这样
{
    @autoreleasepool //创建一个自动释放池
    {
        id ufw = UnityFrameworkLoad();//先加载了UnityFramework
        [ufw runUIApplicationMainWithArgc: argc argv: argv];//runUIApplicationMainWithArgc,这个方式是UnityFramework.h中的方法
        return 0;
    }
}

可以看看UnityFramework.h文件中定义的函数

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

#import "UnityAppController.h"

#include "UndefinePlatforms.h"
#include <mach-o/ldsyms.h>
typedef struct
#ifdef __LP64__
    mach_header_64
#else
    mach_header
#endif
    MachHeader;
#include "RedefinePlatforms.h"


//! Project version number for UnityFramework.
FOUNDATION_EXPORT double UnityFrameworkVersionNumber;

//! Project version string for UnityFramework.
FOUNDATION_EXPORT const unsigned char UnityFrameworkVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <UnityFramework/PublicHeader.h>

#pragma once

// important app life-cycle events
__attribute__ ((visibility("default")))
@protocol UnityFrameworkListener<NSObject>
@optional
- (void)unityDidUnload:(NSNotification*)notification;
- (void)unityDidQuit:(NSNotification*)notification;
@end

__attribute__ ((visibility("default")))
@interface UnityFramework : NSObject
{
}

- (UnityAppController*)appController;

+ (UnityFramework*)getInstance;

- (void)setDataBundleId:(const char*)bundleId;

- (void)runUIApplicationMainWithArgc:(int)argc argv:(char*[])argv;
- (void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts;

- (void)unloadApplication;
- (void)quitApplication:(int)exitCode;

- (void)registerFrameworkListener:(id<UnityFrameworkListener>)obj;
- (void)unregisterFrameworkListener:(id<UnityFrameworkListener>)obj;

- (void)showUnityWindow;
- (void)pause:(bool)pause;

- (void)setExecuteHeader:(const MachHeader*)header;
- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg;

@end

函数实现在Classes文件夹下的main.mm文件中

#include "RegisterFeatures.h"
#include <csignal>
#include "UnityInterface.h"
#include "../UnityFramework/UnityFramework.h"

void UnityInitTrampoline();

// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
const char* AppControllerClassName = "UnityAppController";

#if UNITY_USES_DYNAMIC_PLAYER_LIB
extern "C" void SetAllUnityFunctionsForDynamicPlayerLib();
#endif

extern "C" void UnitySetExecuteMachHeader(const MachHeader* header);

extern "C" __attribute__((visibility("default"))) NSString* const kUnityDidUnload;
extern "C" __attribute__((visibility("default"))) NSString* const kUnityDidQuit;

@implementation UnityFramework
{
    int runCount;
}

UnityFramework* _gUnityFramework = nil;
+ (UnityFramework*)getInstance 
{
    if (_gUnityFramework == nil)
    {
        _gUnityFramework = [[UnityFramework alloc] init];//获取单例时先调用alloc分配内存,再调用init初始化
    }
    return _gUnityFramework;
}

- (UnityAppController*)appController
{
    return GetAppController(); //调用UnityAppController.mm中的方法
}

- (void)setExecuteHeader:(const MachHeader*)header
{
    UnitySetExecuteMachHeader(header);//一个 Unity 引擎的函数,用于设置当前可执行文件的 Mach-O 头信息,header是一个指向可执行文件 Mach-O 头信息的指针
}//在 macOS 或 iOS 上,可执行文件的 Mach-O 头信息包含有关可执行文件的元数据,例如文件类型、CPU 架构、入口点和段信息。Unity 引擎使用该函数来设置当前可执行文件的 Mach-O 头信息,以确保 Unity 引擎可以正确加载并执行游戏逻辑。

- (void)sendMessageToGOWithName:(const char*)goName functionName:(const char*)name message:(const char*)msg
{
    UnitySendMessage(goName, name, msg);
}

- (void)registerFrameworkListener:(id<UnityFrameworkListener>)obj
{
#define REGISTER_SELECTOR(sel, notif_name)                  \
if([obj respondsToSelector:sel])                        \
[[NSNotificationCenter defaultCenter]   addObserver:obj selector:sel name:notif_name object:nil];

    REGISTER_SELECTOR(@selector(unityDidUnload:), kUnityDidUnload);
    REGISTER_SELECTOR(@selector(unityDidQuit:), kUnityDidQuit);

#undef REGISTER_SELECTOR
}

- (void)unregisterFrameworkListener:(id<UnityFrameworkListener>)obj
{
    [[NSNotificationCenter defaultCenter] removeObserver: obj name: kUnityDidUnload object: nil];
    [[NSNotificationCenter defaultCenter] removeObserver: obj name: kUnityDidQuit object: nil];
}

- (void)frameworkWarmup:(int)argc argv:(char*[])argv
{
#if UNITY_USES_DYNAMIC_PLAYER_LIB
    SetAllUnityFunctionsForDynamicPlayerLib();
#endif


    UnityInitTrampoline();
    UnityInitRuntime(argc, argv);

    RegisterFeatures();

    // iOS terminates open sockets when an application enters background mode.
    // The next write to any of such socket causes SIGPIPE signal being raised,
    // even if the request has been done from scripting side. This disables the
    // signal and allows Mono to throw a proper C# exception.
    std::signal(SIGPIPE, SIG_IGN);
}

- (void)setDataBundleId:(const char*)bundleId
{
    UnitySetDataBundleDirWithBundleId(bundleId);
}

- (void)runUIApplicationMainWithArgc:(int)argc argv:(char*[])argv
{
    self->runCount += 1;
    [self frameworkWarmup: argc argv: argv];
    UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String: AppControllerClassName]);
    //UIApplicationMain是iOS应用程序的入口点,一般在入口main函数中调用,unity在frameworkWarmup后调用,其用于启动应用程序并设置应用程序的主运行循环(main run loop)
    //该方法有4个参数,第三个参数是NSString类型的principalClassName,其是应用程序对象所属的类,该类必须继承自UIApplication类,如果所属类字符串的值为nil, UIKit就缺省使用UIApplication类
    //第四个参数是NSString类型的delegateClassName,其是应用程序类的代理类,该函数跟据delegateClassName创建一个delegate对象,并将UIApplication对象中的delegate属性设置为delegate对象,这里创建了UnityAppController,看其头文件是继承了UIApplicationDelegate
    //该函数创建UIApplication对象和AppDelegate对象,然后将控制权交给主运行循环,等待事件的发生。UIApplicationMain还会创建应用程序的主窗口,并将其显示在屏幕上,从而启动应用程序的UI界面。
}

- (void)runEmbeddedWithArgc:(int)argc argv:(char*[])argv appLaunchOpts:(NSDictionary*)appLaunchOpts
{
    if (self->runCount)
    {
        // initialize from partial unload ( sceneLessMode & onPause )
        UnityLoadApplicationFromSceneLessState();
        UnitySuppressPauseMessage();
        [self pause: false];
        [self showUnityWindow];

        // Send Unity start event
        UnitySendEmbeddedLaunchEvent(0);
    }
    else
    {
        // full initialization from ground up
        [self frameworkWarmup: argc argv: argv];

        id app = [UIApplication sharedApplication];

        id appCtrl = [[NSClassFromString([NSString stringWithUTF8String: AppControllerClassName]) alloc] init];
        [appCtrl application: app didFinishLaunchingWithOptions: appLaunchOpts];

        [appCtrl applicationWillEnterForeground: app];
        [appCtrl applicationDidBecomeActive: app];

        // Send Unity start (first time) event
        UnitySendEmbeddedLaunchEvent(1);
    }

    self->runCount += 1;
}

- (void)unloadApplication
{
    UnityUnloadApplication();
}

- (void)quitApplication:(int)exitCode
{
    UnityQuitApplication(exitCode);
}

- (void)showUnityWindow
{
    [[[self appController] window] makeKeyAndVisible];
}

- (void)pause:(bool)pause
{
    UnityPause(pause);
}

@end


#if TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR
#include <pthread.h>

extern "C" int pthread_cond_init$UNIX2003(pthread_cond_t *cond, const pthread_condattr_t *attr)
{ return pthread_cond_init(cond, attr); }
extern "C" int pthread_cond_destroy$UNIX2003(pthread_cond_t *cond)
{ return pthread_cond_destroy(cond); }
extern "C" int pthread_cond_wait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex)
{ return pthread_cond_wait(cond, mutex); }
extern "C" int pthread_cond_timedwait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex,
    const struct timespec *abstime)
{ return pthread_cond_timedwait(cond, mutex, abstime); }

#endif // TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR

调用UIApplicationMain创建了UnityAppController之后,接收iOS系统事件通知即可,接收通知的处理在 UnityAppController.mm文件中。下面是与生命周期相关的事件

先是willFinishLaunchingWithOptions,只发了个通知

- (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    AppController_SendNotificationWithArg(kUnityWillFinishLaunchingWithOptions, launchOptions);
    return YES;
}

void AppController_SendNotificationWithArg(NSString* name, id arg) //Unity引擎用于在iOS平台上发送消息通知的方法
{
    [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg];
}

随后是didFinishLaunchingWithOptions

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    ::printf("-> applicationDidFinishLaunching()\n");

    // send notfications
#if !PLATFORM_TVOS

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"

    if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey])
        UnitySendLocalNotification(notification);

    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

    #pragma clang diagnostic pop

#endif
    //UnityDataBundleDir()是一个用于获取Unity数据包目录的方法,它返回一个NSString对象,表示Unity数据包的路径
    //随后初始化Unity应用程序,但不会创建图形界面
    UnityInitApplicationNoGraphics(UnityDataBundleDir());

    [self selectRenderingAPI];//在iOS平台上,Unity引擎通常支持OpenGL ES和Metal两种渲染API,该方法会根据具体的设备和系统版本等因素来动态地选择使用哪种渲染API
    [UnityRenderingView InitializeForAPI: self.renderingAPI];//UnityRenderingView是用于呈现Unity引擎场景的iOS视图,InitializeForAPI是UnityRenderingView的初始化方法,self.renderingAPI表示Unity引擎所选的渲染API

#if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
    if (@available(iOS 13, tvOS 13, *))
        _window = [[UIWindow alloc] initWithWindowScene: [self pickStartupWindowScene: application.connectedScenes]];
    else
#endif
//UIWindow是iOS中的一个视图对象,用于展示应用程序的用户界面.它是一个特殊的视图,通常作为应用程序中所有视图的容器。在iOS中,每个应用程序都有一个主窗口,即UIWindow对象,它是整个应用程序界面的根视图。所有其他的视图都是添加到UIWindow对象中的。
//UIWindow对象还可以响应用户的触摸事件和手势,同Android一样,Untiy会自己渲染视图,但需要使用操作系统提供的触摸事件
    _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
//创建UnityRenderingView对象,而不是使用iOS系统的视图对象,如UIView或UIWindow,这个视图对象会放入UIWindow中
    _unityView = [self createUnityView];


    [DisplayManager Initialize];//unity的显示器管理类初始化,unity使用DisplayManager来管理显示器并在多个显示器上呈现Unity场景
    _mainDisplay = [DisplayManager Instance].mainDisplay;//设置主显示器
    [_mainDisplay createWithWindow: _window andView: _unityView];//将iOS的窗口UIWindow和Unity的RenderingView同主显示器关联起来

    [self createUI];//用于创建iOS应用程序界面中的常规视图对象,例如按钮、标签、文本框等
    [self preStartUnity];//预启动,在unity启动前可以注册插件

    // if you wont use keyboard you may comment it out at save some memory
    [KeyboardDelegate Initialize];

    return YES;
}

之后是applicationDidBecomeActive

- (void)applicationDidBecomeActive:(UIApplication*)application
{
    ::printf("-> applicationDidBecomeActive()\n");

    [self removeSnapshotViewController];//删除iOS应用程序窗口中的快照视图控制器
    //在iOS应用程序中,当应用程序进入后台时,系统会自动截取当前应用程序的截图,以便在应用程序再次进入前台时快速还原应用程序的状态。\
    //这个截图被称为快照(Snapshot),而用于管理快照的视图控制器被称为快照视图控制器(Snapshot View Controller)
    //unity自己渲染画面,不需要

    if (_unityAppReady)//从后台切换到前台时
    {
        if (UnityIsPaused() && _wasPausedExternal == false)
        {
            UnityWillResume();//会调用OnApplicaionFous
            UnityPause(0);
        }
        if (_wasPausedExternal)
        {
            if (UnityIsFullScreenPlaying())
                TryResumeFullScreenVideo();
        }
        // need to do this with delay because FMOD restarts audio in AVAudioSessionInterruptionNotification handler
        [self performSelector: @selector(updateUnityAudioOutput) withObject: nil afterDelay: 0.1];
        UnitySetPlayerFocus(1);
    }
    else if (!_startUnityScheduled)//刚打开游戏app走这里,调用startUnity
    {
        _startUnityScheduled = true;
        [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
    }

    _didResignActive = false;
}

- (void)startUnity:(UIApplication*)application
{
    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");

    UnityInitApplicationGraphics();//初始化Unity引擎的图形渲染

    // we make sure that first level gets correct display list and orientation
    [[DisplayManager Instance] updateDisplayListCacheInUnity];

    UnityLoadApplication();
    Profiler_InitProfiler();//初始化Profiler

    [self showGameUI];
    [self createDisplayLink];

    UnitySetPlayerFocus(1);

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory: AVAudioSessionCategoryAmbient error: nil];
    if (UnityIsAudioManagerAvailableAndEnabled())
    {
        if (UnityShouldPrepareForIOSRecording())
        {
            [audioSession setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
        }
        else if (UnityShouldMuteOtherAudioSources())
        {
            [audioSession setCategory: AVAudioSessionCategorySoloAmbient error: nil];
        }
    }

    [audioSession setActive: YES error: nil];
    [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
    UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);

#if UNITY_REPLAY_KIT_AVAILABLE
    void InitUnityReplayKit();  // Classes/Unity/UnityReplayKit.mm

    InitUnityReplayKit();
#endif
}

再后是进入后台进入前台

- (void)applicationDidEnterBackground:(UIApplication*)application
{
    ::printf("-> applicationDidEnterBackground()\n");
}

- (void)applicationWillEnterForeground:(UIApplication*)application
{
    ::printf("-> applicationWillEnterForeground()\n");

    // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup)
    if (_unityAppReady)
    {
        // if we were showing video before going to background - the view size may be changed while we are in background
        [GetAppController().unityView recreateRenderingSurfaceIfNeeded];
    }
}

最后是applicationWillResignActive

- (void)applicationWillResignActive:(UIApplication*)application
{
    ::printf("-> applicationWillResignActive()\n");

    if (_unityAppReady)
    {
        UnitySetPlayerFocus(0);

        // signal unity that the frame rendering have ended
        // as we will not get the callback from the display link current frame
        UnityDisplayLinkCallback(0);

        _wasPausedExternal = UnityIsPaused();
        if (_wasPausedExternal == false)
        {
            // Pause Unity only if we don't need special background processing
            // otherwise batched player loop can be called to run user scripts.
            if (!UnityGetUseCustomAppBackgroundBehavior())
            {
#if UNITY_SNAPSHOT_VIEW_ON_APPLICATION_PAUSE
                // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager.
                // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop)
                // NB: We will actually pause after the loop (when calling UnityPause).
                UnityWillPause();
                [self repaint];
                UnityWaitForFrame();

                [self addSnapshotViewController];
#endif
                UnityPause(1);
            }
        }
    }

    _didResignActive = true;
}

【接入SDK】

总体上和Android没什么区别,相比Andorid好点是不用做Jar,因为本质上是C#调用C,C调用OC,一些需要的编译在出包的时候都会编译好。

同样的看是否需要生命周期,不需要生命周期的自己写调用即可。如果需要生命周期,同样是需要继承的,在Android中继承的是Activiry,在iOS就是要继承UnityAppController。

在Plugins/iOS路径下创建CustomAppController.mm​文件。(文件名必须是 ___AppController,前缀可自选,但不能省略;否则在 Build 项目的时候,会被移动到错误的目录中去。)文件主要代码如下:



@interface CustomAppController : UnityAppController

@end

IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)
@implementation CustomAppController


//重写生命周期函数
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    [super application:application didFinishLaunchingWithOptions:launchOptions];
    //自己的代码
    return YES;
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    
    [super applicationDidBecomeActive:appliction]
    //自己的代码
}

//其他生命周期函数

@end

在Build iOS Project时,Unity会自动把该文件复制到Library文件里,原来的 UnityAppController在Classes文件夹里。

通过IMPL_APP_CONTROLLER_SUBCLASS知道要使用我们定制的 CustomAppController 而不是使用默认的 UnityAppController,其定义在UnityAppController.h文件中。可以看到其作用是在load的时候修改了AppControllerClassName。也即在加载UnityFramework时替换名字,这样在创建UIApplicationMain中创建的是我们新建的AppController。

// Put this into mm file with your subclass implementation
// pass subclass name to define

#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) \
@interface ClassName(OverrideAppDelegate)       \
{                                               \
}                                               \
+(void)load;                                    \
@end                                            \
@implementation ClassName(OverrideAppDelegate)  \
+(void)load                                     \
{                                               \
    extern const char* AppControllerClassName;  \
    AppControllerClassName = #ClassName;        \
}                                               \
@end   

【参考】

UnityAppController的定制以及Unity引擎的IL2CPP机 - 简书文章来源地址https://www.toymoban.com/news/detail-548191.html

到了这里,关于Unity与iOS交互(2)——接入SDK的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kotlin & Compose Multiplatform 跨平台开发实践之加入 iOS 支持

    几个月前 Compose Multiplatform 的 iOS 支持就宣布进入了 Alpha 阶段,这意味着它已经具备了一定的可用性。 在它发布 Alpha 的时候,我就第一时间尝鲜,但是只是浅尝辄止,没有做过多的探索,最近恰好有点时间,于是我又重新开始学习 Compose Multiplatform ,并且尝试移植我已有的项

    2024年02月07日
    浏览(34)
  • 【HarmonyOS开发】ArkUI-X 跨平台框架(使用ArkTs开发Android&IOS)

    ArkUI-X 跨平台框架进一步将 ArkUI 开发框架扩展到了多个OS平台,目前支持OpenHarmony、HarmonyOS、Android、 iOS,后续会逐步增加更多平台支持。开发者基于一套主代码,就可以构建支持多平台的精美、高性能应用。 React Native 是一个基于 JavaScript 和 React 的开源框架,由 Facebook 开发和

    2024年01月20日
    浏览(35)
  • 滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南

    滚动条是用户界面中的图形化组件,用于指示和控制内容区域的可滚动范围。当元素内容超出其视窗边界时,滚动条提供可视化线索,并允许用户通过鼠标滚轮、触屏滑动或直接拖动滑块来浏览未显示部分,实现内容的上下或左右滚动。它在保持界面整洁、避免内容溢出的同

    2024年04月27日
    浏览(26)
  • Unity跨平台开发指南(PC/VR/Android/WebGL)

    通常我在进行不同平台的设置时会基于以下几点: 1:创建、开发、打包时我们通常针对Player和Quality设置进行质量的设定 2:在不同平台上运行时,有不同的平台包体大小,加载方式的限定,测试、打包上的区别,帧率稳定60 3:代码封装上的区别,特别针对单一项目转为不同

    2024年01月21日
    浏览(45)
  • 【LocalSend】开源跨平台的局域网文件传输工具,支持IOS、Android、Mac、Windows、Linux

    工作前提条件:设备使用相同的局域网。 LocalSend is a cross-platform app that enables secure communication between devices using a REST API and HTTPS encryption. Unlike other messaging apps that rely on external servers, LocalSend doesn’t require an internet connection or third-party servers, making it a fast and reliable solution for local

    2024年02月17日
    浏览(36)
  • Unity跨平台UI解决方案:可能是最全的FairyGUI系列教程

    FairyGUI的项目文件结构 .objs 内部数据目录。注意:不要加入版本管理,因为这里的内容是不需要共享的。 assets 包内容放置目录,资源内容都在这里面,里面还可以分不同的包,便于区分管理(看下图) settings 配置文件放置目录。 ****.fairy 项目识别文件,也就是项目名称 目录

    2024年04月14日
    浏览(31)
  • 跨平台应用开发进阶(五十)uni-app ios web-view嵌套H5项目白屏问题分析及解决

    应用 uni-app 框架开发好APP上架使用过程中,发现应用经过长时间由后台切换至前台时,通过 webview 方式嵌套的H5页面发生白屏现象。 任何手机设备上,当手机内存不足时,os都会回收资源。一般是先回收后台打开的资源。如果当前应用占用的资源过高,当前应用也有可能崩溃

    2024年02月14日
    浏览(39)
  • Unity与Android交互(4)——接入SDK

    【前言】 unity接入Android SDK有两种方式,一种是把Unity的工程导出google project的形式进行接入,另一种是通过把Android的工程做成Plugins的形式进行接入。我们接入SDK基本都是将SDK作为插件的形式接入的。 对我们接入SDK的人来说,SDK也是分等级的: 第一等级:只有so文件,这种

    2024年02月16日
    浏览(34)
  • Unity下实现跨平台的RTMP推流|轻量级RTSP服务|RTMP播放|RTSP播放低延迟解决方案

    2018年,我们开始在原生RTSP|RTMP直播播放器的基础上,对接了Unity环境下的低延迟播放,毫秒级延迟,发布后,就得到了业内一致的认可。然后我们覆盖了Windows、Android、iOS、Linux的RTMP推送、轻量级RTSP服务和RTSP|RTMP播放。 目前看,Unity环境下,我们在行业内的延迟几乎是最低的

    2024年01月22日
    浏览(41)
  • 【QT+QGIS跨平台编译】之八:【zstd+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

    【libzstd跨平台编译】:Windows环境下编译成果(支撑QGIS跨平台编译,以及二次研发) 【libzstd跨平台编译】ÿ

    2024年01月25日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包