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

Unity2D俯视角游戏:如何实现带避障的玩家瞬移功能?

Unity2D俯视角瞬移:解决障碍物后方合法区域无法瞬移的问题

问题分析

你当前的核心问题是:使用Raycast2D检测路径时,只会拦截第一个碰撞的障碍物,导致目标点在障碍物后方的合法区域时,玩家只能瞬移到障碍物前方,无法到达目标点。这本质是因为基础射线检测只关注路径上的首个碰撞体,没有区分「目标点是否本身可行走」和「路径是否被阻挡」两种情况。

解决方案

根据你的需求(避免瞬移进障碍物,同时处理障碍物后方的合法区域),提供三种针对性实现方案:


方案1:允许穿墙瞬移到后方合法区域

如果你的游戏逻辑允许玩家直接瞬移到视线外但可行走的区域(忽略中间障碍物),可以优先检测目标点是否可行走,而非路径是否畅通:

using UnityEngine;

public class PlayerTeleport : MonoBehaviour
{
    [Header("瞬移配置")]
    public float maxTeleportRange = 6f;
    public float cooldown = 2.5f;
    public LayerMask obstacleLayer;
    [Tooltip("玩家碰撞体半径,用于检测目标点是否会卡进障碍物")]
    public float playerColliderRadius = 0.3f;

    private float _cooldownTimer;
    private Camera _mainCam;

    void Start()
    {
        _mainCam = Camera.main;
        _cooldownTimer = 0f;
    }

    void Update()
    {
        // 更新冷却倒计时
        if (_cooldownTimer > 0)
        {
            _cooldownTimer -= Time.deltaTime;
            // 此处可添加冷却UI更新逻辑
        }

        // 空格键触发瞬移(冷却完成时)
        if (Input.GetKeyDown(KeyCode.Space) && _cooldownTimer <= 0)
        {
            AttemptTeleport();
        }
    }

    void AttemptTeleport()
    {
        // 转换鼠标屏幕坐标为世界坐标(保持Z轴与玩家一致)
        Vector3 mouseWorldPos = _mainCam.ScreenToWorldPoint(Input.mousePosition);
        mouseWorldPos.z = transform.position.z;

        // 限制瞬移距离不超过最大值
        Vector2 targetPos = Vector2.MoveTowards(transform.position, mouseWorldPos, maxTeleportRange);

        // 检测目标点是否被障碍物占据
        bool isTargetSafe = !Physics2D.OverlapCircle(targetPos, playerColliderRadius, obstacleLayer);

        if (isTargetSafe)
        {
            transform.position = targetPos;
            _cooldownTimer = cooldown;
        }
        else
        {
            // 目标点不可行,尝试瞬移到路径上最近的安全点
            RaycastHit2D hit = Physics2D.CircleCast(transform.position, playerColliderRadius, 
                                                  (targetPos - (Vector2)transform.position).normalized, 
                                                  maxTeleportRange, obstacleLayer);
            
            if (hit)
            {
                // 瞬移到障碍物前方(偏移半径避免卡墙)
                transform.position = hit.point - hit.normal * playerColliderRadius;
                _cooldownTimer = cooldown;
            }
        }
    }
}

方案2:仅允许瞬移到直线路径无阻挡的区域

如果你的游戏逻辑禁止穿墙,只允许瞬移到玩家视线内的可行区域,可以用CircleCast替代基础Raycast,模拟玩家的碰撞体积进行路径检测,确保瞬移后不会卡进障碍物:

// 复用方案1的类结构,仅修改AttemptTeleport方法
void AttemptTeleport()
{
    Vector3 mouseWorldPos = _mainCam.ScreenToWorldPoint(Input.mousePosition);
    mouseWorldPos.z = transform.position.z;

    Vector2 direction = (mouseWorldPos - transform.position).normalized;
    float distance = Vector2.Distance(transform.position, mouseWorldPos);
    float teleportDistance = Mathf.Min(distance, maxTeleportRange);

    // 用CircleCast检测玩家体积在路径上是否会碰撞障碍物
    RaycastHit2D hit = Physics2D.CircleCast(transform.position, playerColliderRadius, 
                                          direction, teleportDistance, obstacleLayer);

    if (hit)
    {
        // 瞬移到障碍物碰撞点的安全位置(偏移半径)
        transform.position = hit.point - direction * playerColliderRadius;
    }
    else
    {
        // 路径无阻挡,直接瞬移到目标点
        transform.position = (Vector2)transform.position + direction * teleportDistance;
    }

    _cooldownTimer = cooldown;
}

方案3:瞬移到任何可达的合法区域(复杂场景)

如果你的游戏场景有复杂的障碍物布局,需要瞬移到「绕路可达但直线被挡」的合法区域,可以使用Unity的2D导航系统(NavMesh2D)进行可达性检测:

  1. 先在Unity编辑器中烘焙2D导航网格(Window > AI > Navigation)
  2. 添加以下代码:
using UnityEngine;
using UnityEngine.AI;

public class PlayerTeleport : MonoBehaviour
{
    // 复用之前的配置参数...

    void AttemptTeleport()
    {
        Vector3 mouseWorldPos = _mainCam.ScreenToWorldPoint(Input.mousePosition);
        mouseWorldPos.z = transform.position.z;

        // 检测目标点是否在导航网格上(即可行走)
        if (NavMesh.SamplePosition(mouseWorldPos, out NavMeshHit hit, playerColliderRadius, NavMesh.AllAreas))
        {
            // 检测玩家到目标点的路径是否可达
            if (NavMesh.Raycast(transform.position, hit.position, out NavMeshHit pathHit, NavMesh.AllAreas))
            {
                // 路径被挡,瞬移到最近的可达点
                transform.position = pathHit.position;
            }
            else
            {
                // 路径可达,直接瞬移到目标点
                transform.position = hit.position;
            }
            _cooldownTimer = cooldown;
        }
    }
}

关键注意事项

  • 务必设置正确的LayerMask,确保只检测障碍物层级,避免误判玩家自身或其他可交互物体
  • playerColliderRadius要和玩家实际碰撞体的半径一致,防止瞬移后卡进障碍物
  • 冷却逻辑可以直接复用你已实现的部分,只需替换瞬移触发的核心检测代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:56:02