(client) chore

This commit is contained in:
m0_75251201
2025-08-26 16:00:58 +08:00
parent efbf4f824a
commit f04c89046b
51 changed files with 2390 additions and 617 deletions

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View 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:

View File

@ -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: []

View 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}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1b1aef36872c4f44a907f96bf813e8e5
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View 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);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ef73ec15d93e4f099081b221e275834d
timeCreated: 1756173994

View File

@ -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
};
}
}
}

View File

@ -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)
{
// 优化点1FieldInfo 缓存
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 以确保安全

View File

@ -16,13 +16,4 @@ namespace Data
public bool ssEquippable = false; // 是否可装备
}
public class WeaponDef : ItemDef
{
public AttributesDef attributes;
public WeaponDef() // 构造函数,用于设置武器的默认属性
{
maxStack = 1; // 武器默认最大堆叠为1
ssEquippable = true; // 武器默认可装备
}
}
}

View 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; // 武器默认可装备
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ed90cba91ce41e1ae443cf44a76c932
timeCreated: 1756193931

View 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()
{}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9a009acc975a4e4b993eaa4b853f7109
timeCreated: 1756184938

View File

@ -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);
}
}
}
}

View File

@ -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;
}

View 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();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e65cb3d0f8440c599399cefd9a3a5d7
timeCreated: 1756191938

View File

@ -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();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: e33d58195dd8450a9ec468ede64c8635
timeCreated: 1756048545

View File

@ -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;
}
}
}
}

View 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;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa734e2254034fa4a59fb1e0cdea250f
timeCreated: 1756193577

View File

@ -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>

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -33,7 +33,6 @@ namespace Managers
// 如果已经被初始化,则直接返回
if (tileToTileBaseMapping.Count > 0)
{
Debug.Log($"<color=green>{StepDescription}</color> 已初始化。跳过。");
return;
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,10 @@
using UnityEngine;
using UnityEngine.UI;
namespace Map
{
public class MiniMap : MonoBehaviour
{
public RawImage texture;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: feb227be5c204aaea27e09f754dc05b8
timeCreated: 1756172116

View File

@ -64,7 +64,7 @@ namespace Prefab
// 更新帧计时器
_frameTimer += deltaTime;
float frameDuration = 1f / _fps;
var frameDuration = 1f / _fps;
// 检查帧切换条件
while (_frameTimer >= frameDuration)

View File

@ -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);
}
}

View 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;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5026699f3a94f029628af90ccd8fa8d
timeCreated: 1756183343

View File

@ -0,0 +1,10 @@
using UnityEngine;
namespace UI
{
public class EquipmentUI:MonoBehaviour
{
[SerializeField] private GameObject uiParent;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4432817f903945648bdc0b3a4ba19adc
timeCreated: 1756195128

View File

@ -0,0 +1,9 @@
using UnityEngine;
namespace UI
{
public class ItemUI:MonoBehaviour
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7d651a3517184abcb4aff78629eb7946
timeCreated: 1756195181

View File

@ -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;
}

View 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;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2fcb0352f5bd43a49b164e24a38447e3
timeCreated: 1756183287

View File

@ -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

View File

@ -6,5 +6,5 @@
<description>可以回复一点点血量</description>
<!-- <texture>TestGun</texture>
<itemTexture>TestGunItem</itemTexture> -->
</ImageDef>
</ItemDef>
</Define>