草庐IT

Unity实现跨场景的传送门

示申○言舌 2024-02-03 原文

Unity实现跨场景的传送门

目录

引言

之前写过一篇文章——《Unity第一人称可视化传送门制作》,实现了传送门的基本操作,而且,可以通过传送门观察对面的画面,但一个缺陷是无法跨场景传送。那么,如何实现跨场景的传送呢?
事实上,所谓跨场景传送,本质上就是场景加载,关键是要传送到什么位置去,在在老场景中事先确定好位置,然后新场景加载后,把角色设置到新的目标位置和朝向即可。本质上,这就是跨场景传送数据的问题,Unity中,有个DontDestroyOnLoad方法,可以将一个物体设置为“切换场景时不要销毁”,那么这个物体就会保留到下一个场景中,我们需要的数据就可以保存在这个物体上,等数据使用完了,再把这个物体销毁即可。但是,跨场景的传送门,无法像“同场景中的传送门”一样,可以提前观察到对面的情况,因为在没有进入门之前,场景还没有加载,肯定是无法观察到的,这也是没有办法的。

视频效果

注意观察视频中的Hierarchy窗口,每次传送,场景都真实的切换了。

Unity跨场景传送门

具体实现

public class ProtalDoor : MonoBehaviour
{
    public LayerMask TransLayer;
    public string TargetSceneName;
    public string TargetDoorTag;
    
    private void OnTriggerEnter(Collider other)
    {
        if (((1 << other.gameObject.layer ) & TransLayer) != 0)
        {
            if (TransDataToScene.IsExists)
            {
                // 如果存在数据,表示此时为玩家进入传送后新场景的门
                // 启动玩家控制,销毁跨场景数据
                PlayerController.Active(true);
                TransDataToScene.Destroy();
            }
            else
            {
                // 数据不存在,表示玩家刚进入传送门,此为传送前。
                // 需要禁用玩家控制,然后创建跨场景数据,加载新场景
                PlayerController.Active(false);
                TransDataToScene.SaveData(transform, other.transform, TargetDoorTag);
                HxSceneLoader.LoadScene(TargetSceneName);
            }
        }
    }
}

门的逻辑起始很简单,如果玩家进入了门的碰撞器范围内,就分两种情况:
一是跨场景的数据不存在,那么此时就是传送之前,玩家想要进行传送了。此时,就创建好需要跨场景保存的数据,比如玩家相对门的位置,玩家想要去哪个场景,如果目标场景中有多个门,那要明确目标门的TAG,然后就加载场景。
二是如果跨场景的数据已经存在了,那么就表示此时为传送之后了(传送之后,玩家会被设置到目标门附近,所以也会触发OnTriiggerEnter),此时要做的就是销毁数据,以便下次传送。

数据
public class TransDataToScene : MonoBehaviour
{
    private static TransDataToScene _instance;
    public static bool IsExists => _instance != null;

    public static Vector3 localPos { get; private set; }
    public static Vector3 localRot { get; private set; }
    public static string targetDoorTag { get; private set; }

    public static ProtalDoor targetDoor => string.IsNullOrWhiteSpace(targetDoorTag)
        ? null
        : GameObject.FindWithTag(targetDoorTag)?.GetComponent<ProtalDoor>();

    public static void Destroy()
    {
        if (IsExists)
        {
            Destroy(_instance.gameObject);
            _instance = null;
        }
    }

    public static void SaveData(Transform door, Transform player, string targetTag)
    {
        if (_instance == null)
        {
            GameObject obj = new GameObject("TransData");
            _instance = obj.AddComponent<TransDataToScene>();
            DontDestroyOnLoad(obj);
        }

        localPos = door.InverseTransformPoint(player.position);
        localRot = door.InverseTransformDirection(player.eulerAngles);
        targetDoorTag = targetTag;
    }
}

因为玩家一次只可能使用一个门,所以,这个跨场景传送数据的物体,被设计为单例模式,他唯一的用途,就是在老场景中被创建出来,然后记录玩家相对于源门的相对位置和相对朝向,以及目标门的TAG,然后新场景加载完成后,用它记录好的数据,找到新场景中的目标门物体,最后进行玩家位置的恢复,恢复到和目标门一样的相对位置。

场景异步加载
public static void LoadScene(string name)
{
    // 首先启用加载器,然后启动加载协程。
    Instance.gameObject.SetActive(true);
    Instance.StartCoroutine(Instance.RealLoad(name));
}

private IEnumerator RealLoad(string sceneName)
{
    // 播放界面淡入动画,显示进度条,开始加载
    _animator.SetTrigger(StartLoad);
    var asy = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Single);
    asy.allowSceneActivation = true;

    while (!asy.isDone)
    {
        _text.text = ((asy.progress + 0.09f) * 100f).ToString("0");
        _slider.value = asy.progress + 0.09f;
        yield return null;
    }

    // 加载完成,设置玩家在新场景中的位置,然后UI淡出。
    PlayerController.SetTransform();
    _animator.SetTrigger(LoadDone);
}

场景的加载,有个过渡动画,并且有个进度条,但是我这三个场景都太小了,加载是秒加载的,所以,这个过渡就会一闪而过,如果场景足够大,加载时间长一点,那么这个过渡动画就变的很有必要了。
异步加载使用LoadSceneAsync API。
玩家恢复数据的代码:

public static void SetTransform()
{
    // 获取目标门
    ProtalDoor door = TransDataToScene.targetDoor;
    if (!door )
        return;
        
    // 参照玩家相对源门的位置、朝向,恢复到目标门的相对位置和朝向。
    Instance.transform.position = door.transform.TransformPoint(TransDataToScene.localPos);
    Instance.transform.rotation = Quaternion.Euler(door.transform.TransformDirection(TransDataToScene.localRot));
}

关于门的特效

这个门的模型是自己用Blender做的,美术功底不太好。但这不重要,特效分两部分,一个是门内平面本身的渲染,用Shader Graph连了简单的半透明效果:

然后就是一个粒子特效,用Visual Effect Graph连的:

起始特效方面,完全可以做到更炫酷,比如第三人称的话,可以让角色消融、变成粒子飞升。。。

有关Unity实现跨场景的传送门的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  5. unity---接入Admob - 2

    目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里​编辑 3.解析依赖到项目中

  6. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  9. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  10. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

随机推荐