You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity基于Photon实现玩家摄像头实时显示报空引用异常求解

空引用异常排查与修复方案

核心空引用触发点

  • 类内声明了全局photonView字段,但Start()方法中重复声明了同名局部变量,赋值操作只作用于局部变量,全局photonView始终为null,后续FixedUpdate中调用photonView.RPC()时直接触发空引用。
  • WebCamTexture启动首帧还未完成权限申请、未输出有效画面时,webcamTexture.width/webcamTexture.height为0,直接读取参数、调用GetPixels()会触发空引用或参数错误。
  • 所有客户端无差别执行FixedUpdate内的摄像头采集、编码、发送逻辑,非本地玩家不需要采集本地摄像头,会因本地webcamTexture未初始化触发空引用。
  • 未做组件存在性校验:如果Quad对象未挂载Renderer组件、用户拒绝摄像头权限导致webcamTexture初始化失败,后续调用相关接口都会触发空引用。
  • 逻辑位置和性能问题:FixedUpdate是物理帧回调,和渲染帧不同步;每帧新建Texture2D、编码JPG、调用Resources.UnloadUnusedAssets()开销极大,会导致GC卡顿、内存泄漏,极端情况也会触发资源引用异常。

修复步骤

  • 去掉Start()内photonView的局部变量声明,直接给全局字段赋值,推荐直接继承MonoBehaviourPun,用内置的photonView字段避免手动赋值出错。
  • 加本地玩家判定:只有当前客户端是PhotonView所有者时,才初始化本地摄像头、执行采集编码逻辑,非本地玩家仅走RPC接收画面的逻辑。
  • 加摄像头有效性校验:只有webcamTexture处于播放状态、宽高大于0时才执行画面读取编码,避开启动首帧的空窗期。
  • 缓存Renderer组件引用,不要每次调用都执行GetComponent,减少不必要的性能开销。
  • 把采集逻辑从FixedUpdate移到LateUpdate和渲染帧同步,控制画面发送频率(10-15帧/秒足够面部显示使用),删除每帧调用Resources.UnloadUnusedAssets()的逻辑,该接口开销极大,仅适合在批量卸载资源时调用。
  • 复用Texture2D采集对象,避免每帧新建对象产生大量GC,加摄像头权限申请逻辑适配移动端、WebGL平台。
  • RPC发送目标改为RpcTarget.OthersBuffered,不需要把画面发回本地玩家,本地直接显示原始摄像头画面即可,能省一半编码、带宽开销。

修复后完整代码

using System.Collections;
using UnityEngine;
using Photon.Pun;

public class photonwebcam : MonoBehaviourPun
{  
    WebCamTexture webcamTexture;
    private Renderer quadRenderer;
    // 摄像头画面发送频率,可根据带宽情况调整
    public float sendFrameRate = 15f;
    private float nextSendTime = 0f;
    private Texture2D captureTex;
    // JPG编码质量,0-100,值越低带宽占用越小
    public int jpgQuality = 50;

    void Start()
    {    
        quadRenderer = GetComponent<Renderer>();
        if (quadRenderer == null)
        {
            Debug.LogError("Quad对象未挂载Renderer组件,无法显示画面");
            return;
        }

        // 仅本地玩家初始化摄像头
        if (photonView.IsMine)
        {
            StartCoroutine(RequestCameraPermission());
        }
    }

    IEnumerator RequestCameraPermission()
    {
        // 等待摄像头权限授权
        yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
        if (Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            webcamTexture = new WebCamTexture();
            quadRenderer.material.mainTexture = webcamTexture;
            webcamTexture.Play();
        }
        else
        {
            Debug.LogError("用户拒绝摄像头权限,无法采集本地画面");
        }
    }

    void LateUpdate()
    {   
        // 非本地玩家跳过采集发送逻辑
        if (!photonView.IsMine) return;
        // 摄像头未就绪直接返回
        if (webcamTexture == null || !webcamTexture.isPlaying || webcamTexture.width <= 0 || webcamTexture.height <=0) return;
        // 控制发送频率
        if (Time.time < nextSendTime) return;
        nextSendTime = Time.time + 1f / sendFrameRate;

        // 复用采集纹理,尺寸变化时重建
        if (captureTex == null || captureTex.width != webcamTexture.width || captureTex.height != webcamTexture.height)
        {
            if (captureTex != null) Destroy(captureTex);
            captureTex = new Texture2D(webcamTexture.width, webcamTexture.height, TextureFormat.RGB24, false);
        }
        
        captureTex.SetPixels(webcamTexture.GetPixels());
        captureTex.Apply();
        byte[] bytes = captureTex.EncodeToJPG(jpgQuality);
        // 仅发送给其他玩家,不需要发回本地
        photonView.RPC("ReceiveRemoteVideo", RpcTarget.OthersBuffered, bytes);
    }

    [PunRPC]
    public void ReceiveRemoteVideo(byte[] bytes)
    {   
        if (bytes == null || quadRenderer == null) return;
        
        Texture2D remoteTex = new Texture2D(2, 2);
        remoteTex.LoadImage(bytes);
        quadRenderer.material.mainTexture = remoteTex;
    }

    void OnDestroy()
    {
        // 销毁资源避免内存泄漏
        if (webcamTexture != null && webcamTexture.isPlaying)
        {
            webcamTexture.Stop();
        }
        if (captureTex != null) Destroy(captureTex);
    }
}

额外配置注意事项

  • 挂载脚本的Quad对象必须同时挂载PhotonView组件,且将该脚本添加到PhotonView的RPC监听列表中,否则RPC调用会失败。
  • 多人房间中建议JPG质量不要超过70,否则带宽占用过高会导致房间同步卡顿。
  • 该脚本挂载的Quad需要作为玩家Avatar的子物体,跟随PhotonView网络同步生成,保证每个玩家对应一个自己的面部画面Quad。

内容的提问来源于stack exchange,提问作者g g

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:57:27