123456881 发表于 2021-2-5 10:19

天坑!Unity Render Texture的狗血bug!

这两天做第一人称开发,用render texture做了一个第一人称视角的过滤,结果遇上了一个天大的坑,为了解决这一bug,我在国内外各大论坛寻找了一天半的解决方案也没有遇到类似的问题
我的思路大概是这样的:
考虑到解耦的要求,我的变量希望集中放在game manager中,让后各个小部件获取game manager再去寻找目标变量,
所以整个过程涉及到三个游戏物体,那同步就是一个问题
所以我用了一个协程函数定期更新一下render texture,如果成功再看看能不能在同步完成以后关闭这个协程
IEnumerator CreateRenderTexture(float time)
    {
      while (true)
      {
            CurrentFPSRt = new RenderTexture(Screen.width, Screen.height, 0)
            {
                name = "fpsViewRenderTexture"
            };
            CurrentFPSRt.Create();

            yield return new WaitForSeconds(time);
      }
    }然后我需要让第一人称视角的相机给出拍摄到的画面到target texture上面,这个目标贴图就是game manager里面的那个
    public FPSMovementController fPSMovementController;

    private GameManager gameManager;
    private Camera cam;

    // Start is called before the first frame update
    void Start()
    {
      cam = gameObject.GetComponent<Camera>();
      gameManager = fPSMovementController.gameManager;
    }

    // Update is called once per frame
    void Update()
    {
      if (cam.targetTexture == null)
      {
            if (gameManager.CurrentFPSRt != null)
            {
                cam.targetTexture = gameManager.CurrentFPSRt;
            }
      }
    }这里的脚本主要就是针对同步问题。我的解决方案就是不断检测gameManager有没有更新render texture,如果设置好了,我就把它作为我的相机的目标渲染贴图
最后相似的道理在Canvas上也给一个脚本获取一下game manager的贴图
    public GameManager gameManager;

    RawImage rawImage;

    // Start is called before the first frame update
    void Start()
    {
      rawImage = gameObject.GetComponent<RawImage>();
      rawImage.texture = null;
    }

    // Update is called once per frame
    void Update()
    {
      if (rawImage.texture == null)
      {
            if (gameManager.CurrentFPSRt != null)
            span class="p">{
                rawImage.texture = gameManager.CurrentFPSRt;
            }
      }
    }运行结果依然是天衣无缝
但是,
但是
但是!

当编译完成以后运行结果就是:
是的,这就是运行画面
WDNMD全白??
仔细看看这就是没有给到贴图之前的样子
还有Game视图和编译结果不一样的说法??
然后运行时仔细看了一下各个gameobject的贴图状态,
都是正常的
这就奇怪了!
再仔细一看,
只有Game Manager里面的贴图是黑色的
按理来说三个gameobject的变量都是game manager里面的Rendertexture对象啊,同一个变量怎么可能会有两种结果呢
唯一的可能就是unity的特殊机制,导致了Rendertexture只能赋值一道,
换句话说
Unity的一个bug导致了在编译以后rendertexture类变得不可访问
真有你的啊Unity
于是改变思路,由game object共享的内容只有RawImage对象
找了两天的问题终于解决了。。
总结

为啥这次我遇到的bug怎么找都找不到类似的现象
因为我的思路实在太罕见了
我见到最相似的问题是Google上面有一个开发者问的How to resize render texture?
问出这个问题其实很正常,因为render texture有一个机制是禁止在runtime更改width和height属性,这个也是我决定让game manager共享render texture的动机
但是把render texture变量作为一个公有字段让另外两个脚本去获取才导致编译后和运行时有区别我是实在没想到
编译后和运行时有区别本身也应该属于Unity的bug,正常的脚本就应该是在game视图中与编译后一样
只能说希望官方尽快修复这个bug

当当当当裤裆坦 发表于 2021-2-5 10:23

协程不断创建rendertexture这个操作好迷。。在update中一直检查也好迷

123456865 发表于 2021-2-5 10:27

我这里放的是测试时的代码,因为我不确定问题的原因是不是因为我只创建了一次导致canvas没有获取到,所以我试试多创建几次是不是还是没法获取

内托体头 发表于 2021-2-5 10:30

这里解释一下,我之前没有用协程。
我之前把创建render texture的工作放在了game manager的start函数里,所以我的预期就是不管哪一个脚本先运行,最开始的初始状态game manager的render texture都是空的。
然后再start函数内创建了render texture,所以下一帧一定会被相机和canvas脚本捕获到(因为这时满足相机和canvas的texture是空,而game manager的render texture已经创建的条件)。
对于第二点,也是一样。我之前没有用协程函数的时候就是在start函数内创建新的render texture,两个texture一定获取到的是同一个render texture。
我后来使用协程函数是为了看一下,编译后不能运行是不是因为在start函数内创建render texture会导致canvas无法获取(canvas尝试获取的时候game manager还没有start)但是这并不影响相机和canvas获取的是同一个render texture,下面会解释为什么:
就算你说的使用协程函数导致render texture每次都不一样,update内的所有代码也一定在同一帧执行,也就是说两个update内获取render texture的内容也一定是同时执行的,获取到的render texture也一定是一样的,不会出现获取到两次协程函数运行的情况。
最后还是感谢你对这个bug进行了分析,不过以我来看这就是Unity引擎的问题,希望Unity官方也能尽快修复

形腿望舞 发表于 2021-2-5 10:31

怎么给unity官方提交bug?
页: [1]
查看完整版本: 天坑!Unity Render Texture的狗血bug!