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

Unity中UI元素在Start与Update实例化时位置偏移问题排查

问题解决方案

位置偏移的核心原因是UI元素的RectTransform应该使用anchoredPosition设置位置,而非localPosition。Unity的UI系统中,RectTransform的位置是基于锚点和父容器的布局规则计算的,直接修改localPosition会忽略UI布局逻辑,导致不同时机实例化的物体出现位置偏差。

同时你的代码存在两处可优化点:

  • Start和Update中的实例化逻辑重复,可提取为复用方法
  • Update中全量遍历容器效率低下,建议改为监听Inventory的变更事件(可选)

修改后的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;

public class DisplayInvScript : MonoBehaviour
{
    public int X_SPACE_BETWEEN_ITEMS, NUMBER_OF_COLUMNS, Y_SPACE_BETWEEN_ITEMS;
    public GameObject prefab;
    public Inventory inventory;
    public int x_START, Y_START;

    private Dictionary<InventorySlot, GameObject> itemsDisplayed = new Dictionary<InventorySlot, GameObject>();
    
    void Start()
    {
        RefreshInventoryDisplay();
    }
    
    void Update()
    {
        RefreshInventoryDisplay();
    }

    // 提取复用的刷新逻辑
    private void RefreshInventoryDisplay()
    {
        for (int i = 0; i < inventory.Container.Count; i++)
        {
            var slot = inventory.Container[i];
            if(itemsDisplayed.TryGetValue(slot, out var existingObj))
            {
                // 更新数量文本
                var amtText = existingObj.transform.Find("Item Amt")?.GetComponent<TextMeshProUGUI>();
                if(amtText != null)
                {
                    amtText.text = slot.amount.ToString();
                }
            }
            else
            {
                // 实例化新物品UI
                var obj = Instantiate(prefab, transform);
                var rectTransform = obj.GetComponent<RectTransform>();
                // 使用anchoredPosition设置UI位置
                rectTransform.anchoredPosition = GetAnchoredPosition(i);
                
                // 设置物品信息
                SetItemUI(obj, slot);
                
                itemsDisplayed.Add(slot, obj);
            }
        }
    }

    // 设置物品UI文本的复用方法
    private void SetItemUI(GameObject uiObj, InventorySlot slot)
    {
        var nameText = uiObj.transform.Find("Item Name")?.GetComponent<TextMeshProUGUI>();
        if(nameText != null) nameText.text = slot.item.idName;
        
        var amtText = uiObj.transform.Find("Item Amt")?.GetComponent<TextMeshProUGUI>();
        if(amtText != null) amtText.text = slot.amount.ToString();
        
        var typeText = uiObj.transform.Find("Item Type")?.GetComponent<TextMeshProUGUI>();
        if(typeText != null) typeText.text = slot.item.type.ToString();
    }

    // 返回基于锚点的位置
    public Vector2 GetAnchoredPosition(int i)
    {
        float xPos = x_START + (X_SPACE_BETWEEN_ITEMS * (i % NUMBER_OF_COLUMNS));
        float yPos = Y_START + (-Y_SPACE_BETWEEN_ITEMS * (i / NUMBER_OF_COLUMNS));
        return new Vector2(xPos, yPos);
    }
}

关键修改点说明

  • 替换localPosition为anchoredPosition:这是UI元素位置设置的正确方式,确保位置计算基于父容器的锚点规则,彻底消除不同时机实例化的位置偏差
  • 提取复用方法:把重复的实例化和文本设置逻辑抽成独立方法,提升代码可读性和维护性
  • 使用TryGetValue优化字典访问:减少一次字典查找操作,提升运行效率
  • 用transform.Find替代子物体遍历:直接通过名称定位目标文本组件,比遍历所有子Transform更高效

额外优化建议

  1. 移除Update中的全量刷新:改为在Inventory的物品添加/移除/数量变更时触发刷新事件,避免每帧遍历容器,大幅提升性能
  2. 统一预制体锚点设置:检查预制体的RectTransform锚点是否与父容器匹配,建议设置为(0,1)(左上角对齐),与你的x_START和Y_START布局逻辑匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 14:20:16