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

Unity Pacman左右移动碰撞后卡死及部分方向穿模问题求助

Pacman游戏开发问题

问题现象

  • 角色左右移动时,一旦碰撞到Collider就会卡死,无法向任何方向移动,不确定是Collider还是移动脚本的问题。
  • 若将Pacman对象的Circle Collider 2D组件的Is Trigger勾选为true,角色可移动但出现新问题:向左、向下移动时会穿透Collider并卡在内部,向右、向上移动则正常。

Pacman移动脚本

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

public class PacmanMove : MonoBehaviour
{
    public float speed = 0.4f;
    Vector2 dest = Vector2.zero;

    void Start()
    {
        // 初始目标位置设为当前位置
        dest = transform.position;
    }

    void FixedUpdate()
    {
        // 向目标位置移动
        Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
        GetComponent<Rigidbody2D>().MovePosition(p);

        // 只有到达目标位置时才响应输入
        if ((Vector2)transform.position == dest)
        {
            if (Input.GetKey(KeyCode.UpArrow) && valid(Vector2.up))
                dest = (Vector2)transform.position + Vector2.up;
            if (Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right))
                dest = (Vector2)transform.position + Vector2.right;
            if (Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up))
                dest = (Vector2)transform.position - Vector2.up;
            if (Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right))
                dest = (Vector2)transform.position - Vector2.right;
        }
    }

    // 检测移动方向是否合法
    bool valid(Vector2 dir)
    {
        // 从Pacman相邻位置向自身发射射线
        Vector2 pos = transform.position;
        RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);
        // 仅当射线只检测到自身Collider时,方向合法
        return (hit.collider == GetComponent<Collider2D>());
    }
}

自动添加墙体Collider2D的脚本(疑似存在问题)

using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Tilemaps;

public class AddBoxCollidersToWalls : MonoBehaviour
{
    public Sprite wallSprite;
    public float colliderSize = 1.5f; // BoxCollider2D的尺寸参数

    void Start()
    {
        if (wallSprite == null)
        {
            Debug.LogError("未设置WallSprite引用!请在Inspector面板中指定墙体精灵。");
            return;
        }

        AddCollidersToWallTiles();
    }

    // 为墙体瓦片添加Collider
    private void AddCollidersToWallTiles()
    {
        List<List<Vector2Int>> wallTileRegions = FindWallTileRegions();

        foreach (List<Vector2Int> region in wallTileRegions)
        {
            Vector3 colliderCenter = GetColliderCenter(region);
            Vector2 colliderSize = GetColliderSize(region);

            CreateColliderObject(colliderCenter, colliderSize);
        }
    }

    // 查找墙体瓦片区域
    private List<List<Vector2Int>> FindWallTileRegions()
    {
        List<List<Vector2Int>> wallTileRegions = new List<List<Vector2Int>>();
        Texture2D texture = wallSprite.texture;
        int width = texture.width;
        int height = texture.height;
        Color[] pixels = texture.GetPixels();

        bool[,] visited = new bool[width, height];

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                int pixelIndex = y * width + x;

                // 跳过透明像素或已访问像素
                if (pixels[pixelIndex].a == 0 || visited[x, y])
                    continue;

                List<Vector2Int> region = new List<Vector2Int>();
                FloodFill(x, y, pixels, width, height, ref visited, ref region);
                wallTileRegions.Add(region);
            }
        }

        return wallTileRegions;
    }

    // 洪水填充算法,标记连通的墙体像素区域
    private void FloodFill(int x, int y, Color[] pixels, int width, int height, ref bool[,] visited, ref List<Vector2Int> region)
    {
        if (x < 0 || x >= width || y < 0 || y >= height || visited[x, y] || pixels[y * width + x].a == 0)
            return;

        visited[x, y] = true;
        region.Add(new Vector2Int(x, y));

        FloodFill(x + 1, y, pixels, width, height, ref visited, ref region);
        FloodFill(x - 1, y, pixels, width, height, ref visited, ref region);
        FloodFill(x, y + 1, pixels, width, height, ref visited, ref region);
        FloodFill(x, y - 1, pixels, width, height, ref visited, ref region);
    }

    // 计算Collider的中心位置
    private Vector3 GetColliderCenter(List<Vector2Int> region)
    {
        float sumX = 0f;
        float sumY = 0f;

        foreach (Vector2Int tilePosition in region)
        {
            sumX += tilePosition.x;
            sumY += tilePosition.y;
        }

        float centerX = sumX / region.Count;
        float centerY = sumY / region.Count;

        return new Vector3(centerX / wallSprite.pixelsPerUnit, centerY / wallSprite.pixelsPerUnit, 0f);
    }

    // 计算Collider的尺寸
    private Vector2 GetColliderSize(List<Vector2Int> region)
    {
        int minX = int.MaxValue;
        int minY = int.MaxValue;
        int maxX = int.MinValue;
        int maxY = int.MinValue;

        foreach (Vector2Int tilePosition in region)
        {
            minX = Mathf.Min(minX, tilePosition.x);
            minY = Mathf.Min(minY, tilePosition.y);
            maxX = Mathf.Max(maxX, tilePosition.x);
            maxY = Mathf.Max(maxY, tilePosition.y);
        }

        float tileSizeX = maxX - minX + 1;
        float tileSizeY = maxY - minY + 1;

        // 计算瓦片间的间隙并调整尺寸
        float gapX = Mathf.Max(0f, (tileSizeX - 1) * (1f - colliderSize));
        float gapY = Mathf.Max(0f, (tileSizeY - 1) * (1f - colliderSize));

        return new Vector2((tileSizeX + gapX) / wallSprite.pixelsPerUnit, (tileSizeY + gapY) / wallSprite.pixelsPerUnit);
    }

    // 创建带有Collider的游戏对象
    private void CreateColliderObject(Vector3 center, Vector2 size)
    {
        GameObject colliderObject = new GameObject("WallTileCollider");
        colliderObject.transform.parent = transform;
        colliderObject.transform.localPosition = center;

        BoxCollider2D collider = colliderObject.AddComponent<BoxCollider2D>();
        collider.size = size * colliderSize;
    }
}

相关场景截图

  • Is Trigger开启后向左移动的Pacman:Is Trigger开启后Pacman向左移动穿透Collider
  • Pacman对象Inspector设置:Pacman对象Inspector设置
  • 带Collider的场景地图:带Collider的Pacman场景地图
  • 碰撞后卡死的Pacman:碰撞后卡死的Pacman
  • Wall Collider2D Inspector设置:Wall Collider2D Inspector设置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:57:02