一大波优化
@@ -1,5 +1,6 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GameFramework;
|
||||
namespace IndianOceanAssets.Engine2_5D
|
||||
{
|
||||
@@ -20,8 +21,21 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
[SerializeField]
|
||||
private bool isPlayer; // Is this the player?
|
||||
|
||||
[Header("消隐特效")]
|
||||
[Tooltip("死亡消隐使用的溶解材质(Custom/SpriteDissolve shader)")]
|
||||
[SerializeField] private Material dissolveMaterial;
|
||||
|
||||
[Tooltip("消隐动画持续时间(秒)")]
|
||||
[SerializeField] private float fadeDuration = 2f;
|
||||
|
||||
private bool _isDead = false; // 防止重复触发死亡
|
||||
|
||||
/// <summary>是否已死亡(只读)</summary>
|
||||
public bool IsDead => _isDead;
|
||||
|
||||
/// <summary>最大血量(只读)</summary>
|
||||
public int MaxHealth => maxHealth;
|
||||
|
||||
// Initializes health
|
||||
private void Start()
|
||||
{
|
||||
@@ -64,7 +78,67 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
// Handles death logic and effects
|
||||
public void Die()
|
||||
{
|
||||
Instantiate(deathEffect, transform.position + new Vector3(0f, .5f, 0f), Quaternion.identity);
|
||||
if (deathEffect != null)
|
||||
Instantiate(deathEffect, transform.position + new Vector3(0f, .5f, 0f), Quaternion.identity);
|
||||
|
||||
// 如果有溶解材质,播放消隐动画后再销毁
|
||||
if (dissolveMaterial != null)
|
||||
{
|
||||
StartCoroutine(DissolveAndDestroy());
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 溶解消隐协程:切换材质 → 禁用碰撞/AI → 动画溶解 → 销毁。
|
||||
/// </summary>
|
||||
private IEnumerator DissolveAndDestroy()
|
||||
{
|
||||
// 禁用碰撞体和刚体,防止死亡过程中仍能触发碰撞或受重力下坠
|
||||
foreach (var col in GetComponents<Collider>())
|
||||
col.enabled = false;
|
||||
foreach (var col in GetComponents<Collider2D>())
|
||||
col.enabled = false;
|
||||
foreach (var rb in GetComponents<Rigidbody>())
|
||||
rb.isKinematic = true;
|
||||
foreach (var rb in GetComponents<Rigidbody2D>())
|
||||
rb.isKinematic = true;
|
||||
|
||||
// 禁用所有 MonoBehaviour(AI、动画等),防止死亡后仍移动/攻击
|
||||
foreach (var mb in GetComponents<MonoBehaviour>())
|
||||
{
|
||||
if (mb != this)
|
||||
mb.enabled = false;
|
||||
}
|
||||
|
||||
// 切换所有 SpriteRenderer 到溶解材质
|
||||
var renderers = GetComponentsInChildren<SpriteRenderer>();
|
||||
var originalMats = new Material[renderers.Length][];
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
originalMats[i] = renderers[i].materials;
|
||||
renderers[i].materials = new Material[] { new Material(dissolveMaterial) };
|
||||
}
|
||||
|
||||
// 动画溶解
|
||||
float elapsed = 0f;
|
||||
while (elapsed < fadeDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / fadeDuration);
|
||||
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
if (renderers[i] != null && renderers[i].material != null)
|
||||
renderers[i].material.SetFloat("_DissolveAmount", t);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +30,19 @@ public static class SpiritLanternPrefabCreator
|
||||
// SpiritLantern — 灵灯行为脚本
|
||||
root.AddComponent<SpiritLantern>();
|
||||
|
||||
// CircleCollider2D — 触发器,检测怪物接触
|
||||
// SphereCollider — 触发器,检测怪物接触(使用3D物理系统匹配敌人的CapsuleCollider)
|
||||
// 【碰撞检测说明】
|
||||
// 灵灯是主动检测方:灵灯的 Trigger 检测进入的怪物 Collider2D
|
||||
// 需确保 Edit > Project Settings > Physics 2D 碰撞矩阵中,
|
||||
// 灵灯是主动检测方:灵灯的 Trigger 检测进入的怪物 Collider
|
||||
// 需确保 Edit > Project Settings > Physics 碰撞矩阵中,
|
||||
// 灵灯所在 Layer 与怪物 Layer 的交叉项已勾选
|
||||
CircleCollider2D collider = root.AddComponent<CircleCollider2D>();
|
||||
SphereCollider collider = root.AddComponent<SphereCollider>();
|
||||
collider.isTrigger = true;
|
||||
collider.radius = 0.5f;
|
||||
collider.radius = 5f; // 补偿 Transform scale 0.1,实际碰撞半径 = 5 * 0.1 = 0.5
|
||||
|
||||
// Rigidbody2D — 物理碰撞需要(Trigger 需要至少一方有 Rigidbody)
|
||||
Rigidbody2D rb = root.AddComponent<Rigidbody2D>();
|
||||
// Rigidbody — 物理碰撞需要(Trigger 需要至少一方有 Rigidbody)
|
||||
Rigidbody rb = root.AddComponent<Rigidbody>();
|
||||
rb.isKinematic = true;
|
||||
rb.bodyType = RigidbodyType2D.Kinematic;
|
||||
rb.useGravity = false;
|
||||
|
||||
// 设置 Layer 为 Default
|
||||
root.layer = LayerMask.NameToLayer("Default");
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
[SerializeField] private Transform followTarget;
|
||||
[SerializeField] private Transform maskPlane;
|
||||
[SerializeField] private Vector3 maskOffset = new Vector3(0f, 5f, 0f);
|
||||
[SerializeField] private float planeSize = 50f;
|
||||
|
||||
// Shader Property IDs
|
||||
private static readonly int LightDataID = Shader.PropertyToID("_LightData");
|
||||
|
||||
@@ -31,7 +31,7 @@ Material:
|
||||
- _EchoRadius: 39.41617
|
||||
- _EchoWidth: 2.5
|
||||
- _LightSoftness: 1
|
||||
- _MinBrightness: 0
|
||||
- _MinBrightness: 1
|
||||
m_Colors:
|
||||
- _DarknessColor: {r: 0.01, g: 0.01, b: 0.02, a: 1}
|
||||
- _EchoCenter: {r: -2.4399998, g: -1.5000057, b: 0, a: 0}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
%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: SpriteDissolve
|
||||
m_Shader: {fileID: 4800000, guid: 329aabb9d7e77d04295a944f844af20e, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _CastShadow: 0
|
||||
- _DissolveAmount: 0
|
||||
- _DissolveEdgeWidth: 0.08
|
||||
- _DissolveNoiseScale: 10
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _DissolveEdgeColor: {r: 1, g: 0.8, b: 0.3, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: XngdtS34U3q7jtUf6N0YEJu6txcsCpUWL91rvDjiR9mHR2rHf2SeiDQ=
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,154 @@
|
||||
Shader "Custom/SpriteDissolve"
|
||||
{
|
||||
// 敌人死亡消隐 Shader
|
||||
// 基于噪声的溶解效果,带发光边缘
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_Color ("颜色", Color) = (1, 1, 1, 1)
|
||||
|
||||
[Header(Dissolve)]
|
||||
_DissolveAmount ("溶解进度", Range(0, 1)) = 0
|
||||
_DissolveEdgeWidth ("边缘宽度", Range(0, 0.3)) = 0.05
|
||||
_DissolveEdgeColor ("边缘颜色", Color) = (1, 0.8, 0.3, 1)
|
||||
_DissolveNoiseScale ("噪声缩放", Float) = 10
|
||||
|
||||
// 阴影设置(保持与项目一致)
|
||||
[MaterialToggle] _CastShadow ("投射阴影", Float) = 0
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"RenderPipeline" = "UniversalPipeline"
|
||||
"Queue" = "Transparent"
|
||||
"RenderType" = "Transparent"
|
||||
}
|
||||
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
ZWrite Off
|
||||
Cull Off
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "SpriteDissolve"
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Attributes
|
||||
{
|
||||
float4 positionOS : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float4 positionCS : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 color : COLOR;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
};
|
||||
|
||||
TEXTURE2D(_MainTex);
|
||||
SAMPLER(sampler_MainTex);
|
||||
|
||||
half4 _Color;
|
||||
half _DissolveAmount;
|
||||
half _DissolveEdgeWidth;
|
||||
half4 _DissolveEdgeColor;
|
||||
float _DissolveNoiseScale;
|
||||
|
||||
// 简单的哈希噪声
|
||||
float hash(float2 p)
|
||||
{
|
||||
float3 p3 = frac(float3(p.xyx) * 0.1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return frac((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
// 值噪声(平滑插值)
|
||||
float valueNoise(float2 p)
|
||||
{
|
||||
float2 i = floor(p);
|
||||
float2 f = frac(p);
|
||||
f = f * f * (3.0 - 2.0 * f); // smoothstep
|
||||
|
||||
float a = hash(i);
|
||||
float b = hash(i + float2(1, 0));
|
||||
float c = hash(i + float2(0, 1));
|
||||
float d = hash(i + float2(1, 1));
|
||||
|
||||
return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y);
|
||||
}
|
||||
|
||||
// 分形噪声(多层叠加)
|
||||
float fbm(float2 p)
|
||||
{
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
value += amplitude * valueNoise(p);
|
||||
p *= 2.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Varyings vert(Attributes input)
|
||||
{
|
||||
Varyings output;
|
||||
UNITY_SETUP_INSTANCE_ID(input);
|
||||
UNITY_TRANSFER_INSTANCE_ID(input, output);
|
||||
|
||||
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
|
||||
output.uv = input.uv;
|
||||
output.color = input.color;
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 frag(Varyings input) : SV_Target
|
||||
{
|
||||
UNITY_SETUP_INSTANCE_ID(input);
|
||||
|
||||
half4 texColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
|
||||
half4 color = texColor * _Color * input.color;
|
||||
|
||||
// 如果 alpha 太小,直接丢弃
|
||||
if (color.a < 0.01)
|
||||
discard;
|
||||
|
||||
// 计算噪声
|
||||
float noise = fbm(input.uv * _DissolveNoiseScale);
|
||||
|
||||
// 溶解判定
|
||||
float dissolveEdge = smoothstep(
|
||||
_DissolveAmount - _DissolveEdgeWidth,
|
||||
_DissolveAmount + _DissolveEdgeWidth,
|
||||
noise
|
||||
);
|
||||
|
||||
// 完全溶解区域
|
||||
if (noise < _DissolveAmount)
|
||||
discard;
|
||||
|
||||
// 边缘发光
|
||||
half edgeGlow = (1.0 - dissolveEdge) * step(_DissolveAmount, noise);
|
||||
color.rgb += _DissolveEdgeColor.rgb * edgeGlow * _DissolveEdgeColor.a;
|
||||
|
||||
// 整体淡出(溶解后期整体变淡)
|
||||
color.a *= lerp(1.0, 0.3, _DissolveAmount);
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
FallBack "Sprites/Default"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: DHgW5Hv/AXJ4FVCaTjexQVJQLVXJgr6jZu7Vz9GpO936bn5BeMA1Iv0=
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -155,14 +155,13 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
darknessMaterial: {fileID: 2100000, guid: 6dd18d2379aff3040813ab8005b1898a, type: 2}
|
||||
debugShowAll: 0
|
||||
debugShowAll: 1
|
||||
minBrightness: 0
|
||||
darknessColor: {r: 0.01, g: 0.01, b: 0.02, a: 1}
|
||||
lightSoftness: 1
|
||||
followTarget: {fileID: 0}
|
||||
maskPlane: {fileID: 338266643}
|
||||
maskOffset: {x: 0, y: 0, z: 0}
|
||||
planeSize: 300
|
||||
--- !u!4 &8914096
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2177,7 +2176,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
soulIcon: {fileID: 1702223844}
|
||||
soulCountText: {fileID: 0}
|
||||
soulCountText: {fileID: 1966623134}
|
||||
lifeIcons:
|
||||
- {fileID: 638120134}
|
||||
- {fileID: 418448781}
|
||||
@@ -23732,7 +23731,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 0d5773b80ec8afd4791808534be2cad0, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 6e4b0f3fd19ef6e43bd2b1de2deda712, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 1
|
||||
m_FillCenter: 1
|
||||
@@ -27892,7 +27891,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 2a44135eb81109c46a745051acf0d174, type: 3}
|
||||
m_Sprite: {fileID: 21300000, guid: 53578ea97fc4e91439f189bf8a6cfc1a, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 1
|
||||
m_FillCenter: 1
|
||||
@@ -30863,7 +30862,7 @@ RectTransform:
|
||||
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_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 77247845}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@@ -30891,7 +30890,7 @@ MonoBehaviour:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1966623129}
|
||||
m_Enabled: 0
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
@@ -30904,10 +30903,10 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text:
|
||||
m_text: 0
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontAsset: {fileID: 11400000, guid: 7b2d3c4c2e857f14aaa0e7fb9ebab7e5, type: 2}
|
||||
m_sharedMaterial: {fileID: -1264883736430894452, guid: 7b2d3c4c2e857f14aaa0e7fb9ebab7e5, type: 2}
|
||||
m_fontSharedMaterials:
|
||||
- {fileID: 1767129619}
|
||||
m_fontMaterial: {fileID: 1767129619}
|
||||
@@ -30933,8 +30932,8 @@ MonoBehaviour:
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 74
|
||||
m_fontSizeBase: 74
|
||||
m_fontSize: 71.6
|
||||
m_fontSizeBase: 71.6
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
|
||||
@@ -694,6 +694,8 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
gameState: 0
|
||||
lightShrinkDuration: 2
|
||||
lostFadeOutDuration: 1
|
||||
lightExpandDuration: 1.5
|
||||
lightExpandTargetRadius: 20
|
||||
fadeOutDuration: 1
|
||||
@@ -1247,12 +1249,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: ae410525a1083304f99715898b5338e5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
selectSFX:
|
||||
audioClip: {fileID: 8300000, guid: 370972ec1bb4cc64e8a96ce64057334c, type: 3}
|
||||
volume: 1
|
||||
submitSFX:
|
||||
audioClip: {fileID: 8300000, guid: 6940b0d02566470418c2e593a6ff04c7, type: 3}
|
||||
volume: 1
|
||||
selectSFX: {fileID: 0}
|
||||
submitSFX: {fileID: 0}
|
||||
--- !u!1 &875024152
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3862,7 +3860,7 @@ Canvas:
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_AdditionalShaderChannelsFlag: 25
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 10001
|
||||
@@ -4014,7 +4012,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
soulIcon: {fileID: 438331633}
|
||||
soulCountText: {fileID: 437831458}
|
||||
soulCountText: {fileID: 0}
|
||||
lifeIcons:
|
||||
- {fileID: 670078144}
|
||||
- {fileID: 1117279259}
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: Dy4asi2uAXPSRLtfrAXmNGo8rX/KQN2CcdpDaGMA4CgXobkltjbRQ8w=
|
||||
guid: CnkasiL4AnIXKHpzla4mCR9uO0t74g/sF/nOx1EMdKve+q+dqG0KN8A=
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
@@ -112,48 +112,6 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
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: HMIAndroid
|
||||
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: OpenHarmony
|
||||
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: []
|
||||
|
After Width: | Height: | Size: 51 KiB |
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: DSsbsSuuVi58o7D3QwqOseqio8g1ATIkYVOwcZk70qbC8UtJT5u9diM=
|
||||
guid: CS8b5yr7UC2oP9/o+YSbdAyXctWdoeaSPwLy6JKTBjPaGBTBSFCLwtU=
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
@@ -112,48 +112,6 @@ TextureImporter:
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
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: HMIAndroid
|
||||
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: OpenHarmony
|
||||
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: []
|
||||
|
Before Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: CCgd4Sn+VyhSX6Sy1vJRD19RHy1W+JPDmy9XAVXah14m2R0Z1sqGCZw=
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: XHNM4CqvASia0jo0hG6m5P9t/1WoqAwZ2zwrPjjJXY6Fd1JeD5HXmaQ=
|
||||
TrueTypeFontImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
fontSize: 16
|
||||
forceTextureCase: -2
|
||||
characterSpacing: 0
|
||||
characterPadding: 1
|
||||
includeFontData: 1
|
||||
fontNames:
|
||||
- 392-SS Ni Chang Ti
|
||||
fallbackFontReferences: []
|
||||
customCharacters:
|
||||
fontRenderingMode: 0
|
||||
ascentCalculationMode: 1
|
||||
useLegacyBoundsCalculation: 0
|
||||
shouldRoundAdvanceValue: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,6 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using IndianOceanAssets.Engine2_5D;
|
||||
|
||||
namespace GameFramework
|
||||
@@ -14,9 +15,12 @@ namespace GameFramework
|
||||
/// </summary>
|
||||
public class GameHUD : MonoBehaviour
|
||||
{
|
||||
/// <summary>魂灵图标的 RectTransform(供魂灵掉落物飞行定位)</summary>
|
||||
public static RectTransform SoulIconRect { get; private set; }
|
||||
|
||||
[Header("魂灵计数(左上角)")]
|
||||
[SerializeField] private Image soulIcon;
|
||||
[SerializeField] private Text soulCountText;
|
||||
[SerializeField] private TextMeshProUGUI soulCountText;
|
||||
|
||||
[Header("生命图标(左下角)")]
|
||||
[SerializeField] private Image[] lifeIcons; // 5个生命图标
|
||||
@@ -52,9 +56,16 @@ namespace GameFramework
|
||||
if (pauseButton != null)
|
||||
pauseButton.onClick.AddListener(OnPauseClick);
|
||||
|
||||
// 暴露魂灵图标 RectTransform(供魂灵掉落物飞行定位)
|
||||
if (soulIcon != null)
|
||||
SoulIconRect = soulIcon.rectTransform;
|
||||
|
||||
// 订阅分数事件(魂灵计数复用分数系统)
|
||||
ScoreManager.onScoreChanged += UpdateSoulCount;
|
||||
UpdateSoulCount(ScoreManager.Instance != null ? ScoreManager.Instance.CurrentScore : 0);
|
||||
if (ScoreManager.Instance != null)
|
||||
{
|
||||
ScoreManager.onScoreChanged += UpdateSoulCount;
|
||||
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
|
||||
}
|
||||
|
||||
UpdateLifeIcons();
|
||||
|
||||
@@ -83,7 +94,14 @@ namespace GameFramework
|
||||
private void UpdateSoulCount(int count)
|
||||
{
|
||||
if (soulCountText != null)
|
||||
{
|
||||
soulCountText.text = count.ToString();
|
||||
Debug.Log($"[GameHUD] UpdateSoulCount: {count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[GameHUD] soulCountText 未赋值!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace GameFramework
|
||||
{
|
||||
@@ -8,25 +9,34 @@ namespace GameFramework
|
||||
/// 让 EventSystem 通过 DontDestroyOnLoad 跨场景存活。
|
||||
/// 这样 Gameplay、Scoring 等场景的按钮都能正常响应点击。
|
||||
///
|
||||
/// 如果场景里没有 EventSystem,也可以挂到任意空物体上,
|
||||
/// 脚本会自动创建一个。
|
||||
/// 场景切换时如果新场景自带 EventSystem,会自动销毁重复的那个,
|
||||
/// 始终保证场景中只有一个 EventSystem。
|
||||
/// </summary>
|
||||
public class PersistentEventSystem : MonoBehaviour
|
||||
{
|
||||
void Awake()
|
||||
{
|
||||
// 如果场景里没有 EventSystem,自动创建
|
||||
if (FindObjectOfType<EventSystem>() == null)
|
||||
{
|
||||
var go = new GameObject("EventSystem");
|
||||
go.AddComponent<EventSystem>();
|
||||
go.AddComponent<StandaloneInputModule>();
|
||||
}
|
||||
|
||||
// 让 EventSystem 跨场景存活
|
||||
var eventSystem = FindObjectOfType<EventSystem>();
|
||||
if (eventSystem != null)
|
||||
DontDestroyOnLoad(eventSystem.gameObject);
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// 监听场景加载,清理重复的 EventSystem
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
// 新场景加载后,找到所有 EventSystem,保留自己所在的,销毁其余的
|
||||
var allES = FindObjectsOfType<EventSystem>();
|
||||
foreach (var es in allES)
|
||||
{
|
||||
if (es.gameObject != gameObject)
|
||||
Destroy(es.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ namespace GameFramework
|
||||
[SerializeField] AudioData pickUpSFX;
|
||||
[SerializeField] float respawnTime = 0f; // 0=不复活(永久消失)
|
||||
|
||||
[Header("Auto Assign")]
|
||||
[SerializeField] bool findScoreManagerAutomatically = true;
|
||||
|
||||
Collider pickupCollider;
|
||||
MeshRenderer meshRenderer;
|
||||
SpriteRenderer spriteRenderer;
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 137 KiB |
@@ -0,0 +1,107 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:yousandi.cn,2023:
|
||||
--- !u!1 &1000000000000001
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 7
|
||||
m_Component:
|
||||
- component: {fileID: 1000000000000002}
|
||||
- component: {fileID: 1000000000000003}
|
||||
- component: {fileID: 5026520723394658369}
|
||||
m_Layer: 0
|
||||
m_HasEditorInfo: 1
|
||||
m_Name: SoulDrop
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1000000000000002
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1000000000000001}
|
||||
serializedVersion: 2
|
||||
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: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &1000000000000003
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1000000000000001}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_virtualGeometry: 0
|
||||
m_virtualGeometryShadow: 0
|
||||
m_ShadingRate: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 200
|
||||
m_Sprite: {fileID: 21300000, guid: 26977bf2f4c1c2a439720e4ac20ed699, type: 3}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 0.65, y: 0.92}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!114 &5026520723394658369
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1000000000000001}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 05f5fd3b15112ea4a839e002766a56b9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
flyDuration: 0.8
|
||||
scoreValue: 1
|
||||
delayMin: 0
|
||||
delayMax: 0.3
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: Ci4c43mqVXLpSFSwxyzCy2YR04iF37g3Z6OFbLPfNeyhQQ8wiaslBAI=
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -126,15 +126,15 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
lifetime: 10
|
||||
damageOnContact: 1
|
||||
--- !u!58 &8338025974414621740
|
||||
CircleCollider2D:
|
||||
fadeOutDuration: 2
|
||||
soulDropPrefab: {fileID: 1000000000000001, guid: 5d3fc7696ceb3a24d9234355fee1c344, type: 3}
|
||||
--- !u!135 &8338025974414621740
|
||||
SphereCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6632183076166779216}
|
||||
m_Enabled: 1
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
@@ -143,48 +143,36 @@ CircleCollider2D:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 1
|
||||
m_UsedByEffector: 0
|
||||
m_UsedByComposite: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
serializedVersion: 2
|
||||
m_Radius: 0.5
|
||||
--- !u!50 &8635647722604209322
|
||||
Rigidbody2D:
|
||||
serializedVersion: 4
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Radius: 5
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!54 &8635647722604209322
|
||||
Rigidbody:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6632183076166779216}
|
||||
m_BodyType: 1
|
||||
m_Simulated: 1
|
||||
m_UseFullKinematicContacts: 0
|
||||
m_UseAutoMass: 0
|
||||
serializedVersion: 4
|
||||
m_Mass: 1
|
||||
m_LinearDrag: 0
|
||||
m_Drag: 0
|
||||
m_AngularDrag: 0.05
|
||||
m_GravityScale: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ImplicitCom: 1
|
||||
m_ImplicitTensor: 1
|
||||
m_UseGravity: 0
|
||||
m_IsKinematic: 0
|
||||
m_Interpolate: 0
|
||||
m_SleepingMode: 1
|
||||
m_CollisionDetection: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 0
|
||||
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
@@ -0,0 +1,133 @@
|
||||
fileFormatVersion: 2
|
||||
guid: DXwWsi3/BXk7uR1eMmEfbbF3pQH8K3u2VIV9GkCRR1J/An4VNoLdwm4=
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
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: 1
|
||||
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: 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: 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
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,114 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
|
||||
namespace GameFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 魂灵掉落物 —— 敌人被灵灯击杀后掉落。
|
||||
/// 从敌人位置飞向 HUD 魂灵计数图标,到达后加分并自毁。
|
||||
/// </summary>
|
||||
public class SoulDrop : MonoBehaviour
|
||||
{
|
||||
[Header("飞行参数")]
|
||||
[Tooltip("飞向 HUD 的飞行时间(秒)")]
|
||||
[SerializeField] private float flyDuration = 0.8f;
|
||||
|
||||
[Tooltip("每个掉落物的分数")]
|
||||
[SerializeField] private int scoreValue = 1;
|
||||
|
||||
[Header("延迟")]
|
||||
[Tooltip("出生后的随机延迟范围(秒),避免所有掉落物同时起飞")]
|
||||
[SerializeField] private float delayMin = 0f;
|
||||
[SerializeField] private float delayMax = 0.3f;
|
||||
|
||||
private RectTransform _targetUI;
|
||||
private SpriteRenderer _sr;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_sr = GetComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化掉落物:设置目标 UI 并启动飞行协程。
|
||||
/// </summary>
|
||||
public void Initialize(RectTransform targetUI)
|
||||
{
|
||||
_targetUI = targetUI;
|
||||
StartCoroutine(DelayedFly());
|
||||
}
|
||||
|
||||
private IEnumerator DelayedFly()
|
||||
{
|
||||
// 随机延迟,让掉落物有先后起飞的效果
|
||||
float delay = Random.Range(delayMin, delayMax);
|
||||
if (delay > 0f)
|
||||
yield return new WaitForSeconds(delay);
|
||||
|
||||
yield return StartCoroutine(FlyToHUD());
|
||||
}
|
||||
|
||||
private IEnumerator FlyToHUD()
|
||||
{
|
||||
Vector3 startPos = transform.position;
|
||||
Camera cam = Camera.main;
|
||||
if (cam == null || _targetUI == null)
|
||||
{
|
||||
// 降级处理:直接加分
|
||||
AddScoreAndDestroy();
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 目标:HUD 魂灵图标的屏幕位置
|
||||
Vector3 targetScreenPos = _targetUI.position;
|
||||
// 保持深度一致,避免转换出错
|
||||
float depth = Mathf.Abs(cam.WorldToScreenPoint(startPos).z);
|
||||
targetScreenPos.z = depth;
|
||||
Vector3 targetWorldPos = cam.ScreenToWorldPoint(targetScreenPos);
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < flyDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / flyDuration);
|
||||
|
||||
// 带弧度的飞行轨迹(先高后低)
|
||||
float arc = Mathf.Sin(t * Mathf.PI) * 0.5f;
|
||||
Vector3 pos = Vector3.Lerp(startPos, targetWorldPos, t);
|
||||
pos.y += arc;
|
||||
transform.position = pos;
|
||||
|
||||
// 逐渐缩小 + 旋转
|
||||
float scale = Mathf.Lerp(1f, 0.3f, t);
|
||||
transform.localScale = Vector3.one * scale;
|
||||
transform.Rotate(0, 0, 360f * Time.deltaTime);
|
||||
|
||||
// 渐隐效果(最后 30% 开始变淡)
|
||||
if (_sr != null && t > 0.7f)
|
||||
{
|
||||
float fadeT = (t - 0.7f) / 0.3f;
|
||||
_sr.color = new Color(_sr.color.r, _sr.color.g, _sr.color.b, 1f - fadeT);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
AddScoreAndDestroy();
|
||||
}
|
||||
|
||||
private void AddScoreAndDestroy()
|
||||
{
|
||||
// 确保 ScoreManager 存在(直接从 Gameplay 场景启动时可能没有)
|
||||
if (ScoreManager.Instance == null)
|
||||
{
|
||||
var go = new GameObject("ScoreManager");
|
||||
go.AddComponent<ScoreManager>();
|
||||
Debug.Log("[SoulDrop] 自动创建 ScoreManager");
|
||||
}
|
||||
Debug.Log($"[SoulDrop] 加分 +{scoreValue},当前总分: {ScoreManager.Instance.CurrentScore + scoreValue}");
|
||||
ScoreManager.Instance.AddScore(scoreValue);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: D39JsHz5UCnBU6cE/ziIfIKIuVF1r+F2bQjvK7afzo4asq+9jP9uAW4=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,19 +1,20 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
using GameFramework;
|
||||
|
||||
namespace IndianOceanAssets.Engine2_5D
|
||||
{
|
||||
/// <summary>
|
||||
/// 单个灵灯的行为组件 —— 挂在灵灯 Prefab 上。
|
||||
/// 放置后照亮周围区域(通过 LightSource),
|
||||
/// 怪物碰触时扣 1 点血并自毁回收,
|
||||
/// 存在时间到期后自动回收。
|
||||
/// 只伤害第一个碰触的怪物,触发 2s 渐隐消失流程,
|
||||
/// 怪物死亡后掉落魂灵飞向 HUD。
|
||||
///
|
||||
/// 【碰撞检测说明】
|
||||
/// 灵灯自身持有 CircleCollider2D (isTrigger=true),是主动检测方。
|
||||
/// 当怪物的 Collider2D 进入灵灯的触发区域时,OnTriggerEnter2D 被调用。
|
||||
/// 需确保 Physics2D 碰撞矩阵中,灵灯所在 Layer 与怪物 Layer 可互相触发。
|
||||
/// 灵灯不依赖 whatIsEnemy LayerMask,而是直接用 Tag/Component 判定目标。
|
||||
/// 灵灯自身持有 SphereCollider (isTrigger=true),是主动检测方。
|
||||
/// 使用 3D 物理系统以匹配敌人的 CapsuleCollider(3D)。
|
||||
/// </summary>
|
||||
public class SpiritLantern : MonoBehaviour
|
||||
{
|
||||
@@ -25,6 +26,14 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
[Tooltip("怪物碰触灵灯时受到的伤害")]
|
||||
[SerializeField] private int damageOnContact = 1;
|
||||
|
||||
[Header("渐隐")]
|
||||
[Tooltip("碰触后灵灯渐隐消失的时间(秒)")]
|
||||
[SerializeField] private float fadeOutDuration = 2f;
|
||||
|
||||
[Header("魂灵掉落")]
|
||||
[Tooltip("魂灵掉落物预制体")]
|
||||
[SerializeField] private GameObject soulDropPrefab;
|
||||
|
||||
/// <summary>灵灯被回收时触发(被 SpiritLanternSystem 订阅)</summary>
|
||||
public event Action OnLanternRecalled;
|
||||
|
||||
@@ -40,58 +49,140 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
_ownerSystem = system;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 确保 Rigidbody 为运动学模式,不受重力和物理影响
|
||||
var rb = GetComponent<Rigidbody>();
|
||||
if (rb != null)
|
||||
{
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 存在时间倒计时
|
||||
lifetime -= Time.deltaTime;
|
||||
if (lifetime <= 0f)
|
||||
{
|
||||
Recall();
|
||||
StartRecallSequence();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 怪物碰触灵灯时触发:扣血 + 回收灵灯。
|
||||
/// 灵灯的 Trigger 主动检测进入的碰撞体,排除玩家后对怪物造成伤害。
|
||||
/// 怪物碰触灵灯时触发:只伤害第一个碰到的敌人。
|
||||
/// 已触发回收后不再响应后续碰撞。
|
||||
/// </summary>
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// 排除玩家(玩家 Tag 为 "Player"),避免放置时与玩家碰撞体误触发
|
||||
if (other.CompareTag("Player"))
|
||||
return;
|
||||
// 已触发则忽略后续碰撞
|
||||
if (_recalled) return;
|
||||
|
||||
// 排除玩家
|
||||
if (other.CompareTag("Player")) return;
|
||||
|
||||
// 尝试获取怪物的 HealthSystem
|
||||
HealthSystem health = other.GetComponent<HealthSystem>();
|
||||
if (health != null)
|
||||
{
|
||||
// 在伤害前记录信息(用于掉落,因为 Die() 会 Destroy gameObject)
|
||||
Vector3 deathPos = other.transform.position;
|
||||
int enemyMaxHp = health.MaxHealth;
|
||||
|
||||
health.Damage(damageOnContact);
|
||||
Recall();
|
||||
|
||||
// 如果敌人死亡,生成魂灵掉落
|
||||
if (health.IsDead && soulDropPrefab != null)
|
||||
{
|
||||
Debug.Log($"[SpiritLantern] 敌人死亡! maxHp={enemyMaxHp}, 准备生成魂灵掉落");
|
||||
SpawnSoulDrops(deathPos, enemyMaxHp);
|
||||
}
|
||||
else if (health.IsDead)
|
||||
{
|
||||
Debug.LogWarning("[SpiritLantern] 敌人死亡但 soulDropPrefab 未配置!");
|
||||
}
|
||||
|
||||
// 开始渐隐消失流程
|
||||
StartRecallSequence();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收灵灯:显式取消事件订阅 → 触发通知 → 销毁 GameObject。
|
||||
/// 使用 _recalled 标志防止重复触发。
|
||||
/// 开始回收流程:立即通知系统回收名额 → 禁用碰撞 → 2s 渐隐 → 销毁。
|
||||
/// </summary>
|
||||
private void Recall()
|
||||
private void StartRecallSequence()
|
||||
{
|
||||
if (_recalled) return;
|
||||
_recalled = true;
|
||||
|
||||
// 1. 先通过 System 显式取消事件订阅,防止内存泄漏
|
||||
// 1. 立即取消事件订阅 + 通知系统(回收灵灯名额)
|
||||
if (_ownerSystem != null)
|
||||
{
|
||||
_ownerSystem.UnregisterLantern(this);
|
||||
}
|
||||
|
||||
// 2. 通知其他可能的订阅者(如 UI 等)
|
||||
OnLanternRecalled?.Invoke();
|
||||
OnLanternRecalled = null;
|
||||
|
||||
// 3. 销毁灵灯 GameObject
|
||||
// 2. 禁用碰撞体,开始渐隐
|
||||
var col = GetComponent<Collider>();
|
||||
if (col != null) col.enabled = false;
|
||||
|
||||
StartCoroutine(FadeOutAndDestroy(fadeOutDuration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渐隐协程:SpriteRenderer alpha 和 LightSource intensity 同步归零后销毁。
|
||||
/// </summary>
|
||||
private IEnumerator FadeOutAndDestroy(float duration)
|
||||
{
|
||||
var sr = GetComponent<SpriteRenderer>();
|
||||
var light = GetComponent<LightSource>();
|
||||
|
||||
float startAlpha = sr != null ? sr.color.a : 1f;
|
||||
float startIntensity = light != null ? light.Intensity : 1f;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
|
||||
if (sr != null)
|
||||
sr.color = new Color(sr.color.r, sr.color.g, sr.color.b, Mathf.Lerp(startAlpha, 0f, t));
|
||||
if (light != null)
|
||||
light.SetIntensity(Mathf.Lerp(startIntensity, 0f, t));
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在敌人死亡位置生成魂灵掉落物,飞向 HUD 魂灵计数。
|
||||
/// 数量 = 敌人最大血量 × 2~5 倍。
|
||||
/// </summary>
|
||||
private void SpawnSoulDrops(Vector3 position, int enemyMaxHp)
|
||||
{
|
||||
int count = enemyMaxHp * UnityEngine.Random.Range(2, 6); // 2~5 倍
|
||||
RectTransform hudTarget = GameHUD.SoulIconRect;
|
||||
Debug.Log($"[SpiritLantern] SpawnSoulDrops: count={count}, hudTarget={(hudTarget != null ? hudTarget.name : "NULL")}");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 offset = new Vector3(
|
||||
UnityEngine.Random.Range(-0.3f, 0.3f),
|
||||
UnityEngine.Random.Range(0f, 0.3f),
|
||||
UnityEngine.Random.Range(-0.3f, 0.3f)
|
||||
);
|
||||
GameObject drop = Instantiate(soulDropPrefab, position + offset, Quaternion.identity);
|
||||
SoulDrop soul = drop.GetComponent<SoulDrop>();
|
||||
if (soul != null)
|
||||
soul.Initialize(hudTarget);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全网:如果灵灯被非正常销毁(场景卸载、外部 Destroy 等),
|
||||
/// 确保事件仍然触发,避免 SpiritLanternSystem 的灵灯计数永久丢失。
|
||||
|
||||
@@ -232,10 +232,17 @@ namespace IndianOceanAssets.Engine2_5D
|
||||
_playerHealth.Damage(attackDamage);
|
||||
}
|
||||
|
||||
// 触发攻击动画
|
||||
// 触发攻击动画(先检查参数是否存在,避免警告)
|
||||
if (_animator != null && !string.IsNullOrEmpty(attackAnimTrigger))
|
||||
{
|
||||
_animator.SetTrigger(attackAnimTrigger);
|
||||
for (int i = 0; i < _animator.parameterCount; i++)
|
||||
{
|
||||
if (_animator.GetParameter(i).name == attackAnimTrigger)
|
||||
{
|
||||
_animator.SetTrigger(attackAnimTrigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 播放攻击粒子特效 + 音效(生成在玩家位置,模拟命中效果)
|
||||
|
||||