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

基于Photon的2D平台多人游戏角色控制串位及摄像头异常求助

2D多人平台游戏Photon联机控制串位问题排查与修复

问题描述

开发类似《森林冰火人》的2D多人平台游戏,基于Photon实现联机。已完成房间创建与关卡搭建,但角色创建后出现以下异常:

  • 本地按钮操作会控制其他玩家的角色
  • 双方摄像头与控制权限互相串位

问题代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.Events;
using Unity.VisualScripting;

public class MovemantMan : MonoBehaviourPun, IPunObservable
{
    [SerializeField] private Rigidbody2D rb;
    private bool moveleft;
    private bool moveright;
    private float horizontalMove;
    public float speed = 5f;
    public float jumpedSpeed = 5f;
    private bool isGrounded;
    private bool canDoubleJump;
    public float delayBeforeDoubleJump;
    private SpriteRenderer sr;

    private Vector3 smoothMove;

    private GameObject sceneCamera;
    public GameObject playerCamera;

    private void OnEnable()
    {
        ButtonTrigerCather._myEvent += Mycathcer;
    }

    private void Mycathcer(string obj)
    {
        if(photonView.IsMine)
        {

            switch (obj)
            {
            case "LeftUp":
                moveleft = false;
                Debug.Log("Left");
                break;
            case "RightUp":
                moveright = false;
                break;
            case "LeftDown":
                moveleft = true;
                break;
            case "RightDown":
                moveright = true;
                break;
            case "Jump":
                JumpButton();
                break;
            }
        }
    }

    private void OnDisable()
    {
        ButtonTrigerCather._myEvent -= Mycathcer;
    }

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        sr = GetComponent<SpriteRenderer>();
        moveleft = false;
        moveright = false;

        if (photonView.IsMine)
        {
            playerCamera = GameObject.Find("Main Camera");
            sceneCamera = GameObject.Find("Scene Camera");

            if (sceneCamera != null)
            sceneCamera.SetActive(false);

            if (playerCamera != null)
            playerCamera.SetActive(true);
        }
    }

    // Input handlers
    public void PointerDownLeft()
    {
        moveleft = true;
    }

    public void PointerUpLeft()
    {
        moveleft = false;
    }

    public void PointerDownRight()
    {
        moveright = true;
    }

    public void PointerUpRight()
    {
        moveright = false;
    }

    void Update()
    {
        if (photonView.IsMine)
        {
            ProcessInputs();
        }
        else
        {
            smoothMovement();
        }
    }

    private void ProcessInputs()
    {
        photonView.RPC("MovePlayer", RpcTarget.Others);
        MovePlayer();
    }
    [PunRPC]
    private void MovePlayer()
    {
        if (moveleft)
        {
            horizontalMove = -speed;
            sr.flipX = true;
        }
        else if (moveright)
        {
            horizontalMove = speed;
            sr.flipX = false;
        }
        else
        {
            horizontalMove = 0;
        }

        Vector2 move = new Vector2(horizontalMove, rb.velocity.y);
        rb.velocity = move;
    }

    public void JumpButton()
    {
        if (isGrounded)
        {
            isGrounded = false;
            rb.velocity = Vector2.up * jumpedSpeed;
            Invoke(nameof(EnableDoubleJump), delayBeforeDoubleJump);
        }
        else if (canDoubleJump)
        {
            rb.velocity = Vector2.up * jumpedSpeed;
            canDoubleJump = false;
        }
    }

    private void EnableDoubleJump()
    {
        canDoubleJump = true;
    }

    private void smoothMovement()
    {
        transform.position = Vector3.Lerp(transform.position, smoothMove, Time.deltaTime * 10);
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            stream.SendNext(transform.position);
        }
        else if (stream.IsReading)
        {
            smoothMove = (Vector3)stream.ReceiveNext();
        }
    }

    // Add collision detection to determine if the player is grounded
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
            canDoubleJump = false;  // Reset double jump
        }
    }
}

修复方案与核心修改点

1. 输入权限强制隔离

所有直接修改角色状态的输入方法必须添加photonView.IsMine判断,确保只有本地玩家能操控自己的角色:

public void PointerDownLeft()
{
    if(photonView.IsMine)
        moveleft = true;
}

public void PointerUpLeft()
{
    if(photonView.IsMine)
        moveleft = false;
}

// 同理修改PointerDownRight、PointerUpRight、JumpButton方法

2. 重构移动逻辑为本地权威模式

删除错误的RPC调用,本地玩家直接处理自己的移动,仅同步状态给其他玩家:

private void ProcessInputs()
{
    // 移除RPC调用,本地直接处理移动
    MovePlayer();
}

// 移除MovePlayer的[PunRPC]标签,改为本地私有方法
private void MovePlayer()
{
    if (moveleft)
    {
        horizontalMove = -speed;
        sr.flipX = true;
    }
    else if (moveright)
    {
        horizontalMove = speed;
        sr.flipX = false;
    }
    else
    {
        horizontalMove = 0;
    }

    Vector2 move = new Vector2(horizontalMove, rb.velocity.y);
    rb.velocity = move;
}

3. 摄像头逻辑优化

改为从角色子物体获取摄像头,避免全局查找的不确定性,同时显式禁用非本地玩家的摄像头:

private void Start()
{
    rb = GetComponent<Rigidbody2D>();
    sr = GetComponent<SpriteRenderer>();
    moveleft = false;
    moveright = false;

    // 从角色子物体获取摄像头,避免全局查找冲突
    playerCamera = GetComponentInChildren<Camera>().gameObject;
    sceneCamera = GameObject.Find("Scene Camera");

    if (photonView.IsMine)
    {
        if (sceneCamera != null)
            sceneCamera.SetActive(false);
        if (playerCamera != null)
            playerCamera.SetActive(true);
    }
    else
    {
        // 强制禁用非本地玩家的摄像头
        if (playerCamera != null)
            playerCamera.SetActive(false);
    }
}

4. 增强同步逻辑,提升移动平滑度

在OnPhotonSerializeView中同步速度,让其他玩家的角色移动更自然:

private Vector3 syncVelocity;

private void smoothMovement()
{
    transform.position = Vector3.Lerp(transform.position, smoothMove, Time.deltaTime * 10);
    rb.velocity = Vector2.Lerp(rb.velocity, syncVelocity, Time.deltaTime * 10);
}

public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
    if (stream.IsWriting)
    {
        stream.SendNext(transform.position);
        stream.SendNext(rb.velocity);
    }
    else if (stream.IsReading)
    {
        smoothMove = (Vector3)stream.ReceiveNext();
        syncVelocity = (Vector2)stream.ReceiveNext();
    }
}

5. 碰撞检测权限隔离

确保只有本地玩家的碰撞会修改落地状态:

private void OnCollisionEnter2D(Collision2D collision)
{
    if(!photonView.IsMine) return;
    
    if (collision.gameObject.CompareTag("Ground"))
    {
        isGrounded = true;
        canDoubleJump = false;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 06:22:31