diff --git a/Assets/GameMain/Scripts/Event/RepoForm/RepoItemClickedEventArgs.cs b/Assets/GameMain/Scripts/Event/RepoForm/RepoItemClickedEventArgs.cs index d6ecce0..b7e97e2 100644 --- a/Assets/GameMain/Scripts/Event/RepoForm/RepoItemClickedEventArgs.cs +++ b/Assets/GameMain/Scripts/Event/RepoForm/RepoItemClickedEventArgs.cs @@ -12,20 +12,20 @@ namespace GeometryTD.CustomEvent public long ItemId { get; private set; } - public Vector3 TargetPos { get; private set; } + public Vector2 ScreenPosition { get; private set; } - public static RepoItemClickedEventArgs Create(long itemId, Vector3 targetPos) + public static RepoItemClickedEventArgs Create(long itemId, Vector2 screenPosition) { RepoItemClickedEventArgs args = ReferencePool.Acquire(); args.ItemId = itemId; - args.TargetPos = targetPos; + args.ScreenPosition = screenPosition; return args; } public override void Clear() { ItemId = 0; - TargetPos = Vector3.zero; + ScreenPosition = Vector2.zero; } } } diff --git a/Assets/GameMain/Scripts/UI/Combat/Controller/CombatFinishFormController.cs b/Assets/GameMain/Scripts/UI/Combat/Controller/CombatFinishFormController.cs index fa1dd43..3e24416 100644 --- a/Assets/GameMain/Scripts/UI/Combat/Controller/CombatFinishFormController.cs +++ b/Assets/GameMain/Scripts/UI/Combat/Controller/CombatFinishFormController.cs @@ -390,7 +390,7 @@ namespace GeometryTD.UI TypeText = seed.TypeText, Description = seed.Description ?? string.Empty, Price = 0, - TargetPos = args.TargetPos, + ScreenPosition = args.ScreenPosition, Tags = CloneTags(seed.Tags), TagRuntimes = CloneTagRuntimes(seed.TagRuntimes) }); diff --git a/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs b/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs index e23b3ad..0359514 100644 --- a/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs +++ b/Assets/GameMain/Scripts/UI/Game/Controller/RepoFormController.cs @@ -152,7 +152,7 @@ namespace GeometryTD.UI TypeText = seed.TypeText, Description = seed.Description ?? string.Empty, Price = 0, - TargetPos = args.TargetPos, + ScreenPosition = args.ScreenPosition, Tags = CloneTags(seed.Tags), TagRuntimes = CloneTagRuntimes(seed.TagRuntimes) }); diff --git a/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs b/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs index 88f5e70..e63ad3d 100644 --- a/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs +++ b/Assets/GameMain/Scripts/UI/Game/View/RepoItem.cs @@ -120,7 +120,7 @@ namespace GeometryTD.UI return; } - GameEntry.Event.Fire(this, RepoItemClickedEventArgs.Create(_context.InstanceId, transform.position)); + GameEntry.Event.Fire(this, RepoItemClickedEventArgs.Create(_context.InstanceId, ResolveScreenPosition())); } public void OnBeginDrag(PointerEventData eventData) @@ -239,6 +239,25 @@ namespace GeometryTD.UI } } + private Vector2 ResolveScreenPosition() + { + RectTransform rectTransform = transform as RectTransform; + if (rectTransform == null) + { + return Vector2.zero; + } + + Canvas canvas = GetComponentInParent(); + Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null; + Camera uiCamera = null; + if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay) + { + uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main; + } + + return RectTransformUtility.WorldToScreenPoint(uiCamera, rectTransform.position); + } + private RectTransform ResolveDragRoot() { Canvas canvas = GetComponentInParent(); @@ -348,4 +367,3 @@ namespace GeometryTD.UI } } } - diff --git a/Assets/GameMain/Scripts/UI/General/Context/ItemDescFormContext.cs b/Assets/GameMain/Scripts/UI/General/Context/ItemDescFormContext.cs index f3ce428..8a4fab5 100644 --- a/Assets/GameMain/Scripts/UI/General/Context/ItemDescFormContext.cs +++ b/Assets/GameMain/Scripts/UI/General/Context/ItemDescFormContext.cs @@ -8,7 +8,7 @@ namespace GeometryTD.UI public string TypeText; public string Description; public int Price; - public Vector3 TargetPos; + public Vector2 ScreenPosition; public TagItemContext[] Tags; } } diff --git a/Assets/GameMain/Scripts/UI/General/Controller/ItemDescFormController.cs b/Assets/GameMain/Scripts/UI/General/Controller/ItemDescFormController.cs index b6478d9..ad13ff1 100644 --- a/Assets/GameMain/Scripts/UI/General/Controller/ItemDescFormController.cs +++ b/Assets/GameMain/Scripts/UI/General/Controller/ItemDescFormController.cs @@ -34,7 +34,7 @@ namespace GeometryTD.UI TypeText = rawData.TypeText, Description = BuildDescription(rawData), Price = rawData.Price, - TargetPos = rawData.TargetPos, + ScreenPosition = rawData.ScreenPosition, Tags = BuildTags(rawData) }; } diff --git a/Assets/GameMain/Scripts/UI/General/RawData/ItemDescFormRawData.cs b/Assets/GameMain/Scripts/UI/General/RawData/ItemDescFormRawData.cs index 5ba738d..439bf12 100644 --- a/Assets/GameMain/Scripts/UI/General/RawData/ItemDescFormRawData.cs +++ b/Assets/GameMain/Scripts/UI/General/RawData/ItemDescFormRawData.cs @@ -9,7 +9,7 @@ namespace GeometryTD.UI public string TypeText; public string Description; public int Price; - public Vector3 TargetPos; + public Vector2 ScreenPosition; public TagType[] Tags; public TagRuntimeData[] TagRuntimes; } diff --git a/Assets/GameMain/Scripts/UI/General/View/ItemDescForm.cs b/Assets/GameMain/Scripts/UI/General/View/ItemDescForm.cs index 83fbc7c..c8e4116 100644 --- a/Assets/GameMain/Scripts/UI/General/View/ItemDescForm.cs +++ b/Assets/GameMain/Scripts/UI/General/View/ItemDescForm.cs @@ -14,6 +14,8 @@ namespace GeometryTD.UI [SerializeField] private TMP_Text _itemTypeText; + [SerializeField] private RectTransform _descScrollView; + [SerializeField] private TMP_Text _itemDescription; [SerializeField] private TMP_Text _itemPrice; @@ -30,6 +32,8 @@ namespace GeometryTD.UI [SerializeField] private float _fixedWidth = 300f; + [SerializeField] private float _maxDescriptionViewportHeight = 0f; + [SerializeField] private float _screenEdgePadding = 0f; [SerializeField] private float _sideGap = 16f; @@ -45,7 +49,7 @@ namespace GeometryTD.UI private readonly List _runtimeTagItems = new List(); private ItemDescFormContext _context; - private Vector3 _targetPos; + private Vector2 _targetPos; private bool _isOpened; public void RefreshUI(ItemDescFormContext context) @@ -58,7 +62,7 @@ namespace GeometryTD.UI _context = context; - _targetPos = ConvertWorldToAnchored(_context.TargetPos); + _targetPos = ConvertScreenToAnchored(_context.ScreenPosition); if (_content != null) { @@ -146,16 +150,62 @@ namespace GeometryTD.UI } _itemDescription.ForceMeshUpdate(); - var descriptionSize = _itemDescription.rectTransform.sizeDelta; - descriptionSize.y = _itemDescription.preferredHeight; - _itemDescription.rectTransform.sizeDelta = descriptionSize; float descriptionHeight = Mathf.Max(0f, _itemDescription.preferredHeight); - float targetHeight = _fixedTopHeight + descriptionHeight + _fixedBottomHeight; + SetRectHeight(_itemDescription.rectTransform, descriptionHeight); + + RectTransform scrollContent = _itemDescription.rectTransform.parent as RectTransform; + if (scrollContent != null) + { + SetRectHeight(scrollContent, descriptionHeight); + } + + float viewportHeight = descriptionHeight; + if (_descScrollView != null) + { + viewportHeight = Mathf.Min(descriptionHeight, ResolveMaxDescriptionViewportHeight()); + SetRectHeight(_descScrollView, viewportHeight); + + if (_descScrollView.TryGetComponent(out ScrollRect scrollRect)) + { + scrollRect.verticalNormalizedPosition = 1f; + } + } + + float targetHeight = _fixedTopHeight + viewportHeight + _fixedBottomHeight; Vector2 size = new Vector2(_fixedWidth, targetHeight); _content.sizeDelta = size; } + private float ResolveMaxDescriptionViewportHeight() + { + float maxViewportHeight = _maxDescriptionViewportHeight; + RectTransform parent = _content.parent as RectTransform; + if (parent != null) + { + float availableHeight = parent.rect.height - _fixedTopHeight - _fixedBottomHeight + - _screenEdgePadding * 2f; + availableHeight = Mathf.Max(0f, availableHeight); + maxViewportHeight = maxViewportHeight > 0f + ? Mathf.Min(maxViewportHeight, availableHeight) + : availableHeight; + } + + return maxViewportHeight > 0f ? maxViewportHeight : float.MaxValue; + } + + private static void SetRectHeight(RectTransform rectTransform, float height) + { + if (rectTransform == null) + { + return; + } + + Vector2 size = rectTransform.sizeDelta; + size.y = height; + rectTransform.sizeDelta = size; + } + private void ApplySideAnchoredTargetPos() { if (_content == null) @@ -207,20 +257,20 @@ namespace GeometryTD.UI finalX = minX <= maxX ? Mathf.Clamp(leftCandidateX, minX, maxX) : 0f; } - _content.anchoredPosition3D = new Vector3(finalX, clampedY, _targetPos.z); + _content.anchoredPosition = new Vector2(finalX, clampedY); } - private Vector3 ConvertWorldToAnchored(Vector3 worldPos) + private Vector2 ConvertScreenToAnchored(Vector2 screenPosition) { if (_content == null) { - return worldPos; + return screenPosition; } RectTransform parent = _content.parent as RectTransform; if (parent == null) { - return worldPos; + return screenPosition; } Canvas canvas = _content.GetComponentInParent(); @@ -231,15 +281,13 @@ namespace GeometryTD.UI uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main; } - Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPos); - if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, screenPoint, uiCamera, + if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, screenPosition, uiCamera, out Vector2 localPoint)) { - return worldPos; + return screenPosition; } - float z = _content.anchoredPosition3D.z; - return new Vector3(localPoint.x, localPoint.y, z); + return localPoint; } private static bool IsInsideBounds(float value, float min, float max) diff --git a/Assets/GameMain/UI/UIForms/ItemDescForm.prefab b/Assets/GameMain/UI/UIForms/ItemDescForm.prefab index 3981cf2..5737942 100644 --- a/Assets/GameMain/UI/UIForms/ItemDescForm.prefab +++ b/Assets/GameMain/UI/UIForms/ItemDescForm.prefab @@ -29,8 +29,8 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 7543853000780573685} - {fileID: 1164222149922529831} + - {fileID: 7543853000780573685} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -53,6 +53,7 @@ MonoBehaviour: _content: {fileID: 7543853000780573685} _itemTitle: {fileID: 103163210424128144} _itemTypeText: {fileID: 7330401870143477410} + _descScrollView: {fileID: 5256025271502919839} _itemDescription: {fileID: 6102478747521363808} _itemPrice: {fileID: 3372850760549036539} _tagAreaParent: {fileID: 5974414274741800357} @@ -62,6 +63,7 @@ MonoBehaviour: _fixedTopHeight: 300 _fixedBottomHeight: 200 _fixedWidth: 800 + _maxDescriptionViewportHeight: 1000 _screenEdgePadding: 0 _sideGap: 16 _anchorItemWidth: 0 @@ -163,9 +165,9 @@ RectTransform: m_Children: - {fileID: 2312726378312491185} - {fileID: 2131471118485666816} - - {fileID: 8691033862686863159} - {fileID: 4626694267165989230} - {fileID: 5974414274741800357} + - {fileID: 5256025271502919839} m_Father: {fileID: 6368625289863409235} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} @@ -221,8 +223,83 @@ CanvasGroup: m_Enabled: 1 m_Alpha: 1 m_Interactable: 0 - m_BlocksRaycasts: 0 + m_BlocksRaycasts: 1 m_IgnoreParentGroups: 0 +--- !u!1 &2412012916441856513 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7881731673096662725} + - component: {fileID: 7298444941623630875} + - component: {fileID: 1112425355264495322} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7881731673096662725 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2412012916441856513} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4517295342785105071} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7298444941623630875 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2412012916441856513} + m_CullTransparentMesh: 1 +--- !u!114 &1112425355264495322 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2412012916441856513} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!1 &2619564942114189097 GameObject: m_ObjectHideFlags: 0 @@ -523,11 +600,11 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 7543853000780573685} + m_Father: {fileID: 551487876586793062} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} - m_AnchoredPosition: {x: 0.00000036508, y: -250} + m_AnchoredPosition: {x: 8.5, y: 0} m_SizeDelta: {x: 740, y: 500} m_Pivot: {x: 0.5, y: 1} --- !u!222 &4798810177026917072 @@ -552,7 +629,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 0 + m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 m_OnCullStateChanged: @@ -628,6 +705,371 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &5204679832914275170 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4517295342785105071} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4517295342785105071 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5204679832914275170} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 7881731673096662725} + m_Father: {fileID: 3420065201910878183} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &6212260387770401143 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 551487876586793062} + m_Layer: 5 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &551487876586793062 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6212260387770401143} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 8691033862686863159} + m_Father: {fileID: 5512843442966546476} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0.00007219115} + m_SizeDelta: {x: 0, y: 300} + m_Pivot: {x: 0, y: 1} +--- !u!1 &6492817632056103165 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3420065201910878183} + - component: {fileID: 5652751692287784381} + - component: {fileID: 8483010013983203406} + - component: {fileID: 8645065629472015101} + m_Layer: 5 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3420065201910878183 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6492817632056103165} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4517295342785105071} + m_Father: {fileID: 5256025271502919839} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: -17} + m_Pivot: {x: 1, y: 1} +--- !u!222 &5652751692287784381 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6492817632056103165} + m_CullTransparentMesh: 1 +--- !u!114 &8483010013983203406 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6492817632056103165} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &8645065629472015101 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6492817632056103165} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1112425355264495322} + m_HandleRect: {fileID: 7881731673096662725} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &7502327435573383977 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5512843442966546476} + - component: {fileID: 3800436505147399416} + - component: {fileID: 7535246470184249899} + - component: {fileID: 7266648198088563062} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5512843442966546476 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7502327435573383977} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 551487876586793062} + m_Father: {fileID: 5256025271502919839} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!222 &3800436505147399416 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7502327435573383977} + m_CullTransparentMesh: 1 +--- !u!114 &7535246470184249899 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7502327435573383977} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10917, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &7266648198088563062 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7502327435573383977} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!1 &7827340776580214163 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5256025271502919839} + - component: {fileID: 7512403649071092031} + - component: {fileID: 7359473554909842726} + m_Layer: 5 + m_Name: Scroll View + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5256025271502919839 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7827340776580214163} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5512843442966546476} + - {fileID: 3420065201910878183} + m_Father: {fileID: 7543853000780573685} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 740, y: 500} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7512403649071092031 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7827340776580214163} + m_CullTransparentMesh: 1 +--- !u!114 &7359473554909842726 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7827340776580214163} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 551487876586793062} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 0 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 1 + m_Viewport: {fileID: 5512843442966546476} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 8645065629472015101} + m_HorizontalScrollbarVisibility: 2 + m_VerticalScrollbarVisibility: 2 + m_HorizontalScrollbarSpacing: -3 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] --- !u!1 &8191619470729415355 GameObject: m_ObjectHideFlags: 0 diff --git a/docs/CodeX-TODO.md b/docs/CodeX-TODO.md index 7b93c88..7088f06 100644 --- a/docs/CodeX-TODO.md +++ b/docs/CodeX-TODO.md @@ -215,16 +215,17 @@ | 状态 | ID | 任务 | 交付物路径 | 验收标准 | |-----|-------|-----------------------|-------------------------------------------------------------------------------------------------|----------------| -| [ ] | S5-01 | 先确认 M1 是否保留完整耐久闭环 | `docs/CodeX-TODO.md`
`docs/TODO.md` | 范围先明确,避免边做边改目标 | -| [ ] | S5-02 | 若保留,定义耐久对属性/出战资格的影响方式 | `Assets/GameMain/Scripts/Definition/`
`Assets/GameMain/Scripts/Entity/` | 规则口径清晰且可测试 | -| [ ] | S5-03 | 实现耐久扣减后的实际生效 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/Entity/` | 耐久不再只是展示字段 | -| [ ] | S5-04 | 实现 `0` 耐久销毁或失效闭环 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/UI/` | 归零后行为符合最终口径 | +| [x] | S5-01 | 先确认 M1 是否保留完整耐久闭环 | `docs/CodeX-TODO.md`
`docs/TODO.md` | 已明确按“最小耐久闭环”推进,不扩展维修/折价/自动销毁 | +| [x] | S5-02 | 若保留,定义耐久对属性/出战资格的影响方式 | `Assets/GameMain/Scripts/Definition/`
`Assets/GameMain/Scripts/Entity/` | 已统一为“归零失效、不可参战、非归零不衰减” | +| [x] | S5-03 | 实现耐久扣减后的实际生效 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/Entity/` | 已按“每场固定扣 1、仅扣本场参战塔”接进主链 | +| [x] | S5-04 | 实现 `0` 耐久失效闭环 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/UI/` | 已实现失效不可参战、仓库提示、节点结束后自动踢出参战区 | ### S5 规划结论 - `S5` 当前按“最小耐久闭环”推进,不展开维修、折价、复杂恢复、耐久与 Tag 联动等后续深度系统。 - M1 保留耐久规则,但当前目标只收口到“战斗后真实扣减 + 归零后真实失效 + UI 可解释”,不要求一并实现完整维修玩法。 - 本阶段优先保证耐久从展示字段变成实际规则入口,而不是继续增加额外经济系统或复杂数值衰减。 +- `S5-01 ~ S5-04` 当前代码口径已完成:战斗后会真实扣减本场参战塔耐久,`0` 耐久会拦截参战与战斗入口,仓库详情会显示损坏状态,节点结束后会自动把损坏塔移出参战区并弹提示。 ### S5-01 范围结论 @@ -246,6 +247,7 @@ - 扣减结果必须真实回写到库存快照 / 当前 Run 库存,而不是只改战斗内临时对象。 - 参战合法性统一复用现有校验入口扩展,不额外再造一套“耐久专用判断链”。 - 战斗入口仍需要保留最终二次校验,避免旧快照或外部改动绕过组装区 / 参战区限制。 +- 当前仓库实现已经按此口径落地:不再沿用旧的“低血量才扣耐久、且扣全库存塔”的逻辑。 ### S5-04 推荐闭环口径 @@ -253,13 +255,13 @@ - 装配了 `0` 耐久组件的塔在 UI 上需要给出明确提示,例如“组件已损坏”或“耐久为 0,无法参战”。 - 若塔已在参战区且组件在上一场战斗后归零,需要在后续刷新中把该塔视为非法参战塔,并通过统一校验入口阻止再次进入战斗。 - “自动销毁组件”与“自动拆塔”保留到后续阶段再决定,当前不作为 `S5` 通过标准。 +- 当前仓库实现已经进一步收口:节点结束时会自动检查损坏参战塔,将其移出 `ParticipantTowerInstanceIds`,并通过 `DialogForm` 明确提示玩家。 +- Boss 完成导致 Run 正式结束时,仍会执行损坏塔清理;但当前不会额外插入第二个耐久提示弹窗去覆盖“Run 完成”弹窗。 -### S5 推荐执行顺序 +### S5 当前结论 -1. 先完成 `S5-01`,把 M1 的耐久范围冻结为“最小闭环”,文档先对齐。 -2. 再完成 `S5-02`,把“归零失效、非归零不衰减、不可参战”的规则写成统一口径。 -3. 然后做 `S5-03`,把战斗结算后的耐久扣减与库存回写接进主链路。 -4. 最后做 `S5-04`,补齐参战拦截、UI 提示与 `0` 耐久失效闭环。 +1. `S5-01 ~ S5-04` 已按当前 M1 口径完成,不再作为 `P0-12` 的主阻塞项。 +2. 当前未实现的是长期设计里的维修、自动销毁、耐久折价、连续属性衰减,这些后续若要恢复,应作为新的增强项重新拆分,而不是回滚当前 MVP 口径。 ## 阶段 S6 - 回归与文档收尾 @@ -276,15 +278,15 @@ 2. `S2` 已完成口径对齐,`NodeMapForm` 不再作为 M1 阻塞项。 3. 接下来优先做 `S3`,补齐出战合法性,解决 `P0-10`。 4. 再做 `S4`,统一品质 / Tag 的规则口径,解决 `P0-11`。其中当前优先级已经收口为:`S4-06` 已完成,下一步转入 `S4-07` 的数据表映射。 -5. 最后做 `S5`,按“最小耐久闭环”收口真实扣减、归零失效与参战拦截,解决 `P0-12`。 -6. 全部完成后做 `S6`,补测试并同步文档状态。 +5. `S5` 已按当前 M1 口径完成,后续重点转入 `S6` 的测试补强与文档收尾。 +6. 在 `S6` 收尾后,再决定是否把维修、自动销毁、耐久折价等长期设计拆成新的增强阶段。 ## 本周建议开工顺序 1. `S1` 与 `S2` 已完成口径收口,可直接进入规则侧收尾 2. `S3-01 ~ S3-04` 已完成,当前可转入 `S4` 3. 当前先完成 `S4-07` 的三表口径收口,并把 `Tag.txt` / `TagConfig.txt` / `RarityTagBudget.txt` 的实际消费链稳定下来 -4. 然后转入 `S5-01 ~ S5-04`,按“最小耐久闭环”推进耐久真实生效 +4. `S5-01 ~ S5-04` 已按“最小耐久闭环”落地,当前无需再按旧 TODO 继续拆这部分 5. 最后补 `S6-01 ~ S6-04` ## 备注 diff --git a/docs/TODO.md b/docs/TODO.md index a10b7a5..d084753 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -15,7 +15,7 @@ - `NodeMapForm` 已满足 MVP 所需的节点流程界面;M1 现在的真实缺口是合法出战 / 品质 / Tag / 耐久规则是否真正统一收口。 - `P0-10` 的“三组件完整合法参战”主链已落地:当前参战分配、战斗入口最终校验、失败原因与拦截提示都已接入统一合法性判断入口,但文档仍保留 `[~]`,直到与 `docs/CodeX-TODO.md` 的验收口径完全同步。 - `P0-11` 已不再只是“局部展示字段”:当前品质计算、Tag 生成、塔级 Tag 汇总、首发 7 个 Tag 的战斗效果、以及 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三表驱动链路都已存在;剩余缺口主要是 `S4-07` 的最终文档口径与少量配置收口,而不是主功能缺失。 -- `P0-12` 仍是当前 M1 最明确的规则缺口:已有耐久字段、展示与部分扣减入口,但还没有形成“战斗后真实扣减 -> 归零失效 -> 参战拦截”的最小闭环。 +- `P0-12` 的“最小耐久闭环”主链已落地:当前已实现战斗后按参战塔真实扣减耐久、`0` 耐久失效并拦截参战/战斗入口、仓库详情损坏提示,以及节点结束后自动将损坏塔移出参战区并弹窗说明。 ## 里程碑 M1(P0)- 最小可玩闭环 @@ -32,7 +32,7 @@ | [x] | P0-09 | 敌人掉落与关卡奖励(组件/配件/金币) | `Assets/GameMain/Scripts/Entity/` | 战斗结束能发放掉落并写入库存 | | [~] | P0-10 | 节点后组装:枪口/轴承/底座三组件约束 | `Assets/GameMain/Scripts/Entity/`
`Assets/GameMain/Scripts/UI/Templates/GameScene/` | 当前已形成“三组件完整合法参战”的统一校验链与战斗入口拦截;剩余工作主要是同步文档口径与收尾验收 | | [~] | P0-11 | 品质 / Tag 规则统一入口(白绿蓝紫红) | `Assets/GameMain/Scripts/Definition/`
`Assets/GameMain/Scripts/Entity/` | 当前已完成品质统一、Tag 生成/汇总/展示与首发 7 个 Tag 的战斗生效;剩余工作主要是三表方案的最终收口与文档同步 | -| [~] | P0-12 | 组件/配件耐久生效与 0 耐久销毁 | `Assets/GameMain/Scripts/Entity/` | 当前按“最小耐久闭环”推进:目标是形成战斗后真实扣减、`0` 耐久失效与参战拦截;维修系统、自动销毁等后续深度规则暂不作为本阶段阻塞项 | +| [x] | P0-12 | 组件/配件耐久最小闭环 | `Assets/GameMain/Scripts/Procedure/`
`Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/UI/` | 已实现战斗后真实扣减、`0` 耐久失效拦截、仓库损坏提示与节点结束后的自动移出参战区;自动销毁/维修系统保留到后续阶段 | ## 里程碑 M2(P1)- 核心深度 @@ -64,8 +64,8 @@ ## 本周建议开工顺序 1. 先完成 `P0-11` 的三表口径收口,把 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 的实际消费链与文档彻底对齐 -2. 再推进 `P0-12`,按“最小耐久闭环”实现战斗后真实扣减、`0` 耐久失效与参战拦截 -3. 最后补关键流程 / 规则回归测试,并同步 `docs/TODO.md` 与 `docs/CodeX-TODO.md` 的真实状态 +2. 再补 `S6` 侧的主链路 / 规则回归测试,把当前 `S5` 代码口径固化下来 +3. 最后同步其余执行文档与过期 TODO,避免后续继续按旧的耐久/Tag 描述推进 ## 设计优化 Backlog(新增)