一、常见的不严谨答案

将下面的脚本扔进工程打包即可,也是众多文章中的做法。

能用,但是不严谨。

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Scripting;

[Preserve]
public static class SkipUnityLogo
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    private static void BeforeSplashScreen()
    {
#if UNITY_WEBGL
        Application.focusChanged += ApplicationFocusChanged;
#else
        Task.Run(() => SplashScreen.Stop(SplashScreen.StopBehavior.FadeOut));
#endif
    }

#if UNITY_WEBGL
    private static void ApplicationFocusChanged(bool obj)
    {
        Application.focusChanged -= ApplicationFocusChanged;
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }
    
#endif
}

问题点

需求是停止Splash,但是方法的执行时间是在BeforeSplashScreen,Splash都没有开始,怎么能停止?所以采用了Task.Run来延时,或等待Application.focusChanged获取焦点,获取焦点等于让场景准备好。

解决办法

先明确RuntimeInitializeOnLoadMethod的生命周期:

 以下列表显示了RuntimeInitializeLoadType回调的执行顺序:

  1. 初始化各种底层系统(窗口、程序集、图形等)。
  2. 调用RuntimeInitializeLoadType.SubsystemRegistrationRuntimeInitializeLoadType.AfterAssembliesLoaded回调函数。
  3. 更多设置(输入系统等)
  4. 调用RuntimeInitializeLoadType.BeforeSplashScreen回调函数。
  5. 第一幕开始加载。
  6. RuntimeInitializeLoadType.BeforeSceneLoad回调函数已被调用。场景中的对象已加载,但Awake尚未被调用。所有对象均被视为非活动状态。
  7. AwakeOnEnable在 MonoBehaviours 上调用。
  8. RuntimeInitializeLoadType.AfterSceneLoad回调函数被调用。场景中的对象被视为已完全加载和设置。可以使用 FindObjectsByType 查找活动对象。

我们修改代码,将在第四步调用,改为第六步BeforeSceneLoad调用即可。

最终代码

[Preserve]
public static class SkipUnityLogo
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void BeforeSplashScreen()
    {
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }
}

额外注意

微信小游戏开发的特点是第一个场景尽可能的小,所以在BeforeSplashScreen和BeforeSceneLoad之间的时间很短,就不会有肉眼可见的splash闪烁。

当然,微信小游戏第一个场景尽可能小也是硬性要求。

二、[Preserve]是防止代码被剪裁的一种办法

Unity打包微信小游戏生成wasm包,为了让wasm代码包尽可能的体量小,unity设置代码剪裁一般为High,那么没有被项目在内置首包强引用的模块(比如unity的animation)和代码比如自己写的反射获取的类,就会被在打wasm包的过程中被剔除,那么运行的时候就是找不到相关模块和代码。

用[Preserve]标识得类就会被强制打进wasm包。

所以上面的代码用[Preserve]标识,就是为了这个跳过Splash的类不要在打包过程中被代码剪裁。

三、其他防止代码剪裁的办法

1. [Preserve]

2. 在内置首包中使用,比如为了防止Animator被剪裁,就搞个空物体挂载Animator,Unity打包的时候就知道被使用了,就不会被剪裁。

3. 使用link.xml,在项目中建立一个link.xml,内容例子:
这是我项目中的一个例子:

<linker>
  <!-- 保留 Animation 模块 -->
  <assembly fullname="UnityEngine.AnimationModule">
    <!-- 动画资源本体 -->
    <type fullname="UnityEngine.AnimationClip" preserve="all" />
    <!-- Animator 及控制器 -->
    <type fullname="UnityEngine.Animator" preserve="all" />
    <type fullname="UnityEngine.RuntimeAnimatorController" preserve="all" />
    <type fullname="UnityEngine.AnimatorOverrideController" preserve="all" />
    <!-- 旧版 Animation 组件(如果有用到) -->
    <type fullname="UnityEngine.Animation" preserve="all" />
    <type fullname="UnityEngine.AnimationState" preserve="all" />
    <!-- 状态机相关(如果用到自定义 StateMachineBehaviour) -->
    <type fullname="UnityEngine.StateMachineBehaviour" preserve="all" />
  </assembly>
  <assembly fullname="BallBlastLike">
    <type fullname="_02_Scripts.GameData.PlayerDataManager.Dtos.PlayerDTO" preserve="all" />
    <type fullname="_02_Scripts.GameData.PlayerDataManager.Dtos.CloudFunResultDTO" preserve="all" />
    
  </assembly>
  <assembly fullname="UnityEngine.Physics2DModule" preserve="all"/>
</linker>

可以看到,有为了防止Unity的模块被剪裁,比如动画系统UnityEngine.AnimationModule。

有为了我的代码不被剪裁,比如:
  <assembly fullname="BallBlastLike">
    <type fullname="_02_Scripts.GameData.PlayerDataManager.Dtos.PlayerDTO" preserve="all" />
    <type fullname="_02_Scripts.GameData.PlayerDataManager.Dtos.CloudFunResultDTO" preserve="all" />

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