Unity中解决DontDestroyOnLoad导致的对象重复出现(可能是全网最短解决方案)

DontDestroyOnLoad  加载场景时不销毁游戏对象。对于保留全局对象以及角色切换场景来说是非常实用的方法,但是直接使用该方法有一个弊端,当返回创造对象的场景时,场景将会出现两个对象,即保留的对象和新生成的对象。这样就不符合我们的需求了。

(想直接得到答案请拉到最下端的结论)

网上有很多解决方案,大致分为三种:静态初始化、循环销毁、flag判断以及不再进入初始场景,但是三种方法多多少少会存在一些问题。

首先是静态初始化

public class Global:MonoBehaviour
{
    public static Globalinstance;
    static Global()
    {
        GameObjectgo=newGameObject("Globa");
        DontDestroyOnLoad(go);
        instance=go.AddComponent();
    }
}

作者:霸俊流年
链接:https://www.jianshu.com/p/57b339a79959
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

网上随便找了一段代码,这段代码使得Global对象的构造函数变为静态,这样该对象就会在游戏一开始时被创建,同时挂载DontDestroyOnLoad并成为单例,这样当再次进入该场景时不会被动态生成。这样做的好处是没有循环判断且不会再分配新的内存空间,效率高。但是弊端很明显,游戏对象必然在第一个场景被创建,在不想对象在第一个场景创建的时候就不能直接使用了。

循环销毁:

基本原理是把需要保留的对象放在一个队列里,每次切换场景都循环判断一次场景中是否存在同名对象,若存在则销毁。很明显,该方法消耗性能极大,若把判断代码放在场景控制脚本中,那么耦合度又是一个头疼的问题,总之并不建议去尝试。

flag判断:

使用一个变量去判断场景中是否存在同一对象,如下面代码:

 public static bool isClone;
 public GameObject obj;
 private GameObject cloneObj;
void Awake()
    {
        if(!isClone)
       {
            cloneObj =Instantiate(obj,obj.transform.position,obj.transform.rotation)as GameObject;
            isClone = true;
        }
        DontDestoryOnLoad(cloneObj);
    }

作者:Babybus_Unity
链接:https://www.jianshu.com/p/cd9cc1c3049f
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

结论自然是耦合度高、代码复杂、效率不高等等,其中还要预先封装预制体,也加大了开发难度。

不再进入初始场景:

使用一个场景来进行初始化,简单高效,也就多一个场景以及一个场景切换脚本而已。


我的解决方案:

接下来是我的解决方案,把所有需要保留的对象挂载在名为“Global”的空对象下,并设置tag为“Global”,再把一下代码脚本挂载在Global上,别害怕只有两句代码:

private void Awake()
    {
        if (GameObject.FindGameObjectsWithTag("Global").Length>1)
            Destroy(this.gameObject);
        else
            DontDestroyOnLoad(this.gameObject);
    }

这里使用了一个概念:立即销毁=限制创建,当然,这样描述是不正确的,因为对象的创建事实上发生了 ,所以对于需要保留的对象有一个限制,就是不能在Awake里写入对游戏有明显影响的代码。

这段代码只会在创建对象时调用,所以性能上完全是没有问题的,这样当再次进入第一个场景时,新生成的对象调用Awake函数,发现“Global”的数量大于1,于是立马销毁掉,在玩家看来基本上是看不出有任何问题的。当然你不能一上来就删几个对象或者生产几个对象这样子,作死多了肯定会露馅的。

效果如下,挂载后角色将不会再重复出现:

 

把“Global”做成空的预制体也可以,单独使用这段代码也可以,因为Destroy和DontDestroyOnLoad都是对整个继承树有效的。这样做的话无论是效率还是耦合度都能得到保障,只不过如果某个子类在Awake里有“特别的技巧”,那还是推荐其他方法吧。

-----------2021/4/25 更新----------------

综合一下上面的方法,可以使用下面的代码结合单例模式作为基类使用。原理是一样的借助静态变量origional 判断是否是最初的对象,在第一次创建对象后,origional 被置为false,之后的对象均会在Awake阶段就被Destroy掉。

private static T instance;
private static bool origional = true;

protected virtual void Awake()
{
    if (origional) {
        instance = this as T;
        origional = false;
        DontDestroyOnLoad(this.gameObject);
    } else {
        Destroy(this.gameObject);
    }
}

 

另,如果有正在做或者想做独立游戏的小伙伴,希望可以加入我们的群,一起分享经验或者寻找同伴:

 

  • 22
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值