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

Unity中2D地图缩放时玩家图标位置偏移问题排查与解决

3D游戏2D地图玩家图标缩放偏移问题解决

问题描述

我正在为3D游戏制作一款非迷你型2D地图,希望该地图可独立于游戏环境尺寸进行缩放,同时能在2D地图上以图标形式精准显示玩家位置。当前使用脚本时,玩家图标位置大致正确,但缩放地图时图标位置会发生偏移,期望调整2D地图大小(放大或缩小)时,玩家图标能保持与缩放前一致的对应位置。

原始脚本

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

public class Finalmap : MonoBehaviour
{
    public Transform mapIcon;

    private Vector2 mapOffsetInUnits;
    private float mapScaleFactor;

    public GameObject mapPlane1;
    public GameObject mapPlane2;
    public GameObject player;

    void Start()
    {
        Renderer mapRenderer1 = mapPlane1.GetComponent<Renderer>();
        Vector3 mapSize1 = mapRenderer1.bounds.size;
        float mapWidth1 = mapSize1.x;
        float mapHeight1 = mapSize1.z;

        Renderer mapRenderer2 = mapPlane2.GetComponent<Renderer>();
        Vector3 mapSize2 = mapRenderer2.bounds.size;
        float mapWidth2 = mapSize2.x;
        float mapHeight2 = mapSize2.z;

        mapScaleFactor = Mathf.Min(mapWidth2 / mapWidth1, mapHeight2 / mapHeight1);

        float offsetX = (mapWidth2  - mapWidth1 * mapScaleFactor) / 2f;
        float offsetY = (mapHeight2  - mapHeight1 * mapScaleFactor) / 2f;
        mapOffsetInUnits = new Vector2(offsetX, offsetY);

        Debug.Log("Map offset in units: " + mapOffsetInUnits);

        Debug.Log("Map 1 width: " + mapWidth1 + ", height: " + mapHeight1);
        Debug.Log("Map 2 width: " + mapWidth2 + ", height: " + mapHeight2);
        player = GameObject.FindGameObjectWithTag("Player");
    }
    void Update()
    {
        Vector3 playerPos = player.transform.position;
        Vector3 mapPlane1Pos = mapPlane1.transform.position;
        Quaternion mapPlane1Rot = mapPlane1.transform.rotation;
        Vector3 mapPlane2Pos = mapPlane2.transform.position;
        Quaternion mapPlane2Rot = mapPlane2.transform.rotation;

        float x = ((playerPos.x - mapPlane1Pos.x) * Mathf.Cos(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) + (playerPos.z - mapPlane1Pos.z) * Mathf.Sin(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) - mapOffsetInUnits.x) * mapScaleFactor;
        float y = ((playerPos.z - mapPlane1Pos.z) * Mathf.Cos(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) - (playerPos.x - mapPlane1Pos.x) * Mathf.Sin(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) - mapOffsetInUnits.y) * mapScaleFactor;

        Vector3 iconPos = new Vector3(y, 0f,-x);
        iconPos = Quaternion.Euler(0f, -mapPlane2Rot.eulerAngles.y, 0f) * iconPos + mapPlane2Pos;

        mapIcon.position = iconPos ;
    }
}

问题原因

原始脚本仅在Start()方法中计算一次mapScaleFactor和mapOffsetInUnits,当缩放mapPlane2时,它的实时尺寸会变化,但这两个关键值不会更新,导致后续计算的图标位置出现偏移。此外,直接使用Renderer.bounds.size获取mapPlane1的尺寸会受其自身缩放影响,可能导致基准尺寸计算错误。

修改后的脚本

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

public class Finalmap : MonoBehaviour
{
    public Transform mapIcon;
    public GameObject mapPlane1;
    public GameObject mapPlane2;
    public GameObject player;

    // 缓存mapPlane1的原始尺寸(不受缩放影响)
    private Vector3 originalMapSize1;

    void Start()
    {
        if (mapPlane1 != null)
        {
            Renderer mapRenderer1 = mapPlane1.GetComponent<Renderer>();
            // 计算原始尺寸:当前bounds尺寸除以物体缩放比例
            originalMapSize1 = mapRenderer1.bounds.size / mapPlane1.transform.localScale.x;
        }
        player = GameObject.FindGameObjectWithTag("Player");
    }

    void Update()
    {
        // 空值检查,避免运行时报错
        if (player == null || mapPlane1 == null || mapPlane2 == null) return;

        // 实时计算mapPlane1的当前尺寸(原始尺寸 * 缩放比例)
        Vector3 mapSize1 = originalMapSize1 * mapPlane1.transform.localScale.x;
        float mapWidth1 = mapSize1.x;
        float mapHeight1 = mapSize1.z;

        // 实时获取mapPlane2的当前尺寸
        Renderer mapRenderer2 = mapPlane2.GetComponent<Renderer>();
        Vector3 mapSize2 = mapRenderer2.bounds.size;
        float mapWidth2 = mapSize2.x;
        float mapHeight2 = mapSize2.z;

        // 实时更新缩放因子和偏移量
        float mapScaleFactor = Mathf.Min(mapWidth2 / mapWidth1, mapHeight2 / mapHeight1);
        float offsetX = (mapWidth2 - mapWidth1 * mapScaleFactor) / 2f;
        float offsetY = (mapHeight2 - mapHeight1 * mapScaleFactor) / 2f;
        Vector2 mapOffsetInUnits = new Vector2(offsetX, offsetY);

        Vector3 playerPos = player.transform.position;
        Vector3 mapPlane1Pos = mapPlane1.transform.position;
        Quaternion mapPlane1Rot = mapPlane1.transform.rotation;
        Vector3 mapPlane2Pos = mapPlane2.transform.position;
        Quaternion mapPlane2Rot = mapPlane2.transform.rotation;

        // 拆分玩家相对位置计算,提升可读性
        float playerLocalX = (playerPos.x - mapPlane1Pos.x) * Mathf.Cos(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) + 
                             (playerPos.z - mapPlane1Pos.z) * Mathf.Sin(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad);
        float playerLocalZ = (playerPos.z - mapPlane1Pos.z) * Mathf.Cos(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad) - 
                             (playerPos.x - mapPlane1Pos.x) * Mathf.Sin(mapPlane1Rot.eulerAngles.y * Mathf.Deg2Rad);

        // 转换到mapPlane2坐标系,应用实时缩放和偏移
        float x = (playerLocalX - mapOffsetInUnits.x) * mapScaleFactor;
        float y = (playerLocalZ - mapOffsetInUnits.y) * mapScaleFactor;

        Vector3 iconPos = new Vector3(y, 0f, -x);
        iconPos = Quaternion.Euler(0f, -mapPlane2Rot.eulerAngles.y, 0f) * iconPos + mapPlane2Pos;

        mapIcon.position = iconPos;
    }
}

关键修改点

  • 缓存原始尺寸:在Start()中缓存mapPlane1的原始尺寸,避免其自身缩放影响基准尺寸计算
  • 实时更新参数:将缩放因子和偏移量的计算移到Update()中,确保每次地图缩放时都能获取最新的尺寸值
  • 空值安全检查:增加空值判断,防止运行时出现空引用异常
  • 代码可读性优化:拆分玩家位置计算逻辑,便于后续维护

内容的提问来源于stack exchange,提问作者Shyama prasad Tripathy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:47:44