624 lines
20 KiB
C#
624 lines
20 KiB
C#
using System;
|
|
using BrewMonster.Common;
|
|
using BrewMonster.Network;
|
|
using BrewMonster.Scripts.Managers;
|
|
using BrewMonster.Scripts.Task;
|
|
using BrewMonster.UI;
|
|
using PerfectWorld.Scripts.Managers;
|
|
using PerfectWorld.Scripts.Task;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using BrewMonster.Scripts.UI;
|
|
namespace BrewMonster.Scripts.UI
|
|
{
|
|
public class CDlgAward : AUIDialog
|
|
{
|
|
#region FIELDS AND PROPERTIES
|
|
|
|
private const int MIN_ROW_COUNT_TO_SHOW = 6;
|
|
|
|
[SerializeField] private Button m_pBtn_Confirm;
|
|
[SerializeField] private Button _btnClose;
|
|
[SerializeField] private Vector2Int _itemSize = new Vector2Int(50, 50);
|
|
[SerializeField] private AwardItem _awardItemPrefab;
|
|
[SerializeField] private Transform _itemsParent;
|
|
[SerializeField] private Toggle[] _radioButtons;
|
|
|
|
[Header("ItemDetail")]
|
|
[SerializeField] GameObject itemInfoPanel;
|
|
[SerializeField] Vector2 itemInfoPanelOffset = new Vector2(20f, 0f);
|
|
|
|
[SerializeField] private TextOutlet descriptionOutlet;
|
|
[Header("DEBUG")]
|
|
[SerializeField] private AwardItem[] _awardItems;
|
|
|
|
bool m_bThroughNPC;
|
|
bool m_bSpendContribution;
|
|
|
|
#endregion
|
|
|
|
int _rowCount => _itemSize.x;
|
|
int _columnCount => _itemSize.y;
|
|
|
|
#region UNITY METHODS
|
|
|
|
void Awake()
|
|
{
|
|
SpawnItems();
|
|
InitRadioBtns();
|
|
// this is copy paste from InventoryUI.cs. so it have equip and drop button.
|
|
// temporary disable them
|
|
itemInfoPanel.SetActive(false);
|
|
itemInfoPanel.transform.GetChild(0).gameObject.SetActive(false);
|
|
m_pBtn_Confirm.onClick.AddListener(OnCommand_confirm);
|
|
}
|
|
public override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
// Refresh to toggle state.
|
|
foreach (var toggle in _radioButtons)
|
|
{
|
|
toggle.isOn = false;
|
|
}
|
|
if (itemInfoPanel != null)
|
|
{
|
|
itemInfoPanel.SetActive(false);
|
|
}
|
|
if (descriptionOutlet != null)
|
|
{
|
|
descriptionOutlet.Set(string.Empty);
|
|
}
|
|
_btnClose.onClick.AddListener(OnCloseDlgAward);
|
|
}
|
|
|
|
public override void OnDisable()
|
|
{
|
|
base.OnDisable();
|
|
_btnClose.onClick.RemoveListener(OnCloseDlgAward);
|
|
}
|
|
|
|
private void OnCloseDlgAward()
|
|
{
|
|
if (m_bThroughNPC)
|
|
GetGameUIMan().EndNPCService();
|
|
CloseDialogue();
|
|
}
|
|
|
|
private void InitRadioBtns()
|
|
{
|
|
for (int i = 0; i < _radioButtons.Length; i++)
|
|
{
|
|
_radioButtons[i].isOn = false;
|
|
_radioButtons[i].onValueChanged.AddListener( OnCommand_select );
|
|
}
|
|
|
|
}
|
|
|
|
private void SpawnItems()
|
|
{
|
|
for (int i = 1; i <= _rowCount; i++)
|
|
{
|
|
for (int j = 1; j <= _columnCount; j++)
|
|
{
|
|
AwardItem item = Instantiate(_awardItemPrefab ,_itemsParent);
|
|
item.SetPositionIndex(i, j);
|
|
// Positioning logic can be added here
|
|
}
|
|
}
|
|
_awardItems = new AwardItem[_rowCount];
|
|
_awardItems = GetComponentsInChildren<AwardItem>();
|
|
_awardItemPrefab.gameObject.SetActive(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
protected bool OnInitDialog()
|
|
{
|
|
m_bThroughNPC = false;
|
|
m_bSpendContribution = false;
|
|
return true;
|
|
}
|
|
|
|
public void UpdateAwardItem(ushort idTask, bool throughNPC, bool spendContribution = false)
|
|
{
|
|
AWARD_DATA ad = new AWARD_DATA();
|
|
CECTaskInterface pTask = GetHostPlayer().GetTaskInterface();
|
|
m_bThroughNPC = throughNPC;
|
|
m_bSpendContribution = spendContribution;
|
|
pTask.GetAwardCandidates(idTask, ref ad);
|
|
if (ad.m_ulCandItems > 1)
|
|
{
|
|
string szName = "";
|
|
string strFile = "";
|
|
string szText = "";
|
|
EC_IvtrItem pItem;
|
|
// AUICTranslate trans; // TODO
|
|
// int i, j, idItem, nNum;
|
|
uint idItem, nNum;
|
|
// Image pImage;
|
|
// Image pImage2;
|
|
AwardItem pImage; // Using AwardItem instead of Image
|
|
|
|
SetData(idTask, "");
|
|
// CheckRadioButton(1, 0); // TODO: maybe dont need this
|
|
m_pBtn_Confirm.gameObject.SetActive(false);
|
|
|
|
for (int i=1; i <= _rowCount; i++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d01", i);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break; // All done.
|
|
|
|
// TODO: Show/Hide radio button
|
|
// sprintf(out szName, "Rdo_Award%d", i);
|
|
// GetDlgItem(szName)->Show(false);
|
|
// _radioButtons[i-1].gameObject.SetActive(false);
|
|
GetToggleBtnAt(i).gameObject.SetActive(false);
|
|
|
|
// for (j = 1;; j++)
|
|
for (int j=1; j < _columnCount; j++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d%02d", i, j);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break; // Line done.
|
|
pImage = GetAwardItemAt(i, j);
|
|
if(!pImage) break;
|
|
|
|
if (i - 1 < (int)ad.m_ulCandItems &&
|
|
j - 1 < (int)ad.m_CandItems[i - 1].m_ulAwardItems)
|
|
{
|
|
idItem = ad.m_CandItems[i - 1].m_AwardItems[j - 1].m_ulItemTemplId;
|
|
pItem = EC_IvtrItem.CreateItem((int)idItem, 0, 1);
|
|
nNum = ad.m_CandItems[i - 1].m_AwardItems[j - 1].m_ulItemNum;
|
|
if (nNum > 1)
|
|
{
|
|
a_sprintf(out szText, _AL("%d"), nNum);
|
|
|
|
pImage.SetText(szText);
|
|
}
|
|
else
|
|
pImage.SetText(_AL(""));
|
|
|
|
pItem.GetDetailDataFromLocal();
|
|
string strDesc = pItem.GetDesc();
|
|
pImage.SetHint(strDesc); // TODO
|
|
pImage.SetOnClick(OnAwardItemClicked);
|
|
// af_GetFileTitle(pItem.GetIconFile(), strFile); // TODO
|
|
strFile.ToLower();
|
|
// TODO: Set the image cover here
|
|
// pImage.SetCover(GetGameUIMan().m_pA2DSpriteIcons[CECGameUIMan::ICONS_INVENTORY],
|
|
// GetGameUIMan().m_IconMap[CECGameUIMan::ICONS_INVENTORY][strFile]);
|
|
var sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(pItem.m_tid);
|
|
pImage.SetImage(sprite);
|
|
|
|
EC_IvtrEquip pEquip = (EC_IvtrEquip)EC_IvtrItem.CreateItem(pItem.m_tid, pItem.m_expire_date, pItem.m_iCount);
|
|
pImage.SetColor(
|
|
(pItem.IsEquipment() && pEquip.IsDestroying())
|
|
? new Color32(128, 128, 128, 255)
|
|
: new Color32(255, 255, 255, 255));
|
|
|
|
// delete pItem;
|
|
pItem = null;
|
|
|
|
// sprintf(out szName, "Rdo_Award%d", i);
|
|
// GetDlgItem(szName)->Show(true);
|
|
GetToggleBtnAt(i).gameObject.SetActive(true);
|
|
}
|
|
else
|
|
{
|
|
pImage.ClearCover();
|
|
pImage.SetHint(_AL(""));
|
|
pImage.SetText(_AL(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
// turn off uneccessary rows
|
|
if (ad.m_ulCandItems <= MIN_ROW_COUNT_TO_SHOW)
|
|
{
|
|
// ����������٣�������ʣ����
|
|
// for (i = MIN_ROW_COUNT_TO_SHOW + 1;; i++)
|
|
for (int i = MIN_ROW_COUNT_TO_SHOW + 1; i < _rowCount; i++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d01", i);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break; // All done.
|
|
pImage = GetAwardItemAt(i, 1);
|
|
if (!pImage) break;
|
|
|
|
// sprintf(out szName, "Rdo_Award%d", i);
|
|
// GetDlgItem(szName)->Show(false);
|
|
GetToggleBtnAt(i).gameObject.SetActive(false);
|
|
|
|
for (int j = 1; j < _columnCount; j++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d%02d", i, j);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break;
|
|
// pImage->Show(false);
|
|
pImage = GetAwardItemAt(i, j);
|
|
pImage.Show(false);
|
|
|
|
// pImage2 = (PAUIIMAGEPICTURE)GetDlgItem(AString().Format("Img_%02d%02d", i, j));
|
|
// pImage2->Show(false);
|
|
// pImage2 = GetAwardItemAt(i, j);
|
|
|
|
}
|
|
}
|
|
|
|
// ����ȷ�ϰ�ť��λ�ã����öԻ����С
|
|
// TODO: Thiết lập vị trí của nút xác nhận, thiết lập kích thước của hộp thoại.
|
|
// var pBtnConfirm = m_pBtn_Confirm;
|
|
// AUIObject* pImgLineMid = GetDlgItem(AString().Format("Item_%02d01", MIN_ROW_COUNT_TO_SHOW));
|
|
// AUIObject* pEdiBg = GetDlgItem("background");
|
|
// pBtnConfirm->SetPos(pBtnConfirm->GetPos(true).x,
|
|
// pImgLineMid->GetPos(true).y + pImgLineMid->GetSize().cy + 10);
|
|
// SetSize(GetSize().cx, pBtnConfirm->GetPos(true).y + pBtnConfirm->GetSize().cy + 30);
|
|
// pEdiBg->SetSize(pEdiBg->GetSize().cx, GetSize().cy - pEdiBg->GetPos(true).y - 25);
|
|
}
|
|
else
|
|
{
|
|
// ������ʾȫ���� / Show all rows
|
|
for (int i = MIN_ROW_COUNT_TO_SHOW + 1; i < _rowCount; i++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d01", i);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break; // All done.
|
|
pImage = GetAwardItemAt(i, 1);
|
|
if (!pImage) break; // All done.
|
|
|
|
if (ad.m_ulCandItems >= i)
|
|
{
|
|
// sprintf(out szName, "Rdo_Award%d", i);
|
|
// GetDlgItem(szName)->Show(true);
|
|
GetToggleBtnAt(i).gameObject.SetActive(true);
|
|
}
|
|
|
|
for (int j = 1; j < _columnCount; j++)
|
|
{
|
|
// sprintf(out szName, "Item_%02d%02d", i, j);
|
|
// pImage = (PAUIIMAGEPICTURE)GetDlgItem(szName);
|
|
// if (!pImage) break;
|
|
pImage = GetAwardItemAt(i, j);
|
|
if (!pImage) break;
|
|
pImage.Show(true);
|
|
|
|
// pImage2 = (PAUIIMAGEPICTURE)GetDlgItem(AString().Format("Img_%02d%02d", i, j));
|
|
// pImage2->Show(true);
|
|
// TODO: Handle pImage2 if needed
|
|
// AwardItem pImage2 = GetAwardItemAt(i, j);
|
|
// pImage2?.Show(true);
|
|
}
|
|
}
|
|
|
|
// ����ȷ�ϰ�ť��λ�ã����öԻ����С
|
|
// TODO: Thiết lập vị trí của nút xác nhận, thiết lập kích thước của hộp thoại.
|
|
// AUIObject* pBtnConfirm = GetDlgItem("Btn_Confirm");
|
|
// AUIObject* pImgLineLast = GetDlgItem(AString().Format("Item_%02d01", i - 1));
|
|
// AUIObject* pEdiBg = GetDlgItem("background");
|
|
// pBtnConfirm->SetPos(pBtnConfirm->GetPos(true).x,
|
|
// pImgLineLast->GetPos(true).y + pImgLineLast->GetSize().cy + 10);
|
|
// SetSize(GetSize().cx, pBtnConfirm->GetPos(true).y + pBtnConfirm->GetSize().cy + 30);
|
|
// pEdiBg->SetSize(pEdiBg->GetSize().cx, GetSize().cy - pEdiBg->GetPos(true).y - 25);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Debug: equipment slot template ids only; rebuild with <see cref="EC_IvtrItem.CreateItem"/> +
|
|
/// <see cref="EC_IvtrItem.GetDetailDataFromLocal"/> for local prop/description testing.
|
|
/// </summary>
|
|
public void TestItemInInventory()
|
|
{
|
|
m_bThroughNPC = false;
|
|
m_bSpendContribution = false;
|
|
if (m_pBtn_Confirm != null)
|
|
m_pBtn_Confirm.gameObject.SetActive(false);
|
|
|
|
for (int r = 1; r <= _rowCount; r++)
|
|
GetToggleBtnAt(r)?.gameObject.SetActive(false);
|
|
|
|
var host = GetHostPlayer();
|
|
var inv = host?.GetInventory(InventoryConst.IVTRTYPE_EQUIPPACK);
|
|
if (inv == null)
|
|
{
|
|
ClearAwardGridForLocalEquipmentTest(0);
|
|
return;
|
|
}
|
|
|
|
int capacity = _rowCount * _columnCount;
|
|
int filledCount = 0;
|
|
for (int slot = 0; slot < inv.GetSize(); slot++)
|
|
{
|
|
var eq = inv.GetItem(slot, false);
|
|
if (eq == null)
|
|
continue;
|
|
int tid = eq.GetTemplateID();
|
|
if (tid <= 0)
|
|
continue;
|
|
|
|
if (filledCount >= capacity)
|
|
break;
|
|
|
|
int row = filledCount / _columnCount + 1;
|
|
int col = filledCount % _columnCount + 1;
|
|
var pImage = GetAwardItemAt(row, col);
|
|
if (pImage == null)
|
|
break;
|
|
|
|
var pItem = EC_IvtrItem.CreateItem(tid, 0, 1);
|
|
if (pItem == null)
|
|
continue;
|
|
|
|
pItem.GetDetailDataFromLocal();
|
|
string strDesc = pItem.GetDesc();
|
|
pImage.SetHint(strDesc);
|
|
pImage.SetOnClick(OnAwardItemClicked);
|
|
var sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(pItem.m_tid);
|
|
pImage.SetImage(sprite);
|
|
|
|
var pEquip = (EC_IvtrEquip)EC_IvtrItem.CreateItem(pItem.m_tid, pItem.m_expire_date, pItem.m_iCount);
|
|
pImage.SetColor(
|
|
(pItem.IsEquipment() && pEquip.IsDestroying())
|
|
? new Color32(128, 128, 128, 255)
|
|
: new Color32(255, 255, 255, 255));
|
|
|
|
pImage.SetText(_AL(""));
|
|
pImage.Show(true);
|
|
filledCount++;
|
|
}
|
|
|
|
for (int idx = filledCount; idx < capacity; idx++)
|
|
{
|
|
int row = idx / _columnCount + 1;
|
|
int col = idx % _columnCount + 1;
|
|
var extra = GetAwardItemAt(row, col);
|
|
if (extra == null)
|
|
continue;
|
|
extra.ClearCover();
|
|
extra.SetHint(_AL(""));
|
|
extra.SetText(_AL(""));
|
|
}
|
|
|
|
int lastFilledRow = filledCount == 0 ? 0 : (filledCount - 1) / _columnCount + 1;
|
|
ClearAwardGridForLocalEquipmentTest(lastFilledRow);
|
|
}
|
|
|
|
void ClearAwardGridForLocalEquipmentTest(int lastFilledRow)
|
|
{
|
|
for (int i = lastFilledRow + 1; i <= _rowCount; i++)
|
|
{
|
|
GetToggleBtnAt(i)?.gameObject.SetActive(false);
|
|
for (int j = 1; j <= _columnCount; j++)
|
|
{
|
|
var pImage = GetAwardItemAt(i, j);
|
|
if (pImage == null)
|
|
break;
|
|
pImage.ClearCover();
|
|
pImage.SetHint(_AL(""));
|
|
pImage.SetText(_AL(""));
|
|
pImage.Show(true);
|
|
}
|
|
}
|
|
}
|
|
void sprintf(out string szName, string format, int i)
|
|
{
|
|
// Convert C-style format specifiers to C# format
|
|
// %02d -> zero-padded 2-digit integer (D2 format)
|
|
// %d -> regular integer
|
|
string csharpFormat = format
|
|
.Replace("%02d", "{0:D2}")
|
|
.Replace("%d", "{0}");
|
|
szName = string.Format(csharpFormat, i);
|
|
}
|
|
|
|
void sprintf(out string szName, string format, int i, int j)
|
|
{
|
|
// Convert C-style format specifiers to C# format for two parameters
|
|
// %02d -> zero-padded 2-digit integer (D2 format)
|
|
// %d -> regular integer
|
|
// Replace occurrences sequentially: first -> {0}, second -> {1}
|
|
string csharpFormat = format;
|
|
int paramIndex = 0;
|
|
csharpFormat = System.Text.RegularExpressions.Regex.Replace(csharpFormat, @"%02d|%d", match =>
|
|
{
|
|
string replacement = paramIndex == 0
|
|
? (match.Value == "%02d" ? "{0:D2}" : "{0}")
|
|
: (match.Value == "%02d" ? "{1:D2}" : "{1}");
|
|
paramIndex++;
|
|
return replacement;
|
|
});
|
|
szName = string.Format(csharpFormat, i, j);
|
|
}
|
|
|
|
void a_sprintf(out string szText, string format, uint nNum)
|
|
{
|
|
// Convert C-style format specifiers to C# format
|
|
// %d -> regular integer (for uint)
|
|
string csharpFormat = format.Replace("%d", "{0}");
|
|
szText = string.Format(csharpFormat, nNum);
|
|
}
|
|
|
|
string _AL(string text)
|
|
{
|
|
// Localization helper - returns the string as-is
|
|
// In C++ this would typically handle localization, but for now just return the text
|
|
return text;
|
|
}
|
|
|
|
AwardItem GetAwardItemAt(int row, int column)
|
|
{
|
|
foreach (var item in _awardItems)
|
|
{
|
|
if (item.Position.x == row &&
|
|
item.Position.y == column)
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Toggle GetToggleBtnAt(int row)
|
|
{
|
|
int index = row - 1;
|
|
if( index < 0 || index >= _radioButtons.Length)
|
|
return null;
|
|
return _radioButtons[index];
|
|
}
|
|
|
|
void OnCommand_select(bool toggleValue)
|
|
{
|
|
if(toggleValue) m_pBtn_Confirm.gameObject.SetActive(true);
|
|
}
|
|
|
|
void OnCommand_confirm()
|
|
{
|
|
// int i;
|
|
string szName;
|
|
Toggle pRadio;
|
|
uint idTask = GetData();
|
|
|
|
for( int i = 1; i <= _rowCount ; i++ )
|
|
{
|
|
// sprintf(szName, "Rdo_Award%d", i);
|
|
// pRadio = (PAUIRADIOBUTTON)GetDlgItem(szName);
|
|
// if( !pRadio || !pRadio->IsShow() ) break;
|
|
pRadio = GetToggleBtnAt(i);
|
|
if(!pRadio || !pRadio.gameObject.activeInHierarchy) return;
|
|
|
|
// if( pRadio.IsChecked() )
|
|
if( pRadio.isOn )
|
|
{
|
|
if (m_bThroughNPC) {
|
|
// GetGameSession().c2s_CmdNPCSevReturnTask(idTask, i - 1);
|
|
UnityGameSession.c2s_CmdNPCSevReturnTask((int)idTask, i - 1);
|
|
GetGameUIMan().EndNPCService();
|
|
} else if (m_bSpendContribution) {
|
|
// TODO: Implement spend contribution logic
|
|
// CECTaskInterface pTask = GetHostPlayer().GetTaskInterface();
|
|
// pTask.FinishTaskSpendingWorldContribution(idTask, i - 1);
|
|
} else {
|
|
// TODO: Implement choose award logic
|
|
// CECTaskInterface pTask = GetHostPlayer().GetTaskInterface();
|
|
// pTask.ChooseAward(idTask, i - 1);
|
|
}
|
|
CloseDialogue();
|
|
break;
|
|
}
|
|
}
|
|
|
|
itemInfoPanel.SetActive(false);
|
|
}
|
|
|
|
void OnAwardItemClicked(string hint, bool isOn, AwardItem awardItem)
|
|
{
|
|
if(isOn)
|
|
{
|
|
descriptionOutlet.Set(hint);
|
|
PositionDetailPanelNearButton(awardItem);
|
|
}
|
|
else
|
|
{
|
|
descriptionOutlet.Set("");
|
|
}
|
|
itemInfoPanel.SetActive(isOn);
|
|
RefreshLayout(itemInfoPanel);
|
|
}
|
|
private void PositionDetailPanelNearButton(AwardItem awardItem)
|
|
{
|
|
if (itemInfoPanel == null || awardItem == null)
|
|
{
|
|
return;
|
|
}
|
|
var panelRect = itemInfoPanel.transform as RectTransform;
|
|
if (panelRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var buttonRect = awardItem.transform as RectTransform;
|
|
if (buttonRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var canvas = panelRect.GetComponentInParent<Canvas>();
|
|
if (canvas == null)
|
|
{
|
|
return;
|
|
}
|
|
var parentRect = panelRect.parent as RectTransform;
|
|
if (parentRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Camera eventCamera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
|
|
Vector3 worldCenter = buttonRect.TransformPoint(buttonRect.rect.center);
|
|
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(eventCamera, worldCenter);
|
|
Vector2 localPoint;
|
|
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPoint, eventCamera, out localPoint))
|
|
{
|
|
return;
|
|
}
|
|
|
|
float btnHalfW = buttonRect.rect.width * 0.5f;
|
|
float panelW = panelRect.rect.width;
|
|
float panelH = panelRect.rect.height;
|
|
float pivotX = panelRect.pivot.x;
|
|
float pivotY = panelRect.pivot.y;
|
|
|
|
// Compute right-placement candidate (panel's left edge at button's right edge + offset)
|
|
// 计算右侧放置候选位置(面板左边缘在按钮右边缘 + 偏移量)
|
|
float leftEdgeRightPlacement = localPoint.x + btnHalfW + itemInfoPanelOffset.x;
|
|
float candidateXRight = leftEdgeRightPlacement + pivotX * panelW;
|
|
|
|
// Compute left-placement candidate (panel's right edge at button's left edge - offset)
|
|
// 计算左侧放置候选位置(面板右边缘在按钮左边缘 - 偏移量)
|
|
float rightEdgeLeftPlacement = localPoint.x - btnHalfW - itemInfoPanelOffset.x;
|
|
float candidateXLeft = rightEdgeLeftPlacement - (1f - pivotX) * panelW;
|
|
|
|
// Vertical clamping honoring pivot
|
|
// 垂直方向根据pivot进行限制
|
|
float minY = parentRect.rect.yMin + pivotY * panelH;
|
|
float maxY = parentRect.rect.yMax - (1f - pivotY) * panelH;
|
|
float candidateY = Mathf.Clamp(localPoint.y + itemInfoPanelOffset.y, minY, maxY);
|
|
|
|
// Choose side based on available space
|
|
// 根据可用空间选择放置侧
|
|
float rightEdgeOfRight = candidateXRight + (1f - pivotX) * panelW;
|
|
float canvasRight = parentRect.rect.xMax;
|
|
float canvasLeft = parentRect.rect.xMin;
|
|
float leftEdgeOfLeft = candidateXLeft - pivotX * panelW;
|
|
|
|
Vector2 finalPos;
|
|
if (rightEdgeOfRight <= canvasRight)
|
|
{
|
|
finalPos = new Vector2(candidateXRight, candidateY);
|
|
}
|
|
else if (leftEdgeOfLeft >= canvasLeft)
|
|
{
|
|
finalPos = new Vector2(candidateXLeft, candidateY);
|
|
}
|
|
else
|
|
{
|
|
// Fallback: clamp within canvas horizontally
|
|
// 回退:在画布内水平方向限制
|
|
float minX = canvasLeft + pivotX * panelW;
|
|
float maxX = canvasRight - (1f - pivotX) * panelW;
|
|
finalPos = new Vector2(Mathf.Clamp(candidateXRight, minX, maxX), candidateY);
|
|
}
|
|
|
|
panelRect.anchoredPosition = finalPos;
|
|
}
|
|
public void RefreshLayout(GameObject gameObject)
|
|
{
|
|
var parent = gameObject.GetComponent<RectTransform>();
|
|
|
|
// Force Unity to rebuild layout immediately
|
|
parent.ForceUpdateRectTransforms();
|
|
LayoutRebuilder.ForceRebuildLayoutImmediate(parent);
|
|
}
|
|
}
|
|
}
|