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

Unity中玩家无法随移动平台移动的问题排查请求

问题分析:玩家重置后无法随移动平台移动

问题现象

  • 游戏Build模式下,玩家初始状态跳上移动平台可正常跟随移动;
  • 玩家死亡失活、重置位置并重新激活后,**常规移动模式(movemode=0)下无法随平台移动,但攻击模式(movemode=3)**下功能正常。

相关脚本

移动平台逻辑脚本(PlatformMovement.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlatformMovement : MonoBehaviour
{
    public int PlatformDirection = 0;
    public int MoveSpeed;
    void Update()
    {
        if (PauseMenu.Paused == 0)
        {
            if (PlatformDirection == 0)
            {
                transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime);
            }
            if (PlatformDirection == 1)
            {
                transform.Translate(Vector3.back * MoveSpeed * Time.deltaTime);
            }
        }
    }
    void OnCollisionEnter (Collision col)
    {
        if(col.gameObject.name == "Wall")
        {
            if (PlatformDirection == 0)
            {
                PlatformDirection = 1;
            }
            else
            {
                PlatformDirection = 0;
            }
        }
   }
}

平台触发器脚本(PlatformDetector.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlatformDetector : MonoBehaviour
{
    public GameObject Player;
    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == Player)
        {
            Player.transform.parent = transform;
        }
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject == Player)
        {
            Player.transform.parent = null;
        }
    }
}

玩家控制脚本(PlayerController.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float _moveSpeed;
    public float _gravity;
    public float _jumpSpeed;
    public float _bounceSpeed;
    public static int movemode;
    public GameObject Camera;
    public Animator animator;
    public AudioSource audioSource;
    public AudioClip JumpSound;
    public AudioClip BoingSound;
    public AudioClip HitSound;
    public static int OnIce;

    private CharacterController _controller;
    private Rigidbody rb;
    private float horizontalInput;
    private float verticalInput;

    public static float _directionY;
    
    // Start is called before the first frame update
    void Start()
    {
        _controller = GetComponent<CharacterController>();
        audioSource = gameObject.GetComponent<AudioSource>();
        movemode = 0;
        PauseMenuController.ExitingLevel = 0;
    }

    // Update is called once per frame
    void Update()
    {
        if (movemode == 0)
        {
            _moveSpeed = 5f;
            _gravity = 9.81f;
            _jumpSpeed = 4f;

            if (OnIce == 0)
            {
                horizontalInput = Input.GetAxis("Horizontal");
                verticalInput = Input.GetAxis("Vertical");
            }
            if (OnIce == 1)
            {
                horizontalInput = Input.GetAxis("HorizontalIce");
                verticalInput = Input.GetAxis("VerticalIce");
            }

            Vector3 direction = new Vector3(horizontalInput, 0, verticalInput);
            direction = Camera.transform.TransformDirection(direction);
            if (_controller.isGrounded)
            {
                    if(Input.GetButtonDown("Jump"))
                    {
                        audioSource.PlayOneShot(JumpSound, 1);
                        _directionY = _jumpSpeed;
                    }
            }
            if (!_controller.isGrounded)
            {
                _directionY -= _gravity * Time.deltaTime;
                direction.y = _directionY;
            }
            else
            {
                _directionY -= 0 * Time.deltaTime;
                direction.y = _directionY;
            }

            _controller.Move(direction * _moveSpeed * Time.deltaTime);

            animator.SetBool("isWalking", verticalInput != 0 || horizontalInput != 0);
            animator.SetBool("isGrounded", _controller.isGrounded == true);
            animator.SetBool("Attacking", movemode == 3);
            animator.SetBool("Swimming", movemode == 1);
            animator.SetBool("LevelComplete", movemode == 4);
        }
        if (movemode == 1)
        {
            _moveSpeed = 3f;
            _gravity = 4f;
            _jumpSpeed = 1.5f;

            float horizontalInput = Input.GetAxis("Horizontal");
            float verticalInput = Input.GetAxis("Vertical");

            Vector3 direction = new Vector3(horizontalInput, 0, verticalInput);
            direction = Camera.transform.TransformDirection(direction);

            if(Input.GetButtonDown("Jump"))
            {
                _directionY = _jumpSpeed;
            }
                _directionY -= _gravity * Time.deltaTime;
                direction.y = _directionY;

            _controller.Move(direction * _moveSpeed * Time.deltaTime);

            animator.SetBool("Swimming", movemode == 1);
            animator.SetBool("Attacking", movemode == 3);
        }
        if (movemode == 3)
        {
            animator.SetBool("Attacking", movemode == 3);
        }
        if (movemode == 4)
        {
            animator.SetBool("LevelComplete", movemode == 4);
        }
    }
    void OnTriggerEnter (Collider col)
    {
        if(col.gameObject.name == "Spike")
        {
            if (Pos_reset.invincibilityframe > 2)
            {
                HealthCounter.Health -= 1;
                if (HealthCounter.Health > 0)
                {
                        audioSource.PlayOneShot(HitSound, 1);
                        _directionY = _jumpSpeed;
                }
                Pos_reset.invincibilityframe = 0;
            }
        }
        if(col.gameObject.name == "Trampoline")
        {
            audioSource.PlayOneShot(BoingSound, 1);
            _directionY = _bounceSpeed;
        }
    }
}

