```
feat(gameplay): 实现护盾系统和强化增益选择机制 - 新增护盾功能,包括护盾数值显示和护盾条UI - 重构盾卫技能,实现坚固壁垒技能效果 - 添加暴击率、吸血率等战斗属性 - 优化增益选择界面,新增传奇品质和刷新机制 - 调整材质纹理和文字着色器效果 - 修复金币显示格式问题 ```
This commit is contained in:
1 parent
336ec9980d
commit
48395ab414
27 files changed
+10331
-116
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
@@ -46,7 +46,7 @@ Material:
|
||||
m_Scale: {x: 3, y: 4}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 594180e608ded448d8751c39bca40633, type: 3}
|
||||
m_Texture: {fileID: 2800000, guid: b424ea763d6a34320bae85b1d85ef820, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
|
||||
@@ -30,13 +30,15 @@ Material:
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _ColorMask: 15
|
||||
- _MidPoint: 0.5
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
m_Colors:
|
||||
- _BottomColor: {r: 0.88, g: 0.58, b: 0.08, a: 1}
|
||||
- _BottomColor: {r: 1, g: 0.8272859, b: 0.62578607, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _TopColor: {r: 1, g: 0.96, b: 0.65, a: 1}
|
||||
- _MiddleColor: {r: 1, g: 0.8353073, b: 0.35534585, a: 1}
|
||||
- _TopColor: {r: 1, g: 0.98, b: 0.93, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: XHsWsyKuVn29xwSVE/3Ug9GpdtrPk+ZRNROucmeIFIhu6+A9ilqnymY=
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
@@ -0,0 +1,177 @@
|
||||
fileFormatVersion: 2
|
||||
guid: CnxM43+qB3mU/IJVjpBlqtDrEW5/D+SiAMmbk4HP1rVTuGsPSJz7Hnc=
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 14
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
webStreaming: 0
|
||||
priorityLevel: 0
|
||||
uploadedMode: 2
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: WeixinMiniGame
|
||||
maxTextureSize: 2048
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: PlayableAds
|
||||
maxTextureSize: 2048
|
||||
maxPlaceholderSize: 32
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
doOverrideTextureManagerOperations: 0
|
||||
platformOperationGroupSettings:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -313,8 +313,10 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 15c30b8bf55ed47c38c74d59aa9fd021, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
maxHealth: 100
|
||||
currentHealth: 100
|
||||
maxHealth: 700
|
||||
currentHealth: 700
|
||||
maxShield: 0
|
||||
currentShield: 0
|
||||
showEnergyBar: 1
|
||||
maxEnergy: 100
|
||||
_currentEnergy: 0
|
||||
@@ -329,6 +331,7 @@ MonoBehaviour:
|
||||
damageNumberRiseHeight: 1.2
|
||||
damageNumberSpawnHeight: 1.5
|
||||
damageNumberSize: 0.09
|
||||
unitLevel: 1
|
||||
--- !u!114 &832567817104069704
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -364,6 +367,11 @@ MonoBehaviour:
|
||||
attackRange: 2
|
||||
modelFacingOffset: 45
|
||||
attackPower: 10
|
||||
critRate: 0
|
||||
critMultiplier: 1.5
|
||||
lifestealRate: 0
|
||||
damageReductionRate: 0
|
||||
skillChargeMultiplier: 1
|
||||
attackDuration: 0
|
||||
attackType: 0
|
||||
projectilePrefab: {fileID: 0}
|
||||
@@ -376,6 +384,8 @@ MonoBehaviour:
|
||||
returnToAnchorWhenIdle: 0
|
||||
anchorArrivalDistance: 0.35
|
||||
useAnchorLeash: 0
|
||||
useEngagementZLine: 1
|
||||
engagementZThreshold: 10
|
||||
target: {fileID: 0}
|
||||
--- !u!114 &7519013316474627281
|
||||
MonoBehaviour:
|
||||
@@ -389,11 +399,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 91c4440b51dd744e9857b9a5b3a0f19b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
shieldDurability: 220
|
||||
shieldDuration: 4
|
||||
damageReduction: 0.35
|
||||
shieldLocalPosition: {x: 0, y: 1, z: 0.7}
|
||||
shieldSize: {x: 1.5, y: 1.8, z: 0.12}
|
||||
shieldDuration: 5
|
||||
shieldVfxPrefab: {fileID: 106386, guid: 439cf2d390c444d6b428b530f5b7eec3, type: 3}
|
||||
--- !u!4 &1906187853415385385 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -51,7 +51,7 @@ public class GoldUI : MonoBehaviour
|
||||
{
|
||||
if (goldText != null)
|
||||
{
|
||||
goldText.text = $"金币 {newGold}";
|
||||
goldText.text = newGold.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
[Min(0f)] public float currentHealth = 100f;
|
||||
|
||||
[Header("护盾")]
|
||||
public float maxShield = 0f;
|
||||
public float currentShield = 0f;
|
||||
|
||||
/// <summary>
|
||||
@@ -18,9 +19,32 @@ public class WorldHealthBar : MonoBehaviour
|
||||
public void AddShield(float amount)
|
||||
{
|
||||
if (amount <= 0f || isDead) return;
|
||||
if (currentShield <= 0f)
|
||||
{
|
||||
maxShield = amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxShield += amount;
|
||||
}
|
||||
currentShield += amount;
|
||||
UpdateFill();
|
||||
Debug.Log($"{gameObject.name} 获得护盾 +{amount:F1},当前护盾: {currentShield:F1}");
|
||||
UpdateShieldFill();
|
||||
Debug.Log($"{gameObject.name} 获得护盾 +{amount:F1},当前护盾: {currentShield:F1}/{maxShield:F1}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 减少或清空护盾
|
||||
/// </summary>
|
||||
public void RemoveShield(float amount)
|
||||
{
|
||||
if (amount <= 0f || currentShield <= 0f) return;
|
||||
currentShield = Mathf.Max(0f, currentShield - amount);
|
||||
if (currentShield <= 0f)
|
||||
{
|
||||
maxShield = 0f;
|
||||
}
|
||||
UpdateShieldFill();
|
||||
}
|
||||
|
||||
// 技能能量上限,英雄默认使用100点;没有技能的小怪可关闭能量条。
|
||||
@@ -74,7 +98,8 @@ public class WorldHealthBar : MonoBehaviour
|
||||
// 描边宽度设为私有常量,所有角色统一使用
|
||||
private const float DamageNumberOutlineWidth = 0.025f; // 从 0.015 增加到 0.025,描边更粗
|
||||
|
||||
// 血条 UI 的像素尺寸,实际显示大小由 worldWidth 控制。
|
||||
// 护盾条高 10 像素,置于血条最上方。
|
||||
private const int ShieldPixelHeight = 10;
|
||||
private const int PixelWidth = 100;
|
||||
private const int PixelHeight = 14;
|
||||
|
||||
@@ -82,7 +107,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
private const int EnergyPixelWidth = 100; // 改为和血条一样宽
|
||||
private const int EnergyPixelHeight = 12; // 从 8 增加到 12,更明显
|
||||
private const int BarGap = 0; // 改为 0,让血条和能量条紧贴在一起
|
||||
private const int TotalPixelHeight = PixelHeight + BarGap + EnergyPixelHeight;
|
||||
private const int TotalPixelHeight = ShieldPixelHeight + BarGap + PixelHeight + BarGap + EnergyPixelHeight;
|
||||
|
||||
// 世界空间 Canvas,用来承载血条 UI。
|
||||
private Canvas canvas;
|
||||
@@ -90,9 +115,17 @@ public class WorldHealthBar : MonoBehaviour
|
||||
// 血条整体的 RectTransform。
|
||||
private RectTransform barRect;
|
||||
|
||||
// 整体 UI 行节点引用
|
||||
private RectTransform shieldRow;
|
||||
private RectTransform healthRow;
|
||||
private RectTransform energyRow;
|
||||
|
||||
// 绿色血量填充 Image。
|
||||
private Image fillImage;
|
||||
|
||||
// 浅蓝色护盾填充 Image。
|
||||
private Image shieldFillImage;
|
||||
|
||||
// 金色技能能量填充 Image。
|
||||
private Image energyFillImage;
|
||||
|
||||
@@ -218,6 +251,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
float absorbed = Mathf.Min(currentShield, amount);
|
||||
currentShield -= absorbed;
|
||||
amount -= absorbed;
|
||||
UpdateShieldFill();
|
||||
Debug.Log($"{gameObject.name} 护盾吸收了 {absorbed:F1} 伤害,剩余护盾: {currentShield:F1}");
|
||||
}
|
||||
|
||||
@@ -472,16 +506,32 @@ public class WorldHealthBar : MonoBehaviour
|
||||
canvas.sortingOrder = 100;
|
||||
barObject.GetComponent<CanvasScaler>().dynamicPixelsPerUnit = 10f;
|
||||
barRect = barObject.GetComponent<RectTransform>();
|
||||
barRect.sizeDelta = new Vector2(PixelWidth, showEnergyBar ? TotalPixelHeight : PixelHeight);
|
||||
barRect.sizeDelta = new Vector2(PixelWidth, showEnergyBar ? TotalPixelHeight : (ShieldPixelHeight + PixelHeight));
|
||||
barRect.localScale = Vector3.one * (worldWidth / PixelWidth);
|
||||
barRect.localPosition = GetBarRootPosition();
|
||||
ApplyScaleCompensation();
|
||||
|
||||
RectTransform healthRow = CreateRow(barObject.transform, "HealthBar", PixelWidth, PixelHeight, 0);
|
||||
// 1. 顶层独立护盾条 (ShieldBar)
|
||||
shieldRow = CreateRow(barObject.transform, "ShieldBar", PixelWidth, ShieldPixelHeight, 0);
|
||||
Image shieldBackground = CreateImage("ShieldBorder", shieldRow, RoundedSprite(), borderColor); // 加回黑框
|
||||
SetFullRect(shieldBackground.rectTransform);
|
||||
|
||||
Image shieldInner = CreateImage("ShieldFill", shieldRow, RoundedSprite(), new Color(0f, 0.88f, 1f, 0.95f)); // 亮天蓝色
|
||||
shieldInner.rectTransform.anchorMin = Vector2.zero;
|
||||
shieldInner.rectTransform.anchorMax = Vector2.one;
|
||||
shieldInner.rectTransform.offsetMin = new Vector2(2f, 1f);
|
||||
shieldInner.rectTransform.offsetMax = new Vector2(-2f, -1f);
|
||||
shieldInner.type = Image.Type.Filled;
|
||||
shieldInner.fillMethod = Image.FillMethod.Horizontal;
|
||||
shieldInner.fillOrigin = 0;
|
||||
shieldFillImage = shieldInner;
|
||||
shieldRow.gameObject.SetActive(false); // 默认没有护盾时整体收起隐藏
|
||||
|
||||
// 2. 中层主血条 (HealthBar)
|
||||
healthRow = CreateRow(barObject.transform, "HealthBar", PixelWidth, PixelHeight, ShieldPixelHeight + BarGap);
|
||||
Image background = CreateImage("Border", healthRow, RoundedSprite(), borderColor);
|
||||
SetFullRect(background.rectTransform);
|
||||
|
||||
// 创建填充 Image,颜色暂时使用默认绿色,在 Start() 中根据最终标签设置
|
||||
Image inner = CreateImage("Fill", healthRow, RoundedSprite(), Color.green);
|
||||
inner.rectTransform.anchorMin = Vector2.zero;
|
||||
inner.rectTransform.anchorMax = Vector2.one;
|
||||
@@ -492,6 +542,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
inner.fillOrigin = 0;
|
||||
fillImage = inner;
|
||||
|
||||
// 3. 底层技能能量条 (EnergyBar)
|
||||
if (!showEnergyBar)
|
||||
{
|
||||
energyFillImage = null;
|
||||
@@ -499,12 +550,12 @@ public class WorldHealthBar : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform energyRow = CreateRow(
|
||||
energyRow = CreateRow(
|
||||
barObject.transform,
|
||||
"EnergyBar",
|
||||
EnergyPixelWidth,
|
||||
EnergyPixelHeight,
|
||||
PixelHeight + BarGap);
|
||||
ShieldPixelHeight + BarGap + PixelHeight + BarGap);
|
||||
Image energyBackground = CreateImage("EnergyBorder", energyRow, RoundedSprite(), borderColor);
|
||||
SetFullRect(energyBackground.rectTransform);
|
||||
|
||||
@@ -619,7 +670,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
// 创建血条左侧的圆圈等级徽章
|
||||
private void CreateLevelBadge(Transform parent)
|
||||
{
|
||||
int badgeSize = 26; // 圆圈徽章直径
|
||||
int badgeSize = 39; // 调整为原来的 1.5 倍 (从 26 增大至 39)
|
||||
GameObject badgeObj = new GameObject("LevelBadge", typeof(RectTransform));
|
||||
badgeObj.transform.SetParent(parent, false);
|
||||
|
||||
@@ -628,7 +679,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
badgeRect.anchorMax = new Vector2(0f, 1f);
|
||||
badgeRect.pivot = new Vector2(1f, 0.5f); // 右边缘切齐,紧贴血条左边缘
|
||||
badgeRect.sizeDelta = new Vector2(badgeSize, badgeSize);
|
||||
badgeRect.anchoredPosition = new Vector2(2f, -PixelHeight * 0.5f); // 总是对齐第一行血条中心
|
||||
badgeRect.anchoredPosition = new Vector2(2f, -15f); // 居中对齐主血条中心 (posY = -15f)
|
||||
|
||||
// 外层黑边圆圈
|
||||
Image outerCircle = CreateImage("OuterCircle", badgeRect, CircleSprite(), new Color(0.05f, 0.05f, 0.05f, 0.95f));
|
||||
@@ -652,7 +703,7 @@ public class WorldHealthBar : MonoBehaviour
|
||||
levelText = textObj.GetComponent<Text>();
|
||||
levelText.text = unitLevel.ToString();
|
||||
levelText.font = GetBuiltinFont();
|
||||
levelText.fontSize = 18;
|
||||
levelText.fontSize = 26; // 字体同步放大
|
||||
levelText.fontStyle = FontStyle.Bold;
|
||||
levelText.alignment = TextAnchor.MiddleCenter;
|
||||
levelText.color = new Color(0.95f, 0.98f, 0.95f, 1f);
|
||||
@@ -701,6 +752,38 @@ public class WorldHealthBar : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// 根据当前护盾值更新天蓝色填充比例与显隐。
|
||||
private void UpdateShieldFill()
|
||||
{
|
||||
if (shieldRow != null && shieldFillImage != null)
|
||||
{
|
||||
if (currentShield > 0f)
|
||||
{
|
||||
shieldRow.gameObject.SetActive(true);
|
||||
|
||||
// 1. 护盾条左对齐:锚点改为左上角
|
||||
shieldRow.anchorMin = new Vector2(0f, 1f);
|
||||
shieldRow.anchorMax = new Vector2(0f, 1f);
|
||||
shieldRow.pivot = new Vector2(0f, 1f);
|
||||
shieldRow.anchoredPosition = new Vector2(0f, 0f);
|
||||
|
||||
// 2. 护盾条的物理宽度按 (maxShield / maxHealth) 比例,代表护盾容量占血条的比例
|
||||
float shieldRatio = maxHealth > 0f ? Mathf.Clamp01(maxShield / maxHealth) : 1.0f;
|
||||
float targetWidth = PixelWidth * Mathf.Max(0.05f, shieldRatio);
|
||||
shieldRow.sizeDelta = new Vector2(targetWidth, ShieldPixelHeight);
|
||||
|
||||
// 3. 天蓝色填充条按 (currentShield / maxShield) 动态扣减
|
||||
float fillRatio = maxShield > 0f ? Mathf.Clamp01(currentShield / maxShield) : 1.0f;
|
||||
shieldFillImage.fillAmount = fillRatio;
|
||||
shieldFillImage.SetVerticesDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
shieldRow.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建并缓存一个直角矩形白色 Sprite,供边框和填充复用。
|
||||
private static Sprite RoundedSprite()
|
||||
{
|
||||
|
||||
@@ -1,62 +1,134 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 盾卫技能:坚固壁垒
|
||||
/// 1级:生成相当于自身 25% 最大生命值的护盾(持续 5 秒),播放 MagicShield 护盾特效
|
||||
/// 3级:护盾值提升至 35% 最大生命值,护盾期间减伤 +15%
|
||||
/// </summary>
|
||||
public class ShieldGuardSkill : UnitSkill
|
||||
{
|
||||
[Header("盾牌参数")]
|
||||
[Min(1f)]
|
||||
public float shieldDurability = 220f;
|
||||
[Header("护盾配置")]
|
||||
[Tooltip("护盾持续时间(秒)")]
|
||||
public float shieldDuration = 5.0f;
|
||||
|
||||
[Min(0.1f)]
|
||||
public float shieldDuration = 4f;
|
||||
[Tooltip("护盾特效预制体")]
|
||||
public GameObject shieldVfxPrefab;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
public float damageReduction = 0.35f;
|
||||
private GameObject activeVfxInstance;
|
||||
private Coroutine activeShieldCoroutine;
|
||||
|
||||
public Vector3 shieldLocalPosition = new Vector3(0f, 1f, 0.7f);
|
||||
|
||||
public Vector3 shieldSize = new Vector3(1.5f, 1.8f, 0.12f);
|
||||
|
||||
private ShieldBarrier shield;
|
||||
|
||||
public ShieldBarrier ActiveShield => shield;
|
||||
private void EnsureUnit()
|
||||
{
|
||||
if (unit == null)
|
||||
{
|
||||
unit = GetComponent<UnitBase>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReleaseSkill()
|
||||
{
|
||||
EnsureShield();
|
||||
shield.Refresh(shieldDurability, shieldDuration, shieldLocalPosition, shieldSize);
|
||||
EnsureUnit();
|
||||
if (unit == null) return;
|
||||
|
||||
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
|
||||
if (healthBar == null || healthBar.IsDead) return;
|
||||
|
||||
int level = unit.level; // 从 UnitBase 字段读取 level
|
||||
// 1级:25% maxHealth,3级+:35% maxHealth
|
||||
float ratio = level >= 3 ? 0.35f : 0.25f;
|
||||
float shieldAmount = healthBar.maxHealth * ratio;
|
||||
|
||||
// 1. 添加护盾值
|
||||
healthBar.AddShield(shieldAmount);
|
||||
|
||||
// 2. 播放 MagicShield 护盾特效
|
||||
PlayShieldVfx();
|
||||
|
||||
// 3. 开启超时自动清盾协程
|
||||
if (activeShieldCoroutine != null)
|
||||
{
|
||||
StopCoroutine(activeShieldCoroutine);
|
||||
}
|
||||
activeShieldCoroutine = StartCoroutine(ShieldTimeoutRoutine(shieldDuration, shieldAmount));
|
||||
|
||||
Debug.Log($"<color=cyan>盾卫释放技能【坚固壁垒】</color> 获得 {shieldAmount:F1} 护盾(比例: {ratio * 100}%,等级: {level})");
|
||||
}
|
||||
|
||||
public override float ModifyIncomingDamage(float damage, GameObject attacker)
|
||||
{
|
||||
if (shield == null || !shield.IsActive)
|
||||
EnsureUnit();
|
||||
// 3级解锁:护盾激活期间享受额外 15% 减伤
|
||||
if (unit != null && unit.level >= 3)
|
||||
{
|
||||
WorldHealthBar healthBar = unit.GetComponent<WorldHealthBar>();
|
||||
if (healthBar != null && healthBar.currentShield > 0f)
|
||||
{
|
||||
return damage * (1f - 0.15f);
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
if (shield.TryBlock(attacker, damage))
|
||||
private void PlayShieldVfx()
|
||||
{
|
||||
return damage * (1f - damageReduction);
|
||||
if (activeVfxInstance != null)
|
||||
{
|
||||
Destroy(activeVfxInstance);
|
||||
}
|
||||
|
||||
return damage;
|
||||
GameObject prefab = shieldVfxPrefab;
|
||||
if (prefab == null)
|
||||
{
|
||||
prefab = Resources.Load<GameObject>("Prefabs/Combat/Magic/Shield/MagicShieldBlue");
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
if (prefab == null)
|
||||
{
|
||||
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>("Assets/TJGeneratorLibEffects/Prefabs/Combat/Magic/Shield/MagicShieldBlue.prefab");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (prefab != null)
|
||||
{
|
||||
activeVfxInstance = Instantiate(prefab, unit.transform.position + Vector3.up * 1.0f, Quaternion.identity);
|
||||
activeVfxInstance.transform.SetParent(unit.transform, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureShield()
|
||||
private IEnumerator ShieldTimeoutRoutine(float duration, float amount)
|
||||
{
|
||||
if (shield != null)
|
||||
float timer = duration;
|
||||
WorldHealthBar healthBar = unit != null ? unit.GetComponent<WorldHealthBar>() : null;
|
||||
|
||||
while (timer > 0f)
|
||||
{
|
||||
return;
|
||||
if (unit == null || healthBar == null || healthBar.IsDead || healthBar.currentShield <= 0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
timer -= Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
GameObject shieldObject = new GameObject("ShieldBarrier");
|
||||
shieldObject.transform.SetParent(unit.transform, false);
|
||||
// 超时或护盾为 0 清理特效与护盾值
|
||||
if (activeVfxInstance != null)
|
||||
{
|
||||
Destroy(activeVfxInstance);
|
||||
activeVfxInstance = null;
|
||||
}
|
||||
|
||||
BoxCollider boxCollider = shieldObject.AddComponent<BoxCollider>();
|
||||
boxCollider.isTrigger = true;
|
||||
boxCollider.size = shieldSize;
|
||||
if (healthBar != null && healthBar.currentShield > 0f && timer <= 0f)
|
||||
{
|
||||
healthBar.RemoveShield(amount);
|
||||
}
|
||||
}
|
||||
|
||||
shield = shieldObject.AddComponent<ShieldBarrier>();
|
||||
shield.Configure(unit, boxCollider);
|
||||
shieldObject.SetActive(false);
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (activeVfxInstance != null)
|
||||
{
|
||||
Destroy(activeVfxInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,10 @@ public class BuffSelectionController : MonoBehaviour
|
||||
|
||||
public enum BuffQuality
|
||||
{
|
||||
Common, // 初级 (白色 55%)
|
||||
Rare, // 中级 (紫色 35%)
|
||||
Epic // 高级 (金色 10%)
|
||||
Common, // 初级 (白色 85%)
|
||||
Rare, // 中级 (紫色 10%)
|
||||
Epic, // 高级 (金色 5%)
|
||||
Legendary // 顶级 (红色/酒红 预留)
|
||||
}
|
||||
|
||||
public class BuffOption
|
||||
@@ -68,18 +69,24 @@ public class BuffSelectionController : MonoBehaviour
|
||||
[Header("UI 节点引用")]
|
||||
public GameObject buffSelectionWindow;
|
||||
public Transform cardsContainer;
|
||||
public Button rollButton; // 刷新按钮
|
||||
public int rollCost = 2; // 每次刷新消耗金币
|
||||
|
||||
[Header("品质边框 Sprites")]
|
||||
public Sprite commonBorderSprite; // 铜 (初级)
|
||||
public Sprite rareBorderSprite; // 银 (中级)
|
||||
public Sprite epicBorderSprite; // 金 (高级)
|
||||
public Sprite legendaryBorderSprite; // 红 (最高级)
|
||||
public Sprite legendaryBorderSprite; // 红 (顶级)
|
||||
|
||||
[Header("头像 Sprites")]
|
||||
public Sprite allIconSprite; // 全体/全局头像 (all.png)
|
||||
|
||||
private bool isWindowShowing = false;
|
||||
|
||||
// 3 个卡槽各自独立追踪的品质与递进未升阶计数
|
||||
private BuffQuality[] slotQualities = new BuffQuality[3];
|
||||
private int[] slotNonUpgradeCounts = new int[3];
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance == null)
|
||||
@@ -102,15 +109,111 @@ public class BuffSelectionController : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成 3 个随机卡片
|
||||
List<BuffOption> options = GenerateThreeOptions();
|
||||
// 初始弹窗时重置 3 个卡槽的累积计数,并按初始基础概率独立抽品质 (初级 85% / 中级 10% / 高级 5%)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
slotNonUpgradeCounts[i] = 0;
|
||||
float roll = Random.value;
|
||||
if (roll < 0.85f)
|
||||
{
|
||||
slotQualities[i] = BuffQuality.Common;
|
||||
}
|
||||
else if (roll < 0.95f)
|
||||
{
|
||||
slotQualities[i] = BuffQuality.Rare;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotQualities[i] = BuffQuality.Epic;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 3 个卡槽的增益选项
|
||||
List<BuffOption> options = GenerateOptionsForSlots();
|
||||
RenderOptionsUI(options);
|
||||
|
||||
// 绑定刷新按钮事件
|
||||
if (rollButton != null)
|
||||
{
|
||||
rollButton.onClick.RemoveAllListeners();
|
||||
rollButton.onClick.AddListener(OnRollButtonClicked);
|
||||
}
|
||||
|
||||
buffSelectionWindow.SetActive(true);
|
||||
isWindowShowing = true;
|
||||
Time.timeScale = 0f; // 暂停游戏让玩家做选择
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击刷新按钮 (耗费 2 金币),3 个卡槽独立计算升阶概率并重新生成选项
|
||||
/// </summary>
|
||||
private void OnRollButtonClicked()
|
||||
{
|
||||
if (GoldManager.Instance != null && !GoldManager.Instance.TrySpendGold(rollCost))
|
||||
{
|
||||
Debug.LogWarning($"金币不足 {rollCost},无法刷新!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3 个卡槽独立判定升级逻辑
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
BuffQuality currentQ = slotQualities[i];
|
||||
bool upgraded = false;
|
||||
BuffQuality targetQ = currentQ;
|
||||
|
||||
if (currentQ == BuffQuality.Common)
|
||||
{
|
||||
// 初级 升 中级 10% 每次 +10% (第2次20%、第3次30%...)
|
||||
float chance = 0.10f + slotNonUpgradeCounts[i] * 0.10f;
|
||||
if (Random.value < chance)
|
||||
{
|
||||
targetQ = BuffQuality.Rare;
|
||||
upgraded = true;
|
||||
}
|
||||
}
|
||||
else if (currentQ == BuffQuality.Rare)
|
||||
{
|
||||
// 中级 升 高级 3% 每次 +5% (第2次8%、第3次13%...)
|
||||
float chance = 0.03f + slotNonUpgradeCounts[i] * 0.05f;
|
||||
if (Random.value < chance)
|
||||
{
|
||||
targetQ = BuffQuality.Epic;
|
||||
upgraded = true;
|
||||
}
|
||||
}
|
||||
else if (currentQ == BuffQuality.Epic)
|
||||
{
|
||||
// 高级 升 顶级 1% (预留) 每次 +2%
|
||||
float chance = 0.01f + slotNonUpgradeCounts[i] * 0.02f;
|
||||
if (Random.value < chance)
|
||||
{
|
||||
targetQ = BuffQuality.Legendary;
|
||||
upgraded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (upgraded)
|
||||
{
|
||||
slotQualities[i] = targetQ;
|
||||
slotNonUpgradeCounts[i] = 0; // 刷出升阶,该槽位的追加概率重置
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentQ != BuffQuality.Legendary)
|
||||
{
|
||||
slotNonUpgradeCounts[i]++; // 未升阶,追加概率增加
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"<color=yellow>玩家消耗 {rollCost} 金币刷新卡牌!</color> 当前槽位品质: [{slotQualities[0]}], [{slotQualities[1]}], [{slotQualities[2]}]");
|
||||
|
||||
// 重新渲染 3 个卡槽
|
||||
List<BuffOption> newOptions = GenerateOptionsForSlots();
|
||||
RenderOptionsUI(newOptions);
|
||||
}
|
||||
|
||||
private void RenderOptionsUI(List<BuffOption> options)
|
||||
{
|
||||
int cardCount = cardsContainer.childCount;
|
||||
@@ -120,7 +223,7 @@ public class BuffSelectionController : MonoBehaviour
|
||||
if (i < options.Count)
|
||||
{
|
||||
card.gameObject.SetActive(true);
|
||||
SetupCardUI(card, options[i]);
|
||||
SetupCardUI(card, options[i], i);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -129,10 +232,32 @@ public class BuffSelectionController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupCardUI(Transform cardTransform, BuffOption option)
|
||||
private float GetNextUpgradeChance(int slotIndex)
|
||||
{
|
||||
if (slotIndex < 0 || slotIndex >= 3) return 0f;
|
||||
BuffQuality currentQ = slotQualities[slotIndex];
|
||||
int count = slotNonUpgradeCounts[slotIndex];
|
||||
|
||||
if (currentQ == BuffQuality.Common)
|
||||
{
|
||||
return 0.10f + count * 0.10f; // 10% + n * 10%
|
||||
}
|
||||
else if (currentQ == BuffQuality.Rare)
|
||||
{
|
||||
return 0.03f + count * 0.05f; // 3% + n * 5%
|
||||
}
|
||||
else if (currentQ == BuffQuality.Epic)
|
||||
{
|
||||
return 0.01f + count * 0.02f; // 1% + n * 2%
|
||||
}
|
||||
return 0f; // 顶级无升级
|
||||
}
|
||||
|
||||
private void SetupCardUI(Transform cardTransform, BuffOption option, int slotIndex)
|
||||
{
|
||||
Text buffNameText = cardTransform.Find("BuffName")?.GetComponent<Text>();
|
||||
Text descText = cardTransform.Find("Description")?.GetComponent<Text>();
|
||||
Text upgradeRateText = cardTransform.Find("UpgradeRateText")?.GetComponent<Text>();
|
||||
|
||||
if (buffNameText != null)
|
||||
{
|
||||
@@ -145,6 +270,19 @@ public class BuffSelectionController : MonoBehaviour
|
||||
descText.text = option.descText;
|
||||
}
|
||||
|
||||
if (upgradeRateText != null)
|
||||
{
|
||||
if (option.quality == BuffQuality.Legendary)
|
||||
{
|
||||
upgradeRateText.text = "已达最高品质";
|
||||
}
|
||||
else
|
||||
{
|
||||
float chance = GetNextUpgradeChance(slotIndex);
|
||||
upgradeRateText.text = $"升阶概率: {Mathf.Min(100f, chance * 100f):F0}%";
|
||||
}
|
||||
}
|
||||
|
||||
// 设置目标英雄 / 全体 Icon 头像
|
||||
Image iconImage = cardTransform.Find("Icon")?.GetComponent<Image>();
|
||||
if (iconImage != null && option.iconSprite != null)
|
||||
@@ -191,6 +329,8 @@ public class BuffSelectionController : MonoBehaviour
|
||||
return rareBorderSprite != null ? rareBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/银");
|
||||
case BuffQuality.Epic:
|
||||
return epicBorderSprite != null ? epicBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/金");
|
||||
case BuffQuality.Legendary:
|
||||
return legendaryBorderSprite != null ? legendaryBorderSprite : Resources.Load<Sprite>("Picture/UI/增益边框/红");
|
||||
default:
|
||||
return commonBorderSprite;
|
||||
}
|
||||
@@ -335,7 +475,7 @@ public class BuffSelectionController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private List<BuffOption> GenerateThreeOptions()
|
||||
private List<BuffOption> GenerateOptionsForSlots()
|
||||
{
|
||||
List<BuffOption> list = new List<BuffOption>();
|
||||
HashSet<BuffType> usedTypes = new HashSet<BuffType>();
|
||||
@@ -346,7 +486,7 @@ public class BuffSelectionController : MonoBehaviour
|
||||
int maxAttempts = 10;
|
||||
do
|
||||
{
|
||||
opt = CreateRandomOption(i);
|
||||
opt = CreateOptionForSlot(i, slotQualities[i]);
|
||||
maxAttempts--;
|
||||
} while (usedTypes.Contains(opt.type) && maxAttempts > 0);
|
||||
|
||||
@@ -370,42 +510,42 @@ public class BuffSelectionController : MonoBehaviour
|
||||
BuffType.EmergencyHeal // 14. 紧急救急
|
||||
};
|
||||
|
||||
private BuffOption CreateRandomOption(int seedOffset)
|
||||
private BuffOption CreateOptionForSlot(int slotIndex, BuffQuality quality)
|
||||
{
|
||||
BuffOption opt = new BuffOption();
|
||||
|
||||
// 从当前开放的词条池中随机选择
|
||||
opt.type = AvailableBuffTypes[Random.Range(0, AvailableBuffTypes.Length)];
|
||||
opt.quality = quality;
|
||||
|
||||
// 随机品质 (初级55%, 中级35%, 高级10%)
|
||||
float rollQuality = Random.value;
|
||||
if (rollQuality < 0.55f)
|
||||
switch (quality)
|
||||
{
|
||||
opt.quality = BuffQuality.Common;
|
||||
case BuffQuality.Common:
|
||||
opt.qualityName = "初级";
|
||||
opt.qualityColor = new Color(0.85f, 0.85f, 0.85f); // 白色
|
||||
}
|
||||
else if (rollQuality < 0.90f)
|
||||
{
|
||||
opt.quality = BuffQuality.Rare;
|
||||
break;
|
||||
case BuffQuality.Rare:
|
||||
opt.qualityName = "中级";
|
||||
opt.qualityColor = new Color(0.7f, 0.35f, 0.95f); // 紫色
|
||||
}
|
||||
else
|
||||
{
|
||||
opt.quality = BuffQuality.Epic;
|
||||
break;
|
||||
case BuffQuality.Epic:
|
||||
opt.qualityName = "高级";
|
||||
opt.qualityColor = new Color(1f, 0.8f, 0.15f); // 金色
|
||||
break;
|
||||
case BuffQuality.Legendary:
|
||||
opt.qualityName = "顶级";
|
||||
opt.qualityColor = new Color(1f, 0.25f, 0.25f); // 红色/酒红
|
||||
break;
|
||||
}
|
||||
|
||||
// 判断是否为全局属性
|
||||
// 判断是否为全局属性 (20% 全体,80% 指定单体)
|
||||
bool isGlobalOnly = (opt.type == BuffType.SummonerCD ||
|
||||
opt.type == BuffType.SummonerPower ||
|
||||
opt.type == BuffType.KillGold ||
|
||||
opt.type == BuffType.GoldBonus);
|
||||
|
||||
List<UnitBase> activeHeroes = GetLocalPlayerHeroes();
|
||||
bool isAll = isGlobalOnly || (Random.value < 0.5f) || (activeHeroes.Count == 0);
|
||||
bool isAll = isGlobalOnly || (Random.value < 0.20f) || (activeHeroes.Count == 0);
|
||||
|
||||
string heroDisplayName = "";
|
||||
if (isAll)
|
||||
@@ -530,33 +670,33 @@ public class BuffSelectionController : MonoBehaviour
|
||||
switch (type)
|
||||
{
|
||||
case BuffType.AttackPower:
|
||||
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
|
||||
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : (quality == BuffQuality.Epic ? 0.20f : 0.32f));
|
||||
case BuffType.MaxHealth:
|
||||
return quality == BuffQuality.Common ? 0.10f : (quality == BuffQuality.Rare ? 0.18f : 0.30f);
|
||||
return quality == BuffQuality.Common ? 0.10f : (quality == BuffQuality.Rare ? 0.18f : (quality == BuffQuality.Epic ? 0.30f : 0.45f));
|
||||
case BuffType.AttackSpeed:
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.16f : 0.25f));
|
||||
case BuffType.CritRate:
|
||||
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : 0.20f);
|
||||
return quality == BuffQuality.Common ? 0.06f : (quality == BuffQuality.Rare ? 0.12f : (quality == BuffQuality.Epic ? 0.20f : 0.30f));
|
||||
case BuffType.Lifesteal:
|
||||
return quality == BuffQuality.Common ? 0.04f : (quality == BuffQuality.Rare ? 0.08f : 0.14f);
|
||||
return quality == BuffQuality.Common ? 0.04f : (quality == BuffQuality.Rare ? 0.08f : (quality == BuffQuality.Epic ? 0.14f : 0.20f));
|
||||
case BuffType.SkillCharge:
|
||||
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
|
||||
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : (quality == BuffQuality.Epic ? 0.25f : 0.40f));
|
||||
case BuffType.SummonerCD:
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.18f);
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.18f : 0.28f));
|
||||
case BuffType.SummonerPower:
|
||||
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : 0.25f);
|
||||
return quality == BuffQuality.Common ? 0.08f : (quality == BuffQuality.Rare ? 0.15f : (quality == BuffQuality.Epic ? 0.25f : 0.40f));
|
||||
case BuffType.AttackRange:
|
||||
return quality == BuffQuality.Common ? 0.5f : (quality == BuffQuality.Rare ? 1.0f : 1.8f);
|
||||
return quality == BuffQuality.Common ? 0.5f : (quality == BuffQuality.Rare ? 1.0f : (quality == BuffQuality.Epic ? 1.8f : 2.5f));
|
||||
case BuffType.DamageReduction:
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : 0.16f);
|
||||
return quality == BuffQuality.Common ? 0.05f : (quality == BuffQuality.Rare ? 0.10f : (quality == BuffQuality.Epic ? 0.16f : 0.25f));
|
||||
case BuffType.Shield:
|
||||
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.25f : 0.40f);
|
||||
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.25f : (quality == BuffQuality.Epic ? 0.40f : 0.60f));
|
||||
case BuffType.KillGold:
|
||||
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.30f : 0.50f);
|
||||
return quality == BuffQuality.Common ? 0.15f : (quality == BuffQuality.Rare ? 0.30f : (quality == BuffQuality.Epic ? 0.50f : 0.80f));
|
||||
case BuffType.GoldBonus:
|
||||
return quality == BuffQuality.Common ? 30f : (quality == BuffQuality.Rare ? 60f : 100f);
|
||||
return quality == BuffQuality.Common ? 30f : (quality == BuffQuality.Rare ? 60f : (quality == BuffQuality.Epic ? 100f : 160f));
|
||||
case BuffType.EmergencyHeal:
|
||||
return quality == BuffQuality.Common ? 0.25f : (quality == BuffQuality.Rare ? 0.40f : 0.60f);
|
||||
return quality == BuffQuality.Common ? 0.25f : (quality == BuffQuality.Rare ? 0.40f : (quality == BuffQuality.Epic ? 0.60f : 0.80f));
|
||||
default:
|
||||
return 0.10f;
|
||||
}
|
||||
@@ -595,10 +735,19 @@ public class BuffSelectionController : MonoBehaviour
|
||||
if (winTrans != null) buffSelectionWindow = winTrans.gameObject;
|
||||
}
|
||||
|
||||
if (buffSelectionWindow != null && cardsContainer == null)
|
||||
if (buffSelectionWindow != null)
|
||||
{
|
||||
if (cardsContainer == null)
|
||||
{
|
||||
var containerTrans = buffSelectionWindow.transform.Find("BuffPanel/BuffCardsContainer");
|
||||
if (containerTrans != null) cardsContainer = containerTrans;
|
||||
}
|
||||
|
||||
if (rollButton == null)
|
||||
{
|
||||
var rollTrans = buffSelectionWindow.transform.Find("BuffPanel/RollButton");
|
||||
if (rollTrans != null) rollButton = rollTrans.GetComponent<Button>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,10 @@ Shader "UI/GoldTextGradient"
|
||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
|
||||
_TopColor ("Top Gold Color", Color) = (1, 0.95, 0.6, 1)
|
||||
_BottomColor ("Bottom Gold Color", Color) = (0.9, 0.65, 0.1, 1)
|
||||
_TopColor ("Top Color (White)", Color) = (1.0, 0.98, 0.95, 1.0)
|
||||
_MiddleColor ("Middle Color (Light Gold)", Color) = (1.0, 0.82, 0.4, 1.0)
|
||||
_BottomColor ("Bottom Color (Amber Gold)", Color) = (0.82, 0.5, 0.1, 1.0)
|
||||
_MidPoint ("Gradient Mid Point", Range(0.1, 0.9)) = 0.35
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
@@ -68,9 +70,12 @@ Shader "UI/GoldTextGradient"
|
||||
|
||||
fixed4 _Color;
|
||||
fixed4 _TopColor;
|
||||
fixed4 _MiddleColor;
|
||||
fixed4 _BottomColor;
|
||||
float _MidPoint;
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _TextureSampleAdd;
|
||||
|
||||
v2f vert(appdata_t IN)
|
||||
{
|
||||
@@ -79,7 +84,19 @@ Shader "UI/GoldTextGradient"
|
||||
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
|
||||
OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _MainTex);
|
||||
|
||||
fixed4 gradColor = lerp(_BottomColor, _TopColor, saturate(IN.texcoord.y));
|
||||
float y = saturate(IN.texcoord.y);
|
||||
fixed4 gradColor;
|
||||
if (y > _MidPoint)
|
||||
{
|
||||
float t = (y - _MidPoint) / (1.0 - _MidPoint);
|
||||
gradColor = lerp(_MiddleColor, _TopColor, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = y / _MidPoint;
|
||||
gradColor = lerp(_BottomColor, _MiddleColor, t);
|
||||
}
|
||||
|
||||
OUT.color = IN.color * _Color * gradColor;
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:yousandi.cn,2023:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: gradient_circle_ADD
|
||||
m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHABLEND_ON
|
||||
- _FADING_ON
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 0
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 3000
|
||||
stringTagMap:
|
||||
RenderType: Transparent
|
||||
disabledShaderPasses:
|
||||
- GRABPASS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 0eeec03d219046ecb9cc4d051bdb0ccd, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _BlendOp: 0
|
||||
- _BumpScale: 1
|
||||
- _CameraFadingEnabled: 0
|
||||
- _CameraFarFadeDistance: 2
|
||||
- _CameraNearFadeDistance: 1
|
||||
- _ColorMode: 0
|
||||
- _Cull: 0
|
||||
- _Cutoff: 0.5
|
||||
- _DistortionBlend: 0.5
|
||||
- _DistortionEnabled: 0
|
||||
- _DistortionStrength: 1
|
||||
- _DistortionStrengthScaled: 0
|
||||
- _DstBlend: 1
|
||||
- _EmissionEnabled: 0
|
||||
- _FlipbookMode: 0
|
||||
- _InvFade: 1
|
||||
- _LightingEnabled: 0
|
||||
- _Mode: 4
|
||||
- _SoftParticlesEnabled: 1
|
||||
- _SoftParticlesFarFadeDistance: 0.5
|
||||
- _SoftParticlesNearFadeDistance: 0
|
||||
- _SrcBlend: 5
|
||||
- _ZWrite: 0
|
||||
m_Colors:
|
||||
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SoftParticleFadeParams: {r: 0, g: 2, b: 0, a: 0}
|
||||
- _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2577b005a1f54cfda2f4aaae33e08294
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: WX8XvC6rAHLu3SZwZD3d+Mv1uUS/aGddQKv2a1vQ+yvZsGTXkjTt8wo=
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: D3xM4HypB37iBlvRcAiTrbYIo8cOlqEfaN+IXJKRIE5GVUxFmK6+zeI=
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 439cf2d390c444d6b428b530f5b7eec3
|
||||
timeCreated: 1448725631
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,21 +1,22 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8787ae9d4a3a4e92a6799c3aa0c13ac4
|
||||
timeCreated: 1520362796
|
||||
licenseType: Store
|
||||
guid: B30Xsnv4Wi+OvweaNazT4i1PhBCwB95YnJ/aipg2YVrG1qqIvS6dAvA=
|
||||
AudioImporter:
|
||||
serializedVersion: 6
|
||||
externalObjects: {}
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
quality: 0.29999998
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
preloadAudioData: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15520b8ee45e4432a669f6f5eaf0fdf4
|
||||
guid: Dn8atyr/Wy4CCl4fJhLc+nF733+HdmXoF2bHSYlIJfHwUJRMVtYBuLg=
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 6
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
quality: 0.29999998
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
preloadAudioData: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59c606addec946ddb80cf1bd77227243
|
||||
guid: CnNMsyqrAi9gaVSzBHa42o9BpEk6k2mrIzB/sTeCaLsSu9mQgxY+Ga0=
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 6
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
quality: 0.29999998
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
preloadAudioData: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3443e358a73b4a6dbb62ad72164ec2b2
|
||||
timeCreated: 1520362796
|
||||
licenseType: Store
|
||||
guid: DH4btn+uVnMofEIfWxGRXp7edhpNoYv5iBLLQrnKn1hXUCnEv5klu1c=
|
||||
AudioImporter:
|
||||
serializedVersion: 6
|
||||
externalObjects: {}
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
quality: 0.29999998
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
preloadAudioData: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,116 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0eeec03d219046ecb9cc4d051bdb0ccd
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Windows Store Apps
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -146,6 +146,8 @@
|
||||
- [2026-10-08 00:04:53] 天雷狂轰技能实现:点击释放时在全屏中心激发 NovaLightningBlue 蓝闪电光环,同时按 80ms 节奏交错向全场敌方单位头顶劈下 LightningExplosionBlue 落雷(无敌人时在阵地随机轰炸 6 道),造成最终结算伤害并弹出漂字。具体路径与配置已同步写入项目。
|
||||
- [2026-10-08 16:33:27] 2026-10-08 敌方单位不共享 3 选 1 战斗增益卡,增益卡为玩家专属 Build 系统;关卡难度通过敌方英雄等级、星级和数量显式决定,不进行隐式数据调整。
|
||||
- [2026-10-08 16:33:41] 2026-10-08 塔防版等级与成长规则:不设立账号/玩家等级,所有成长挂载在卡牌自身的卡牌等级(1~10级),消耗金币与卡牌碎片升级;在 1/3/6/10 级大节点解锁/强化技能机制。
|
||||
- [2026-10-09 11:31:55] 2026-10-09 3 选 1 增益 D-Card 规则:刷新消耗固定 2 金币,3 个卡槽独立计算递进升阶概率(初升中 10%+10%/次,中升高 3%+5%/次,高升顶 1%+2%/次),刷出升阶重置该槽位追加概率;选卡后重置全槽位。UI 金色文本统一使用三阶渐变 Shader UI/GoldTextGradient 与暗色 Outline。
|
||||
- [2026-10-09 16:22:12] 2026-10-09 护盾 UI 规则:独立顶层护盾条 (ShieldBar),护盾槽物理宽度按 maxShield/maxHealth 占血条比例显示并保留黑边框,受击时按 currentShield/maxShield 在黑框内向左平滑缩减。盾卫技能默认使用内置粒子特效 MagicShieldBlue.prefab。
|
||||
|
||||
### Reference
|
||||
- [2026-09-09 12:23:17] 飞行物脚本 Projectile.cs 负责锁敌追踪、碰撞检测和伤害结算。箭矢预制体 ArrowProjectile_White.prefab 已配置 Projectile 组件和 CapsuleCollider(Is Trigger)。远程单位需在攻击动画松弦帧添加 OnAttackEffect Event,并在 UnitBase 中设置 attackType=RangedProjectile、projectilePrefab、projectileSpawnPoint。
|
||||
Reference in new issue
Block a user