(client) chore
This commit is contained in:
@ -84,11 +84,11 @@ PrefabInstance:
|
||||
m_Modifications:
|
||||
- target: {fileID: 697189026367054479, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -0.93
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 697189026367054479, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: -0.25
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 697189026367054479, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
@ -187,8 +187,10 @@ MonoBehaviour:
|
||||
animatorPrefab: {fileID: 0}
|
||||
imagePrefab: {fileID: 0}
|
||||
healthBarPrefab: {fileID: 0}
|
||||
entityPrefab: {fileID: 0}
|
||||
direction: {x: 0, y: 0, z: 0}
|
||||
body: {fileID: 0}
|
||||
affiliation:
|
||||
canSelect: 1
|
||||
hitBarUIShowTime: 5
|
||||
currentDimensionId:
|
||||
_hitBarUIShowTime: 5
|
||||
|
BIN
Client/Assets/Resources/Image/Square.png
Normal file
BIN
Client/Assets/Resources/Image/Square.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
173
Client/Assets/Resources/Image/Square.png.meta
Normal file
173
Client/Assets/Resources/Image/Square.png.meta
Normal file
@ -0,0 +1,173 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3987fd52691151942a0279746bc41c15
|
||||
TextureImporter:
|
||||
internalIDToNameTable:
|
||||
- first:
|
||||
213: 7482667652216324306
|
||||
second: Square
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 2
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 256
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: 0
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: iOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites:
|
||||
- serializedVersion: 2
|
||||
name: Square
|
||||
rect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 256
|
||||
height: 256
|
||||
alignment: 0
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
customData:
|
||||
outline: []
|
||||
physicsShape: []
|
||||
tessellationDetail: 0
|
||||
bones: []
|
||||
spriteID: 2d009a6b596c7d760800000000000000
|
||||
internalID: 7482667652216324306
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape:
|
||||
- - {x: -128, y: 128}
|
||||
- {x: -128, y: -128}
|
||||
- {x: 128, y: -128}
|
||||
- {x: 128, y: 128}
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable:
|
||||
Square: 7482667652216324306
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -123,7 +123,6 @@ Transform:
|
||||
- {fileID: 5549544358816209289}
|
||||
- {fileID: 1697214530303839877}
|
||||
- {fileID: 1404278780950315184}
|
||||
- {fileID: 8903644794067773966}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &3332598847335032684
|
||||
@ -253,88 +252,6 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 697189026367054479}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &6276067436382167109
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8903644794067773966}
|
||||
- component: {fileID: 2936989077236630990}
|
||||
- component: {fileID: 7296562035276564141}
|
||||
m_Layer: 6
|
||||
m_Name: Vision
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &8903644794067773966
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6276067436382167109}
|
||||
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: 697189026367054479}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!58 &2936989077236630990
|
||||
CircleCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6276067436382167109}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Radius: 10
|
||||
--- !u!114 &7296562035276564141
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6276067436382167109}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e33d58195dd8450a9ec468ede64c8635, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_collider: {fileID: 2936989077236630990}
|
||||
--- !u!1001 &3478909419655914534
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -391,6 +308,18 @@ PrefabInstance:
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3249131573413190125, guid: c9e9ee0f8c58b5b45b2e85c7de8251b0, type: 3}
|
||||
propertyPath: m_Color.b
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3249131573413190125, guid: c9e9ee0f8c58b5b45b2e85c7de8251b0, type: 3}
|
||||
propertyPath: m_Color.g
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3249131573413190125, guid: c9e9ee0f8c58b5b45b2e85c7de8251b0, type: 3}
|
||||
propertyPath: m_Color.r
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
|
167
Client/Assets/Resources/Prefab/UI/Bar.prefab
Normal file
167
Client/Assets/Resources/Prefab/UI/Bar.prefab
Normal file
@ -0,0 +1,167 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &5797149502888100339
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2529712523917175612}
|
||||
- component: {fileID: 9163052883457021891}
|
||||
- component: {fileID: 5309915153765986547}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2529712523917175612
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5797149502888100339}
|
||||
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: 6633399472656308268}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &9163052883457021891
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5797149502888100339}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5309915153765986547
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5797149502888100339}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 0, b: 0.9424448, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 7482667652216324306, guid: 3987fd52691151942a0279746bc41c15, type: 3}
|
||||
m_Type: 3
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 0
|
||||
m_FillAmount: 0.507
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7666974979659500467
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6633399472656308268}
|
||||
- component: {fileID: 775118057073664828}
|
||||
- component: {fileID: 789982864557509744}
|
||||
- component: {fileID: 8924509912883067279}
|
||||
m_Layer: 5
|
||||
m_Name: Bar
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6633399472656308268
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7666974979659500467}
|
||||
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: 2529712523917175612}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 5}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &775118057073664828
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7666974979659500467}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &789982864557509744
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7666974979659500467}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &8924509912883067279
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7666974979659500467}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a5026699f3a94f029628af90ccd8fa8d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
image: {fileID: 5309915153765986547}
|
7
Client/Assets/Resources/Prefab/UI/Bar.prefab.meta
Normal file
7
Client/Assets/Resources/Prefab/UI/Bar.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b1aef36872c4f44a907f96bf813e8e5
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -1729,8 +1729,8 @@ MonoBehaviour:
|
||||
loadingUI: {fileID: 1989361079}
|
||||
progressBar: {fileID: 521385226}
|
||||
describeText: {fileID: 1145531079}
|
||||
duration: 0.1
|
||||
fadeDuration: 0.1
|
||||
duration: 0
|
||||
fadeDuration: 0
|
||||
--- !u!1 &1989361079
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AI
|
||||
@ -12,14 +13,9 @@ namespace AI
|
||||
}
|
||||
|
||||
|
||||
public static bool IsPlayer(Entity.Entity entity)
|
||||
{
|
||||
return entity.PlayerControlled;
|
||||
}
|
||||
|
||||
public static bool HasEnemyInSight(Entity.Entity entity)
|
||||
{
|
||||
return entity.visionRangeDetector.GetNearestNEntitiesByRelation(1, Relation.Hostile).Count > 0;
|
||||
return Managers.EntityManage.Instance.ExistsHostile(entity.currentDimensionId,entity.entityPrefab);
|
||||
}
|
||||
}
|
||||
}
|
@ -83,8 +83,11 @@ namespace CameraControl
|
||||
{
|
||||
_dragOrigin = _camera.ScreenToWorldPoint(Input.mousePosition);
|
||||
_isDragging = true;
|
||||
Program.Instance.focusedEntity.PlayerControlled = false;
|
||||
Program.Instance.focusedEntity = null; // Clear focus when manually moving camera
|
||||
if (Program.Instance.focusedEntity)
|
||||
{
|
||||
Program.Instance.focusedEntity.PlayerControlled = false;
|
||||
Program.Instance.focusedEntity = null; // Clear focus when manually moving camera
|
||||
}
|
||||
}
|
||||
|
||||
// During drag
|
||||
|
15
Client/Assets/Scripts/CameraControl/MiniMapCamera.cs
Normal file
15
Client/Assets/Scripts/CameraControl/MiniMapCamera.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace CameraControl
|
||||
{
|
||||
public class MiniMapCamera : MonoBehaviour, Base.ITick
|
||||
{
|
||||
public void Tick()
|
||||
{
|
||||
if (Program.Instance.focusedEntity)
|
||||
{
|
||||
transform.position = Program.Instance.focusedEntity.transform.position + new Vector3(0, 0, -10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef73ec15d93e4f099081b221e275834d
|
||||
timeCreated: 1756173994
|
@ -11,18 +11,5 @@ namespace Data
|
||||
public int attackSpeed = 2;
|
||||
public int attackRange = 3;
|
||||
public int attackTargetCount = 1;
|
||||
public AttributesDef Clone()
|
||||
{
|
||||
return new AttributesDef
|
||||
{
|
||||
health = this.health,
|
||||
moveSpeed = this.moveSpeed,
|
||||
attack = this.attack,
|
||||
defense = this.defense,
|
||||
attackSpeed = this.attackSpeed,
|
||||
attackRange = this.attackRange,
|
||||
attackTargetCount = this.attackTargetCount
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -228,7 +228,7 @@ namespace Data
|
||||
public static Define LoadDefineClass(XElement defineDoc, string className)
|
||||
{
|
||||
// 优化点1和2:反射缓存和改进的类型查找
|
||||
if (!_typeCache.TryGetValue(className, out Type type))
|
||||
if (!_typeCache.TryGetValue(className, out var type))
|
||||
{
|
||||
// 首先尝试使用 CoreNamespace
|
||||
var fullClassName = CoreNamespace + className;
|
||||
@ -249,7 +249,7 @@ namespace Data
|
||||
}
|
||||
|
||||
// 优化点1:构造函数缓存
|
||||
if (!_constructorCache.TryGetValue(type, out ConstructorInfo constructor))
|
||||
if (!_constructorCache.TryGetValue(type, out var constructor))
|
||||
{
|
||||
constructor = type.GetConstructor(Type.EmptyTypes);
|
||||
if (constructor == null)
|
||||
@ -305,7 +305,7 @@ namespace Data
|
||||
public static void DefaultInitDefine(Define define, XElement defineDoc, Type defineType)
|
||||
{
|
||||
// 优化点1:FieldInfo 缓存
|
||||
if (!_fieldCache.TryGetValue(defineType, out FieldInfo[] fields))
|
||||
if (!_fieldCache.TryGetValue(defineType, out var fields))
|
||||
{
|
||||
fields = defineType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
_fieldCache[defineType] = fields;
|
||||
@ -320,7 +320,7 @@ namespace Data
|
||||
try
|
||||
{
|
||||
// 优化点4:重构 ProcessArrayField 并引入通用转换辅助方法
|
||||
object value = ConvertXElementValueToType(element, field.FieldType);
|
||||
var value = ConvertXElementValueToType(element, field.FieldType);
|
||||
field.SetValue(define, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -368,7 +368,7 @@ namespace Data
|
||||
private static object ProcessArrayField(Type fieldType, XElement element)
|
||||
{
|
||||
// 获取集合的元素类型
|
||||
Type elementType = fieldType.IsArray
|
||||
var elementType = fieldType.IsArray
|
||||
? fieldType.GetElementType()
|
||||
: fieldType.GetGenericArguments().FirstOrDefault(); // 使用 FirstOrDefault 以确保安全
|
||||
|
||||
|
@ -16,13 +16,4 @@ namespace Data
|
||||
public bool ssEquippable = false; // 是否可装备
|
||||
}
|
||||
|
||||
public class WeaponDef : ItemDef
|
||||
{
|
||||
public AttributesDef attributes;
|
||||
public WeaponDef() // 构造函数,用于设置武器的默认属性
|
||||
{
|
||||
maxStack = 1; // 武器默认最大堆叠为1
|
||||
ssEquippable = true; // 武器默认可装备
|
||||
}
|
||||
}
|
||||
}
|
18
Client/Assets/Scripts/Data/WeaponDef.cs
Normal file
18
Client/Assets/Scripts/Data/WeaponDef.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace Data
|
||||
{
|
||||
public enum WeaponType
|
||||
{
|
||||
Melee, // 近战武器
|
||||
Ranged // 远程武器
|
||||
}
|
||||
public class WeaponDef : ItemDef
|
||||
{
|
||||
public WeaponType type = WeaponType.Melee;
|
||||
public AttributesDef attributes;
|
||||
public WeaponDef() // 构造函数,用于设置武器的默认属性
|
||||
{
|
||||
maxStack = 1; // 武器默认最大堆叠为1
|
||||
ssEquippable = true; // 武器默认可装备
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Data/WeaponDef.cs.meta
Normal file
3
Client/Assets/Scripts/Data/WeaponDef.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ed90cba91ce41e1ae443cf44a76c932
|
||||
timeCreated: 1756193931
|
27
Client/Assets/Scripts/Entity/Attributes.cs
Normal file
27
Client/Assets/Scripts/Entity/Attributes.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Data;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class Attributes
|
||||
{
|
||||
public int health = 10;
|
||||
public float moveSpeed = 1;
|
||||
public int attack = 1;
|
||||
public int defense = 0;
|
||||
public int attackSpeed = 2;
|
||||
public int attackRange = 3;
|
||||
public int attackTargetCount = 1;
|
||||
public Attributes(AttributesDef def)
|
||||
{
|
||||
health = def.health;
|
||||
moveSpeed = def.moveSpeed;
|
||||
attack = def.attack;
|
||||
defense = def.defense;
|
||||
attackSpeed = def.attackSpeed;
|
||||
attackRange = def.attackRange;
|
||||
attackTargetCount = def.attackTargetCount;
|
||||
}
|
||||
public Attributes()
|
||||
{}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/Attributes.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/Attributes.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a009acc975a4e4b993eaa4b853f7109
|
||||
timeCreated: 1756184938
|
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AI;
|
||||
using Base;
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
|
||||
@ -9,20 +6,74 @@ namespace Entity
|
||||
{
|
||||
public class Character : Entity
|
||||
{
|
||||
public CharacterDef characterDef;
|
||||
|
||||
private void Start()
|
||||
// ------------- 新增 / 修改部分 START -------------
|
||||
|
||||
// 声明 Inventory 字段。更改为 private set 以确保 Inventory 实例只能在 Character 内部设置。
|
||||
public Inventory Inventory { get; private set; }
|
||||
|
||||
public override void Init(EntityDef entityDef)
|
||||
{
|
||||
aiTree = new JobGiver_RandomWander();
|
||||
attributes = new AttributesDef();
|
||||
base.Init(entityDef);
|
||||
|
||||
// 1. 初始化背包:
|
||||
// 在这里创建并初始化 Inventory 实例。
|
||||
// 'this' 指的是当前 Character 实例,作为 Inventory 的拥有者。
|
||||
// 容量可以根据 entityDef 或一个默认值来设定。这里使用默认值 20。
|
||||
Inventory = new Inventory(this, 3);
|
||||
|
||||
// (可选) 可以在此订阅背包事件,以便 Character 对背包变化做出响应,
|
||||
// 例如更新角色状态、播放音效或更新UI。
|
||||
// Inventory.OnItemAdded += HandleItemAddedToInventory;
|
||||
// Inventory.OnItemRemoved += HandleItemRemovedFromInventory;
|
||||
// Inventory.OnInventoryChanged += HandleInventoryChanged;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
/// <summary>
|
||||
/// 尝试将指定物品添加到角色的背包中。
|
||||
/// </summary>
|
||||
/// <param name="itemResource">要尝试添加的物品资源。</param>
|
||||
/// <param name="quantity">要尝试添加的数量。</param>
|
||||
/// <returns>未成功添加到背包的物品数量。
|
||||
/// 如果返回0,表示所有物品都成功添加;
|
||||
/// 如果返回quantity,表示未能添加任何物品;
|
||||
/// 如果返回一个介于0和quantity之间的值,表示部分物品被添加。</returns>
|
||||
public int TryPickupItem(Item.ItemResource itemResource, int quantity)
|
||||
{
|
||||
if (characterDef == null)
|
||||
return;
|
||||
if (Inventory == null)
|
||||
{
|
||||
Debug.LogError($"Character '{this.name}' inventory is not initialized. Cannot pickup item.");
|
||||
return quantity; // 如果背包未初始化,则视为未能添加任何物品
|
||||
}
|
||||
|
||||
// 直接调用 Inventory 实例的 AddItem 方法。
|
||||
// AddItem 方法已经包含了严谨的逻辑来处理物品堆叠、新槽位分配、容量检查以及无效输入的警告。
|
||||
int remainingQuantity = Inventory.AddItem(itemResource, quantity);
|
||||
|
||||
return remainingQuantity;
|
||||
}
|
||||
|
||||
// (可选) 示例事件处理方法,用于展示如何响应背包事件
|
||||
// private void HandleItemAddedToInventory(Item.ItemResource item, int count)
|
||||
// {
|
||||
// Debug.Log($"{DefName} picked up {count} x {item.Name}. Current total: {Inventory.GetItemCount(item)}");
|
||||
// // 例如更新UI、播放音效、完成任务等
|
||||
// }
|
||||
|
||||
// private void HandleItemRemovedFromInventory(Item.ItemResource item, int count)
|
||||
// {
|
||||
// Debug.Log($"{DefName} removed {count} x {item.Name}. Current total: {Inventory.GetItemCount(item)}");
|
||||
// // 例如更新UI、播放音效、更新制作状态等
|
||||
// }
|
||||
|
||||
// private void HandleInventoryChanged()
|
||||
// {
|
||||
// Debug.Log($"{DefName}'s inventory changed. Occupied slots: {Inventory.OccupiedSlotsCount}/{Inventory.Capacity}");
|
||||
// // 例如通知背包UI刷新显示
|
||||
// }
|
||||
|
||||
// ------------- 新增 / 修改部分 END -------------
|
||||
|
||||
|
||||
public override void TryAttack()
|
||||
{
|
||||
if (IsAttacking)
|
||||
@ -35,4 +86,4 @@ namespace Entity
|
||||
dir - Position, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using Base;
|
||||
using Data;
|
||||
using Prefab;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
|
||||
namespace Entity
|
||||
@ -29,7 +28,9 @@ namespace Entity
|
||||
|
||||
public ProgressBarPrefab healthBarPrefab;
|
||||
|
||||
public VisionRangeDetector visionRangeDetector;
|
||||
public EntityPrefab entityPrefab;
|
||||
public EntityDef entityDef;
|
||||
|
||||
/// <summary>
|
||||
/// 人工智能行为树,定义实体的行为逻辑。
|
||||
/// </summary>
|
||||
@ -43,8 +44,7 @@ namespace Entity
|
||||
/// <summary>
|
||||
/// 实体的属性定义,包括生命值、攻击力、防御力等。
|
||||
/// </summary>
|
||||
public AttributesDef attributes = new();
|
||||
|
||||
public Attributes attributes=new();
|
||||
/// <summary>
|
||||
/// 实体当前的移动方向。
|
||||
/// </summary>
|
||||
@ -69,7 +69,8 @@ namespace Entity
|
||||
/// 表示实体是否处于追逐状态(影响移动速度)。
|
||||
/// </summary>
|
||||
public bool IsChase { set; get; } = true;
|
||||
|
||||
|
||||
public string currentDimensionId = null;
|
||||
|
||||
|
||||
|
||||
@ -149,7 +150,6 @@ namespace Entity
|
||||
// 协程引用
|
||||
private Coroutine _attackCoroutine;
|
||||
|
||||
protected EntityDef entityDef;
|
||||
|
||||
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
@ -163,7 +163,7 @@ namespace Entity
|
||||
/// <param name="entityDef">实体的定义数据。</param>
|
||||
public virtual void Init(EntityDef entityDef)
|
||||
{
|
||||
attributes = entityDef.attributes.Clone();
|
||||
attributes = new Attributes(entityDef.attributes);
|
||||
aiTree = Utils.BehaviorTree.ConvertToAIBase(entityDef.behaviorTree);
|
||||
affiliation = entityDef.affiliation?.defName;
|
||||
InitBody(entityDef.drawingOrder);
|
||||
@ -227,7 +227,7 @@ namespace Entity
|
||||
var animators = targetObj.GetComponentsInChildren<SpriteAnimator>();
|
||||
if (animators.Length > 0)
|
||||
{
|
||||
animatorsForOrientation.AddRange(animators.Cast<ITick>());
|
||||
animatorsForOrientation.AddRange(animators);
|
||||
}
|
||||
stateAnimNodes[orientation] = animatorsForOrientation;
|
||||
}
|
||||
|
24
Client/Assets/Scripts/Entity/Pickup.cs
Normal file
24
Client/Assets/Scripts/Entity/Pickup.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Base;
|
||||
using Data;
|
||||
using Item;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class Pickup:Entity
|
||||
{
|
||||
public ItemResource itemResource;
|
||||
protected override void AutoBehave()
|
||||
{
|
||||
}
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
var entity = other.GetComponent<Character>();
|
||||
if (entity == null) return;
|
||||
if (entity.TryPickupItem(itemResource, 1) == 0)
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/Pickup.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/Pickup.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e65cb3d0f8440c599399cefd9a3a5d7
|
||||
timeCreated: 1756191938
|
@ -1,188 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 视野范围检测器组件。
|
||||
/// 挂载到包含触发器碰撞体的GameObject上,用于检测视野范围内的Entity。
|
||||
/// </summary>
|
||||
public class VisionRangeDetector : MonoBehaviour
|
||||
{
|
||||
// 允许在Inspector中手动指定Collider,或在Awake时自动获取。
|
||||
[SerializeField] [Tooltip("用于检测视野范围的触发器碰撞体。如果未指定,将在Awake时自动获取。")]
|
||||
private Collider2D _collider;
|
||||
|
||||
// 缓存所属的Entity组件,用于获取派系信息。
|
||||
private Entity _ownerEntity;
|
||||
|
||||
// 将List改为HashSet,优化添加和移除的性能。
|
||||
private HashSet<Entity> _entitiesInRange = new HashSet<Entity>(); // 维护在视野范围内的Entity集合
|
||||
private Transform _myTransform; // 缓存Transform组件以提高性能
|
||||
|
||||
// 【逻辑修改】用于记录上次清理的帧数,避免在同一帧内重复清理。
|
||||
private int _lastCleanupFrame = -1;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_myTransform = transform; // 缓存自身Transform
|
||||
|
||||
// 1. 检查并确保GameObject上存在触发器碰撞体
|
||||
if (_collider == null)
|
||||
{
|
||||
_collider = GetComponent<Collider2D>();
|
||||
}
|
||||
|
||||
if (_collider == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"VisionRangeDetector on {gameObject.name} requires a Collider component. Please add one or assign it in the Inspector.",
|
||||
this);
|
||||
enabled = false; // 如果没有碰撞体,禁用此组件
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_collider.isTrigger)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"VisionRangeDetector on {gameObject.name} works best with a Trigger Collider. Setting isTrigger to true.",
|
||||
this);
|
||||
_collider.isTrigger = true;
|
||||
}
|
||||
|
||||
// 2. 检查AffiliationManager是否存在
|
||||
if (AffiliationManager.Instance == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
"AffiliationManager.Instance is null. Please ensure an AffiliationManager exists in the scene.",
|
||||
this);
|
||||
enabled = false; // 如果没有派系管理器,禁用此组件
|
||||
return; // 提前返回,避免后续操作依赖于AffiliationManager
|
||||
}
|
||||
|
||||
// 3. 获取所属的Entity组件,并使用其affiliation。
|
||||
_ownerEntity = GetComponentInParent<Entity>();
|
||||
if (_ownerEntity == null)
|
||||
{
|
||||
_ownerEntity = GetComponent<Entity>();
|
||||
}
|
||||
|
||||
if (_ownerEntity == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"VisionRangeDetector on {gameObject.name}: No Entity component found in parent or self. This component cannot determine its affiliation and will be disabled.",
|
||||
this);
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 【逻辑修改】移除LateUpdate,清理逻辑移至获取方法中。
|
||||
// private void LateUpdate()
|
||||
// {
|
||||
// _entitiesInRange.RemoveWhere(e => e == null);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 【逻辑修改】在获取实体列表前,执行一次清理操作(每帧最多一次)。
|
||||
/// </summary>
|
||||
private void CleanEntitiesInRange()
|
||||
{
|
||||
// 只有当当前帧与上次清理的帧不同时才执行清理
|
||||
if (Time.frameCount != _lastCleanupFrame)
|
||||
{
|
||||
_entitiesInRange.RemoveWhere(e => e == null);
|
||||
_lastCleanupFrame = Time.frameCount; // 更新上次清理的帧数
|
||||
// Debug.Log($"Cleaned _entitiesInRange in frame {Time.frameCount}. Current count: {_entitiesInRange.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当有其他碰撞体进入触发器范围时调用。
|
||||
/// </summary>
|
||||
/// <param name="other">进入触发器的碰撞体。</param>
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
Entity entity = other.GetComponent<Entity>();
|
||||
if (entity != null)
|
||||
{
|
||||
if (entity.gameObject == _ownerEntity.gameObject || entity.transform.IsChildOf(_ownerEntity.transform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entitiesInRange.Add(entity))
|
||||
{
|
||||
// Debug.Log($"Entity '{entity.name}' (Affiliation: {entity.affiliation}) entered range. Total: {_entitiesInRange.Count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当有其他碰撞体离开触发器范围时调用。
|
||||
/// </summary>
|
||||
/// <param name="other">离开触发器的碰撞体。</param>
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
Entity entity = other.GetComponent<Entity>();
|
||||
if (entity != null)
|
||||
{
|
||||
if (_entitiesInRange.Remove(entity))
|
||||
{
|
||||
// Debug.Log($"Entity '{entity.name}' (Affiliation: {entity.affiliation}) exited range. Total: {_entitiesInRange.Count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取视野范围内最近的N个实体。
|
||||
/// </summary>
|
||||
/// <param name="n">要获取的实体数量。</param>
|
||||
/// <returns>最近的N个实体列表。</returns>
|
||||
public List<Entity> GetNearestNEntities(int n)
|
||||
{
|
||||
if (n <= 0) return new List<Entity>();
|
||||
|
||||
// 【逻辑修改】在获取前清理一次集合
|
||||
CleanEntitiesInRange();
|
||||
|
||||
return _entitiesInRange
|
||||
.OrderBy(e => Vector3.Distance(_myTransform.position, e.transform.position))
|
||||
.Take(n)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取视野范围内最近的N个指定关系的实体(敌对、友好、中立)。
|
||||
/// </summary>
|
||||
/// <param name="n">要获取的实体数量。</param>
|
||||
/// <param name="targetRelation">目标派系关系。</param>
|
||||
/// <returns>最近的N个指定关系的实体列表。</returns>
|
||||
public List<Entity> GetNearestNEntitiesByRelation(int n, Relation targetRelation)
|
||||
{
|
||||
if (AffiliationManager.Instance == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
"AffiliationManager.Instance is null. Cannot get entities by relation. VisionRangeDetector should have been disabled if this was an issue.",
|
||||
this);
|
||||
return new List<Entity>();
|
||||
}
|
||||
|
||||
if (n <= 0) return new List<Entity>();
|
||||
|
||||
// 【逻辑修改】在获取前清理一次集合
|
||||
CleanEntitiesInRange();
|
||||
|
||||
return _entitiesInRange
|
||||
.Where(e => AffiliationManager.Instance.GetRelation(_ownerEntity.affiliation, e.affiliation) ==
|
||||
targetRelation)
|
||||
.OrderBy(e => Vector3.Distance(_myTransform.position, e.transform.position))
|
||||
.Take(n)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e33d58195dd8450a9ec468ede64c8635
|
||||
timeCreated: 1756048545
|
@ -1,41 +1,71 @@
|
||||
using System;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Item
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示游戏中的一个物品资源,包含了物品的各项基本属性和数据。
|
||||
/// 这是一个只读的数据结构,用于存储物品的定义信息。
|
||||
/// </summary>
|
||||
public class ItemResource
|
||||
{
|
||||
public string DefName { get; protected set; } // 新增:物品的定义名称,唯一标识符
|
||||
public string Name { get; protected set; } // 物品的显示名称 (来自 label)
|
||||
/// <summary>
|
||||
/// 物品的定义名称,通常作为其唯一标识符。
|
||||
/// </summary>
|
||||
public string DefName { get; protected set; }
|
||||
/// <summary>
|
||||
/// 物品的显示名称(例如,在UI中显示的名称)。
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
/// <summary>
|
||||
/// 物品的描述文本。
|
||||
/// </summary>
|
||||
public string Description { get; protected set; }
|
||||
/// <summary>
|
||||
/// 物品的图标精灵。
|
||||
/// </summary>
|
||||
public Sprite Icon { get; protected set; }
|
||||
/// <summary>
|
||||
/// 物品的稀有度。
|
||||
/// </summary>
|
||||
public ItemRarity Rarity { get; protected set; }
|
||||
/// <summary>
|
||||
/// 物品的最大堆叠数量。
|
||||
/// </summary>
|
||||
public int MaxStack { get; protected set; }
|
||||
/// <summary>
|
||||
/// 指示物品是否可以被装备。
|
||||
/// </summary>
|
||||
public bool IsEquippable { get; protected set; }
|
||||
|
||||
// 构造函数,现在接受 defName
|
||||
public ItemResource(string defName, string name, string description, Sprite icon, ItemRarity rarity, int maxStack, bool isEquippable)
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数:通过 ItemDef 对象初始化 ItemResource。
|
||||
/// </summary>
|
||||
/// <param name="def">物品的定义对象。</param>
|
||||
/// <exception cref="ArgumentNullException">如果传入的 ItemDef 为 null,则抛出此异常。</exception>
|
||||
public ItemResource(ItemDef def)
|
||||
{
|
||||
DefName = defName; // 赋值 defName
|
||||
Name = name;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
Rarity = rarity;
|
||||
MaxStack = maxStack;
|
||||
IsEquippable = isEquippable;
|
||||
// 参数校验:在构造函数中进行参数非空检查至关重要,避免空引用异常。
|
||||
if (def == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(def), "创建 ItemResource 时,ItemDef 不能为 null。");
|
||||
}
|
||||
|
||||
// 从 ItemDef 对象中直接赋值 ItemResource 的所有属性。
|
||||
// 这将创建 ItemResource 的逻辑完全封装在类内部,外部调用方无需关心具体属性的提取过程。
|
||||
DefName = def.defName;
|
||||
Name = def.label;
|
||||
Description = def.description;
|
||||
Icon = PackagesImageManager.Instance.GetSprite(def.texture);
|
||||
Rarity = def.rarity;
|
||||
MaxStack = def.maxStack;
|
||||
IsEquippable = def.ssEquippable;
|
||||
if (!Icon && def.texture == null)
|
||||
{
|
||||
Debug.LogWarning($"ItemResource: Failed to load sprite for texture '{def.texture}' for item '{def.defName}'. Icon will be null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class WeaponResource : ItemResource
|
||||
{
|
||||
public AttributesDef Attributes { get; private set; }
|
||||
|
||||
// 构造函数,调用基类构造函数并传递 defName
|
||||
public WeaponResource(string defName, string name, string description, Sprite icon, ItemRarity rarity, int maxStack, bool isEquippable, AttributesDef attributes)
|
||||
: base(defName, name, description, icon, rarity, maxStack, isEquippable)
|
||||
{
|
||||
Attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
33
Client/Assets/Scripts/Item/WeaponResource.cs
Normal file
33
Client/Assets/Scripts/Item/WeaponResource.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Data;
|
||||
using Entity;
|
||||
|
||||
namespace Item
|
||||
{
|
||||
public class WeaponResource : ItemResource
|
||||
{
|
||||
public Attributes Attributes { get; private set; }
|
||||
|
||||
public WeaponType Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数:通过 WeaponDef 对象初始化 WeaponResource。
|
||||
/// </summary>
|
||||
/// <param name="def">武器的定义对象。</param>
|
||||
/// <exception cref="ArgumentNullException">如果传入的 WeaponDef 为 null,则抛出此异常。</exception>
|
||||
public WeaponResource(WeaponDef def)
|
||||
: base(def) // 调用基类 ItemResource 的构造函数,直接传入 WeaponDef。
|
||||
{
|
||||
// 参数校验:确保 WeaponDef 对象不为空,避免空引用异常。
|
||||
if (def == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(def), "创建 WeaponResource 时,WeaponDef 不能为 null。");
|
||||
}
|
||||
|
||||
// 初始化武器的属性。Attributes 对象通过 WeaponDef 中的属性数据进行构建。
|
||||
Attributes = new Attributes(def.attributes);
|
||||
// 初始化武器类型,直接从 WeaponDef 定义中获取。
|
||||
Type = def.type;
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Item/WeaponResource.cs.meta
Normal file
3
Client/Assets/Scripts/Item/WeaponResource.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa734e2254034fa4a59fb1e0cdea250f
|
||||
timeCreated: 1756193577
|
@ -85,6 +85,10 @@ namespace Managers
|
||||
return GetRelation(affiliation1.defName, affiliation2.defName);
|
||||
}
|
||||
|
||||
public Relation GetRelation(Entity.Entity entity1, Entity.Entity entity2)
|
||||
{
|
||||
return GetRelation(entity1.affiliation, entity2.affiliation);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取两个阵营名称之间的关系。
|
||||
/// </summary>
|
||||
|
@ -56,7 +56,7 @@ namespace Managers
|
||||
lock (_lock)
|
||||
{
|
||||
// 交换读写缓冲区
|
||||
T temp = _readBuffer;
|
||||
var temp = _readBuffer;
|
||||
_readBuffer = _writeBuffer;
|
||||
_writeBuffer = temp;
|
||||
}
|
||||
@ -133,7 +133,7 @@ namespace Managers
|
||||
/// <returns>缓冲区中的第一条消息,如果缓冲区为空则返回 null。</returns>
|
||||
public static string ReadMessage()
|
||||
{
|
||||
if (MessageQueue.TryDequeue(out string message))
|
||||
if (MessageQueue.TryDequeue(out var message))
|
||||
{
|
||||
// 释放一个缓冲区空间
|
||||
BufferSemaphore.Release();
|
||||
|
@ -335,7 +335,7 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Define> result = new List<Define>();
|
||||
var result = new List<Define>();
|
||||
|
||||
// 从命名定义中查询。
|
||||
if (defines.TryGetValue(defineType, out var namedDefinitions))
|
||||
@ -368,7 +368,7 @@ namespace Managers
|
||||
{
|
||||
var defineType = typeof(T).Name;
|
||||
|
||||
List<Define> allDefines = QueryDefinesByType(defineType)?.ToList();
|
||||
var allDefines = QueryDefinesByType(defineType)?.ToList();
|
||||
if (allDefines == null || allDefines.Count == 0)
|
||||
{
|
||||
return null;
|
||||
@ -413,7 +413,7 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Define> result = new List<Define>();
|
||||
var result = new List<Define>();
|
||||
|
||||
// 仅从命名定义中查询。
|
||||
if (defines.TryGetValue(defineType, out var namedDefinitions))
|
||||
@ -440,7 +440,7 @@ namespace Managers
|
||||
{
|
||||
var defineType = typeof(T).Name;
|
||||
|
||||
List<Define> allDefines = QueryNamedDefinesByType(defineType)?.ToList();
|
||||
var allDefines = QueryNamedDefinesByType(defineType)?.ToList();
|
||||
if (allDefines == null || allDefines.Count == 0)
|
||||
{
|
||||
return null;
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Base;
|
||||
using Data;
|
||||
using Entity;
|
||||
using Map;
|
||||
using Prefab;
|
||||
@ -39,13 +40,16 @@ namespace Managers
|
||||
|
||||
/// <summary> 角色实体的预制体。 </summary>
|
||||
public EntityPrefab characterPrefab;
|
||||
|
||||
/// <summary> 建筑实体的预制体。 </summary>
|
||||
public EntityPrefab buildingPrefab;
|
||||
|
||||
/// <summary> 子弹实体的预制体。 </summary>
|
||||
public EntityPrefab bulletPrefab;
|
||||
|
||||
/// <summary> 默认实体的预制体,用于生成失败时的回退。 </summary>
|
||||
public EntityPrefab defaultEntityPrefab;
|
||||
|
||||
|
||||
// 已移除:RegisterDimension 和 UnregisterDimension 方法的相关注释,因为这些方法已不存在且与逻辑无关。
|
||||
|
||||
/// <summary>
|
||||
@ -80,7 +84,7 @@ namespace Managers
|
||||
public void Tick()
|
||||
{
|
||||
// 获取当前管理器中所有活跃维度ID的副本,以安全地迭代和移除。
|
||||
List<string> activeDimensionIdsInManager = _dimensionFactionEntities.Keys.ToList();
|
||||
var activeDimensionIdsInManager = _dimensionFactionEntities.Keys.ToList();
|
||||
|
||||
foreach (var dimensionId in activeDimensionIdsInManager)
|
||||
{
|
||||
@ -88,7 +92,8 @@ namespace Managers
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:跳过维度 '{dimensionId}' 的Tick处理,因为其维度对象在程序(Program)中已不再活跃(或从未注册)。正在清理相关内部数据。");
|
||||
Debug.LogWarning(
|
||||
$"实体管理器:跳过维度 '{dimensionId}' 的Tick处理,因为其维度对象在程序(Program)中已不再活跃(或从未注册)。正在清理相关内部数据。");
|
||||
// 如果 Program 不再有这个维度,说明它已经失效或被注销,EntityManage 需要清理自己的相关数据。
|
||||
_dimensionFactionEntities.Remove(dimensionId);
|
||||
_dimensionLayerCache.Remove(dimensionId);
|
||||
@ -96,7 +101,7 @@ namespace Managers
|
||||
RemovePendingAdditionsForDimension(dimensionId);
|
||||
continue; // 跳过处理这个不存在的维度
|
||||
}
|
||||
|
||||
|
||||
// 现在真正处理的是 _dimensionFactionEntities 中该维度ID对应的字典。
|
||||
var factionDict = _dimensionFactionEntities[dimensionId];
|
||||
|
||||
@ -149,7 +154,7 @@ namespace Managers
|
||||
_dimensionLayerCache.Remove(dimensionId); // 对应的层级缓存也可以清理。
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理待添加实体 (现在维度感知)。
|
||||
if (_pendingAdditions.Any())
|
||||
{
|
||||
@ -163,7 +168,8 @@ namespace Managers
|
||||
// 再次检查维度是否活跃(在 Program 中注册),防止在添加前维度被 Program 注销。
|
||||
if (Program.Instance.GetDimension(dimensionId) == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:尝试将实体 '{entityPrefab?.name ?? "null"}' 添加到在程序(Program)中未注册或不活跃的维度 '{dimensionId}'。该实体将被销毁。");
|
||||
Debug.LogError(
|
||||
$"实体管理器:尝试将实体 '{entityPrefab?.name ?? "null"}' 添加到在程序(Program)中未注册或不活跃的维度 '{dimensionId}'。该实体将被销毁。");
|
||||
if (entityPrefab != null && entityPrefab.gameObject != null) Destroy(entityPrefab.gameObject);
|
||||
_pendingAdditions.Remove(pending); // 立即从待添加列表中移除已处理的条目。
|
||||
continue;
|
||||
@ -215,6 +221,7 @@ namespace Managers
|
||||
remainingPendingAdditions.Add(pending);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingAdditions = remainingPendingAdditions;
|
||||
}
|
||||
|
||||
@ -262,7 +269,7 @@ namespace Managers
|
||||
throw new InvalidOperationException(
|
||||
$"在 '{instantiatedEntity.name}' 上缺少 EntityPrefab 组件,无法完成实体初始化。");
|
||||
}
|
||||
|
||||
entityComponent.entity.currentDimensionId=dimensionId;
|
||||
entityComponent.Init(def);
|
||||
extraInit?.Invoke(entityComponent);
|
||||
|
||||
@ -296,11 +303,9 @@ namespace Managers
|
||||
{
|
||||
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
|
||||
layerCacheForDimension = _dimensionLayerCache[dimensionId]; // 更新引用。
|
||||
Debug.LogWarning($"实体管理器:按需为维度 '{dimensionId}' 初始化层级缓存。");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"实体管理器:在层级缓存中未找到维度 '{dimensionId}',且在程序(Program)中也未注册。无法创建层级 '{layerName}'。");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -490,6 +495,208 @@ namespace Managers
|
||||
entityComponent.DefaultInit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,查找与源实体具有特定关系的最近实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
/// <param name="targetRelationship">目标实体与源实体之间的期望关系(例如,Friendly, Hostile)。</param>
|
||||
/// <returns>找到的最近实体预制体,如果没有找到符合条件的实体则返回 null。</returns>
|
||||
public EntityPrefab FindNearestEntityByRelation(string dimensionId, EntityPrefab sourceEntityPrefab,
|
||||
Relation targetRelationship)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常。
|
||||
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:FindNearestEntityByRelation 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:FindNearestEntityByRelation 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 初始化追踪变量:设置初始值,用于在遍历过程中追踪最近的实体及其距离。
|
||||
// 使用平方距离 (SqrMagnitude) 可以避免在每次距离计算时进行昂贵的开方运算,从而提高性能。
|
||||
EntityPrefab nearestTarget = null;
|
||||
var minDistanceSqr = float.MaxValue;
|
||||
var sourcePos = sourceEntityPrefab.transform.position;
|
||||
|
||||
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
|
||||
var affiliationManager = AffiliationManager.Instance;
|
||||
if (affiliationManager == null)
|
||||
{
|
||||
Debug.LogError("实体管理器:FindNearestEntityByRelation 方法中,AffiliationManager 实例为空。无法确定实体关系。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 遍历所有派系和实体:_dimensionFactionEntities 按维度和派系组织,需要遍历所有派系才能找到维度内的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values) // factionDict.Values 是 LinkedList<EntityPrefab> 的集合
|
||||
{
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效或已死亡的实体,确保只处理活跃的实体。
|
||||
// 2. 在寻找“最近”实体时,通常指的是 *除了自身以外* 的实体。
|
||||
// 如果需要包含自身(例如,当 targetRelationship 是 AffiliationManager.Relation.Self 时),
|
||||
// 可以根据具体需求调整此逻辑,但默认行为是排除自身。
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 关系判断:使用 AffiliationManager 提供的接口判断源实体与当前遍历实体之间的关系。
|
||||
var currentRelation =
|
||||
affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity);
|
||||
if (currentRelation == targetRelationship)
|
||||
{
|
||||
// 距离计算与最近实体更新:
|
||||
// 1. 计算与源实体的距离(使用平方距离优化)。
|
||||
// 2. 如果当前实体更近,则更新 nearestTarget 和 minDistanceSqr。
|
||||
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
|
||||
if (distanceSqr < minDistanceSqr)
|
||||
{
|
||||
minDistanceSqr = distanceSqr;
|
||||
nearestTarget = currentEntityPrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestTarget; // 返回找到的最近实体
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,判断是否存在任何与源实体敌对的活跃实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
/// <returns>如果存在敌对实体则返回 true,否则返回 false。</returns>
|
||||
public bool ExistsHostile(string dimensionId, EntityPrefab sourceEntityPrefab)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常。
|
||||
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:ExistsHostile 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:ExistsHostile 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
|
||||
var affiliationManager = AffiliationManager.Instance;
|
||||
if (affiliationManager == null)
|
||||
{
|
||||
Debug.LogError("实体管理器:ExistsHostile 方法中,AffiliationManager 实例为空。无法确定实体关系。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values)
|
||||
{
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效或已死亡的实体。
|
||||
// 2. 排除源实体自身,避免自身判断为敌对。
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 关系判断:判断当前实体与源实体是否为“敌对”关系。
|
||||
if (affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity) ==
|
||||
Data.Relation.Hostile)
|
||||
{
|
||||
return true; // 找到第一个敌对实体即返回 true,提高效率。
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // 遍历完所有实体都未找到敌对实体。
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,判断源实体视野范围内是否存在任何与源实体敌对的活跃实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
/// <param name="sightRange">视野范围,实体间的距离小于等于此值视为在视野内。</param>
|
||||
/// <returns>如果视野范围内存在敌对实体则返回 true,否则返回 false。</returns>
|
||||
public bool ExistsHostileInSightRange(string dimensionId, EntityPrefab sourceEntityPrefab, float sightRange)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常及不合理的范围值。
|
||||
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:ExistsHostileInSightRange 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sightRange <= 0)
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:ExistsHostileInSightRange 方法中,视野范围必须为正值。接收到的值为: {sightRange}。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:ExistsHostileInSightRange 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
|
||||
var affiliationManager = AffiliationManager.Instance;
|
||||
if (affiliationManager == null)
|
||||
{
|
||||
Debug.LogError("实体管理器:ExistsHostileInSightRange 方法中,AffiliationManager 实例为空。无法确定实体关系。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 预计算平方距离:避免在循环内部重复计算平方根,提高性能。
|
||||
var sightRangeSqr = sightRange * sightRange;
|
||||
var sourcePos = sourceEntityPrefab.transform.position;
|
||||
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values)
|
||||
{
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效或已死亡的实体。
|
||||
// 2. 排除源实体自身。
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 关系判断:判断当前实体与源实体是否为“敌对”关系。
|
||||
if (affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity) ==
|
||||
Data.Relation.Hostile)
|
||||
{
|
||||
// 距离判断:如果关系敌对,进一步判断其是否在视野范围内。
|
||||
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
|
||||
if (distanceSqr <= sightRangeSqr)
|
||||
{
|
||||
return true; // 找到第一个视野范围内的敌对实体即返回 true。
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // 遍历完所有实体都未找到视野范围内的敌对实体。
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器启动时调用,订阅场景加载事件。
|
||||
/// </summary>
|
||||
@ -513,8 +720,8 @@ namespace Managers
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
_dimensionFactionEntities.Clear(); // 清理所有旧场景的实体数据。
|
||||
_dimensionLayerCache.Clear(); // 清理所有旧场景的层级缓存。
|
||||
|
||||
_dimensionLayerCache.Clear(); // 清理所有旧场景的层级缓存。
|
||||
|
||||
// 场景加载时清理待添加列表中的所有实体,因为它们可能属于旧场景或未在任何维度中被处理。
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
@ -523,14 +730,15 @@ namespace Managers
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingAdditions.Clear(); // 清理待添加实体列表。
|
||||
|
||||
// 在新场景加载完成后,遍历 Program 中已注册的所有维度,并为每个维度初始化 EntityManage 的内部数据结构。
|
||||
// 此时 Program.Awake() 应该已经完成,所有 Dimension 组件的 Awake() 也已执行,Program.RegisteredDimensions 应该包含了当前场景的所有维度。
|
||||
foreach (var registeredDimensionEntry in Program.Instance.RegisteredDimensions)
|
||||
{
|
||||
string dimensionId = registeredDimensionEntry.Key;
|
||||
Dimension dimension = registeredDimensionEntry.Value;
|
||||
var dimensionId = registeredDimensionEntry.Key;
|
||||
var dimension = registeredDimensionEntry.Value;
|
||||
|
||||
if (dimension != null) // 确保维度实例本身不是null。
|
||||
{
|
||||
@ -541,7 +749,8 @@ namespace Managers
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:在场景加载初始化期间,Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
|
||||
Debug.LogWarning(
|
||||
$"实体管理器:在场景加载初始化期间,Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -559,7 +768,8 @@ namespace Managers
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
if (defaultEntityPrefab == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
|
||||
Debug.LogError(
|
||||
$"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -36,59 +36,27 @@ namespace Managers
|
||||
$"ItemResourceManager: Duplicate itemDef.defName found: {def.defName}. Skipping this item.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var itemIcon = Managers.PackagesImageManager.Instance.GetSprite(def.texture);
|
||||
if (!itemIcon)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"ItemResourceManager: Failed to load sprite for texture '{def.texture}' for item '{def.defName}'. Icon will be null.");
|
||||
}
|
||||
|
||||
var itemName = string.IsNullOrEmpty(def.label) ? def.defName : def.label;
|
||||
if (string.IsNullOrEmpty(def.label))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"ItemResourceManager: ItemDef '{def.defName}' has an empty label. Using defName as item name.");
|
||||
}
|
||||
|
||||
var itemDescription = def.description ?? string.Empty;
|
||||
|
||||
|
||||
Item.ItemResource itemResource;
|
||||
|
||||
if (def is WeaponDef currentWeaponDef)
|
||||
{
|
||||
itemResource = new Item.WeaponResource(
|
||||
def.defName, // 传递 defName
|
||||
itemName,
|
||||
itemDescription,
|
||||
itemIcon,
|
||||
currentWeaponDef.rarity,
|
||||
currentWeaponDef.maxStack,
|
||||
currentWeaponDef.ssEquippable,
|
||||
currentWeaponDef.attributes
|
||||
currentWeaponDef
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemResource = new Item.ItemResource(
|
||||
def.defName, // 传递 defName
|
||||
itemName,
|
||||
itemDescription,
|
||||
itemIcon,
|
||||
def.rarity,
|
||||
def.maxStack,
|
||||
def.ssEquippable
|
||||
def
|
||||
);
|
||||
}
|
||||
|
||||
_items.Add(def.defName, itemResource);
|
||||
|
||||
// 将物品添加到按显示名称查找的字典 (这里仍然使用 itemResource.Name,因为字典的目的是按显示名称查找)
|
||||
if (!_itemsByName.ContainsKey(itemResource.Name))
|
||||
{
|
||||
_itemsByName.Add(itemResource.Name, new List<Item.ItemResource>());
|
||||
}
|
||||
|
||||
_itemsByName[itemResource.Name].Add(itemResource);
|
||||
}
|
||||
}
|
||||
@ -97,8 +65,6 @@ namespace Managers
|
||||
{
|
||||
return _items.GetValueOrDefault(defName, null);
|
||||
}
|
||||
|
||||
// FindItemByName 和 FindAllItemsByName 保持不变,因为它们是按显示名称查找的
|
||||
public Item.ItemResource FindItemByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return null;
|
||||
|
@ -33,7 +33,6 @@ namespace Managers
|
||||
// 如果已经被初始化,则直接返回
|
||||
if (tileToTileBaseMapping.Count > 0)
|
||||
{
|
||||
Debug.Log($"<color=green>{StepDescription}</color> 已初始化。跳过。");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
@ -44,6 +45,7 @@ namespace Map
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
|
||||
// 1. 确保 DimensionId 已初始化,这会触发 DimensionId 属性的 getter 逻辑
|
||||
var id = DimensionId;
|
||||
// 2. 创建一个用于存放此维度下所有实体的根GameObject,方便管理
|
||||
@ -68,6 +70,7 @@ namespace Map
|
||||
{
|
||||
Program.Instance.SetFocusedDimension(_dimensionId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@ -77,14 +80,6 @@ namespace Map
|
||||
{
|
||||
Program.Instance.UnregisterDimension(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 在应用程序退出时,Program.Instance 可能已经为null,这通常是正常的,不打LogError
|
||||
Debug.Log(
|
||||
"[Dimension] Program.Instance is null during OnDestroy. Skipping unregister for Dimension: " +
|
||||
DimensionId, this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
10
Client/Assets/Scripts/Map/MiniMap.cs
Normal file
10
Client/Assets/Scripts/Map/MiniMap.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Map
|
||||
{
|
||||
public class MiniMap : MonoBehaviour
|
||||
{
|
||||
public RawImage texture;
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Map/MiniMap.cs.meta
Normal file
3
Client/Assets/Scripts/Map/MiniMap.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: feb227be5c204aaea27e09f754dc05b8
|
||||
timeCreated: 1756172116
|
@ -64,7 +64,7 @@ namespace Prefab
|
||||
|
||||
// 更新帧计时器
|
||||
_frameTimer += deltaTime;
|
||||
float frameDuration = 1f / _fps;
|
||||
var frameDuration = 1f / _fps;
|
||||
|
||||
// 检查帧切换条件
|
||||
while (_frameTimer >= frameDuration)
|
||||
|
@ -1,5 +1,5 @@
|
||||
using System; // 添加引用,用于 Action 委托
|
||||
using System.Collections.Generic;
|
||||
using Logging;
|
||||
using Map;
|
||||
using UnityEngine;
|
||||
using Utils;
|
||||
@ -22,8 +22,9 @@ public class Program : Singleton<Program>
|
||||
|
||||
/// <summary>
|
||||
/// 当前活跃焦点的维度唯一标识符,可能为空。
|
||||
/// 变更为属性,并私有化setter,确保通过 SetFocusedDimension 方法集中管理其更新。
|
||||
/// </summary>
|
||||
public string focuseDimensionId = null;
|
||||
public string focuseDimensionId { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 当前聚焦的维度对象实例。当 <see cref="focuseDimensionId"/> 不为空时,此属性指向对应的维度实例。
|
||||
@ -40,6 +41,12 @@ public class Program : Singleton<Program>
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Dimension> RegisteredDimensions => _registeredDimensions;
|
||||
|
||||
/// <summary>
|
||||
/// 当焦点维度发生变化时触发的事件。
|
||||
/// 事件参数为新的焦点 Dimension 实例,如果焦点被清除则为 null。
|
||||
/// </summary>
|
||||
public event Action<Dimension> OnFocusedDimensionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个维度实例到 Program。
|
||||
/// </summary>
|
||||
@ -51,7 +58,7 @@ public class Program : Singleton<Program>
|
||||
Debug.LogError("[Program] Attempted to register a null Dimension.");
|
||||
return;
|
||||
}
|
||||
string id = dimension.DimensionId;
|
||||
var id = dimension.DimensionId;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
Debug.LogError($"[Program] Attempted to register a Dimension with an empty or null ID: {dimension.name}");
|
||||
@ -64,10 +71,12 @@ public class Program : Singleton<Program>
|
||||
}
|
||||
_registeredDimensions.Add(id, dimension);
|
||||
|
||||
// 如果注册的维度是当前焦点维度,则更新 FocusedDimension 属性
|
||||
// 修改理由:确保任何对焦点的潜在更新都通过 SetFocusedDimension 进行,
|
||||
// 从而集中管理焦点状态的同步和事件的触发。
|
||||
// 如果注册的维度恰好是当前 focuseDimensionId, SetFocusedDimension(id) 将负责更新 FocusedDimension 并触发事件。
|
||||
if (focuseDimensionId == id)
|
||||
{
|
||||
FocusedDimension = dimension;
|
||||
SetFocusedDimension(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,14 +91,15 @@ public class Program : Singleton<Program>
|
||||
Debug.LogWarning("[Program] Attempted to unregister a null Dimension. Skipping.");
|
||||
return;
|
||||
}
|
||||
string id = dimension.DimensionId;
|
||||
var id = dimension.DimensionId;
|
||||
if (_registeredDimensions.Remove(id))
|
||||
{
|
||||
// 如果注销的维度是当前焦点维度,则清除焦点
|
||||
// 修改理由:确保任何对焦点的潜在更新都通过 SetFocusedDimension 进行,
|
||||
// 从而集中管理焦点状态的同步和事件的触发。
|
||||
// 如果注销的维度是当前焦点维度,则调用 SetFocusedDimension(null) 清除焦点并触发事件。
|
||||
if (focuseDimensionId == id)
|
||||
{
|
||||
focuseDimensionId = null;
|
||||
FocusedDimension = null;
|
||||
SetFocusedDimension(null); // 清除焦点
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -105,30 +115,55 @@ public class Program : Singleton<Program>
|
||||
/// <returns>对应的Dimension实例,如果未找到则返回null。</returns>
|
||||
public Dimension GetDimension(string dimensionId)
|
||||
{
|
||||
_registeredDimensions.TryGetValue(dimensionId, out Dimension dimension);
|
||||
_registeredDimensions.TryGetValue(dimensionId, out var dimension);
|
||||
return dimension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前聚焦的维度。
|
||||
/// 这是更改焦点维度的唯一官方入口。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要设置为焦点的维度的唯一标识符。如果传入null或空字符串,将清除当前焦点维度。</param>
|
||||
public void SetFocusedDimension(string dimensionId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dimensionId))
|
||||
// 1. 确定新的焦点维度及其ID
|
||||
Dimension newFocusedDimension = null; // 默认为清除焦点
|
||||
string newFocuseDimensionId = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(dimensionId))
|
||||
{
|
||||
focuseDimensionId = null;
|
||||
FocusedDimension = null;
|
||||
if (_registeredDimensions.TryGetValue(dimensionId, out var foundDimension))
|
||||
{
|
||||
newFocuseDimensionId = dimensionId;
|
||||
newFocusedDimension = foundDimension;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果尝试设置未注册的维度,警告并直接返回,不改变当前焦点状态。
|
||||
Debug.LogWarning($"[Program] Attempted to set focus to unregistered dimension ID: '{dimensionId}'. Focus not changed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果 dimensionId 为 null 或空, newFocusedDimension 和 newFocuseDimensionId 将保持为 null,表示清除焦点。
|
||||
|
||||
// 2. 优化:检查是否实际发生变化,避免不必要的更新和事件触发
|
||||
// 只有当新的ID或新的维度对象引用与当前的不一致时,才执行更新。
|
||||
// 例如,如果 focuseDimensionId 已经是 "someId",FocusedDimension 已经是 "someDimensionObject",
|
||||
// 再次调用 SetFocusedDimension("someId") 不会触发事件。
|
||||
// 但如果 focuseDimensionId 是 "someId",FocusedDimension 是 null (因为维度还未注册),
|
||||
// 而此时注册了 "someId" 对应的维度并调用 SetFocusedDimension("someId"),FocusedDimension 将从 null 变为实际对象,
|
||||
// 这算作一次变化,会触发事件。
|
||||
if (focuseDimensionId == newFocuseDimensionId && FocusedDimension == newFocusedDimension)
|
||||
{
|
||||
// Debug.Log($"[Program] SetFocusedDimension: ID '{dimensionId}' already current. No change needed.");
|
||||
return;
|
||||
}
|
||||
if (_registeredDimensions.TryGetValue(dimensionId, out Dimension newFocusedDimension))
|
||||
{
|
||||
focuseDimensionId = dimensionId;
|
||||
FocusedDimension = newFocusedDimension;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[Program] Attempted to set focus to unregistered dimension ID: '{dimensionId}'. Focus not changed.");
|
||||
}
|
||||
|
||||
// 3. 更新内部状态
|
||||
focuseDimensionId = newFocuseDimensionId;
|
||||
FocusedDimension = newFocusedDimension;
|
||||
|
||||
// 4. 触发事件
|
||||
OnFocusedDimensionChanged?.Invoke(FocusedDimension);
|
||||
}
|
||||
}
|
||||
|
16
Client/Assets/Scripts/UI/BarUI.cs
Normal file
16
Client/Assets/Scripts/UI/BarUI.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class BarUI:MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image image;
|
||||
|
||||
public float Progress
|
||||
{
|
||||
get => image.fillAmount;
|
||||
set => image.fillAmount = value;
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/UI/BarUI.cs.meta
Normal file
3
Client/Assets/Scripts/UI/BarUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5026699f3a94f029628af90ccd8fa8d
|
||||
timeCreated: 1756183343
|
10
Client/Assets/Scripts/UI/EquipmentUI.cs
Normal file
10
Client/Assets/Scripts/UI/EquipmentUI.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class EquipmentUI:MonoBehaviour
|
||||
{
|
||||
[SerializeField] private GameObject uiParent;
|
||||
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/UI/EquipmentUI.cs.meta
Normal file
3
Client/Assets/Scripts/UI/EquipmentUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4432817f903945648bdc0b3a4ba19adc
|
||||
timeCreated: 1756195128
|
9
Client/Assets/Scripts/UI/ItemUI.cs
Normal file
9
Client/Assets/Scripts/UI/ItemUI.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class ItemUI:MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/UI/ItemUI.cs.meta
Normal file
3
Client/Assets/Scripts/UI/ItemUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d651a3517184abcb4aff78629eb7946
|
||||
timeCreated: 1756195181
|
@ -42,7 +42,7 @@ namespace UI
|
||||
// 如果日志数量减少,清理多余的条目
|
||||
if (logs.Count < _lastLogCount)
|
||||
{
|
||||
for (int i = logs.Count; i < _lastLogCount; i++)
|
||||
for (var i = logs.Count; i < _lastLogCount; i++)
|
||||
{
|
||||
Destroy(_logItems[i].gameObject);
|
||||
}
|
||||
@ -51,7 +51,7 @@ namespace UI
|
||||
}
|
||||
|
||||
// 更新现有条目
|
||||
for (int i = 0; i < Math.Min(logs.Count, _logItems.Count); i++)
|
||||
for (var i = 0; i < Math.Min(logs.Count, _logItems.Count); i++)
|
||||
{
|
||||
UpdateLogEntry(_logItems[i], logs[logs.Count - 1 - i]);
|
||||
}
|
||||
@ -59,7 +59,7 @@ namespace UI
|
||||
// 添加新的条目
|
||||
if (logs.Count > _lastLogCount)
|
||||
{
|
||||
for (int i = _lastLogCount; i < logs.Count; i++)
|
||||
for (var i = _lastLogCount; i < logs.Count; i++)
|
||||
{
|
||||
CreateLogEntry(logs[logs.Count - 1 - i]);
|
||||
}
|
||||
@ -85,7 +85,7 @@ namespace UI
|
||||
logItem.Label = entry.ToString();
|
||||
|
||||
// 设置文本颜色(根据日志类型)
|
||||
if (logColors.TryGetValue(entry.Type, out Color color))
|
||||
if (logColors.TryGetValue(entry.Type, out var color))
|
||||
{
|
||||
logItem.text.color = color;
|
||||
}
|
||||
|
21
Client/Assets/Scripts/UI/PlayerStateUI.cs
Normal file
21
Client/Assets/Scripts/UI/PlayerStateUI.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class PlayerStateUI:MonoBehaviour,Base.ITick
|
||||
{
|
||||
[SerializeField] private BarUI focusedEntityHP;
|
||||
[SerializeField] private BarUI lastEntityHP;
|
||||
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var focusedEntity = Program.Instance.focusedEntity;
|
||||
if (focusedEntity)
|
||||
{
|
||||
focusedEntityHP.Progress = (float)focusedEntity.attributes.health/focusedEntity.entityDef.attributes.health;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/UI/PlayerStateUI.cs.meta
Normal file
3
Client/Assets/Scripts/UI/PlayerStateUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fcb0352f5bd43a49b164e24a38447e3
|
||||
timeCreated: 1756183287
|
@ -38,7 +38,7 @@ namespace UI
|
||||
windowResolution.AddOptions(options);
|
||||
if (currentSettings.windowResolution != null)
|
||||
{
|
||||
int resolutionIndex = System.Array.FindIndex(Base.Setting.CommonResolutions, r => r == currentSettings.windowResolution);
|
||||
var resolutionIndex = System.Array.FindIndex(Base.Setting.CommonResolutions, r => r == currentSettings.windowResolution);
|
||||
windowResolution.value = resolutionIndex >= 0 ? resolutionIndex : 0;
|
||||
}
|
||||
else
|
||||
|
File diff suppressed because one or more lines are too long
@ -6,5 +6,5 @@
|
||||
<description>可以回复一点点血量</description>
|
||||
<!-- <texture>TestGun</texture>
|
||||
<itemTexture>TestGunItem</itemTexture> -->
|
||||
</ImageDef>
|
||||
</ItemDef>
|
||||
</Define>
|
Reference in New Issue
Block a user