核心原因

  1. CharacterController的Move方法覆盖父物体移动:常规移动模式(movemode=0)下,玩家持续调用_controller.Move(),该方法直接修改玩家的世界空间位置,会忽略父物体(平台)的位移影响;而攻击模式(movemode=3)下没有调用Move方法,所以平台的父物体移动能正常生效。
  2. 重置后触发器未正确触发:玩家死亡失活再激活时,若激活位置已经在平台的触发器范围内,OnTriggerEnter事件不会触发,导致玩家没有被设置为平台的子物体,自然无法跟随移动。
  3. 重置时父物体状态未清理:玩家重置过程中,如果没有将父物体重置为null,可能导致后续触发器逻辑出现异常,无法正确绑定平台父物体。

修复方案

方案1:兼容CharacterController与平台移动(推荐)

修改玩家移动逻辑,主动叠加平台的移动速度,避免Move方法覆盖父物体位移:

  • 在PlatformMovement.cs中添加获取平台速度的方法:
public Vector3 GetPlatformVelocity()
{
    if (PauseMenu.Paused == 0)
    {
        return PlatformDirection == 0 ? Vector3.forward * MoveSpeed : Vector3.back * MoveSpeed;
    }
    return Vector3.zero;
}
  • 在PlayerController.cs的movemode=0逻辑中,修改Move调用:
// 计算移动方向后,添加平台速度
Vector3 platformVelocity = Vector3.zero;
if (transform.parent != null)
{
    PlatformMovement platform = transform.parent.GetComponent<PlatformMovement>();
    if (platform != null)
    {
        platformVelocity = platform.GetPlatformVelocity();
    }
}

// 叠加平台速度后再移动
_controller.Move((direction * _moveSpeed + platformVelocity) * Time.deltaTime);

方案2:重置时主动检测平台触发器

在玩家重置激活的脚本(如Pos_reset)中,添加主动检测逻辑,确保玩家在平台上时正确设置父物体:

// 玩家激活后执行
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, 0.2f);
foreach (var collider in nearbyColliders)
{
    PlatformDetector detector = collider.GetComponent<PlatformDetector>();
    if (detector != null && detector.Player == gameObject)
    {
        transform.parent = detector.transform;
        break;
    }
}

方案3:替换CharacterController为Rigidbody

如果项目逻辑允许,将玩家的CharacterController替换为运动学Rigidbody(勾选Is Kinematic),通过物理引擎处理平台跟随,避免手动移动与父物体位移的冲突。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 16:45:01