补全 UI 设计
- `ItemDescForm` 道具详细信息面板,目前还未添加 DescText 内容的赋值 - `RepoForm` 道具入槽逻辑调整,为了适应 `ItemDescForm` 的点击打开逻辑,点击道具自动匹配槽位改成拖动道具到相应槽位 - 添加了一系列图片素材
This commit is contained in:
+42
-2
@@ -10,9 +10,15 @@ namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class CombatResourceManager
|
||||
{
|
||||
// 每次击杀敌人后,进入“掉组件判定”的概率:
|
||||
// chance = clamp(DropChanceBase + (phaseIndex - 1) * DropChancePerPhase, 0, DropChanceCap)
|
||||
private const float DropChanceBase = 0.05f;
|
||||
private const float DropChancePerPhase = 0.0125f;
|
||||
private const float DropChanceCap = 0.45f;
|
||||
private const float DropChancePerPhase = 0.2f;
|
||||
private const float DropChanceCap = 0.2f;
|
||||
|
||||
// 用于把阶段映射到 [0,1] 的稀有度曲线输入:
|
||||
// phaseT = clamp01((phaseIndex - 1) / RarityCurveScalePhase)
|
||||
// 该值越大,稀有度随阶段提升的节奏越慢。
|
||||
private const float RarityCurveScalePhase = 30f;
|
||||
|
||||
private readonly List<DROutGameDropPool> _eligibleDropPoolBuffer = new();
|
||||
@@ -78,6 +84,13 @@ namespace GeometryTD.CustomComponent
|
||||
_rewardInventory.Gold += gold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 击杀敌人时的掉落入口。
|
||||
/// displayPhaseIndex 会影响:
|
||||
/// 1) 总掉落概率(由 DropChanceBase / DropChancePerPhase / DropChanceCap 决定)
|
||||
/// 2) 稀有度倾向(通过 phaseT 进入 RollRarity)
|
||||
/// themeType 会按关卡主题过滤掉落池(LevelThemeType 不匹配直接不参与)。
|
||||
/// </summary>
|
||||
public bool TryRollOutGameItemDrop(int displayPhaseIndex, LevelThemeType themeType)
|
||||
{
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
@@ -113,6 +126,10 @@ namespace GeometryTD.CustomComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接抽取一个具体掉落物(不走前面的总掉率门槛)。
|
||||
/// 但 displayPhaseIndex 和 themeType 仍会通过掉落池过滤、稀有度加权影响结果。
|
||||
/// </summary>
|
||||
public bool TryRollOutGameItem(int displayPhaseIndex, LevelThemeType themeType, out TowerCompItemData droppedItem)
|
||||
{
|
||||
droppedItem = null;
|
||||
@@ -178,6 +195,12 @@ namespace GeometryTD.CustomComponent
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 两阶段加权抽样:
|
||||
/// 1) 先按主题 + 阶段区间过滤候选行
|
||||
/// 2) 先抽稀有度(稀有度权重 = 同稀有度所有行的 row.Weight * 稀有度曲线权重 之和)
|
||||
/// 3) 在该稀有度内再按 row.Weight 抽具体行
|
||||
/// </summary>
|
||||
private bool TryPickDropPoolRow(int displayPhaseIndex, LevelThemeType themeType, out DROutGameDropPool selectedRow)
|
||||
{
|
||||
selectedRow = null;
|
||||
@@ -197,11 +220,13 @@ namespace GeometryTD.CustomComponent
|
||||
continue;
|
||||
}
|
||||
|
||||
// 主题过滤:主题不匹配的行,概率为 0。
|
||||
if (row.LevelThemeType != themeType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 阶段过滤:不在 [MinPhase, MaxPhase] 的行,概率为 0。
|
||||
if (displayPhaseIndex < row.MinPhase || displayPhaseIndex > row.MaxPhase)
|
||||
{
|
||||
continue;
|
||||
@@ -230,6 +255,7 @@ namespace GeometryTD.CustomComponent
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在已选稀有度内,row.Weight 是线性权重倍率。
|
||||
totalWeight += Mathf.Max(1, row.Weight);
|
||||
}
|
||||
|
||||
@@ -260,6 +286,12 @@ namespace GeometryTD.CustomComponent
|
||||
return selectedRow != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在过滤后的候选行里抽稀有度。
|
||||
/// 实际稀有度权重公式:
|
||||
/// rarityWeight = sum(max(1, row.Weight) * GetRarityCurveWeight(row.Rarity, phaseT))
|
||||
/// 其中 phaseT = clamp01((displayPhaseIndex - 1) / RarityCurveScalePhase)。
|
||||
/// </summary>
|
||||
private RarityType RollRarity(int displayPhaseIndex, List<DROutGameDropPool> candidates)
|
||||
{
|
||||
_rarityRollWeightBuffer.Clear();
|
||||
@@ -318,6 +350,14 @@ namespace GeometryTD.CustomComponent
|
||||
return RarityType.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回各稀有度在当前阶段的曲线权重。
|
||||
/// 返回值越大,该稀有度在 RollRarity 中越容易被抽到。
|
||||
/// phaseT 取值范围为 [0,1]:
|
||||
/// - White/Green:钟形趋势(前中期权重更高)
|
||||
/// - Blue/Purple:随阶段近似线性上升
|
||||
/// - Red:随阶段二次上升(前期很低,后期明显抬升)
|
||||
/// </summary>
|
||||
private static float GetRarityCurveWeight(RarityType rarityType, float phaseT)
|
||||
{
|
||||
float hump = Mathf.Exp(-Mathf.Pow((phaseT - 0.35f) / 0.28f, 2f));
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.Resource;
|
||||
using GeometryTD.CustomUtility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public class SpriteCacheComponent : GameFrameworkComponent
|
||||
{
|
||||
[SerializeField] private float _pixelsPerUnit = 100f;
|
||||
[SerializeField] private Vector2 _defaultPivot = new(0.5f, 0.5f);
|
||||
|
||||
private Dictionary<string, Sprite> _spriteCache;
|
||||
private Dictionary<string, List<Action<Sprite>>> _pendingCallbacks;
|
||||
private ResourceComponent _resource;
|
||||
|
||||
void Start()
|
||||
{
|
||||
_spriteCache = new Dictionary<string, Sprite>();
|
||||
_pendingCallbacks = new Dictionary<string, List<Action<Sprite>>>();
|
||||
_resource = GameEntry.Resource;
|
||||
}
|
||||
|
||||
public void GetSprite(string assetName, Action<Sprite> callback)
|
||||
{
|
||||
if (_spriteCache.TryGetValue(assetName, out var sprite))
|
||||
{
|
||||
callback?.Invoke(sprite);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pendingCallbacks.TryGetValue(assetName, out var pendingList))
|
||||
{
|
||||
pendingList.Add(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingCallbacks[assetName] = new List<Action<Sprite>> { callback };
|
||||
_resource.LoadAsset
|
||||
(
|
||||
AssetUtility.GetTextureAsset(assetName),
|
||||
Constant.AssetPriority.UIFormAsset,
|
||||
new LoadAssetCallbacks(
|
||||
(resourcePath, asset, duration, userData) =>
|
||||
{
|
||||
Log.Debug(resourcePath);
|
||||
Texture2D texture = asset as Texture2D;
|
||||
Sprite loadedSprite = null;
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
loadedSprite = Sprite.Create(
|
||||
texture,
|
||||
new Rect(0, 0, texture.width, texture.height),
|
||||
_defaultPivot,
|
||||
_pixelsPerUnit);
|
||||
|
||||
_spriteCache[assetName] = loadedSprite;
|
||||
}
|
||||
|
||||
if (_pendingCallbacks.TryGetValue(assetName, out var callbacks))
|
||||
{
|
||||
_pendingCallbacks.Remove(assetName);
|
||||
for (int i = 0; i < callbacks.Count; i++)
|
||||
{
|
||||
callbacks[i]?.Invoke(loadedSprite);
|
||||
}
|
||||
}
|
||||
},
|
||||
(resourcePath, status, errorMessage, userData) =>
|
||||
{
|
||||
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
|
||||
assetName,
|
||||
resourcePath,
|
||||
errorMessage);
|
||||
|
||||
if (_pendingCallbacks.TryGetValue(assetName, out var callbacks))
|
||||
{
|
||||
_pendingCallbacks.Remove(assetName);
|
||||
for (int i = 0; i < callbacks.Count; i++)
|
||||
{
|
||||
callbacks[i]?.Invoke(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_spriteCache.Clear();
|
||||
_pendingCallbacks.Clear();
|
||||
_resource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b0237c7a9ed81745b92fe3f1bd45af4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user