(client) feat:添加消息定义,添加故事定义及其运算,武器动画可用,装备UI可用以及切换武器 fix:修复快速攻击导致协程释放出错卡死,重构为计时器,修复类型转换错误导致报错
This commit is contained in:
@ -104,7 +104,7 @@ GameObject:
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &2538501538304779414
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -191,3 +191,4 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
lifeTime: 3
|
||||
text: {fileID: 6038809438566997470}
|
||||
rectTransform: {fileID: 4733477304358545543}
|
||||
|
@ -51,6 +51,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
lifeTime: 3
|
||||
imageAnimator: {fileID: 7017785333925882891}
|
||||
rectTransform: {fileID: 8059943382946057764}
|
||||
--- !u!1001 &2344443364570347575
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -51,7 +51,8 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
lifeTime: 3
|
||||
text: {fileID: 0}
|
||||
text: {fileID: 8031956023686942989}
|
||||
rectTransform: {fileID: 2680222037762035614}
|
||||
--- !u!222 &362591481350297095
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
|
8
Client/Assets/Resources/tile.meta
Normal file
8
Client/Assets/Resources/tile.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe8297ebbb60d184da00012052494ce9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
185
Client/Assets/Resources/tile/BaseBuildingTile.prefab
Normal file
185
Client/Assets/Resources/tile/BaseBuildingTile.prefab
Normal file
@ -0,0 +1,185 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1630661159867178894
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7335345693260792414}
|
||||
- component: {fileID: 3679711633576053631}
|
||||
- component: {fileID: 2123075777024597353}
|
||||
m_Layer: 0
|
||||
m_Name: Layer1
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7335345693260792414
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1630661159867178894}
|
||||
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: 5500597088665946460}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1839735485 &3679711633576053631
|
||||
Tilemap:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1630661159867178894}
|
||||
m_Enabled: 1
|
||||
m_Tiles: {}
|
||||
m_AnimatedTiles: {}
|
||||
m_TileAssetArray: []
|
||||
m_TileSpriteArray: []
|
||||
m_TileMatrixArray: []
|
||||
m_TileColorArray: []
|
||||
m_TileObjectToInstantiateArray: []
|
||||
m_AnimationFrameRate: 1
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_Origin: {x: 0, y: 0, z: 0}
|
||||
m_Size: {x: 0, y: 0, z: 1}
|
||||
m_TileAnchor: {x: 0.5, y: 0.5, z: 0}
|
||||
m_TileOrientation: 0
|
||||
m_TileOrientationMatrix:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
--- !u!483693784 &2123075777024597353
|
||||
TilemapRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1630661159867178894}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
|
||||
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: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
m_MaxChunkCount: 16
|
||||
m_MaxFrameAge: 16
|
||||
m_SortOrder: 0
|
||||
m_Mode: 0
|
||||
m_DetectChunkCullingBounds: 0
|
||||
m_MaskInteraction: 0
|
||||
--- !u!1 &6974972188433201251
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5500597088665946460}
|
||||
- component: {fileID: 2491941156507880022}
|
||||
m_Layer: 0
|
||||
m_Name: BaseBuildingTile
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &5500597088665946460
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6974972188433201251}
|
||||
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:
|
||||
- {fileID: 7335345693260792414}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!156049354 &2491941156507880022
|
||||
Grid:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6974972188433201251}
|
||||
m_Enabled: 1
|
||||
m_CellSize: {x: 1, y: 1, z: 0}
|
||||
m_CellGap: {x: 0, y: 0, z: 0}
|
||||
m_CellLayout: 0
|
||||
m_CellSwizzle: 0
|
||||
--- !u!114 &8640838404236873408
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 12395, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name: Palette Settings
|
||||
m_EditorClassIdentifier:
|
||||
cellSizing: 0
|
||||
m_TransparencySortMode: 0
|
||||
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 515800086299f5b4da62d2d59a6c3e4a
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
614
Client/Assets/Scenes/Base.unity
Normal file
614
Client/Assets/Scenes/Base.unity
Normal file
@ -0,0 +1,614 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &564255131
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 564255135}
|
||||
- component: {fileID: 564255134}
|
||||
- component: {fileID: 564255133}
|
||||
- component: {fileID: 564255132}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &564255132
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 564255131}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &564255133
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 564255131}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!223 &564255134
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 564255131}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!224 &564255135
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 564255131}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!1 &1014522559
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1014522561}
|
||||
- component: {fileID: 1014522560}
|
||||
m_Layer: 0
|
||||
m_Name: Grid
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!156049354 &1014522560
|
||||
Grid:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1014522559}
|
||||
m_Enabled: 1
|
||||
m_CellSize: {x: 1, y: 1, z: 0}
|
||||
m_CellGap: {x: 0, y: 0, z: 0}
|
||||
m_CellLayout: 0
|
||||
m_CellSwizzle: 0
|
||||
--- !u!4 &1014522561
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1014522559}
|
||||
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:
|
||||
- {fileID: 1603095358}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1225578458
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1225578461}
|
||||
- component: {fileID: 1225578460}
|
||||
- component: {fileID: 1225578459}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1225578459
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1225578458}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_MoveRepeatDelay: 0.5
|
||||
m_MoveRepeatRate: 0.1
|
||||
m_XRTrackingOrigin: {fileID: 0}
|
||||
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_DeselectOnBackgroundClick: 1
|
||||
m_PointerBehavior: 0
|
||||
m_CursorLockBehavior: 0
|
||||
m_ScrollDeltaPerTick: 6
|
||||
--- !u!114 &1225578460
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1225578458}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &1225578461
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1225578458}
|
||||
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!1 &1603095357
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1603095358}
|
||||
- component: {fileID: 1603095360}
|
||||
- component: {fileID: 1603095359}
|
||||
m_Layer: 0
|
||||
m_Name: Tilemap
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1603095358
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1603095357}
|
||||
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: 1014522561}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!483693784 &1603095359
|
||||
TilemapRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1603095357}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
|
||||
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: 0
|
||||
m_ChunkSize: {x: 32, y: 32, z: 32}
|
||||
m_ChunkCullingBounds: {x: 0, y: 0, z: 0}
|
||||
m_MaxChunkCount: 16
|
||||
m_MaxFrameAge: 16
|
||||
m_SortOrder: 0
|
||||
m_Mode: 0
|
||||
m_DetectChunkCullingBounds: 0
|
||||
m_MaskInteraction: 0
|
||||
--- !u!1839735485 &1603095360
|
||||
Tilemap:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1603095357}
|
||||
m_Enabled: 1
|
||||
m_Tiles: {}
|
||||
m_AnimatedTiles: {}
|
||||
m_TileAssetArray: []
|
||||
m_TileSpriteArray: []
|
||||
m_TileMatrixArray: []
|
||||
m_TileColorArray: []
|
||||
m_TileObjectToInstantiateArray: []
|
||||
m_AnimationFrameRate: 1
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_Origin: {x: 0, y: 0, z: 0}
|
||||
m_Size: {x: 0, y: 0, z: 1}
|
||||
m_TileAnchor: {x: 0.5, y: 0.5, z: 0}
|
||||
m_TileOrientation: 0
|
||||
m_TileOrientationMatrix:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
--- !u!1 &1634944190
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1634944193}
|
||||
- component: {fileID: 1634944192}
|
||||
- component: {fileID: 1634944191}
|
||||
- component: {fileID: 1634944194}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1634944191
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1634944190}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1634944192
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1634944190}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 1
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &1634944193
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1634944190}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
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!114 &1634944194
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1634944190}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_Version: 2
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 1634944193}
|
||||
- {fileID: 564255135}
|
||||
- {fileID: 1225578461}
|
||||
- {fileID: 1014522561}
|
7
Client/Assets/Scenes/Base.unity.meta
Normal file
7
Client/Assets/Scenes/Base.unity.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4a25b7f7ab6fef4f9b6c506cebce83c
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -366,6 +366,51 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 194192102}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &206478243
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 206478245}
|
||||
- component: {fileID: 206478244}
|
||||
m_Layer: 0
|
||||
m_Name: MessageUI
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &206478244
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 206478243}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 067858a7493442389cc3f7624b83e1f3, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
isGlobal: 1
|
||||
--- !u!4 &206478245
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 206478243}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0.06356, y: 0.07093, 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!1 &336157944
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -2168,3 +2213,4 @@ SceneRoots:
|
||||
- {fileID: 1200712023}
|
||||
- {fileID: 117511565}
|
||||
- {fileID: 911482833}
|
||||
- {fileID: 206478245}
|
||||
|
@ -21,6 +21,10 @@ namespace Base
|
||||
public Vector2Int windowResolution = new(1920, 1080);
|
||||
|
||||
public string[] loadOrder;
|
||||
|
||||
public bool showHealthBarByHit = true;
|
||||
public bool alwaysShowHealthBar = false;
|
||||
public bool showHitNumber = true;
|
||||
}
|
||||
|
||||
// 当前游戏设置
|
||||
|
@ -3,6 +3,6 @@ namespace Data
|
||||
public class EventDef : Define
|
||||
{
|
||||
public string workClass;
|
||||
public string value;
|
||||
public string parameter;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ namespace Data
|
||||
}
|
||||
public class ItemDef : Define
|
||||
{
|
||||
public float FPS = 0;
|
||||
public float FPS = 3;
|
||||
public string[] textures;
|
||||
public ItemRarity rarity = ItemRarity.Common;
|
||||
public int maxStack = 10; // 最大堆叠数量,默认为10
|
||||
|
@ -12,6 +12,7 @@ namespace Data
|
||||
public BulletDef bullet;
|
||||
public string[] attackAnimation;
|
||||
public float attackDetectionTime = 0;
|
||||
public bool useEntityAttackAnimation = true;
|
||||
public WeaponDef() // 构造函数,用于设置武器的默认属性
|
||||
{
|
||||
maxStack = 1; // 武器默认最大堆叠为1
|
||||
|
@ -12,7 +12,7 @@ namespace Entity
|
||||
public override void SetTarget(Vector3 pos)
|
||||
{
|
||||
base.SetTarget(pos);
|
||||
RotateTransformToDirection(transform, direction);
|
||||
Utils.RotateTool.RotateTransformToDirection(transform, direction);
|
||||
}
|
||||
|
||||
protected override void AutoBehave()
|
||||
@ -41,19 +41,6 @@ namespace Entity
|
||||
attributes.health -= 1;
|
||||
}
|
||||
|
||||
// 旋转对象到指定方向
|
||||
public static void RotateTransformToDirection(Transform transform, Vector3 targetDirection)
|
||||
{
|
||||
// 确保目标方向不是零向量
|
||||
if (targetDirection == Vector3.zero)
|
||||
return;
|
||||
|
||||
// 计算当前向上方向与目标方向之间的角度
|
||||
var angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
|
||||
|
||||
|
||||
// 应用旋转
|
||||
transform.rotation = Quaternion.Euler(0f, 0f, angle);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ namespace Entity
|
||||
public override void Init(EntityDef entityDef)
|
||||
{
|
||||
Inventory = new Inventory(this, 3);
|
||||
|
||||
Inventory.OnInventoryChanged += InventoryChange;
|
||||
CurrentSelected = 0;
|
||||
base.Init(entityDef);
|
||||
}
|
||||
@ -64,7 +64,12 @@ namespace Entity
|
||||
public override WeaponResource GetCurrentWeapon()
|
||||
{
|
||||
var currentSelectItem = Inventory.GetSlot(CurrentSelected);
|
||||
return (WeaponResource)currentSelectItem?.Item;
|
||||
return currentSelectItem?.Item as WeaponResource;
|
||||
}
|
||||
|
||||
private void InventoryChange()
|
||||
{
|
||||
InitWeaponAnimator();
|
||||
}
|
||||
}
|
||||
}
|
@ -76,6 +76,8 @@ namespace Entity
|
||||
/// </summary>
|
||||
public Vector3 direction;
|
||||
|
||||
public Vector3 attackDirection;
|
||||
|
||||
/// <summary>
|
||||
/// 实体的身体部分,用于挂载动画和图像节点。
|
||||
/// </summary>
|
||||
@ -137,7 +139,10 @@ namespace Entity
|
||||
public bool IsDead => attributes.health <= 0;
|
||||
|
||||
public bool IsShowingHealthBarUI => _hitBarUIShowTimer > 0;
|
||||
public bool IsAttacking => _attackCoroutine != null;
|
||||
public bool IsAttacking => _attackTimer > 0;
|
||||
private float _attackTimer = 0;
|
||||
private float _attackDetectionTime = 0;
|
||||
private WeaponResource currentAttackWeapon;
|
||||
|
||||
/// <summary>
|
||||
/// 当实体受到伤害时触发的事件。
|
||||
@ -170,9 +175,7 @@ namespace Entity
|
||||
/// 当前实体的状态
|
||||
/// </summary>
|
||||
private EntityState _currentState = EntityState.Idle;
|
||||
|
||||
// 协程引用
|
||||
private Coroutine _attackCoroutine;
|
||||
|
||||
|
||||
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
@ -200,19 +203,19 @@ namespace Entity
|
||||
{
|
||||
if (weaponAnimator && weaponAnimator.transform.childCount > 0)
|
||||
{
|
||||
foreach (GameObject child in weaponAnimator.transform)
|
||||
foreach (Transform child in weaponAnimator.transform)
|
||||
{
|
||||
Destroy(child);
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
var currentWeapon = GetCurrentWeapon();
|
||||
if (currentWeapon?.AttackAnimationDef == null)
|
||||
if (currentWeapon?.AttackAnimation == null)
|
||||
return;
|
||||
weaponItem=GameObjectCreate.SpriteAnimator(currentWeapon.Icon.ToArray(), weaponAnimator.transform);
|
||||
weaponItem.SetFPS(currentWeapon.FPS);
|
||||
|
||||
weaponAttackAnimation = GameObjectCreate.SpriteAnimator(currentWeapon.AttackAnimationDef.ToArray(),
|
||||
weaponAttackAnimation = GameObjectCreate.SpriteAnimator(currentWeapon.AttackAnimation.ToArray(),
|
||||
weaponAnimator.transform);
|
||||
weaponAttackAnimation.SetFPS(currentWeapon.FPS);
|
||||
weaponAttackAnimation.gameObject.SetActive(false);
|
||||
@ -330,7 +333,7 @@ namespace Entity
|
||||
{
|
||||
AutoBehave();
|
||||
}
|
||||
|
||||
|
||||
if (IsShowingHealthBarUI)
|
||||
{
|
||||
_hitBarUIShowTimer -= Time.deltaTime;
|
||||
@ -339,6 +342,30 @@ namespace Entity
|
||||
HideHealthBar();
|
||||
}
|
||||
}
|
||||
|
||||
if (_attackTimer > 0)
|
||||
{
|
||||
_attackTimer -= Time.deltaTime;
|
||||
if (currentAttackWeapon != null && _attackTimer <= _attackDetectionTime)
|
||||
{
|
||||
if (currentAttackWeapon.Type == WeaponType.Melee)
|
||||
{
|
||||
ExecuteMeleeAttack(currentAttackWeapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteRangedAttack(currentAttackWeapon);
|
||||
}
|
||||
|
||||
currentAttackWeapon = null;
|
||||
}
|
||||
|
||||
if (_attackTimer <= 0)
|
||||
{
|
||||
|
||||
SetBodyTexture(EntityState.Idle, _currentOrientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -358,21 +385,39 @@ namespace Entity
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动基于武器的攻击协程
|
||||
_attackCoroutine = StartCoroutine(AttackFlow(currentWeapon));
|
||||
StartAttack(currentWeapon);
|
||||
}
|
||||
|
||||
private void StartAttack(WeaponResource weaponResource)
|
||||
{
|
||||
_attackTimer = weaponResource.AttackCooldown;
|
||||
_attackDetectionTime = weaponResource.AttackDetectionTime;
|
||||
if (weaponResource.AttackAnimationTime > 0)
|
||||
{
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(
|
||||
weaponResource.AttackAnimation.ToArray(), Position,transform, weaponResource.AttackAnimationTime,
|
||||
weaponResource.FPS);
|
||||
}
|
||||
if (weaponResource.UseEntityAttackAnimation)
|
||||
{
|
||||
SetBodyTexture(
|
||||
weaponResource.Type == WeaponType.Melee ? EntityState.MeleeAttack : EntityState.RangedAttack,
|
||||
_currentOrientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
HideCurrentBodyTexture();
|
||||
}
|
||||
|
||||
currentAttackWeapon = weaponResource;
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetBodyTexture(EntityState state, Orientation orientation)
|
||||
public void SetBodyTexture(EntityState state, Orientation orientation)
|
||||
{
|
||||
if (bodyNodes.TryGetValue(_currentState, out var stateNode))
|
||||
{
|
||||
if (stateNode.TryGetValue(_currentOrientation, out var node))
|
||||
{
|
||||
node.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
HideCurrentBodyTexture();
|
||||
if (IsAttacking && !currentAttackWeapon.UseEntityAttackAnimation)
|
||||
return;
|
||||
if (bodyNodes.TryGetValue(state, out var showStateNode))
|
||||
{
|
||||
if (showStateNode.TryGetValue(orientation, out var showNode))
|
||||
@ -385,13 +430,22 @@ namespace Entity
|
||||
_currentOrientation = orientation;
|
||||
}
|
||||
|
||||
public void HideCurrentBodyTexture()
|
||||
{
|
||||
if (!bodyNodes.TryGetValue(_currentState, out var stateNode)) return;
|
||||
if (stateNode.TryGetValue(_currentOrientation, out var node))
|
||||
{
|
||||
node.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据方向尝试移动实体。
|
||||
/// </summary>
|
||||
public virtual void TryMove()
|
||||
{
|
||||
if (IsAttacking)
|
||||
return;
|
||||
// if (IsAttacking)
|
||||
// return;
|
||||
transform.position += direction * (attributes.moveSpeed * Time.deltaTime);
|
||||
SetBodyTexture(EntityState.Walking, _currentOrientation);
|
||||
_walkingTimer = 2;
|
||||
@ -428,8 +482,15 @@ namespace Entity
|
||||
OnEntityDied?.Invoke(this);
|
||||
}
|
||||
|
||||
ShowHealthBar(); // 无论是否死亡,都更新血条UI
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(hit.ToString(), Position);
|
||||
if (Setting.Instance.CurrentSettings.showHealthBarByHit)
|
||||
{
|
||||
ShowHealthBar();
|
||||
}
|
||||
|
||||
if (Setting.Instance.CurrentSettings.showHitNumber)
|
||||
{
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(hit.ToString(), Position);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowHealthBar()
|
||||
@ -488,6 +549,10 @@ namespace Entity
|
||||
}
|
||||
|
||||
SetBodyTexture(_currentState, ori);
|
||||
if (!PlayerControlled)
|
||||
{
|
||||
attackDirection=direction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -523,6 +588,12 @@ namespace Entity
|
||||
/// </summary>
|
||||
protected virtual void UpdatePlayerControls()
|
||||
{
|
||||
if (Input.GetMouseButton(0))
|
||||
{
|
||||
var mousePos = MousePosition.GetWorldPosition();
|
||||
attackDirection = new Vector3(mousePos.x,mousePos.y) - Position;
|
||||
}
|
||||
|
||||
// 获取当前键盘输入状态(2D 移动,只使用 X 和 Y 轴)
|
||||
var inputDirection = Vector2.zero;
|
||||
|
||||
@ -566,89 +637,9 @@ namespace Entity
|
||||
// 调用 TryMove 方法处理实际移动逻辑
|
||||
TryMove();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// NEW: ShakeInDirectionCoroutine 签名修改以接收持续时间
|
||||
private IEnumerator ShakeInDirectionCoroutine(float duration)
|
||||
{
|
||||
var originalPosition = transform.position; // 记录原始位置
|
||||
// 在攻击动画持续时间内进行抖动效果
|
||||
transform.position += direction * 0.5f;
|
||||
yield return new WaitForSeconds(duration);
|
||||
transform.position = originalPosition;
|
||||
}
|
||||
|
||||
// NEW: AttackFlow 现在接收 WeaponResource 参数
|
||||
protected IEnumerator AttackFlow(WeaponResource weapon) // 将可见性改为 protected,允许子类访问
|
||||
{
|
||||
|
||||
// STEP 4: 等待到攻击判定时间
|
||||
var elapsedTime = 0f;
|
||||
while (elapsedTime < weapon.AttackCooldown)
|
||||
{
|
||||
if (IsDead)
|
||||
{
|
||||
/* 如果实体在此期间死亡,立刻中断 */
|
||||
break;
|
||||
}
|
||||
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null; // 等待一帧
|
||||
}
|
||||
|
||||
// 如果实体在等待期间死亡,清理并退出
|
||||
if (IsDead)
|
||||
{
|
||||
CleanupAttack(weapon);
|
||||
yield break;
|
||||
}
|
||||
|
||||
ExecuteWeaponAction(weapon);
|
||||
|
||||
var remainingAnimationTime = weapon.AttackAnimationTime - elapsedTime;
|
||||
if (remainingAnimationTime > 0)
|
||||
{
|
||||
yield return new WaitForSeconds(remainingAnimationTime);
|
||||
}
|
||||
else if (weapon.AttackAnimationTime > 0)
|
||||
{
|
||||
yield return new WaitForSeconds(weapon.AttackAnimationTime - elapsedTime);
|
||||
}
|
||||
|
||||
// STEP 7: 清理攻击状态
|
||||
CleanupAttack(weapon);
|
||||
}
|
||||
|
||||
private void CleanupAttack(WeaponResource weapon)
|
||||
{
|
||||
if (wearponAttackAnimationNodeRoot)
|
||||
{
|
||||
Destroy(wearponAttackAnimationNodeRoot);
|
||||
wearponAttackAnimationNodeRoot = null;
|
||||
}
|
||||
|
||||
_attackCoroutine = null;
|
||||
}
|
||||
|
||||
protected virtual void ExecuteWeaponAction(WeaponResource weapon) // 将可见性改为 protected,允许子类重写
|
||||
{
|
||||
if (weapon == null) return; // 安全检查
|
||||
|
||||
switch (weapon.Type)
|
||||
{
|
||||
case WeaponType.Melee:
|
||||
ExecuteMeleeAttack(weapon);
|
||||
break;
|
||||
case WeaponType.Ranged:
|
||||
ExecuteRangedAttack(weapon);
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning($"未知武器类型: {weapon.Type} for {name}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ExecuteMeleeAttack(WeaponResource weapon)
|
||||
{
|
||||
if (weapon.Attributes == null)
|
||||
@ -690,13 +681,7 @@ namespace Entity
|
||||
|
||||
// 获取子弹方向。这里使用实体当前的移动方向作为子弹发射方向
|
||||
// 更复杂的逻辑可能根据鼠标位置、目标位置等确定
|
||||
var bulletDirection = direction; // 实体当前的朝向
|
||||
if (PlayerControlled && Input.GetMouseButton(0)) // 玩家控制时,如果鼠标按下,尝试朝鼠标方向发射
|
||||
{
|
||||
var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
mouseWorldPos.z = transform.position.z; // 保持Z轴一致
|
||||
bulletDirection = (mouseWorldPos - transform.position).normalized;
|
||||
}
|
||||
var bulletDirection = attackDirection; // 实体当前的朝向
|
||||
|
||||
// 如果没有明确的方向,给一个默认值以防万一
|
||||
if (bulletDirection == Vector3.zero) bulletDirection = Vector3.down;
|
||||
|
@ -12,6 +12,7 @@ namespace Entity
|
||||
public class Pickup : Entity
|
||||
{
|
||||
public ItemResource itemResource;
|
||||
|
||||
protected override void AutoBehave()
|
||||
{
|
||||
}
|
||||
@ -49,6 +50,7 @@ namespace Entity
|
||||
var animator = animatorObj.GetComponent<SpriteAnimator>();
|
||||
animator.SetSprites(texture.ToArray());
|
||||
}
|
||||
|
||||
|
||||
SetBodyTexture(EntityState.Idle, Orientation.Down);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic; // 新增,用于List<T>
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
using Newtonsoft.Json; // 新增Newtonsoft.Json的引用
|
||||
|
||||
namespace EventWorkClass
|
||||
{
|
||||
@ -13,14 +15,15 @@ namespace EventWorkClass
|
||||
public class EntityGenerateConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 要生成的实体定义名 (defName)。
|
||||
/// 要生成的实体定义列表,将从中随机选择。
|
||||
/// </summary>
|
||||
public string EntityDefName;
|
||||
public List<EntityDefinitionEntry> DefinitionsToChooseFrom;
|
||||
|
||||
/// <summary>
|
||||
/// 要生成的实体定义的具体类型名,例如 "CharacterDef" 或 "MonsterDef"。
|
||||
/// 用于 DefineManager 的严格类型查找。
|
||||
/// 要生成的实体总数。
|
||||
/// </summary>
|
||||
public string EntityDefTypeName;
|
||||
public int Count = 1; // 默认生成一个
|
||||
|
||||
/// <summary>
|
||||
/// 生成位置类型。
|
||||
/// </summary>
|
||||
@ -37,7 +40,7 @@ namespace EventWorkClass
|
||||
/// <summary>
|
||||
/// 用于 InsideSpecificBuildingType 类型:目标建筑的类型ID。
|
||||
/// </summary>
|
||||
public string BuildingTypeId;
|
||||
public string BuildingTypeDefName;
|
||||
/// <summary>
|
||||
/// 用于 AroundTargetEntity 类型:目标派系的定义名 (factionDefName)。
|
||||
/// </summary>
|
||||
@ -45,12 +48,29 @@ namespace EventWorkClass
|
||||
/// <summary>
|
||||
/// 用于 AtPredefinedSpawnPoint 类型:预定义生成点的ID。
|
||||
/// </summary>
|
||||
public string SpawnPointId;
|
||||
public string SpawnPointTileMapDefName;
|
||||
/// <summary>
|
||||
/// 用于 OffMap 类型:距离地图边界的额外偏移量。
|
||||
/// </summary>
|
||||
public float OffMapOffset = 5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单个实体定义的配置条目。
|
||||
/// </summary>
|
||||
[Serializable] // 保持可序列化,以便在Unity Inspector中显示
|
||||
public class EntityDefinitionEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体定义名 (defName)。
|
||||
/// </summary>
|
||||
public string DefName;
|
||||
/// <summary>
|
||||
/// 实体定义的具体类型名。
|
||||
/// </summary>
|
||||
public string DefTypeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定义生成器生成地图实体的可能位置类型。
|
||||
/// </summary>
|
||||
@ -91,10 +111,11 @@ namespace EventWorkClass
|
||||
/// </summary>
|
||||
AtPredefinedSpawnPoint
|
||||
}
|
||||
|
||||
public class Event_EntityGenerater : EventWorkClassBase
|
||||
{
|
||||
private EntityGenerateConfig _config;
|
||||
private EntityDef _aimEntity;
|
||||
private List<EntityDef> _validatedEntityDefs; // 用于存储所有已验证的实体定义
|
||||
|
||||
/// <summary>
|
||||
/// 初始化实体生成器事件。
|
||||
@ -109,26 +130,46 @@ namespace EventWorkClass
|
||||
}
|
||||
try
|
||||
{
|
||||
_config = JsonUtility.FromJson<EntityGenerateConfig>(value);
|
||||
if (_config == null)
|
||||
{
|
||||
Debug.LogError($"无法解析配置JSON: {value}");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(_config.EntityDefTypeName))
|
||||
{
|
||||
Debug.LogError($"实体定义类型名为空或null (实体定义名: '{_config.EntityDefName}')。无法查找实体定义。");
|
||||
return;
|
||||
}
|
||||
_aimEntity = (EntityDef)DefineManager.Instance.FindDefine(_config.EntityDefTypeName,_config.EntityDefName);
|
||||
if (_aimEntity == null)
|
||||
{
|
||||
Debug.LogError($"未找到实体定义 (名称: '{_config.EntityDefName}', 类型: '{_config.EntityDefTypeName}')。请检查配置。");
|
||||
}
|
||||
_config = JsonConvert.DeserializeObject<EntityGenerateConfig>(value); // 使用Newtonsoft.Json
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"解析配置JSON时出错: {value}。异常信息: {ex.Message}");
|
||||
return; // 解析失败,直接返回
|
||||
}
|
||||
|
||||
if (_config == null)
|
||||
{
|
||||
Debug.LogError($"无法解析配置JSON: {value}");
|
||||
return;
|
||||
}
|
||||
if (_config.DefinitionsToChooseFrom == null || _config.DefinitionsToChooseFrom.Count == 0)
|
||||
{
|
||||
Debug.LogError($"实体生成配置中没有定义任何要生成的实体。请检查 'DefinitionsToChooseFrom' 列表。");
|
||||
return;
|
||||
}
|
||||
|
||||
_validatedEntityDefs = new List<EntityDef>();
|
||||
foreach (var entry in _config.DefinitionsToChooseFrom)
|
||||
{
|
||||
if (string.IsNullOrEmpty(entry.DefTypeName))
|
||||
{
|
||||
Debug.LogWarning($"实体定义类型名为空或null (实体定义名: '{entry.DefName}')。跳过此定义。");
|
||||
continue;
|
||||
}
|
||||
var entityDef = (EntityDef)DefineManager.Instance.FindDefine(entry.DefTypeName, entry.DefName);
|
||||
if (entityDef == null)
|
||||
{
|
||||
Debug.LogWarning($"未找到实体定义 (名称: '{entry.DefName}', 类型: '{entry.DefTypeName}')。请检查配置。跳过此定义。");
|
||||
}
|
||||
else
|
||||
{
|
||||
_validatedEntityDefs.Add(entityDef);
|
||||
}
|
||||
}
|
||||
if (_validatedEntityDefs.Count == 0)
|
||||
{
|
||||
Debug.LogError($"所有配置的实体定义都无效或未找到。事件初始化失败。");
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,33 +184,39 @@ namespace EventWorkClass
|
||||
Debug.LogError("事件配置(_config)为空。Init()可能失败了。");
|
||||
return;
|
||||
}
|
||||
if (_aimEntity == null)
|
||||
if (_validatedEntityDefs == null || _validatedEntityDefs.Count == 0)
|
||||
{
|
||||
Debug.LogError($"目标实体定义为空 (名称: {_config.EntityDefName}, 类型: {_config.EntityDefTypeName})。无法生成实体。");
|
||||
Debug.LogError("没有有效实体定义可供生成。请检查Init()方法和配置。");
|
||||
return;
|
||||
}
|
||||
var position = GetPosition(dimensionID);
|
||||
// 检查 GetPosition 是否返回了有效的非零位置,除非它是 AroundSpecificCoordinates 且中心点就是 Vector3.zero
|
||||
if (position == Vector3.zero && (_config.LocationType != EntitySpawnLocationType.AroundSpecificCoordinates || _config.CenterCoordinates != Vector3.zero))
|
||||
|
||||
for (int i = 0; i < _config.Count; i++)
|
||||
{
|
||||
Debug.LogWarning($"未能为类型 {_config.LocationType} 获取有效的生成位置。实体可能在原点 (0,0,0) 生成。");
|
||||
// 随机选择一个实体定义
|
||||
var selectedEntityDef = _validatedEntityDefs[Random.Range(0, _validatedEntityDefs.Count)];
|
||||
|
||||
var position = GetPosition(dimensionID);
|
||||
// 检查 GetPosition 是否返回了有效的非零位置,除非它是 AroundSpecificCoordinates 且中心点就是 Vector3.zero
|
||||
if (position == Vector3.zero && (_config.LocationType != EntitySpawnLocationType.AroundSpecificCoordinates
|
||||
|| _config.CenterCoordinates != Vector3.zero))
|
||||
{
|
||||
Debug.LogWarning($"未能为类型 {_config.LocationType} 获取有效的生成位置。实体可能在原点 (0,0,0) 生成。");
|
||||
}
|
||||
|
||||
if (selectedEntityDef is CharacterDef characterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateEntity(dimensionID, characterDef, position);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selectedEntityDef is MonsterDef monsterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateMonsterEntity(dimensionID, monsterDef, position);
|
||||
continue;
|
||||
}
|
||||
Debug.LogWarning($"目标实体 '{selectedEntityDef.defName}' (类型: {selectedEntityDef.GetType().Name}) 既不是 CharacterDef 也不是 MonsterDef。" +
|
||||
$"如果你想生成其他类型,EntityManage需要一个通用的生成实体方法。没有生成此实体。");
|
||||
}
|
||||
|
||||
if (_aimEntity is CharacterDef characterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateEntity(dimensionID, characterDef, position);
|
||||
Debug.Log($"已在维度 {dimensionID} 的 {position} 位置生成角色 '{characterDef.defName}'。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_aimEntity is MonsterDef monsterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateMonsterEntity(dimensionID, monsterDef, position);
|
||||
Debug.Log($"已在维度 {dimensionID} 的 {position} 位置生成怪物 '{monsterDef.defName}'。");
|
||||
return;
|
||||
}
|
||||
Debug.LogWarning($"目标实体 '{_aimEntity.defName}' (类型: {_aimEntity.GetType().Name}) 既不是 CharacterDef 也不是 MonsterDef。" +
|
||||
$"如果你想生成其他类型,EntityManage需要一个通用的生成实体方法。没有生成任何实体。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -287,12 +334,12 @@ namespace EventWorkClass
|
||||
}
|
||||
case EntitySpawnLocationType.InsideSpecificBuildingType:
|
||||
{
|
||||
Debug.LogWarning($"类型为 'InsideSpecificBuildingType' ({_config.BuildingTypeId}) 的生成逻辑尚未实现。返回 Vector3.zero。");
|
||||
Debug.LogWarning($"类型为 'InsideSpecificBuildingType' ({_config.BuildingTypeDefName}) 的生成逻辑尚未实现。返回 Vector3.zero。");
|
||||
return Vector3.zero;
|
||||
}
|
||||
case EntitySpawnLocationType.AtPredefinedSpawnPoint:
|
||||
{
|
||||
Debug.LogWarning($"类型为 'AtPredefinedSpawnPoint' ({_config.SpawnPointId}) 的生成逻辑尚未实现。返回 Vector3.zero。");
|
||||
Debug.LogWarning($"类型为 'AtPredefinedSpawnPoint' ({_config.SpawnPointTileMapDefName}) 的生成逻辑尚未实现。返回 Vector3.zero。");
|
||||
return Vector3.zero;
|
||||
}
|
||||
case EntitySpawnLocationType.None:
|
||||
|
@ -16,9 +16,11 @@ namespace Item
|
||||
public BulletDef Bullet { get; private set; }
|
||||
public WeaponType Type { get; private set; }
|
||||
|
||||
public IReadOnlyList<Sprite> AttackAnimationDef { get; private set; }
|
||||
public IReadOnlyList<Sprite> AttackAnimation { get; private set; }
|
||||
public float AttackAnimationTime { get; private set; }
|
||||
public float AttackCooldown => 1f/Attributes.attackSpeed;
|
||||
public float AttackCooldown => 1f / Attributes.attackSpeed;
|
||||
public float AttackDetectionTime { get; private set; }
|
||||
public bool UseEntityAttackAnimation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数:通过 WeaponDef 对象初始化 WeaponResource。
|
||||
@ -40,9 +42,10 @@ namespace Item
|
||||
// 初始化武器类型,直接从 WeaponDef 定义中获取。
|
||||
Type = def.type;
|
||||
|
||||
AttackAnimationDef = PackagesImageManager.Instance.GetSprites(def.attackAnimation);
|
||||
AttackAnimationTime = AttackAnimationDef.Count / FPS;
|
||||
|
||||
AttackAnimation = PackagesImageManager.Instance.GetSprites(def.attackAnimation);
|
||||
AttackAnimationTime = AttackAnimation.Count / FPS;
|
||||
AttackDetectionTime = def.attackDetectionTime;
|
||||
UseEntityAttackAnimation = def.useEntityAttackAnimation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using Data;
|
||||
using EventWorkClass;
|
||||
using Utils;
|
||||
using UnityEngine;
|
||||
using Base;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
@ -12,7 +13,7 @@ namespace Managers
|
||||
/// 事件管理器,负责事件的加载、注册和执行。
|
||||
/// 遵循单例模式,并在启动流程中扮演一个管理器角色。
|
||||
/// </summary>
|
||||
class EventManager : Singleton<EventManager>, ILaunchManager
|
||||
class EventManager : Singleton<EventManager>, ILaunchManager, ITick // 实现 ITick 接口
|
||||
{
|
||||
/// <summary>
|
||||
/// 存储所有已加载的事件定义,键为事件名称,值为对应的事件工作类实例。
|
||||
@ -20,26 +21,38 @@ namespace Managers
|
||||
public Dictionary<string, EventWorkClassBase> EventDefs { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前加载步骤的描述,用于启动流程的进度显示。
|
||||
/// 存储所有已加载的故事定义,键为故事名称,值为对应的故事定义实例。
|
||||
/// </summary>
|
||||
public string StepDescription => "正在载入事件";
|
||||
public Dictionary<string, StoryDef> StoryDefs { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化事件管理器,从定义管理器中加载所有事件定义并实例化其工作类。
|
||||
/// 存储当前正在播放的所有故事实例。
|
||||
/// </summary>
|
||||
private readonly List<StoryPlayer> _activeStoryPlayers = new List<StoryPlayer>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前加载步骤的描述,用于启动流程的进度显示。
|
||||
/// </summary>
|
||||
public string StepDescription => "正在载入事件和故事";
|
||||
|
||||
/// <summary>
|
||||
/// 初始化事件管理器,从定义管理器中加载所有事件定义和故事定义。
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
// 如果事件定义已经加载,则直接返回,避免重复初始化。
|
||||
if (EventDefs != null)
|
||||
// 如果事件和故事定义已经加载,则直接返回,避免重复初始化。
|
||||
if (EventDefs != null && StoryDefs != null)
|
||||
return;
|
||||
|
||||
var defs = DefineManager.Instance.QueryDefinesByType<EventDef>();
|
||||
// 从定义管理器查询并加载所有事件定义。
|
||||
var eventDefs = DefineManager.Instance.QueryDefinesByType<EventDef>();
|
||||
EventDefs = new Dictionary<string, EventWorkClassBase>();
|
||||
foreach (var def in defs)
|
||||
var loadedEventCount = 0;
|
||||
foreach (var def in eventDefs)
|
||||
{
|
||||
if (EventDefs.ContainsKey(def.defName))
|
||||
{
|
||||
Debug.LogWarning($"警告:事件名称重复,已跳过加载名称为 {def.defName} 的事件定义。");
|
||||
Debug.LogWarning($"警告:事件名称重复,已跳过加载名称为 '{def.defName}' 的事件定义。");
|
||||
continue;
|
||||
}
|
||||
var eventWorker = GetAndInstantiateEventWorker(def.workClass);
|
||||
@ -48,19 +61,45 @@ namespace Managers
|
||||
Debug.LogWarning($"警告:未能找到或实例化名称为 '{def.workClass}' 的事件工作类,已跳过加载名称为 '{def.defName}' 的事件定义。");
|
||||
continue;
|
||||
}
|
||||
eventWorker.Init(def.value);
|
||||
eventWorker.Init(def.parameter);
|
||||
EventDefs.Add(def.defName, eventWorker);
|
||||
loadedEventCount++;
|
||||
}
|
||||
Debug.Log($"事件管理器初始化完成,共载入 {EventDefs.Count} 个事件。");
|
||||
Debug.Log($"事件管理器初始化完成,共载入 {loadedEventCount} 个事件。");
|
||||
|
||||
// 从定义管理器查询并加载所有故事定义。
|
||||
var storyDefs = DefineManager.Instance.QueryDefinesByType<StoryDef>();
|
||||
StoryDefs = new Dictionary<string, StoryDef>();
|
||||
var loadedStoryCount = 0;
|
||||
foreach (var storyDef in storyDefs)
|
||||
{
|
||||
if (StoryDefs.ContainsKey(storyDef.defName))
|
||||
{
|
||||
Debug.LogWarning($"警告:故事名称重复,已跳过加载名称为 '{storyDef.defName}' 的故事定义。");
|
||||
continue;
|
||||
}
|
||||
StoryDefs.Add(storyDef.defName, storyDef);
|
||||
loadedStoryCount++;
|
||||
}
|
||||
Debug.Log($"事件管理器初始化完成,共载入 {loadedStoryCount} 个故事。");
|
||||
|
||||
// 将自身注册到时钟系统,以便每帧更新故事播放。
|
||||
Clock.AddTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理事件管理器,释放所有已加载的事件定义。
|
||||
/// 清理事件管理器,释放所有已加载的事件和故事定义。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
// 从时钟系统移除自身,停止接收Tick更新。
|
||||
Clock.RemoveTick(this);
|
||||
// 清理所有正在播放的故事实例。
|
||||
_activeStoryPlayers.Clear();
|
||||
// 释放事件定义字典。
|
||||
EventDefs = null;
|
||||
Debug.Log("事件管理器已清理。");
|
||||
// 释放故事定义字典。
|
||||
StoryDefs = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -76,7 +115,7 @@ namespace Managers
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EventDefs.ContainsKey(eventName))
|
||||
if (!EventDefs.TryGetValue(eventName, out var eventWorker))
|
||||
{
|
||||
Debug.LogWarning($"警告:未能找到名称为 '{eventName}' 的事件定义,已跳过执行该事件。");
|
||||
return;
|
||||
@ -84,9 +123,89 @@ namespace Managers
|
||||
// 假设 Program.Instance 和 FocusedDimensionId 存在且可访问。
|
||||
// 如果 dimensionID 为 null,则使用当前焦点维度ID。
|
||||
dimensionID ??= Program.Instance.FocusedDimensionId;
|
||||
EventDefs[eventName].Run(dimensionID);
|
||||
eventWorker.Run(dimensionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放指定名称的故事。
|
||||
/// </summary>
|
||||
/// <param name="storyName">要播放的故事的名称。</param>
|
||||
/// <param name="dimensionID">故事执行的维度ID,如果为null,将使用当前焦点的维度ID。</param>
|
||||
public void PlayStory(string storyName, string dimensionID = null)
|
||||
{
|
||||
if (StoryDefs == null || StoryDefs.Count == 0)
|
||||
{
|
||||
Debug.LogError($"错误:故事定义尚未加载或已被清理。无法播放故事 '{storyName}'。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StoryDefs.TryGetValue(storyName, out var storyDef))
|
||||
{
|
||||
Debug.LogWarning($"警告:未能找到名称为 '{storyName}' 的故事定义,已跳过播放该故事。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (storyDef.storyStage == null || storyDef.storyStage.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"警告:故事 '{storyDef.defName}' 没有定义任何阶段,已跳过播放。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保 dimensionID 有效,如果为 null 则使用当前焦点维度ID。
|
||||
dimensionID ??= Program.Instance.FocusedDimensionId;
|
||||
|
||||
// 创建一个新的 StoryPlayer 实例并添加到活跃列表中。
|
||||
var player = new StoryPlayer(storyDef, dimensionID, this);
|
||||
_activeStoryPlayers.Add(player);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新,用于驱动所有活跃故事的播放逻辑。
|
||||
/// 作为 <see cref="ITick"/> 接口的实现,由时钟系统调用。
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
if (_activeStoryPlayers.Count == 0) return;
|
||||
|
||||
// 获取自上一帧以来的时间增量。
|
||||
var deltaTime = Time.deltaTime;
|
||||
|
||||
// 遍历所有正在播放的故事实例,并更新它们的状态。
|
||||
// 为了避免在迭代过程中修改列表,先收集要移除的,再统一移除。
|
||||
var playersToRemove = new List<StoryPlayer>();
|
||||
foreach (var player in _activeStoryPlayers)
|
||||
{
|
||||
player.Update(deltaTime);
|
||||
if (player.IsFinished)
|
||||
{
|
||||
playersToRemove.Add(player);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除所有已完成的故事实例。
|
||||
foreach (var player in playersToRemove)
|
||||
{
|
||||
_activeStoryPlayers.Remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串颜色值解析为 <see cref="Color"/> 对象。
|
||||
/// 支持十六进制颜色(如 "#RRGGBB" 或 "#AARRGGBB")或标准颜色名称。
|
||||
/// </summary>
|
||||
/// <param name="colorString">颜色字符串。</param>
|
||||
/// <returns>解析后的 <see cref="Color"/> 对象。如果解析失败,返回白色。</returns>
|
||||
private Color ParseColor(string colorString)
|
||||
{
|
||||
if (ColorUtility.TryParseHtmlString(colorString, out var color))
|
||||
{
|
||||
return color;
|
||||
}
|
||||
Debug.LogWarning($"警告:无法解析颜色字符串 '{colorString}'。使用默认白色。");
|
||||
return Color.white; // 默认返回白色
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据类名从指定命名空间和程序集下获取并实例化一个 <see cref="EventWorkClassBase"/> 的子类。
|
||||
/// </summary>
|
||||
@ -102,7 +221,7 @@ namespace Managers
|
||||
// 1. 确定要搜索的程序集。
|
||||
if (assemblyToSearch == null)
|
||||
{
|
||||
// 默认从 EventWorkClassBase 所在的程序集查找,通常其实现类也会在这个程序集。
|
||||
// 默认从 EventWorkClassBase 所在的程序集查找,通常其实现类也位于此程序集。
|
||||
assemblyToSearch = typeof(EventWorkClassBase).Assembly;
|
||||
}
|
||||
|
||||
@ -141,14 +260,182 @@ namespace Managers
|
||||
}
|
||||
catch (MissingMethodException ex)
|
||||
{
|
||||
Debug.LogError($"错误:类 '{fullTypeName}' 没有公共的无参构造函数。详情: {ex.Message}");
|
||||
Debug.LogError($"错误:类 '{fullTypeName}' 没有公共的无参构造函数。详细信息: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"实例化类 '{fullTypeName}' 时发生未知错误。详情: {ex.Message}");
|
||||
Debug.LogError($"实例化类 '{fullTypeName}' 时发生未知错误。详细信息: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个正在播放的故事实例的状态机。
|
||||
/// </summary>
|
||||
private class StoryPlayer
|
||||
{
|
||||
/// <summary> 故事播放阶段的状态枚举。 </summary>
|
||||
private enum State
|
||||
{
|
||||
Initializing, // 刚开始,准备处理第一个阶段的 lastWaitTime
|
||||
WaitingLastTime, // 等待当前阶段的 lastWaitTime
|
||||
ExecutingStage, // 执行当前阶段的事件/消息
|
||||
WaitingNextTime, // 等待当前阶段的 nextWaitTime
|
||||
Finished // 故事播放完毕
|
||||
}
|
||||
|
||||
/// <summary> 获取当前正在播放的故事定义。 </summary>
|
||||
public StoryDef StoryDef { get; private set; }
|
||||
/// <summary> 获取故事播放所在的维度ID。 </summary>
|
||||
public string DimensionID { get; private set; }
|
||||
/// <summary> 获取一个值,表示故事是否已播放完毕。 </summary>
|
||||
public bool IsFinished { get; private set; } = false;
|
||||
|
||||
// 事件管理器实例,用于调用 Action 和 ParseColor 方法。
|
||||
private readonly EventManager _eventManager;
|
||||
// 当前故事阶段的索引。
|
||||
private int _currentStageIndex;
|
||||
// 当前阶段的等待计时器。
|
||||
private float _currentWaitTimer;
|
||||
// 当前故事播放器所处的状态。
|
||||
private State _currentState;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="StoryPlayer"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="storyDef">要播放的故事定义。</param>
|
||||
/// <param name="dimensionId">故事播放所在的维度ID。</param>
|
||||
/// <param name="eventManager">事件管理器实例,用于执行事件和解析颜色。</param>
|
||||
public StoryPlayer(StoryDef storyDef, string dimensionId, EventManager eventManager)
|
||||
{
|
||||
StoryDef = storyDef;
|
||||
DimensionID = dimensionId;
|
||||
_eventManager = eventManager;
|
||||
_currentStageIndex = 0;
|
||||
_currentWaitTimer = 0f;
|
||||
_currentState = State.Initializing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新故事播放器的状态。
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">自上一帧以来的时间增量。</param>
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (IsFinished) return;
|
||||
|
||||
// 如果故事阶段定义为null或为空,则立即标记故事完成。
|
||||
if (StoryDef.storyStage == null || StoryDef.storyStage.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"警告:故事 '{StoryDef.defName}' 没有定义任何阶段,StoryPlayer 将立即完成。");
|
||||
IsFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStage = StoryDef.storyStage[_currentStageIndex];
|
||||
|
||||
switch (_currentState)
|
||||
{
|
||||
case State.Initializing:
|
||||
// 从当前阶段的 lastWaitTime 开始等待,如果 lastWaitTime 小于等于 0 则直接跳过等待执行阶段。
|
||||
if (currentStage.lastWaitTime > 0)
|
||||
{
|
||||
_currentState = State.WaitingLastTime;
|
||||
_currentWaitTimer = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有 lastWaitTime,直接执行阶段。
|
||||
_currentState = State.ExecutingStage;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.WaitingLastTime:
|
||||
_currentWaitTimer += deltaTime;
|
||||
if (_currentWaitTimer >= currentStage.lastWaitTime)
|
||||
{
|
||||
// 等待时间已到,切换到执行阶段。
|
||||
_currentWaitTimer = 0f;
|
||||
_currentState = State.ExecutingStage;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.ExecutingStage:
|
||||
// 处理事件
|
||||
if (currentStage.eventDef != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentStage.eventDef.defName))
|
||||
{
|
||||
_eventManager.Action(currentStage.eventDef.defName, DimensionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"警告:故事 '{StoryDef.defName}' 阶段 {_currentStageIndex} 包含一个没有定义名称的事件,已跳过。");
|
||||
}
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
if (currentStage.messageDef != null)
|
||||
{
|
||||
if (MessageManager.Instance == null)
|
||||
{
|
||||
Debug.LogError($"错误:MessageManager 实例不存在,无法显示故事 '{StoryDef.defName}' 的消息。");
|
||||
}
|
||||
else
|
||||
{
|
||||
var messageColor = _eventManager.ParseColor(currentStage.messageDef.color);
|
||||
MessageManager.Instance.DisplayMessage(currentStage.messageDef.text, currentStage.messageDef.type, messageColor);
|
||||
}
|
||||
}
|
||||
|
||||
// 决定下一个状态
|
||||
if (currentStage.nextWaitTime > 0)
|
||||
{
|
||||
_currentState = State.WaitingNextTime;
|
||||
_currentWaitTimer = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有 nextWaitTime,直接推进到下一个阶段。
|
||||
AdvanceToNextStage();
|
||||
}
|
||||
break;
|
||||
|
||||
case State.WaitingNextTime:
|
||||
_currentWaitTimer += deltaTime;
|
||||
if (_currentWaitTimer >= currentStage.nextWaitTime)
|
||||
{
|
||||
// 等待时间已到,推进到下一个阶段。
|
||||
_currentWaitTimer = 0f;
|
||||
AdvanceToNextStage();
|
||||
}
|
||||
break;
|
||||
|
||||
case State.Finished:
|
||||
// 故事已经完成,不再更新。
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推进到下一个故事阶段,或标记故事完成。
|
||||
/// </summary>
|
||||
private void AdvanceToNextStage()
|
||||
{
|
||||
_currentStageIndex++;
|
||||
if (_currentStageIndex < StoryDef.storyStage.Length)
|
||||
{
|
||||
// 还有后续阶段,重置状态以处理下一个阶段的 lastWaitTime。
|
||||
_currentState = State.Initializing;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 所有阶段播放完毕,标记故事完成。
|
||||
_currentState = State.Finished;
|
||||
IsFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ namespace Managers
|
||||
public class ItemResourceManager : Utils.Singleton<ItemResourceManager>, ILaunchManager
|
||||
{
|
||||
private ItemResource defaultItem;
|
||||
private readonly Dictionary<string, Item.ItemResource> _items = new();
|
||||
private readonly Dictionary<string, List<Item.ItemResource>> _itemsByName = new(); // 保持按显示名称查找的字典
|
||||
private readonly Dictionary<string, ItemResource> _items = new();
|
||||
private readonly Dictionary<string, List<ItemResource>> _itemsByName = new(); // 保持按显示名称查找的字典
|
||||
|
||||
public string StepDescription => "加载物品定义中";
|
||||
|
||||
@ -39,17 +39,17 @@ namespace Managers
|
||||
continue;
|
||||
}
|
||||
|
||||
Item.ItemResource itemResource;
|
||||
ItemResource itemResource;
|
||||
|
||||
if (def is WeaponDef currentWeaponDef)
|
||||
{
|
||||
itemResource = new Item.WeaponResource(
|
||||
itemResource = new WeaponResource(
|
||||
currentWeaponDef
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemResource = new Item.ItemResource(
|
||||
itemResource = new ItemResource(
|
||||
def
|
||||
);
|
||||
}
|
||||
@ -57,26 +57,26 @@ namespace Managers
|
||||
_items.Add(def.defName, itemResource);
|
||||
if (!_itemsByName.ContainsKey(itemResource.Name))
|
||||
{
|
||||
_itemsByName.Add(itemResource.Name, new List<Item.ItemResource>());
|
||||
_itemsByName.Add(itemResource.Name, new List<ItemResource>());
|
||||
}
|
||||
_itemsByName[itemResource.Name].Add(itemResource);
|
||||
}
|
||||
}
|
||||
|
||||
public Item.ItemResource GetItem(string defName)
|
||||
public ItemResource GetItem(string defName)
|
||||
{
|
||||
return _items.GetValueOrDefault(defName, defaultItem);
|
||||
}
|
||||
public Item.ItemResource FindItemByName(string itemName)
|
||||
public ItemResource FindItemByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return defaultItem;
|
||||
return _itemsByName.GetValueOrDefault(itemName)?.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Item.ItemResource> FindAllItemsByName(string itemName)
|
||||
public List<ItemResource> FindAllItemsByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return new List<Item.ItemResource>();
|
||||
return _itemsByName.GetValueOrDefault(itemName, new List<Item.ItemResource>());
|
||||
if (string.IsNullOrEmpty(itemName)) return new List<ItemResource>();
|
||||
return _itemsByName.GetValueOrDefault(itemName, new List<ItemResource>());
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
206
Client/Assets/Scripts/Managers/MessageManager.cs
Normal file
206
Client/Assets/Scripts/Managers/MessageManager.cs
Normal file
@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using Base;
|
||||
using Data;
|
||||
using Prefab;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement; // 新增引用
|
||||
using UnityEngine.UI; // 用于 LayoutGroup 和 RectTransform
|
||||
using TMPro; // 用于 TMP_Text
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class MessageManager:Utils.MonoSingleton<MessageManager>
|
||||
{
|
||||
private RectTransform _canvas;
|
||||
private TemporaryAnimatorText _temporaryAnimatorTextPrefab; // 重命名,表示是预制体
|
||||
private RectTransform _passiveHintContainer; // 用于PassiveHint的容器
|
||||
|
||||
// SceneManager.sceneLoaded 注册/取消注册
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
public void DisplayMessage(string message, Data.PromptDisplayCategory type,Color? color=null)
|
||||
{
|
||||
if (!_canvas)
|
||||
{
|
||||
Debug.LogWarning($"MessageManager: Canvas is not available. Message '{message}' ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PromptDisplayCategory.FocusedEntityOverheadText:
|
||||
if (Program.Instance.FocusedEntity == null)
|
||||
return;
|
||||
// GenerateTemporaryAnimation的第三个参数是显示时间
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(message,
|
||||
Program.Instance.FocusedEntity.Position, 5); // 5秒显示时间
|
||||
break;
|
||||
|
||||
case PromptDisplayCategory.PassiveHint:
|
||||
if (_passiveHintContainer == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot display PassiveHint: PassiveHintContainer is not available. Please ensure Canvas is present.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 实例化消息文本作为PassiveHintContainer的子对象
|
||||
var hintTextInstance = Instantiate(_temporaryAnimatorTextPrefab, _passiveHintContainer.transform);
|
||||
|
||||
// 确保它在Layout Group中正确显示
|
||||
var hintTextRect = hintTextInstance.GetComponent<RectTransform>();
|
||||
|
||||
// 如果temporaryAnimatorText有ContentSizeFitter,这里可能不需要设置sizeDelta,但为了LayoutGroup能识别,可以添加LayoutElement
|
||||
var layoutElement = hintTextInstance.GetComponent<LayoutElement>();
|
||||
if (layoutElement == null) layoutElement = hintTextInstance.gameObject.AddComponent<LayoutElement>();
|
||||
layoutElement.minHeight = 30; // 最小高度
|
||||
layoutElement.preferredWidth = _passiveHintContainer.sizeDelta.x; // 适应容器的宽度
|
||||
|
||||
// 设置字体样式
|
||||
var hintTmpText = hintTextInstance.GetComponent<TMP_Text>();
|
||||
if (hintTmpText != null)
|
||||
{
|
||||
hintTmpText.fontSize = 24; // 较小的字体
|
||||
hintTmpText.alignment = TextAlignmentOptions.TopLeft; // 左上对齐
|
||||
hintTmpText.enableAutoSizing = false; // 关闭自动调整字体,保持统一
|
||||
hintTmpText.SetText(message); // 先设置文本,确保布局计算正确
|
||||
if(color.HasValue)
|
||||
hintTmpText.color = color.Value;
|
||||
}
|
||||
|
||||
hintTextInstance.Init(message); // Init 方法会处理动画和生命周期
|
||||
// TemporaryAnimatorText 应该在 Init 内部设置好 lifeTime 并自动销毁。
|
||||
break;
|
||||
|
||||
case PromptDisplayCategory.ScreenCenterLargeText:
|
||||
var textInstance = GameObject.Instantiate(_temporaryAnimatorTextPrefab, _canvas.transform); // 使用预制体变量
|
||||
|
||||
// 设置RectTransform实现全屏
|
||||
var textRect = textInstance.GetComponent<RectTransform>();
|
||||
textRect.anchorMin = Vector2.zero; // (0,0)
|
||||
textRect.anchorMax = Vector2.one; // (1,1)
|
||||
textRect.offsetMin = Vector2.zero; // 左下角偏移为(0,0)
|
||||
textRect.offsetMax = Vector2.zero; // 右上角偏移为(0,0)
|
||||
|
||||
|
||||
// 获取TMP_Text并设置字体等
|
||||
var tmpText = textInstance.GetComponent<TMP_Text>();
|
||||
if (tmpText != null)
|
||||
{
|
||||
tmpText.fontSize = 80; // 设置一个较大的字体大小
|
||||
tmpText.alignment = TextAlignmentOptions.Center; // 居中对齐
|
||||
tmpText.enableAutoSizing = true; // 允许自动调整字体大小以适应文本框 (可选)
|
||||
tmpText.SetText(message); // 先设置文本,确保布局计算正确
|
||||
if(color.HasValue)
|
||||
tmpText.color = color.Value;
|
||||
}
|
||||
|
||||
textInstance.Init(message); // Init 方法会处理动画和生命周期
|
||||
// textInstance.lifeTime 可以在 Init 方法内部设置,如果 Init 没有提供参数,这里就无法直接设置。
|
||||
// 假设 Init 已经处理好生命周期。
|
||||
break;
|
||||
case PromptDisplayCategory.Default:
|
||||
break;
|
||||
case PromptDisplayCategory.FocusedEntityChatBubble:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
// 在单例第一次初始化时加载预制体
|
||||
_temporaryAnimatorTextPrefab =
|
||||
Resources.Load<TemporaryAnimatorText>("Prefab/TemporaryAnimation/UITemporaryAnimationText");
|
||||
|
||||
if (_temporaryAnimatorTextPrefab == null)
|
||||
{
|
||||
Debug.LogError("Failed to load TemporaryAnimatorText prefab. Check the path: Prefab/TemporaryAnimation/UITemporaryAnimationText");
|
||||
}
|
||||
|
||||
// 首次启动时也尝试查找 Canvas 和创建 PassiveHint 容器
|
||||
// 这可以处理管理器在 Canvas 和其他 UI 元素之前启动的情况
|
||||
if (SceneManager.GetActiveScene().isLoaded)
|
||||
{
|
||||
OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 场景加载完成回调,用于查找并设置Canvas和PassiveHint容器
|
||||
/// </summary>
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
// 尝试查找Canvas
|
||||
var mainCanvas = FindAnyObjectByType<Canvas>(); // 查找场景中的第一个Canvas
|
||||
if (mainCanvas != null)
|
||||
{
|
||||
_canvas = mainCanvas.GetComponent<RectTransform>();
|
||||
InitializePassiveHintContainer();
|
||||
}
|
||||
else
|
||||
{
|
||||
_canvas = null;
|
||||
// 如果没有Canvas,确保容器也被清除,避免引用无效对象
|
||||
if (_passiveHintContainer != null)
|
||||
{
|
||||
Destroy(_passiveHintContainer.gameObject);
|
||||
_passiveHintContainer = null;
|
||||
}
|
||||
Debug.LogWarning($"MessageManager: No Canvas found in scene '{scene.name}'. UI messages might not display.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建并初始化PassiveHint的UI容器
|
||||
/// </summary>
|
||||
private void InitializePassiveHintContainer()
|
||||
{
|
||||
if (_canvas == null)
|
||||
{
|
||||
Debug.LogError("Cannot initialize PassiveHint container: Canvas is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果容器已经存在,先销毁旧的,确保每次场景加载都是新的容器
|
||||
if (_passiveHintContainer != null)
|
||||
{
|
||||
Destroy(_passiveHintContainer.gameObject);
|
||||
_passiveHintContainer = null;
|
||||
}
|
||||
|
||||
// 创建一个新的GameObject作为容器
|
||||
var containerGO = new GameObject("PassiveHintContainer");
|
||||
_passiveHintContainer = containerGO.AddComponent<RectTransform>();
|
||||
_passiveHintContainer.SetParent(_canvas, false); // false表示不保留世界坐标
|
||||
|
||||
// 设置容器的位置和大小:左侧中间
|
||||
_passiveHintContainer.anchorMin = new Vector2(0, 0.5f);
|
||||
_passiveHintContainer.anchorMax = new Vector2(0, 0.5f);
|
||||
_passiveHintContainer.pivot = new Vector2(0, 0.5f); // 锚点和枢轴都在左中
|
||||
_passiveHintContainer.anchoredPosition = new Vector2(100, 0); // 从左边距100(示例值),Y轴中心
|
||||
_passiveHintContainer.sizeDelta = new Vector2(400, 600); // 示例宽度和高度
|
||||
|
||||
// 添加VerticalLayoutGroup
|
||||
var layoutGroup = containerGO.AddComponent<VerticalLayoutGroup>();
|
||||
layoutGroup.childAlignment = TextAnchor.UpperLeft; // 子元素靠左上排布
|
||||
layoutGroup.spacing = 10; // 消息之间的间距
|
||||
layoutGroup.padding = new RectOffset(10, 10, 10, 10); // 容器内边距
|
||||
|
||||
// 添加ContentSizeFitter,以便容器根据内容调整大小 (可选,根据实际需求)
|
||||
// fitter 会自动调整自身大小以适应子内容
|
||||
var fitter = containerGO.AddComponent<ContentSizeFitter>();
|
||||
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Managers/MessageManager.cs.meta
Normal file
3
Client/Assets/Scripts/Managers/MessageManager.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 067858a7493442389cc3f7624b83e1f3
|
||||
timeCreated: 1757231873
|
@ -18,9 +18,6 @@ namespace Managers
|
||||
[SerializeField] private TemporaryAnimatorSprite temporaryAnimatorSpritePrefab;
|
||||
[SerializeField] private TemporaryAnimatorText temporaryAnimatorTextPrefab;
|
||||
[SerializeField] private TemporaryAnimatorText temporaryAnimatorUITextPrefab;
|
||||
|
||||
private Func<float, float> defaultXoffset = f => Mathf.Sin(f * 3);
|
||||
private Func<float, float> defaultYoffset = f => f;
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
@ -89,6 +86,15 @@ namespace Managers
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个非UI文本临时动画。
|
||||
/// </summary>
|
||||
/// <param name="str">动画的文本内容。</param>
|
||||
/// <param name="position">动画在世界坐标系中的初始位置。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimation(string str, Vector3 position, float lifeTime = 3, float fps = 3,
|
||||
Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
@ -99,13 +105,42 @@ namespace Managers
|
||||
return;
|
||||
}
|
||||
|
||||
xShift ??= defaultXoffset;
|
||||
yShift ??= defaultYoffset;
|
||||
var textObj = Instantiate(temporaryAnimatorTextPrefab, _temporaryAnimationLevel.transform);
|
||||
textObj.transform.position = new Vector3(position.x,position.y);
|
||||
textObj.Init(str, fps);
|
||||
textObj.SetAnimationFunctions(xShift, yShift);
|
||||
textObj.lifeTime = lifeTime;
|
||||
textObj.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个非UI文本临时动画,可指定父级Transform。
|
||||
/// </summary>
|
||||
/// <param name="str">动画的文本内容。</param>
|
||||
/// <param name="position">动画在世界坐标系中的初始位置。</param>
|
||||
/// <param name="parent">动画的父级Transform。如果为null,则使用默认的非UI动画层。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimation(string str, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3,
|
||||
Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
{
|
||||
Transform actualParent = parent ?? _temporaryAnimationLevel?.transform;
|
||||
|
||||
if (actualParent == null)
|
||||
{
|
||||
Debug.LogError("TemporaryAnimationLevel or specified parent is not initialized. Cannot generate non-UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
var textObj = Instantiate(temporaryAnimatorTextPrefab, actualParent);
|
||||
textObj.transform.position = position; // 外部传入的position应视为世界坐标
|
||||
textObj.Init(str, fps);
|
||||
textObj.SetAnimationFunctions(xShift, yShift);
|
||||
textObj.lifeTime = lifeTime;
|
||||
textObj.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -113,32 +148,64 @@ namespace Managers
|
||||
/// </summary>
|
||||
/// <param name="sprites">动画的精灵帧数组。</param>
|
||||
/// <param name="position">动画在世界坐标系中的初始位置。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position,float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
{
|
||||
if (_temporaryAnimationLevel == null)
|
||||
if (!_temporaryAnimationLevel)
|
||||
{
|
||||
Debug.LogError("TemporaryAnimationLevel is not initialized. Cannot generate non-UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
xShift ??= defaultXoffset;
|
||||
yShift ??= defaultYoffset;
|
||||
var obj = Instantiate(temporaryAnimatorSpritePrefab, _temporaryAnimationLevel.transform);
|
||||
obj.transform.position = new Vector3(position.x,position.y);
|
||||
obj.Init(sprites, fps);
|
||||
obj.SetAnimationFunctions(xShift, yShift);
|
||||
obj.lifeTime = lifeTime;
|
||||
obj.gameObject.SetActive(true);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个非UI精灵临时动画,可指定父级Transform。
|
||||
/// </summary>
|
||||
/// <param name="sprites">动画的精灵帧数组。</param>
|
||||
/// <param name="position">动画在世界坐标系中的初始位置。</param>
|
||||
/// <param name="parent">动画的父级Transform。如果为null,则使用默认的非UI动画层。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimation(Sprite[] sprites, Vector3 position, Transform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
{
|
||||
Transform actualParent = parent ?? _temporaryAnimationLevel?.transform;
|
||||
|
||||
if (actualParent == null)
|
||||
{
|
||||
Debug.LogError("TemporaryAnimationLevel or specified parent is not initialized. Cannot generate non-UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = Instantiate(temporaryAnimatorSpritePrefab, actualParent);
|
||||
obj.transform.position = position; // 外部传入的position应视为世界坐标
|
||||
obj.Init(sprites, fps);
|
||||
obj.SetAnimationFunctions(xShift, yShift);
|
||||
obj.lifeTime = lifeTime;
|
||||
obj.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个UI文本临时动画。
|
||||
/// 此重载将传入的 <paramref name="position"/> 视为屏幕坐标 (如鼠标位置),并将其自动转换为Canvas局部坐标。
|
||||
/// </summary>
|
||||
/// <param name="str">动画的文本内容。</param>
|
||||
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
|
||||
/// <param name="position">动画在屏幕坐标系中的初始位置(如鼠标位置)。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
@ -150,22 +217,86 @@ namespace Managers
|
||||
Debug.LogError("TemporaryAnimationUILevel is not initialized. Cannot generate UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
xShift ??= defaultXoffset;
|
||||
yShift ??= defaultYoffset;
|
||||
|
||||
var textObj = Instantiate(temporaryAnimatorUITextPrefab, _temporaryAnimationUILevel.transform);
|
||||
textObj.transform.position = position;
|
||||
|
||||
RectTransform canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform;
|
||||
if (canvasRectTransform != null)
|
||||
{
|
||||
// 获取Canvas组件,特别是RenderMode为ScreenSpaceCamera时需要worldCamera
|
||||
Canvas rootCanvas = canvasRectTransform.root.GetComponent<Canvas>();
|
||||
Camera eventCamera = null;
|
||||
if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
eventCamera = rootCanvas.worldCamera; // 使用Canvas指定的相机
|
||||
if (eventCamera == null) // 如果Canvas没有指定相机,尝试主相机
|
||||
{
|
||||
eventCamera = Camera.main;
|
||||
}
|
||||
}
|
||||
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out var localPoint))
|
||||
{
|
||||
textObj.rectTransform.anchoredPosition = localPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
textObj.rectTransform.anchoredPosition = position;
|
||||
Debug.LogWarning("Failed to convert screen point to local point for UI text animation. Using raw position.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
textObj.rectTransform.anchoredPosition = position;
|
||||
Debug.LogWarning("TemporaryAnimationUILevel is not a RectTransform. Unable to convert screen point. Using raw position.");
|
||||
}
|
||||
|
||||
textObj.Init(str, fps);
|
||||
textObj.SetAnimationFunctions(xShift, yShift);
|
||||
textObj.lifeTime = lifeTime;
|
||||
textObj.gameObject.SetActive(true);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个UI文本临时动画,可指定父级RectTransform。
|
||||
/// 此重载将传入的 <paramref name="position"/> 视为相对于父级RectTransform的 anchoredPosition。
|
||||
/// </summary>
|
||||
/// <param name="str">动画的文本内容。</param>
|
||||
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
|
||||
/// <param name="parent">动画的父级RectTransform。如果为null,则使用默认的UI动画层。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimationUI(string str, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
{
|
||||
RectTransform actualParent = parent ?? (_temporaryAnimationUILevel?.transform as RectTransform);
|
||||
|
||||
if (actualParent == null)
|
||||
{
|
||||
Debug.LogError("TemporaryAnimationUILevel or specified parent is not initialized or not a RectTransform. Cannot generate UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
var textObj = Instantiate(temporaryAnimatorUITextPrefab);
|
||||
textObj.rectTransform.SetParent(actualParent, false); // SetParent with worldPositionStay: false
|
||||
textObj.rectTransform.anchoredPosition = position;
|
||||
textObj.Init(str, fps);
|
||||
textObj.SetAnimationFunctions(xShift, yShift);
|
||||
textObj.lifeTime = lifeTime;
|
||||
textObj.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个UI精灵临时动画。
|
||||
/// 此重载将传入的 <paramref name="position"/> 视为屏幕坐标 (如鼠标位置),并将其自动转换为Canvas局部坐标。
|
||||
/// </summary>
|
||||
/// <param name="sprites">动画的精灵帧数组。</param>
|
||||
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
|
||||
/// <param name="position">动画在屏幕坐标系中的初始位置(如鼠标位置)。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
@ -178,13 +309,76 @@ namespace Managers
|
||||
return;
|
||||
}
|
||||
|
||||
xShift ??= defaultXoffset;
|
||||
yShift ??= defaultYoffset;
|
||||
var obj = Instantiate(temporaryAnimatorImageUIPrefab, _temporaryAnimationUILevel.transform);
|
||||
obj.transform.position = position;
|
||||
|
||||
RectTransform canvasRectTransform = _temporaryAnimationUILevel.transform as RectTransform;
|
||||
if (canvasRectTransform != null)
|
||||
{
|
||||
// 获取Canvas组件,特别是RenderMode为ScreenSpaceCamera时需要worldCamera
|
||||
Canvas rootCanvas = canvasRectTransform.root.GetComponent<Canvas>();
|
||||
Camera eventCamera = null;
|
||||
if (rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || rootCanvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
eventCamera = rootCanvas.worldCamera; // 使用Canvas指定的相机
|
||||
if (eventCamera == null) // 如果Canvas没有指定相机,尝试主相机
|
||||
{
|
||||
eventCamera = Camera.main;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 localPoint;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, position, eventCamera, out localPoint))
|
||||
{
|
||||
obj.rectTransform.anchoredPosition = localPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.rectTransform.anchoredPosition = position;
|
||||
Debug.LogWarning("Failed to convert screen point to local point for UI sprite animation. Using raw position.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.rectTransform.anchoredPosition = position;
|
||||
Debug.LogWarning("TemporaryAnimationUILevel is not a RectTransform. Unable to convert screen point. Using raw position.");
|
||||
}
|
||||
|
||||
obj.Init(sprites, fps);
|
||||
obj.SetAnimationFunctions(xShift, yShift);
|
||||
obj.lifeTime = lifeTime;
|
||||
obj.gameObject.SetActive(true);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成一个UI精灵临时动画,可指定父级RectTransform。
|
||||
/// 此重载将传入的 <paramref name="position"/> 视为相对于父级RectTransform的 anchoredPosition。
|
||||
/// </summary>
|
||||
/// <param name="sprites">动画的精灵帧数组。</param>
|
||||
/// <param name="position">动画在UI坐标系(RectTransform.anchoredPosition)中的初始位置。</param>
|
||||
/// <param name="parent">动画的父级RectTransform。如果为null,则使用默认的UI动画层。</param>
|
||||
/// <param name="lifeTime">动画的生命周期(秒)。</param>
|
||||
/// <param name="fps">动画帧率。</param>
|
||||
/// <param name="xShift">X轴位移函数。</param>
|
||||
/// <param name="yShift">Y轴位移函数。</param>
|
||||
public void GenerateTemporaryAnimationUI(Sprite[] sprites, Vector2 position, RectTransform parent, float lifeTime = 3, float fps = 3, Func<float, float> xShift = null,
|
||||
Func<float, float> yShift = null)
|
||||
{
|
||||
RectTransform actualParent = parent ?? (_temporaryAnimationUILevel?.transform as RectTransform);
|
||||
|
||||
if (actualParent == null)
|
||||
{
|
||||
Debug.LogError("TemporaryAnimationUILevel or specified parent is not initialized or not a RectTransform. Cannot generate UI animation.");
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = Instantiate(temporaryAnimatorImageUIPrefab);
|
||||
obj.rectTransform.SetParent(actualParent, false); // SetParent with worldPositionStay: false
|
||||
obj.rectTransform.anchoredPosition = position;
|
||||
obj.Init(sprites, fps);
|
||||
obj.SetAnimationFunctions(xShift, yShift);
|
||||
obj.lifeTime = lifeTime;
|
||||
obj.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
@ -198,20 +392,23 @@ namespace Managers
|
||||
var existingCanvas = FindFirstObjectByType<Canvas>();
|
||||
if (existingCanvas != null)
|
||||
{
|
||||
EnsureEventSystemExists(); // 即使Canvas已存在,也要确保EventSystem存在
|
||||
EnsureEventSystemExists();
|
||||
return existingCanvas;
|
||||
}
|
||||
|
||||
// 创建 Canvas GameObject
|
||||
var canvasGO = new GameObject("Canvas");
|
||||
canvasGO.layer = LayerMask.NameToLayer("UI"); // 建议将UI设置为UI层
|
||||
// 添加 Canvas 组件
|
||||
canvasGO.layer = LayerMask.NameToLayer("UI");
|
||||
var newCanvas = canvasGO.AddComponent<Canvas>();
|
||||
newCanvas.renderMode = RenderMode.ScreenSpaceOverlay; // 最常见的UI渲染模式
|
||||
// 添加其他必要的UI组件
|
||||
newCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
|
||||
// 确保有相机,以便RectTransformUtility.ScreenPointToLocalPointInRectangle可以工作
|
||||
// 对于ScreenSpaceOverlay,worldCamera可以为null,但为了通用性和健壮性,这里设置为Camera.main,
|
||||
// 以防Canvas.renderMode将来调整为ScreenSpaceCamera或WorldSpace
|
||||
newCanvas.worldCamera = Camera.main;
|
||||
|
||||
canvasGO.AddComponent<CanvasScaler>();
|
||||
canvasGO.AddComponent<GraphicRaycaster>();
|
||||
// 确保场景中存在 EventSystem,以便UI交互(按钮点击等)能正常工作
|
||||
EnsureEventSystemExists();
|
||||
return newCanvas;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace Prefab
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_originalPosition = transform.position; // 初始位置只需在组件生命周期中获取一次
|
||||
_originalPosition = transform.localPosition; // 初始位置只需在组件生命周期中获取一次
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@ -51,7 +51,7 @@ namespace Prefab
|
||||
var yOffset = GetYPosition?.Invoke(currentAnimationTime) ?? 0f;
|
||||
|
||||
// 应用偏移量到物体位置,保持原始Z轴不变
|
||||
transform.position = _originalPosition + new Vector3(xOffset, yOffset, 0);
|
||||
transform.localPosition = _originalPosition + new Vector3(xOffset, yOffset, 0);
|
||||
|
||||
// 如果达到销毁条件,在完成最后一帧动画更新后销毁物体
|
||||
if (shouldDestroy)
|
||||
|
@ -6,7 +6,8 @@ namespace Prefab
|
||||
public class TemporaryAnimatorImageUI:TemporaryAnimator
|
||||
{
|
||||
public UIImageAnimator imageAnimator;
|
||||
|
||||
public RectTransform rectTransform;
|
||||
|
||||
public void Init(Sprite[] sprite,float fps=3)
|
||||
{
|
||||
if (imageAnimator == null)
|
||||
|
@ -10,11 +10,12 @@ namespace Prefab
|
||||
|
||||
public void Init(Sprite[] textures,float fps=3)
|
||||
{
|
||||
if(spriteAnimator == null)
|
||||
if(!spriteAnimator)
|
||||
spriteAnimator = GetComponentInChildren<SpriteAnimator>();
|
||||
if (!spriteAnimator) return;
|
||||
spriteAnimator.SetSprites(textures);
|
||||
spriteAnimator.SetFPS(fps);
|
||||
spriteAnimator.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,8 @@ namespace Prefab
|
||||
/// </summary>
|
||||
[SerializeField] public TMP_Text text;
|
||||
|
||||
public RectTransform rectTransform;
|
||||
|
||||
/// <summary>
|
||||
/// 当前播放的动画帧索引。
|
||||
/// </summary>
|
||||
|
@ -23,7 +23,7 @@ public class Program : Singleton<Program>
|
||||
/// 当前聚焦的实体对象。
|
||||
/// 变更为属性,并私有化setter,确保通过 SetFocusedEntity 方法集中管理其更新和事件触发。
|
||||
/// </summary>
|
||||
public Entity.Entity FocusedEntity { get; private set; } = null;
|
||||
public Entity.Entity FocusedEntity => FocusedDimension?.focusEntity;
|
||||
|
||||
/// <summary>
|
||||
/// 当焦点实体发生变化时触发的事件。
|
||||
@ -175,7 +175,7 @@ public class Program : Singleton<Program>
|
||||
return;
|
||||
}
|
||||
|
||||
if (FocusedEntity != null)
|
||||
if (FocusedEntity)
|
||||
{
|
||||
FocusedEntity.PlayerControlled = false;
|
||||
}
|
||||
@ -200,17 +200,11 @@ public class Program : Singleton<Program>
|
||||
{
|
||||
// 优化:检查是否实际发生变化,避免不必要的更新和事件触发。
|
||||
// 如果新的实体与当前焦点实体相同(按引用比较),则不执行任何操作。
|
||||
if (FocusedEntity == entity)
|
||||
if (FocusedEntity == entity||!FocusedDimension)
|
||||
{
|
||||
// Debug.Log($"[Program] SetFocusedEntity: Entity '{entity?.name ?? "null"}' already current. No change needed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新内部状态
|
||||
FocusedEntity = entity;
|
||||
if(FocusedDimension)
|
||||
FocusedDimension.focusEntity=entity;
|
||||
|
||||
FocusedDimension.focusEntity=entity;
|
||||
// 触发事件,将新的焦点实体(或 null)作为参数传递。
|
||||
OnFocusedEntityChanged?.Invoke(FocusedEntity);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ namespace UI
|
||||
{
|
||||
InitReloadGameButton();
|
||||
InitEvent();
|
||||
InitStory();
|
||||
InitCharacter();
|
||||
InitMonster();
|
||||
InitBuilding();
|
||||
@ -72,17 +73,22 @@ namespace UI
|
||||
|
||||
private void InitEvent()
|
||||
{
|
||||
// 假设存在 Data.EventDef 类型,且它继承自 Data.DefBase,并包含一个可作为标签的字段。
|
||||
// 如果事件触发逻辑不同于生成实体,需要在此处定义相应的回调。
|
||||
InitDefineButtons<EventDef>(
|
||||
"事件菜单",
|
||||
"未定义任何事件",
|
||||
def => def.label,
|
||||
def => string.IsNullOrEmpty(def.label) ? def.defName : def.label,
|
||||
eventDef => { Managers.EventManager.Instance.Action(eventDef.defName); });
|
||||
}
|
||||
|
||||
private void InitStory()
|
||||
{
|
||||
InitDefineButtons<StoryDef>(
|
||||
"据泵菜单",
|
||||
"未定义任何剧本",
|
||||
def => string.IsNullOrEmpty(def.label) ? def.defName : def.label,
|
||||
eventDef =>
|
||||
{
|
||||
// TODO: 在这里实现事件触发逻辑
|
||||
Debug.Log($"触发事件: {eventDef.label}");
|
||||
// 示例: Managers.EventManager.Instance.TriggerEvent(eventDef.id);
|
||||
Managers.EventManager.Instance.PlayStory(eventDef.defName);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,139 @@
|
||||
using System;
|
||||
using Base;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UI
|
||||
{
|
||||
|
||||
public class EquipmentUI : MonoBehaviour
|
||||
/// <summary>
|
||||
/// 装备UI类,负责显示当前聚焦角色的装备(物品栏中的前三个槽位)。
|
||||
/// </summary>
|
||||
public class EquipmentUI : MonoBehaviour,ITick
|
||||
{
|
||||
public ItemUI currentUse;
|
||||
public ItemUI two;
|
||||
public ItemUI three;
|
||||
// 这些公共变量用于在 Inspector 中分配 ItemUI 对象,分别对应当前使用、第二个和第三个装备槽。
|
||||
public ItemUI currentUse; // 当前正在使用的装备槽 UI
|
||||
public ItemUI two; // 角色物品栏中的第二个槽位对应的 UI
|
||||
public ItemUI three; // 角色物品栏中的第三个槽位对应的 UI
|
||||
|
||||
// 存储当前聚焦的实体(通常是玩家角色)。
|
||||
private Character focusedEntity;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 订阅 Program 实例的聚焦实体改变事件,以便在聚焦实体变化时更新 UI。
|
||||
Program.Instance.OnFocusedEntityChanged += FocusEntityChanged;
|
||||
// 组件启动时初始化 UI 显示。
|
||||
UpdateUI();
|
||||
|
||||
Clock.AddTick(this);
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 在销毁时取消订阅事件,防止内存泄漏。
|
||||
// 检查 Program.Instance 是否仍然存在,以避免在场景销毁时出现空引用异常。
|
||||
if (Program.Instance != null)
|
||||
{
|
||||
Program.Instance.OnFocusedEntityChanged -= FocusEntityChanged;
|
||||
}
|
||||
// 如果聚焦实体及其背包存在,则取消订阅背包改变事件。
|
||||
if (focusedEntity != null && focusedEntity.Inventory != null)
|
||||
{
|
||||
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
|
||||
}
|
||||
Clock.RemoveTick(this);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (!focusedEntity) return;
|
||||
if (Input.GetKeyDown(KeyCode.Alpha1))
|
||||
{
|
||||
|
||||
}
|
||||
else if(Input.GetKeyDown(KeyCode.Alpha2))
|
||||
{
|
||||
focusedEntity.CurrentSelected = focusedEntity.CurrentSelected == 0 ? 1 : 0;
|
||||
UpdateUI();
|
||||
}
|
||||
else if(Input.GetKeyDown(KeyCode.Alpha3))
|
||||
{
|
||||
focusedEntity.CurrentSelected = focusedEntity.CurrentSelected == 2 ? 1 : 2;
|
||||
UpdateUI();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当聚焦实体发生改变时调用此方法。
|
||||
/// </summary>
|
||||
/// <param name="e">新的聚焦实体。</param>
|
||||
private void FocusEntityChanged(Entity.Entity e)
|
||||
{
|
||||
// 如果存在旧的聚焦实体,则先取消订阅其背包改变事件。
|
||||
if (focusedEntity)
|
||||
{
|
||||
focusedEntity.Inventory.OnInventoryChanged -= UpdateUI;
|
||||
}
|
||||
|
||||
// 设置新的聚焦实体,并尝试将其转换为 Character 类型。
|
||||
focusedEntity = e as Character;
|
||||
|
||||
// 如果新的聚焦实体存在且是 Character 类型,则订阅其背包改变事件。
|
||||
if (focusedEntity)
|
||||
{
|
||||
focusedEntity.Inventory.OnInventoryChanged += UpdateUI;
|
||||
}
|
||||
|
||||
// 聚焦实体改变后更新 UI 显示。
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新装备 UI 的显示。
|
||||
/// 根据聚焦角色的当前选中物品和物品栏状态来更新UI。
|
||||
/// </summary>
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (focusedEntity)
|
||||
{
|
||||
var currentSelectedIndex = focusedEntity.CurrentSelected;
|
||||
var nonCurrentUseCounter = 0; // 用于计数非当前选中项的索引,以分配给 two 和 three
|
||||
|
||||
// 遍历角色的前三个装备槽(假设物品栏只显示前3个槽位)。
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var slot = focusedEntity.Inventory.GetSlot(i);
|
||||
|
||||
if (i == currentSelectedIndex)
|
||||
{
|
||||
// 如果是当前选中的槽位,则将物品信息显示在 currentUse UI 上。
|
||||
currentUse.SetDisplayItem(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是当前选中的槽位,则根据计数器决定显示在 two 或 three UI 上。
|
||||
if (nonCurrentUseCounter == 0)
|
||||
{
|
||||
two.SetDisplayItem(slot);
|
||||
}
|
||||
else if (nonCurrentUseCounter == 1)
|
||||
{
|
||||
three.SetDisplayItem(slot);
|
||||
}
|
||||
nonCurrentUseCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有聚焦的实体,则清空所有装备槽的 UI 显示。
|
||||
currentUse.SetDisplayItem(null);
|
||||
two.SetDisplayItem(null);
|
||||
three.SetDisplayItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
21
Client/Assets/Scripts/Utils/RotateTool.cs
Normal file
21
Client/Assets/Scripts/Utils/RotateTool.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
public static class RotateTool
|
||||
{
|
||||
// 旋转对象到指定方向
|
||||
|
||||
public static void RotateTransformToDirection(Transform transform, Vector3 targetDirection)
|
||||
{
|
||||
// 确保目标方向不是零向量
|
||||
if (targetDirection == Vector3.zero)
|
||||
return;
|
||||
|
||||
// 计算当前向上方向与目标方向之间的角度
|
||||
var angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
|
||||
// 应用旋转
|
||||
transform.rotation = Quaternion.Euler(0f, 0f, angle);
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Utils/RotateTool.cs.meta
Normal file
3
Client/Assets/Scripts/Utils/RotateTool.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d25c353b3bed400f92bccc0986b22420
|
||||
timeCreated: 1757241772
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user