(client) feat:实现热重载,实现多维度,实现武器,实现掉落物,实现状态UI,实现攻击AI #44
@ -89,6 +89,17 @@ PrefabInstance:
|
||||
insertIndex: 4
|
||||
addedObject: {fileID: 6245381882962021176}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
--- !u!114 &473404334762913218 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 7296562035276564141, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
m_PrefabInstance: {fileID: 7193170239846001519}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e33d58195dd8450a9ec468ede64c8635, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &1196523683669800718 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 8307348883874536545, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
@ -130,11 +141,12 @@ MonoBehaviour:
|
||||
animatorPrefab: {fileID: 2113064398104960506, guid: ea9af70ce0f4c8b4a9de58ac63074156, type: 3}
|
||||
imagePrefab: {fileID: 1922746734790246249, guid: a6657f26d735fab4690c8185980fda29, type: 3}
|
||||
healthBarPrefab: {fileID: 1196523683669800718}
|
||||
visionRangeDetector: {fileID: 473404334762913218}
|
||||
direction: {x: 0, y: 0, z: 0}
|
||||
body: {fileID: 4949477718940635697}
|
||||
affiliation:
|
||||
canSelect: 1
|
||||
hitBarUIShowTime: 5
|
||||
_hitBarUIShowTime: 5
|
||||
--- !u!114 &6487788869574860317
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -73,6 +73,7 @@ PrefabInstance:
|
||||
- {fileID: 4717642781780051128, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
m_RemovedGameObjects:
|
||||
- {fileID: 1328271255896522146, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
- {fileID: 6276067436382167109, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 887327274103887133, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
@ -103,7 +104,9 @@ MonoBehaviour:
|
||||
body: {fileID: 8200005766896563996}
|
||||
affiliation:
|
||||
canSelect: 1
|
||||
hitBarUIShowTime: 5
|
||||
_hitBarUIShowTime: 5
|
||||
bulletSource: {fileID: 0}
|
||||
lifeTime: 10
|
||||
--- !u!1 &8200005766896563996 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 2838206730318674270, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
|
@ -88,16 +88,28 @@ MonoBehaviour:
|
||||
animatorPrefab: {fileID: 2113064398104960506, guid: ea9af70ce0f4c8b4a9de58ac63074156, type: 3}
|
||||
imagePrefab: {fileID: 1922746734790246249, guid: a6657f26d735fab4690c8185980fda29, type: 3}
|
||||
healthBarPrefab: {fileID: 8215007830330368681}
|
||||
visionRangeDetector: {fileID: 7208762515041450085}
|
||||
direction: {x: 0, y: 0, z: 0}
|
||||
body: {fileID: 2750404322221410198}
|
||||
affiliation:
|
||||
canSelect: 1
|
||||
hitBarUIShowTime: 5
|
||||
_hitBarUIShowTime: 5
|
||||
--- !u!1 &2750404322221410198 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 2838206730318674270, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
m_PrefabInstance: {fileID: 92380775595425480}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &7208762515041450085 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 7296562035276564141, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
m_PrefabInstance: {fileID: 92380775595425480}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e33d58195dd8450a9ec468ede64c8635, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &8215007830330368681 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 8307348883874536545, guid: 6cd8b01a0f57372438dc30c864ae1530, type: 3}
|
||||
|
@ -123,6 +123,7 @@ Transform:
|
||||
- {fileID: 5549544358816209289}
|
||||
- {fileID: 1697214530303839877}
|
||||
- {fileID: 1404278780950315184}
|
||||
- {fileID: 8903644794067773966}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &3332598847335032684
|
||||
@ -252,6 +253,88 @@ 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
|
||||
|
@ -16,7 +16,7 @@ RenderTexture:
|
||||
m_Height: 150
|
||||
m_AntiAliasing: 1
|
||||
m_MipCount: -1
|
||||
m_DepthStencilFormat: 0
|
||||
m_DepthStencilFormat: 90
|
||||
m_ColorFormat: 8
|
||||
m_MipMap: 0
|
||||
m_GenerateMips: 1
|
||||
|
@ -589,6 +589,7 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 741561707}
|
||||
- component: {fileID: 741561708}
|
||||
- component: {fileID: 741561709}
|
||||
m_Layer: 0
|
||||
m_Name: OutsideMap
|
||||
m_TagString: Untagged
|
||||
@ -627,9 +628,21 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 81339b242c794b8d9d28d7111a46bfbf, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
baseLevel: {fileID: 1084213392}
|
||||
buildLevel: {fileID: 1204950904}
|
||||
plantLevel: {fileID: 71139918}
|
||||
--- !u!114 &741561709
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 741561706}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1cf2b22a0f934c5b852b5cdd588d82fa, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
defaultOpen: 1
|
||||
_dimensionId:
|
||||
mapGenerator: {fileID: 741561708}
|
||||
--- !u!1 &828003921
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -843,7 +856,6 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
textureLevel: {fileID: 828003924}
|
||||
dataOffset: {x: 0, y: 0}
|
||||
--- !u!1 &1204850182
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AI
|
||||
@ -15,5 +16,10 @@ namespace AI
|
||||
{
|
||||
return entity.PlayerControlled;
|
||||
}
|
||||
|
||||
public static bool HasEnemyInSight(Entity.Entity entity)
|
||||
{
|
||||
return entity.visionRangeDetector.GetNearestNEntitiesByRelation(1, Relation.Hostile).Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -74,222 +74,5 @@ namespace AI
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackPlayerJob : JobBase
|
||||
{
|
||||
private EntityPrefab currentTarget; // 当前追踪的目标玩家
|
||||
private LinkedList<EntityPrefab> players; // 玩家实体列表
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
{
|
||||
base.StartJob(target);
|
||||
UpdateTarget();
|
||||
}
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!currentTarget || currentTarget.entity.IsDead)
|
||||
{
|
||||
// 如果当前目标无效,则重新查找最近的玩家
|
||||
UpdateTarget();
|
||||
}
|
||||
|
||||
if (currentTarget)
|
||||
{
|
||||
var targetPosition = new Vector3(currentTarget.Position.x, currentTarget.Position.y, 0);
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTarget()
|
||||
{
|
||||
players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
|
||||
if (players == null || players.Count == 0)
|
||||
{
|
||||
currentTarget = null;
|
||||
StopJob();
|
||||
return;
|
||||
}
|
||||
currentTarget = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (player.entity.IsDead) continue; // 跳过无效玩家
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
}
|
||||
public class AttackPlayerJob : JobBase
|
||||
{
|
||||
private EntityPrefab player;
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!player || !IsPlayerInRange())
|
||||
{
|
||||
StopJob(); // 如果玩家不在范围内,停止攻击工作
|
||||
return;
|
||||
}
|
||||
entity.TryAttack();
|
||||
}
|
||||
|
||||
private bool IsPlayerInRange()
|
||||
{
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
return distance <= entity.attributes.attackRange;
|
||||
}
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
{
|
||||
base.StartJob(target);
|
||||
|
||||
// 查找最近的玩家作为目标
|
||||
LinkedList<EntityPrefab> players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
player = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (!IsPlayerValid(player)) continue;
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
private bool IsPlayerValid(EntityPrefab player)
|
||||
{
|
||||
return player && !player.entity.IsDead;
|
||||
}
|
||||
}
|
||||
public class RangedAttackJob : JobBase
|
||||
{
|
||||
private EntityPrefab player;
|
||||
|
||||
protected override void UpdateJob()
|
||||
{
|
||||
if (!player || !IsPlayerValid(player))
|
||||
{
|
||||
StopJob(); // 如果当前目标无效,停止工作
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance <= entity.attributes.attackRange)
|
||||
{
|
||||
// 如果在攻击范围内,进行攻击
|
||||
entity.TryAttack();
|
||||
}
|
||||
else if (distance > entity.attributes.attackRange && distance < MaxTrackDistance)
|
||||
{
|
||||
// 如果距离过远,靠近目标
|
||||
MoveTowardsPlayer();
|
||||
}
|
||||
else if (distance < entity.attributes.attackRange)
|
||||
{
|
||||
// 如果距离过近,远离目标
|
||||
MoveAwayFromPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveTowardsPlayer()
|
||||
{
|
||||
Vector3 targetPosition = new Vector3(player.Position.x, player.Position.y, 0);
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
|
||||
private void MoveAwayFromPlayer()
|
||||
{
|
||||
Vector3 direction = entity.Position - player.Position;
|
||||
direction.Normalize();
|
||||
Vector3 targetPosition = entity.Position + direction * RetreatDistance;
|
||||
|
||||
entity.SetTarget(targetPosition);
|
||||
entity.TryMove();
|
||||
}
|
||||
|
||||
public override void StartJob(Entity.Entity target)
|
||||
{
|
||||
base.StartJob(target);
|
||||
|
||||
// 查找最近的玩家作为目标
|
||||
var players = Managers.EntityManage.Instance.FindEntitiesByFaction("Player");
|
||||
player = GetNearestPlayer(players);
|
||||
}
|
||||
|
||||
private EntityPrefab GetNearestPlayer(LinkedList<EntityPrefab> players)
|
||||
{
|
||||
EntityPrefab nearestPlayer = null;
|
||||
float minDistance = float.MaxValue;
|
||||
|
||||
foreach (var player in players)
|
||||
{
|
||||
if (!IsPlayerValid(player)) continue;
|
||||
|
||||
float distance = Vector3.Distance(
|
||||
new Vector3(player.Position.x, player.Position.y, 0),
|
||||
new Vector3(entity.Position.x, entity.Position.y, 0)
|
||||
);
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
private bool IsPlayerValid(EntityPrefab player)
|
||||
{
|
||||
return player && !player.entity.IsDead;
|
||||
}
|
||||
|
||||
private const float MaxTrackDistance = 20f; // 最大追踪距离
|
||||
private const float RetreatDistance = 3f; // 后退距离
|
||||
}
|
||||
|
||||
}
|
@ -8,13 +8,7 @@ namespace AI
|
||||
}
|
||||
}
|
||||
|
||||
public class JobGiver_Enemies : AIBase
|
||||
{
|
||||
public override JobBase GetJob(Entity.Entity target)
|
||||
{
|
||||
return new TrackPlayerJob();
|
||||
}
|
||||
}
|
||||
|
||||
public class JobGiver_RandomWander : AIBase
|
||||
{
|
||||
public override JobBase GetJob(Entity.Entity target)
|
||||
|
@ -8,16 +8,21 @@ namespace Data
|
||||
Epic,
|
||||
Legendary
|
||||
}
|
||||
public class ItemDef:Define
|
||||
public class ItemDef : Define
|
||||
{
|
||||
public ImageDef texture;
|
||||
public ItemRarity rarity = ItemRarity.Common;
|
||||
public int maxStack = 1; // 最大堆叠数量,默认为1
|
||||
public int maxStack = 10; // 最大堆叠数量,默认为10
|
||||
public bool ssEquippable = false; // 是否可装备
|
||||
}
|
||||
|
||||
public class WeaponDef : ItemDef
|
||||
{
|
||||
public AttributesDef attributes;
|
||||
public WeaponDef() // 构造函数,用于设置武器的默认属性
|
||||
{
|
||||
maxStack = 1; // 武器默认最大堆叠为1
|
||||
ssEquippable = true; // 武器默认可装备
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ namespace Entity
|
||||
return;
|
||||
var buttonDef = def.Values.First();
|
||||
Vector3 dir = Utils.MousePosition.GetWorldPosition();
|
||||
Managers.EntityManage.Instance.GenerateBulletEntity((BulletDef)buttonDef, Position,
|
||||
Managers.EntityManage.Instance.GenerateBulletEntity(Program.Instance.focuseDimensionId,(BulletDef)buttonDef, Position,
|
||||
dir - Position, this);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace Entity
|
||||
|
||||
public ProgressBarPrefab healthBarPrefab;
|
||||
|
||||
|
||||
public VisionRangeDetector visionRangeDetector;
|
||||
/// <summary>
|
||||
/// 人工智能行为树,定义实体的行为逻辑。
|
||||
/// </summary>
|
||||
@ -154,7 +154,7 @@ namespace Entity
|
||||
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
private float _hitBarUIShowTimer = 0;
|
||||
private int _walkingTimer = 1;
|
||||
private int _walkingTimer = 0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -219,16 +219,17 @@ namespace Entity
|
||||
{
|
||||
targetObj = InitBodyPart(nodeDef, body); // 创建新对象
|
||||
}
|
||||
|
||||
|
||||
stateBodyNodes[orientation] = targetObj;
|
||||
|
||||
// 提取动画组件(安全处理空组件情况)
|
||||
// 逻辑修改:确保 stateAnimNodes[orientation] 总是被初始化为一个列表
|
||||
var animatorsForOrientation = new List<ITick>(); // 总是创建一个新的列表
|
||||
var animators = targetObj.GetComponentsInChildren<SpriteAnimator>();
|
||||
if (animators.Length > 0)
|
||||
{
|
||||
stateAnimNodes[orientation] = animators.Cast<ITick>().ToList();
|
||||
animatorsForOrientation.AddRange(animators.Cast<ITick>());
|
||||
}
|
||||
// 无动画组件时保持 stateAnimNodes[orientation] 为 null(符合原始逻辑)
|
||||
stateAnimNodes[orientation] = animatorsForOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,13 +464,15 @@ namespace Entity
|
||||
|
||||
_currentState = state;
|
||||
_currentOrientation = orientation;
|
||||
|
||||
if (bodyAnimationNode.TryGetValue(_currentState, out var animationNode))
|
||||
|
||||
if (bodyAnimationNode.TryGetValue(_currentState, out var animationNode) &&
|
||||
animationNode.TryGetValue(_currentOrientation, out var value))
|
||||
{
|
||||
if (animationNode.TryGetValue(_currentOrientation, out var value))
|
||||
{
|
||||
_currentAnimatorCache=value;
|
||||
}
|
||||
_currentAnimatorCache = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentAnimatorCache = new List<ITick>(); // 如果没有找到动画,则使用空列表
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,70 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Item;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class Inventory
|
||||
{
|
||||
public Entity from; // 物品所属实体
|
||||
public List<ItemBase> items = new List<ItemBase>(); // 背包中的物品列表
|
||||
public Entity From { get; private set; }
|
||||
private readonly List<InventorySlot> _slots;
|
||||
public int Capacity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加物品到背包
|
||||
/// </summary>
|
||||
/// <param name="item">要添加的物品</param>
|
||||
/// <param name="count">添加的数量</param>
|
||||
public void AddItem(ItemResource resource, int count)
|
||||
public event System.Action<Item.ItemResource, int> OnItemAdded;
|
||||
public event System.Action<Item.ItemResource, int> OnItemRemoved;
|
||||
public event System.Action OnInventoryChanged;
|
||||
|
||||
public Inventory(Entity owner, int capacity = 20)
|
||||
{
|
||||
if (count <= 0) return; // 如果数量小于等于0,直接返回
|
||||
From = owner;
|
||||
Capacity = Mathf.Max(1, capacity);
|
||||
_slots = new List<InventorySlot>(Capacity);
|
||||
}
|
||||
|
||||
// 检查背包中是否已存在相同物品
|
||||
foreach (var item in items)
|
||||
public int AddItem(Item.ItemResource itemResource, int quantity)
|
||||
{
|
||||
if (itemResource == null || quantity <= 0)
|
||||
{
|
||||
if (item.resource.Equals(resource))
|
||||
Debug.LogWarning(
|
||||
$"Inventory for {From?.ToString() ?? "Unknown"}: Attempted to add null item or zero/negative quantity.");
|
||||
return quantity;
|
||||
}
|
||||
|
||||
var remainingQuantity = quantity;
|
||||
var addedTotal = 0;
|
||||
|
||||
// 1. 尝试堆叠到现有槽位 (使用 DefName 进行比较)
|
||||
if (itemResource.MaxStack > 1)
|
||||
{
|
||||
foreach (var slot in _slots.Where(s =>
|
||||
s.Item != null && s.Item.DefName == itemResource.DefName && !s.IsFull))
|
||||
{
|
||||
item.count += count; // 增加数量
|
||||
return;
|
||||
var addedToSlot = slot.AddQuantity(remainingQuantity);
|
||||
remainingQuantity -= addedToSlot;
|
||||
addedTotal += addedToSlot;
|
||||
if (remainingQuantity <= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到相同物品,则创建新物品并添加到背包
|
||||
var newItem = new ItemBase { resource = resource, count = count };
|
||||
items.Add(newItem);
|
||||
// 2. 如果还有剩余,尝试添加到新槽位
|
||||
while (remainingQuantity > 0 && _slots.Count < Capacity)
|
||||
{
|
||||
var quantityToAdd = Mathf.Min(remainingQuantity, itemResource.MaxStack);
|
||||
var newSlot = new InventorySlot(itemResource, quantityToAdd);
|
||||
_slots.Add(newSlot);
|
||||
remainingQuantity -= quantityToAdd;
|
||||
addedTotal += quantityToAdd;
|
||||
}
|
||||
|
||||
if (addedTotal > 0)
|
||||
{
|
||||
OnItemAdded?.Invoke(itemResource, addedTotal);
|
||||
OnInventoryChanged?.Invoke();
|
||||
}
|
||||
|
||||
if (remainingQuantity > 0)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Inventory for {From?.ToString() ?? "Unknown"} is full or cannot stack. {remainingQuantity} of {itemResource.Name} (DefName: {itemResource.DefName}) could not be added.");
|
||||
}
|
||||
|
||||
return remainingQuantity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从背包中取出物品
|
||||
/// </summary>
|
||||
/// <param name="itemName">物品名称</param>
|
||||
/// <param name="count">取出的数量</param>
|
||||
/// <returns>是否成功取出</returns>
|
||||
public bool RemoveItem(string itemName, int count)
|
||||
public int RemoveItem(Item.ItemResource itemResource, int quantity)
|
||||
{
|
||||
if (count <= 0) return false; // 如果数量小于等于0,直接返回失败
|
||||
|
||||
foreach (var item in items)
|
||||
if (itemResource == null || quantity <= 0)
|
||||
{
|
||||
if (item.resource.name == itemName)
|
||||
{
|
||||
if (item.count >= count)
|
||||
{
|
||||
item.count -= count; // 减少数量
|
||||
if (item.count == 0)
|
||||
{
|
||||
items.Remove(item); // 如果数量为0,则移除该物品
|
||||
}
|
||||
Debug.LogWarning(
|
||||
$"Inventory for {From?.ToString() ?? "Unknown"}: Attempted to remove null item or zero/negative quantity.");
|
||||
return quantity;
|
||||
}
|
||||
|
||||
return true; // 成功取出
|
||||
}
|
||||
else
|
||||
var remainingQuantity = quantity;
|
||||
var removedTotal = 0;
|
||||
var slotsToClean = new List<InventorySlot>();
|
||||
|
||||
// 从后往前遍历,以便安全地移除空槽位 (使用 DefName 进行比较)
|
||||
for (var i = _slots.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var slot = _slots[i];
|
||||
if (slot.Item != null && slot.Item.DefName == itemResource.DefName)
|
||||
{
|
||||
var removedFromSlot = slot.RemoveQuantity(remainingQuantity);
|
||||
remainingQuantity -= removedFromSlot;
|
||||
removedTotal += removedFromSlot;
|
||||
|
||||
if (slot.IsEmpty)
|
||||
{
|
||||
return false; // 数量不足
|
||||
slotsToClean.Add(slot);
|
||||
}
|
||||
|
||||
if (remainingQuantity <= 0) break;
|
||||
}
|
||||
}
|
||||
return false; // 未找到物品
|
||||
}
|
||||
|
||||
|
||||
foreach (var slot in slotsToClean)
|
||||
{
|
||||
_slots.Remove(slot);
|
||||
}
|
||||
|
||||
if (removedTotal > 0)
|
||||
{
|
||||
OnItemRemoved?.Invoke(itemResource, removedTotal);
|
||||
OnInventoryChanged?.Invoke();
|
||||
}
|
||||
|
||||
if (remainingQuantity > 0)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Inventory for {From?.ToString() ?? "Unknown"}: Not enough {itemResource.Name} (DefName: {itemResource.DefName}). {remainingQuantity} could not be removed.");
|
||||
}
|
||||
|
||||
return remainingQuantity;
|
||||
}
|
||||
|
||||
public bool HasItem(Item.ItemResource itemResource, int quantity = 1)
|
||||
{
|
||||
if (itemResource == null || quantity <= 0) return false;
|
||||
return GetItemCount(itemResource) >= quantity;
|
||||
}
|
||||
|
||||
public int GetItemCount(Item.ItemResource itemResource)
|
||||
{
|
||||
if (itemResource == null) return 0;
|
||||
// 使用 DefName 进行计数
|
||||
return _slots.Where(s => s.Item != null && s.Item.DefName == itemResource.DefName)
|
||||
.Sum(s => s.Quantity);
|
||||
}
|
||||
|
||||
public IReadOnlyList<InventorySlot> GetSlots()
|
||||
{
|
||||
return _slots.AsReadOnly();
|
||||
}
|
||||
|
||||
public int OccupiedSlotsCount => _slots.Count;
|
||||
public int AvailableSlotsCount => Capacity - _slots.Count;
|
||||
}
|
||||
}
|
78
Client/Assets/Scripts/Entity/InventorySlot.cs
Normal file
78
Client/Assets/Scripts/Entity/InventorySlot.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示背包中的一个槽位,存储特定类型的物品及其数量。
|
||||
/// </summary>
|
||||
public class InventorySlot
|
||||
{
|
||||
public Item.ItemResource Item { get; private set; } // 槽位中的物品资源
|
||||
public int Quantity { get; private set; } // 槽位中物品的数量
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的背包槽位。
|
||||
/// </summary>
|
||||
/// <param name="item">槽位中的物品资源。</param>
|
||||
/// <param name="quantity">初始数量。</param>
|
||||
public InventorySlot(Item.ItemResource item, int quantity)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
Debug.LogError("InventorySlot cannot be initialized with a null ItemResource.");
|
||||
return;
|
||||
}
|
||||
|
||||
Item = item;
|
||||
Quantity = Mathf.Max(0, quantity); // 确保数量不为负
|
||||
// 确保初始数量不超过最大堆叠
|
||||
if (Quantity > Item.MaxStack)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Initial quantity {Quantity} for {Item.Name} exceeds MaxStack {Item.MaxStack}. Clamping to MaxStack.");
|
||||
Quantity = Item.MaxStack;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向槽位中添加指定数量的物品。
|
||||
/// </summary>
|
||||
/// <param name="amount">要添加的数量。</param>
|
||||
/// <returns>实际添加的数量。</returns>
|
||||
public int AddQuantity(int amount)
|
||||
{
|
||||
if (Item == null || amount <= 0) return 0;
|
||||
|
||||
var spaceLeft = Item.MaxStack - Quantity;
|
||||
if (spaceLeft <= 0) return 0; // 槽位已满
|
||||
|
||||
var added = Mathf.Min(amount, spaceLeft);
|
||||
Quantity += added;
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从槽位中移除指定数量的物品。
|
||||
/// </summary>
|
||||
/// <param name="amount">要移除的数量。</param>
|
||||
/// <returns>实际移除的数量。</returns>
|
||||
public int RemoveQuantity(int amount)
|
||||
{
|
||||
if (Item == null || amount <= 0) return 0;
|
||||
|
||||
var removed = Mathf.Min(amount, Quantity);
|
||||
Quantity -= removed;
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取槽位是否为空。
|
||||
/// </summary>
|
||||
public bool IsEmpty => Quantity <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取槽位是否已满。
|
||||
/// </summary>
|
||||
public bool IsFull => Item != null && Quantity >= Item.MaxStack;
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/InventorySlot.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/InventorySlot.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c58fcd7fb7147ce82fe20e68cb7c8d4
|
||||
timeCreated: 1756031108
|
@ -118,7 +118,7 @@ namespace Entity
|
||||
private void BecomeDefault()
|
||||
{
|
||||
entity.Kill();
|
||||
Managers.EntityManage.Instance.GenerateDefaultEntity(entity.Position);
|
||||
Managers.EntityManage.Instance.GenerateDefaultEntity(Program.Instance.focuseDimensionId,entity.Position);
|
||||
}
|
||||
|
||||
private void StartControl()
|
||||
|
188
Client/Assets/Scripts/Entity/VisionRangeDetector.cs
Normal file
188
Client/Assets/Scripts/Entity/VisionRangeDetector.cs
Normal file
@ -0,0 +1,188 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/VisionRangeDetector.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/VisionRangeDetector.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e33d58195dd8450a9ec468ede64c8635
|
||||
timeCreated: 1756048545
|
@ -1,12 +1,41 @@
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Item
|
||||
{
|
||||
public class ItemResource
|
||||
{
|
||||
public string name;
|
||||
public string description;
|
||||
|
||||
public Sprite icon;
|
||||
public string DefName { get; protected set; } // 新增:物品的定义名称,唯一标识符
|
||||
public string Name { get; protected set; } // 物品的显示名称 (来自 label)
|
||||
public string Description { get; protected set; }
|
||||
public Sprite Icon { get; protected set; }
|
||||
public ItemRarity Rarity { get; protected set; }
|
||||
public int MaxStack { get; protected set; }
|
||||
public bool IsEquippable { get; protected set; }
|
||||
|
||||
// 构造函数,现在接受 defName
|
||||
public ItemResource(string defName, string name, string description, Sprite icon, ItemRarity rarity, int maxStack, bool isEquippable)
|
||||
{
|
||||
DefName = defName; // 赋值 defName
|
||||
Name = name;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
Rarity = rarity;
|
||||
MaxStack = maxStack;
|
||||
IsEquippable = isEquippable;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,105 +6,165 @@ using UnityEngine;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// 阵营管理器,负责管理游戏中的所有阵营定义及其相互关系。
|
||||
/// 继承自 <see cref="Utils.Singleton{T}"/> ,确保全局只有一个实例。
|
||||
/// </summary>
|
||||
public class AffiliationManager:Utils.Singleton<AffiliationManager>
|
||||
{
|
||||
//定义名,阵营定义
|
||||
/// <summary>
|
||||
/// 存储所有已加载的阵营定义,键为阵营的唯一名称,值为对应的 <see cref="AffiliationDef"/> 对象。
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, AffiliationDef> _affiliations = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化阵营管理器,从 <see cref="DefineManager"/> 加载所有 <see cref="AffiliationDef"/>。
|
||||
/// 在首次需要阵营数据时调用。
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
// 如果管理器已经初始化,则直接返回,避免重复加载。
|
||||
if (_affiliations.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var affiliationList = Managers.DefineManager.Instance.QueryDefinesByType<AffiliationDef>();
|
||||
if (affiliationList == null ||affiliationList.Length==0)
|
||||
if (affiliationList == null || affiliationList.Length == 0)
|
||||
{
|
||||
// 记录警告,说明未能加载任何阵营定义。
|
||||
Debug.LogWarning("未找到任何 AffiliationDef 或阵营列表为空。");
|
||||
return;
|
||||
}
|
||||
foreach (var affiliation in affiliationList)
|
||||
{
|
||||
_affiliations.Add(affiliation.defName, affiliation);
|
||||
// 尝试将阵营定义添加到字典中。如果名称已存在,则记录错误。
|
||||
if (!_affiliations.TryAdd(affiliation.defName, affiliation))
|
||||
{
|
||||
Debug.LogError($"添加阵营 '{affiliation.defName}' 失败。已存在同名阵营。");
|
||||
}
|
||||
}
|
||||
// 验证并修复加载后的阵营关系,以确保数据一致性。
|
||||
ValidateAndFixRelationships();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有已加载的阵营定义。
|
||||
/// 在游戏会话结束或需要重新加载所有定义时调用。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_affiliations.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据阵营定义名称获取其默认名称。
|
||||
/// </summary>
|
||||
/// <param name="defName">阵营的定义名称。</param>
|
||||
/// <returns>阵营的默认名称。</returns>
|
||||
public string GetAffiliationName(string defName)
|
||||
{
|
||||
return _affiliations[defName].defName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取两个阵营间的关系。
|
||||
/// </summary>
|
||||
/// <param name="affiliation1">第一个阵营的 <see cref="AffiliationDef"/> 对象。</param>
|
||||
/// <param name="affiliation2">第二个阵营的 <see cref="AffiliationDef"/> 对象。</param>
|
||||
/// <returns>两个阵营之间的 <see cref="Relation"/>。</returns>
|
||||
public Relation GetRelation(AffiliationDef affiliation1, AffiliationDef affiliation2)
|
||||
{
|
||||
// 如果任一阵营定义为空,则返回中立关系。
|
||||
if (affiliation1 == null || affiliation2 == null)
|
||||
{
|
||||
return Relation.Neutral; // 如果任一阵营不存在,返回中立关系
|
||||
return Relation.Neutral;
|
||||
}
|
||||
return GetRelation(affiliation1.defName, affiliation2.defName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取两个阵营名称之间的关系。
|
||||
/// </summary>
|
||||
/// <param name="factionName1">第一个阵营的名称。</param>
|
||||
/// <param name="factionName2">第二个阵营的名称。</param>
|
||||
/// <returns>两个阵营之间的 <see cref="Relation"/>。</returns>
|
||||
public Relation GetRelation(string factionName1, string factionName2)
|
||||
{
|
||||
// 如果查询的是同一个派系,默认是友好关系
|
||||
// 如果查询的是同一个阵营,则默认为友好关系。
|
||||
if (factionName1 == factionName2)
|
||||
{
|
||||
return Relation.Friendly;
|
||||
}
|
||||
|
||||
// 尝试获取两个派系的定义
|
||||
// 尝试获取两个阵营的定义。
|
||||
if (!_affiliations.TryGetValue(factionName1, out var faction1) ||
|
||||
!_affiliations.TryGetValue(factionName2, out _))
|
||||
{
|
||||
// 如果第一个阵营存在,但第二个不存在,返回第一个阵营的默认关系;
|
||||
// 否则(两个都不存在或第一个不存在),返回中立。
|
||||
if (faction1 != null) return faction1.defaultRelation;
|
||||
// 注意:由于上面已经有一个 TryGetValue 判断,
|
||||
// 此时 faction1 为 null 表示 factionName1 也不存在,所以应返回中立。
|
||||
return Relation.Neutral;
|
||||
}
|
||||
|
||||
// 检查faction1是否明确将faction2列为敌对
|
||||
// 检查 faction1 是否将 faction2 明确列为敌对。
|
||||
if (faction1.hostileFactions != null && faction1.hostileFactions.Contains(factionName2))
|
||||
{
|
||||
return Relation.Hostile;
|
||||
}
|
||||
|
||||
// 检查faction1是否明确将faction2列为友好
|
||||
// 检查 faction1 是否将 faction2 明确列为友好。
|
||||
if (faction1.friendlyFactions != null && faction1.friendlyFactions.Contains(factionName2))
|
||||
{
|
||||
return Relation.Friendly;
|
||||
}
|
||||
|
||||
// 检查faction1是否明确将faction2列为中立
|
||||
// 检查 faction1 是否将 faction2 明确列为中立。
|
||||
if (faction1.neutralFactions != null && faction1.neutralFactions.Contains(factionName2))
|
||||
{
|
||||
return Relation.Neutral;
|
||||
}
|
||||
|
||||
// 如果faction1没有明确设置与faction2的关系,则使用faction1的默认关系
|
||||
// 如果 faction1 没有明确设置与 faction2 的关系,则使用 faction1 的默认关系。
|
||||
return faction1.defaultRelation;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置两个阵营之间的关系
|
||||
/// 设置两个阵营之间的关系。
|
||||
/// </summary>
|
||||
/// <param name="factionName1">第一个阵营名称</param>
|
||||
/// <param name="factionName2">第二个阵营名称</param>
|
||||
/// <param name="relation">要设置的关系</param>
|
||||
/// <param name="factionName1">第一个阵营的名称。</param>
|
||||
/// <param name="factionName2">第二个阵营的名称。</param>
|
||||
/// <param name="relation">要设置的 <see cref="Relation"/>。</param>
|
||||
/// <exception cref="ArgumentException">当尝试设置同一个阵营的关系或其中一个阵营不存在时抛出。</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">当传入的关系类型无效时抛出。</exception>
|
||||
public void SetRelation(string factionName1, string factionName2, Relation relation)
|
||||
{
|
||||
// 不能设置自己与自己的关系
|
||||
// 不能设置自己与自己的关系。
|
||||
if (factionName1 == factionName2)
|
||||
{
|
||||
throw new ArgumentException("Cannot set relation between the same faction");
|
||||
throw new ArgumentException("不能设置同一个阵营之间的关系");
|
||||
}
|
||||
|
||||
// 确保两个阵营都存在
|
||||
// 确保两个阵营都存在。
|
||||
if (!_affiliations.TryGetValue(factionName1, out var faction1) ||
|
||||
!_affiliations.TryGetValue(factionName2, out _))
|
||||
{
|
||||
throw new ArgumentException("One or both factions do not exist");
|
||||
throw new ArgumentException("一个或两个阵营不存在");
|
||||
}
|
||||
|
||||
// 确保关系列表已初始化
|
||||
// 确保关系列表已初始化,避免空引用异常。
|
||||
faction1.hostileFactions ??= new List<string>();
|
||||
faction1.friendlyFactions ??= new List<string>();
|
||||
faction1.neutralFactions ??= new List<string>();
|
||||
|
||||
// 先移除所有现有关系
|
||||
// 先移除 factionName2 在 faction1 所有关系列表中的现有关系。
|
||||
faction1.hostileFactions.Remove(factionName2);
|
||||
faction1.friendlyFactions.Remove(factionName2);
|
||||
faction1.neutralFactions.Remove(factionName2);
|
||||
|
||||
// 添加新关系
|
||||
// 根据传入的关系类型,将 factionName2 添加到对应列表中。
|
||||
switch (relation)
|
||||
{
|
||||
case Relation.Hostile:
|
||||
@ -117,43 +177,45 @@ namespace Managers
|
||||
faction1.neutralFactions.Add(factionName2);
|
||||
break;
|
||||
default:
|
||||
// 如果传入的关系类型无效,抛出异常。
|
||||
throw new ArgumentOutOfRangeException(nameof(relation), relation, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查并修复派系关系,确保没有冲突(按友好 > 敌对 > 中立 的优先级)
|
||||
/// 检查并修复所有阵营之间的关系,以确保没有冲突。
|
||||
/// 修复遵循优先级规则:友好关系优先于敌对关系,敌对关系优先于中立关系。
|
||||
/// </summary>
|
||||
private void ValidateAndFixRelationships()
|
||||
{
|
||||
foreach (var faction in _affiliations.Values)
|
||||
{
|
||||
// 确保所有关系列表已初始化
|
||||
// 确保所有关系列表已初始化,避免空引用异常。
|
||||
faction.hostileFactions ??= new List<string>();
|
||||
faction.friendlyFactions ??= new List<string>();
|
||||
faction.neutralFactions ??= new List<string>();
|
||||
|
||||
// 检查所有敌对派系
|
||||
// 遍历并检查所有敌对阵营。由于可能修改列表,使用 ToList() 创建副本进行遍历。
|
||||
foreach (var hostileFaction in faction.hostileFactions.ToList())
|
||||
{
|
||||
// 如果敌对派系同时存在于友好列表中,移除敌对关系(友好优先)
|
||||
// 如果敌对阵营同时存在于友好列表中,则移除其敌对关系(友好关系具有更高优先级)。
|
||||
if (faction.friendlyFactions.Contains(hostileFaction))
|
||||
{
|
||||
faction.hostileFactions.Remove(hostileFaction);
|
||||
continue;
|
||||
continue; // 继续检查下一个敌对阵营。
|
||||
}
|
||||
|
||||
// 如果敌对派系同时存在于中立列表中,移除中立关系(敌对优先)
|
||||
// 如果敌对阵营同时存在于中立列表中,则移除其中立关系(敌对关系具有更高优先级)。
|
||||
if (faction.neutralFactions.Contains(hostileFaction))
|
||||
{
|
||||
faction.neutralFactions.Remove(hostileFaction);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查所有中立派系
|
||||
// 遍历并检查所有中立阵营。由于可能修改列表,使用 ToList() 创建副本进行遍历。
|
||||
foreach (var neutralFaction in faction.neutralFactions.ToList())
|
||||
{
|
||||
// 如果中立派系同时存在于友好列表中,移除中立关系(友好优先)
|
||||
// 如果中立阵营同时存在于友好列表中,则移除其中立关系(友好关系具有更高优先级)。
|
||||
if (faction.friendlyFactions.Contains(neutralFaction))
|
||||
{
|
||||
faction.neutralFactions.Remove(neutralFaction);
|
||||
@ -162,4 +224,4 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,11 +218,11 @@ namespace Managers
|
||||
/// <returns>如果找到,返回转换为目标类型的 Define 对象;否则返回 null。</returns>
|
||||
public T FindDefine<T>(string defineName) where T : Define
|
||||
{
|
||||
foreach (var typeDict in defines.Values)
|
||||
if (defines.TryGetValue(typeof(T).Name, out var typeDict))
|
||||
{
|
||||
if (typeDict.TryGetValue(defineName, out var define) && define is T result)
|
||||
if (typeDict.TryGetValue(defineName, out var define))
|
||||
{
|
||||
return result;
|
||||
return (T)define;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -3,102 +3,271 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Base;
|
||||
using Entity;
|
||||
using Map;
|
||||
using Prefab;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
|
||||
public class EntityManage : Utils.MonoSingleton<EntityManage>, ITick
|
||||
{
|
||||
public Dictionary<string, LinkedList<EntityPrefab>> factionEntities = new();
|
||||
// --- 新增:维度感知的实体存储结构 ---
|
||||
// 外层字典:DimensionId -> 内层字典
|
||||
// 内层字典:FactionKey -> LinkedList<EntityPrefab>
|
||||
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
|
||||
|
||||
// --- 新增:当前场景中活跃的维度实例 ---
|
||||
private Dictionary<string, Dimension> _activeDimensions = new();
|
||||
|
||||
// --- 新增:维度感知的层级缓存 ---
|
||||
// DimensionId -> LayerName -> Transform
|
||||
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
|
||||
|
||||
// --- 待添加实体列表,现在包含 DimensionId ---
|
||||
private List<Tuple<string, string, EntityPrefab>>
|
||||
_pendingAdditions = new(); // Item1: DimensionId, Item2: FactionKey, Item3: EntityPrefab
|
||||
|
||||
// --- 现有预制体 (保持不变) ---
|
||||
public EntityPrefab characterPrefab;
|
||||
public EntityPrefab buildingPrefab;
|
||||
public EntityPrefab bulletPrefab;
|
||||
|
||||
public EntityPrefab defaultEntityPrefab;
|
||||
|
||||
private Dictionary<string, Transform> layerCache = new Dictionary<string, Transform>();
|
||||
private List<Tuple<string, EntityPrefab>> pendingAdditions = new();
|
||||
|
||||
public LinkedList<EntityPrefab> FindEntitiesByFaction(string factionKey)
|
||||
public void RegisterDimension(Dimension dimension)
|
||||
{
|
||||
if (factionEntities.TryGetValue(factionKey, out var entities))
|
||||
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
|
||||
{
|
||||
return entities; // 如果找到,返回对应的实体列表
|
||||
Debug.LogError("Attempted to register a null or invalid Dimension.");
|
||||
return;
|
||||
}
|
||||
|
||||
return new(); // 如果未找到,返回一个空列表
|
||||
if (_activeDimensions.TryGetValue(dimension.DimensionId, out var existingDimension))
|
||||
{
|
||||
if (existingDimension == dimension)
|
||||
{
|
||||
// 【逻辑修改】如果是同一个实例重复注册,只做信息提示,不警告
|
||||
Debug.Log($"Dimension with ID '{dimension.DimensionId}' already registered with the same instance. Skipping re-registration.");
|
||||
return; // 已经注册且是同一个实例,直接返回
|
||||
}
|
||||
else
|
||||
{
|
||||
// 【逻辑修改】如果是不同实例但ID相同,这是严重错误
|
||||
Debug.LogError(
|
||||
$"CRITICAL ERROR: Dimension with ID '{dimension.DimensionId}' is already registered with a DIFFERENT instance ({existingDimension.name} vs {dimension.name}). This indicates a duplicate DimensionId in the scene. The new instance will overwrite the old one, which may lead to unexpected behavior. Please ensure all Dimension objects have unique IDs.");
|
||||
// 允许覆盖,但以错误日志形式提示,强制开发者修复场景配置。
|
||||
}
|
||||
}
|
||||
|
||||
_activeDimensions[dimension.DimensionId] = dimension;
|
||||
Debug.Log($"Dimension '{dimension.DimensionId}' registered with EntityManage.");
|
||||
|
||||
// 为新注册的维度初始化其数据结构
|
||||
// 这些检查是必要的,以防止在重复注册时清空现有数据。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimension.DimensionId))
|
||||
{
|
||||
_dimensionFactionEntities[dimension.DimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
|
||||
}
|
||||
|
||||
if (!_dimensionLayerCache.ContainsKey(dimension.DimensionId))
|
||||
{
|
||||
_dimensionLayerCache[dimension.DimensionId] = new Dictionary<string, Transform>();
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
foreach (var faction in factionEntities)
|
||||
{
|
||||
var entitiesToRemove = new List<EntityPrefab>();
|
||||
|
||||
foreach (var entityPrefab in faction.Value)
|
||||
/// <summary>
|
||||
/// 从实体管理器注销一个维度。
|
||||
/// </summary>
|
||||
/// <param name="dimension">要注销的维度实例。</param>
|
||||
public void UnregisterDimension(Dimension dimension)
|
||||
{
|
||||
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
|
||||
{
|
||||
Debug.LogError("Attempted to unregister a null or invalid Dimension.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeDimensions.Remove(dimension.DimensionId))
|
||||
{
|
||||
// 当维度被注销时,清理其所有相关的实体和层级缓存
|
||||
_dimensionFactionEntities.Remove(dimension.DimensionId);
|
||||
_dimensionLayerCache.Remove(dimension.DimensionId);
|
||||
|
||||
// 【逻辑修改】立即清理_pendingAdditions中属于该维度的实体
|
||||
// 创建一个新列表来存储不属于该维度的实体
|
||||
var remainingPendingAdditions = new List<Tuple<string, string, EntityPrefab>>();
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (entityPrefab.entity.IsDead)
|
||||
if (pending.Item1 == dimension.DimensionId)
|
||||
{
|
||||
entitiesToRemove.Add(entityPrefab);
|
||||
// 销毁实体GameObject
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null) // 增加gameObject的null检查
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ITick itike = entityPrefab.entity;
|
||||
itike.Tick();
|
||||
remainingPendingAdditions.Add(pending);
|
||||
}
|
||||
}
|
||||
_pendingAdditions = remainingPendingAdditions; // 更新_pendingAdditions列表
|
||||
}
|
||||
}
|
||||
|
||||
// 删除所有标记为死亡的实体
|
||||
foreach (var entityToRemove in entitiesToRemove)
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取一个活跃的维度实例。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <returns>对应的 Dimension 实例,如果不存在则为 null。</returns>
|
||||
public Dimension GetDimension(string dimensionId)
|
||||
{
|
||||
_activeDimensions.TryGetValue(dimensionId, out var dimension);
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// --- 查找实体 (现在维度感知) ---
|
||||
/// <summary>
|
||||
/// 在指定维度中,根据派系键查找所有实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <param name="factionKey">派系键。</param>
|
||||
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
|
||||
public LinkedList<EntityPrefab> FindEntitiesByFaction(string dimensionId, string factionKey)
|
||||
{
|
||||
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
if (factionDict.TryGetValue(factionKey, out var entities))
|
||||
{
|
||||
faction.Value.Remove(entityToRemove);
|
||||
Destroy(entityToRemove.gameObject);
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingAdditions.Any())
|
||||
{
|
||||
foreach (var entity in pendingAdditions)
|
||||
{
|
||||
if (!factionEntities.ContainsKey(entity.Item1))
|
||||
{
|
||||
factionEntities[entity.Item1] = new LinkedList<EntityPrefab>();
|
||||
}
|
||||
return new LinkedList<EntityPrefab>(); // 如果未找到,返回一个空列表
|
||||
}
|
||||
|
||||
factionEntities[entity.Item1].AddLast(entity.Item2);
|
||||
// --- Tick 方法 (现在维度感知) ---
|
||||
public void Tick()
|
||||
{
|
||||
// 遍历每个活跃的维度
|
||||
foreach (var dimensionEntry in _dimensionFactionEntities.ToList()) // ToList() 避免在迭代时修改字典
|
||||
{
|
||||
var dimensionId = dimensionEntry.Key;
|
||||
var factionDict = dimensionEntry.Value;
|
||||
|
||||
// 检查维度对象本身是否仍然活跃在场景中
|
||||
// 注意:这里检查的是 _activeDimensions,确保 Dimension 对象实例仍然存在且被管理器追踪。
|
||||
if (!_activeDimensions.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Skipping Tick for dimension '{dimensionId}' as its Dimension object is no longer active. Clearing its entities.");
|
||||
_dimensionFactionEntities.Remove(dimensionId); // 移除已失效维度的实体数据
|
||||
_dimensionLayerCache.Remove(dimensionId); // 移除已失效维度的层级缓存
|
||||
continue;
|
||||
}
|
||||
|
||||
pendingAdditions.Clear();
|
||||
foreach (var faction in factionDict)
|
||||
{
|
||||
var entitiesToRemove = new List<EntityPrefab>();
|
||||
var currentEntities = faction.Value.ToList(); // 创建副本以避免在迭代时修改列表
|
||||
|
||||
foreach (var entityPrefab in currentEntities)
|
||||
{
|
||||
// 检查实体预制体或其内部实体是否已销毁或死亡
|
||||
if (entityPrefab == null || entityPrefab.entity == null || entityPrefab.entity.IsDead)
|
||||
{
|
||||
entitiesToRemove.Add(entityPrefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
ITick itike = entityPrefab.entity;
|
||||
itike.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
// 删除所有标记为死亡的实体
|
||||
foreach (var entityToRemove in entitiesToRemove)
|
||||
{
|
||||
faction.Value.Remove(entityToRemove);
|
||||
if (entityToRemove != null) // 确保它没有被外部销毁
|
||||
{
|
||||
Destroy(entityToRemove.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理待添加实体 (现在维度感知)
|
||||
if (_pendingAdditions.Any())
|
||||
{
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
var dimensionId = pending.Item1;
|
||||
var factionKey = pending.Item2;
|
||||
var entityPrefab = pending.Item3;
|
||||
|
||||
// 再次检查维度是否活跃,防止在添加前维度被注销
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Attempted to add entity '{entityPrefab.name}' to unregistered or inactive dimension '{dimensionId}'. Entity will be destroyed.");
|
||||
if (entityPrefab != null) Destroy(entityPrefab.gameObject);
|
||||
continue;
|
||||
}
|
||||
|
||||
var factionDict = _dimensionFactionEntities[dimensionId];
|
||||
if (!factionDict.ContainsKey(factionKey))
|
||||
{
|
||||
factionDict[factionKey] = new LinkedList<EntityPrefab>();
|
||||
}
|
||||
|
||||
factionDict[factionKey].AddLast(entityPrefab);
|
||||
}
|
||||
|
||||
_pendingAdditions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的Def生成实体对象(内部通用方法)。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="prefab">要实例化的预制体</param>
|
||||
/// <param name="parent">生成的父级Transform</param>
|
||||
/// <param name="pos">生成位置</param>
|
||||
/// <param name="def">实体定义对象</param>
|
||||
/// <param name="extraInit">额外的初始化操作(如子弹方向设置)</param>
|
||||
/// <returns>成功时返回EntityPrefab组件,失败时返回null</returns>
|
||||
private EntityPrefab GenerateEntityInternal(
|
||||
string dimensionId, // 新增参数:维度ID
|
||||
GameObject prefab,
|
||||
Transform parent,
|
||||
Vector3 pos,
|
||||
Data.EntityDef def, // 所有Def类型需继承自BaseDef
|
||||
Data.EntityDef def,
|
||||
Action<EntityPrefab> extraInit = null)
|
||||
{
|
||||
// 验证维度是否活跃
|
||||
if (!_activeDimensions.TryGetValue(dimensionId, out var dimension))
|
||||
{
|
||||
Debug.LogError($"Cannot generate entity: Dimension '{dimensionId}' is not active or registered.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取或创建实体所属的层级Transform,并确保其在维度根下
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel"); // 使用一个默认层级名称
|
||||
if (parentLayer == null)
|
||||
{
|
||||
Debug.LogError($"Failed to get or create parent layer for entity in dimension '{dimensionId}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject instantiatedEntity = null;
|
||||
try
|
||||
{
|
||||
// 实例化实体
|
||||
instantiatedEntity = Instantiate(prefab, pos, Quaternion.identity, parent);
|
||||
// 实例化实体,并将其父级设置为维度下的层级Transform
|
||||
instantiatedEntity = Instantiate(prefab, pos, Quaternion.identity, parentLayer);
|
||||
|
||||
// 获取并验证EntityPrefab组件
|
||||
var entityComponent = instantiatedEntity.GetComponent<EntityPrefab>();
|
||||
if (!entityComponent)
|
||||
{
|
||||
@ -106,204 +275,231 @@ namespace Managers
|
||||
$"EntityPrefab component missing on: {instantiatedEntity.name}");
|
||||
}
|
||||
|
||||
// 初始化核心数据
|
||||
entityComponent.Init(def);
|
||||
|
||||
// 执行类型特有的额外初始化
|
||||
extraInit?.Invoke(entityComponent);
|
||||
|
||||
// 管理派系列表
|
||||
var factionKey = def.attributes.defName ?? "default";
|
||||
pendingAdditions.Add(Tuple.Create(factionKey, entityComponent));
|
||||
var factionKey = def.attributes.defName ?? "default"; // 假设 attributes.defName 是派系键
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent)); // 添加维度ID
|
||||
return entityComponent;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// 清理失败实例
|
||||
if (instantiatedEntity) Destroy(instantiatedEntity);
|
||||
|
||||
Debug.LogError($"Entity generation failed: {ex.Message}\n{ex.StackTrace}");
|
||||
Debug.LogError($"Entity generation failed in dimension '{dimensionId}': {ex.Message}\n{ex.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动态创建层(如果层不存在)
|
||||
/// 动态创建层(如果层不存在),现在是维度感知的。
|
||||
/// 每个维度有自己的层级结构,根在 Dimension.DimensionRoot 下。
|
||||
/// </summary>
|
||||
private Transform EnsureLayerExists(string layerName)
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <param name="layerName">要确保存在的层级名称。</param>
|
||||
/// <returns>层级Transform,如果维度不存在或其根Transform为空则返回null。</returns>
|
||||
private Transform EnsureLayerExists(string dimensionId, string layerName)
|
||||
{
|
||||
// 先从缓存中查找
|
||||
if (layerCache.TryGetValue(layerName, out var layerTransform))
|
||||
// 尝试从维度层级缓存中获取
|
||||
if (!_dimensionLayerCache.TryGetValue(dimensionId, out var layerCacheForDimension))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Dimension '{dimensionId}' not found in layer cache. This should not happen if dimension is registered.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
|
||||
{
|
||||
return layerTransform;
|
||||
}
|
||||
|
||||
// 如果缓存中没有,尝试通过 transform.Find 查找
|
||||
layerTransform = transform.Find(layerName);
|
||||
// 如果缓存中没有,尝试在维度根下查找
|
||||
var dimension = GetDimension(dimensionId);
|
||||
if (dimension == null || dimension.DimensionRoot == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Dimension '{dimensionId}' or its root transform is null. Cannot create layer '{layerName}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
layerTransform = dimension.DimensionRoot.Find(layerName);
|
||||
|
||||
if (!layerTransform)
|
||||
{
|
||||
// 如果层不存在,动态创建
|
||||
// 如果层不存在,动态创建并将其父级设置为维度的根Transform
|
||||
var layerObject = new GameObject(layerName);
|
||||
layerTransform = layerObject.transform;
|
||||
layerTransform.SetParent(dimension.DimensionRoot);
|
||||
}
|
||||
|
||||
// 将新创建的层加入缓存
|
||||
layerCache[layerName] = layerTransform;
|
||||
|
||||
layerCacheForDimension[layerName] = layerTransform;
|
||||
return layerTransform;
|
||||
}
|
||||
|
||||
// --- 公共生成方法 (现在维度感知) ---
|
||||
/// <summary>
|
||||
/// 根据PawnDef生成普通实体
|
||||
/// 在指定维度中,根据PawnDef生成普通实体。
|
||||
/// </summary>
|
||||
public void GenerateEntity(Data.EntityDef entityDef, Vector3 pos)
|
||||
public void GenerateEntity(string dimensionId, Data.EntityDef entityDef, Vector3 pos)
|
||||
{
|
||||
// 验证关键参数
|
||||
if (!characterPrefab)
|
||||
{
|
||||
Debug.LogError("entityPrefab is null! Assign a valid prefab.");
|
||||
GenerateDefaultEntity(pos);
|
||||
Debug.LogError("characterPrefab is null! Assign a valid prefab.");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityDef == null)
|
||||
{
|
||||
Debug.LogError("EntityDef is null! Cannot generate entity.");
|
||||
GenerateDefaultEntity(pos);
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保层存在
|
||||
var entityLevelTransform = EnsureLayerExists("EntityLevel");
|
||||
|
||||
// 调用通用生成逻辑
|
||||
var result = GenerateEntityInternal(
|
||||
dimensionId,
|
||||
characterPrefab.gameObject,
|
||||
entityLevelTransform,
|
||||
pos,
|
||||
entityDef
|
||||
);
|
||||
|
||||
if (!result) GenerateDefaultEntity(pos);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成建筑实体(位置使用Vector3Int)
|
||||
/// 在指定维度中,生成建筑实体(位置使用Vector3Int)。
|
||||
/// </summary>
|
||||
public void GenerateBuildingEntity(Data.BuildingDef buildingDef, Vector3Int pos)
|
||||
public void GenerateBuildingEntity(string dimensionId, Data.BuildingDef buildingDef, Vector3Int pos)
|
||||
{
|
||||
// 修正:检查正确的预制体 (buildingPrefab)
|
||||
if (!buildingPrefab)
|
||||
{
|
||||
Debug.LogError("buildingPrefab is null! Assign a valid prefab.");
|
||||
GenerateDefaultEntity(pos);
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buildingDef == null)
|
||||
{
|
||||
Debug.LogError("BuildingDef is null! Cannot generate building.");
|
||||
GenerateDefaultEntity(pos);
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
var worldPos = new Vector3(pos.x, pos.y, pos.z);
|
||||
|
||||
// 确保层存在
|
||||
var buildingLevelTransform = EnsureLayerExists("BuildingLevel");
|
||||
|
||||
var result = GenerateEntityInternal(
|
||||
dimensionId,
|
||||
buildingPrefab.gameObject,
|
||||
buildingLevelTransform,
|
||||
worldPos,
|
||||
buildingDef
|
||||
);
|
||||
|
||||
if (!result) GenerateDefaultEntity(worldPos);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, worldPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成子弹实体(含方向设置)
|
||||
/// 在指定维度中,生成子弹实体(含方向设置)。
|
||||
/// </summary>
|
||||
public void GenerateBulletEntity(Data.BulletDef bulletDef, Vector3 pos, Vector3 dir,
|
||||
public void GenerateBulletEntity(string dimensionId, Data.BulletDef bulletDef, Vector3 pos, Vector3 dir,
|
||||
Entity.Entity source = null)
|
||||
{
|
||||
// 修正:检查正确的预制体 (bulletPrefab)
|
||||
if (!bulletPrefab)
|
||||
{
|
||||
Debug.LogError("bulletPrefab is null! Assign a valid prefab.");
|
||||
GenerateDefaultEntity(pos);
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bulletDef == null)
|
||||
{
|
||||
Debug.LogError("BulletDef is null! Cannot generate bullet.");
|
||||
GenerateDefaultEntity(pos);
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保层存在
|
||||
var bulletLevelTransform = EnsureLayerExists("BulletLevel");
|
||||
|
||||
var result = GenerateEntityInternal(
|
||||
dimensionId,
|
||||
bulletPrefab.gameObject,
|
||||
bulletLevelTransform,
|
||||
pos,
|
||||
bulletDef,
|
||||
// 子弹特有的方向设置
|
||||
entityComponent => entityComponent.entity.SetTarget(pos + dir)
|
||||
);
|
||||
if (result.entity is Bullet bullet)
|
||||
|
||||
// 确保 result 不为 null 且 entity 是 Bullet 类型
|
||||
if (result != null && result.entity is Bullet bullet)
|
||||
{
|
||||
bullet.bulletSource = source;
|
||||
if (source) bullet.affiliation = source.affiliation;
|
||||
if (source != null) bullet.affiliation = source.affiliation; // 确保 source 不为 null
|
||||
}
|
||||
|
||||
if (!result) GenerateDefaultEntity(pos);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成默认实体(错误回退)
|
||||
/// 在指定维度中,生成默认实体(错误回退)。
|
||||
/// </summary>
|
||||
public void GenerateDefaultEntity(Vector3 pos)
|
||||
public void GenerateDefaultEntity(string dimensionId, Vector3 pos)
|
||||
{
|
||||
// 确保层存在
|
||||
var entityLevelTransform = EnsureLayerExists("EntityLevel");
|
||||
if (!_activeDimensions.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Cannot generate default entity: Dimension '{dimensionId}' is not active or registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = Instantiate(defaultEntityPrefab, pos, Quaternion.identity, entityLevelTransform);
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
|
||||
if (parentLayer == null)
|
||||
{
|
||||
Debug.LogError($"Failed to get parent transform for default entity in dimension '{dimensionId}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = Instantiate(defaultEntityPrefab, pos, Quaternion.identity, parentLayer);
|
||||
var entityComponent = entity.GetComponent<EntityPrefab>();
|
||||
|
||||
const string factionKey = "default";
|
||||
pendingAdditions.Add(Tuple.Create(factionKey, entityComponent));
|
||||
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
entityComponent.DefaultInit();
|
||||
}
|
||||
|
||||
|
||||
// --- 单例生命周期与场景切换处理 ---
|
||||
protected override void OnStart()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 场景加载完成时的回调。
|
||||
/// 清理旧场景的实体数据,并重新扫描新场景中的维度。
|
||||
/// </summary>
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
factionEntities.Clear();
|
||||
layerCache.Clear();
|
||||
pendingAdditions.Clear();
|
||||
_dimensionFactionEntities.Clear(); // 清理所有维度下的实体数据
|
||||
_dimensionLayerCache.Clear(); // 清理所有维度下的层级缓存
|
||||
_pendingAdditions.Clear(); // 清理待添加实体列表
|
||||
_activeDimensions.Clear(); // 清理活跃维度列表,因为旧场景的维度对象已被销毁
|
||||
}
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var pre = Resources.Load<GameObject>("Default/DefaultEntity");
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
layerCache.Clear();
|
||||
if (defaultEntityPrefab == null)
|
||||
{
|
||||
var pre = Resources.Load<GameObject>("Default/DefaultEntity");
|
||||
if (pre != null)
|
||||
{
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"Failed to load DefaultEntity prefab from Resources/Default/DefaultEntity. Please ensure it exists at 'Assets/Resources/Default/DefaultEntity.prefab'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,46 +2,122 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Data;
|
||||
using Item;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class ItemResourceManager:Utils.Singleton<ItemResourceManager>
|
||||
public class ItemResourceManager : Utils.Singleton<ItemResourceManager>
|
||||
{
|
||||
//定义名,物品
|
||||
public Dictionary<string,Item.ItemResource> items;
|
||||
|
||||
|
||||
private readonly Dictionary<string, Item.ItemResource> _items = new();
|
||||
private readonly Dictionary<string, List<Item.ItemResource>> _itemsByName = new(); // 保持按显示名称查找的字典
|
||||
|
||||
public void Init()
|
||||
{
|
||||
var itemDefs = Managers.DefineManager.Instance.QueryDefinesByType<ItemDef>();
|
||||
if(itemDefs==null||itemDefs.Length==0)
|
||||
var baseItemDefs = Managers.DefineManager.Instance.QueryDefinesByType<ItemDef>();
|
||||
var weaponDefs = Managers.DefineManager.Instance.QueryDefinesByType<WeaponDef>();
|
||||
|
||||
var allDefs = new List<ItemDef>();
|
||||
if (baseItemDefs != null) allDefs.AddRange(baseItemDefs);
|
||||
if (weaponDefs != null) allDefs.AddRange(weaponDefs);
|
||||
|
||||
if (allDefs.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("ItemResourceManager: No ItemDef or WeaponDef found to initialize.");
|
||||
return;
|
||||
foreach (var itemDef in itemDefs)
|
||||
{
|
||||
var item=new Item.ItemResource();
|
||||
item.name = itemDef.label;
|
||||
item.description = itemDef.description;
|
||||
|
||||
item.icon = Managers.PackagesImageManager.Instance.GetSprite(itemDef.texture);
|
||||
}
|
||||
|
||||
foreach (var def in allDefs)
|
||||
{
|
||||
if (_items.ContainsKey(def.defName))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"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
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemResource = new Item.ItemResource(
|
||||
def.defName, // 传递 defName
|
||||
itemName,
|
||||
itemDescription,
|
||||
itemIcon,
|
||||
def.rarity,
|
||||
def.maxStack,
|
||||
def.ssEquippable
|
||||
);
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
Debug.Log($"ItemResourceManager: Initialized {_items.Count} items.");
|
||||
}
|
||||
|
||||
public ItemResource GetItem(string defName)
|
||||
public Item.ItemResource GetItem(string defName)
|
||||
{
|
||||
return items.GetValueOrDefault(defName,null);
|
||||
return _items.GetValueOrDefault(defName, null);
|
||||
}
|
||||
// <summary>
|
||||
/// 按物品名称查找物品
|
||||
/// </summary>
|
||||
/// <param name="itemName">要查找的物品名称</param>
|
||||
/// <returns>找到的物品对象,如果未找到则返回 null</returns>
|
||||
public ItemResource FindItemByName(string itemName)
|
||||
|
||||
// FindItemByName 和 FindAllItemsByName 保持不变,因为它们是按显示名称查找的
|
||||
public Item.ItemResource FindItemByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return items.Values.FirstOrDefault(item => item.name == itemName);
|
||||
if (string.IsNullOrEmpty(itemName)) return null;
|
||||
return _itemsByName.GetValueOrDefault(itemName)?.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Item.ItemResource> FindAllItemsByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return new List<Item.ItemResource>();
|
||||
return _itemsByName.GetValueOrDefault(itemName, new List<Item.ItemResource>());
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
_itemsByName.Clear();
|
||||
Debug.Log("ItemResourceManager: All item resources cleared.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,9 +1,114 @@
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Map
|
||||
{
|
||||
public class Dimension:MonoBehaviour
|
||||
/// <summary>
|
||||
/// 表示游戏中的一个维度或场景区域。
|
||||
/// 实体管理器将根据此维度来组织和管理实体。
|
||||
/// </summary>
|
||||
public class Dimension : MonoBehaviour
|
||||
{
|
||||
|
||||
[SerializeField] private bool defaultOpen = false;
|
||||
|
||||
[SerializeField] [Tooltip("此维度的唯一标识符。如果为空,将使用GameObject的名称。")]
|
||||
private string _dimensionId;
|
||||
|
||||
[SerializeField] public MapGenerator mapGenerator;
|
||||
|
||||
/// <summary>
|
||||
/// 获取此维度的唯一标识符。
|
||||
/// </summary>
|
||||
public string DimensionId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_dimensionId))
|
||||
{
|
||||
_dimensionId = gameObject.name; // 如果未设置,默认使用GameObject名称
|
||||
Debug.LogWarning(
|
||||
$"Dimension ID not explicitly set for {gameObject.name}. Using GameObject name as ID: {_dimensionId}",
|
||||
this);
|
||||
}
|
||||
|
||||
return _dimensionId;
|
||||
}
|
||||
// 内部设置器,允许在Awake中初始化或编辑器中赋值
|
||||
private set => _dimensionId = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 此维度下所有实体的根Transform。用于组织场景层级。
|
||||
/// </summary>
|
||||
public Transform DimensionRoot { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 1. 确保 DimensionId 已初始化,这会触发 DimensionId 属性的 getter 逻辑
|
||||
var id = DimensionId;
|
||||
// 2. 创建一个用于存放此维度下所有实体的根GameObject,方便管理
|
||||
var rootObj = new GameObject($"_Entities_{id}");
|
||||
rootObj.transform.SetParent(this.transform); // 将其作为Dimension对象的子对象
|
||||
DimensionRoot = rootObj.transform;
|
||||
// 3. 注册此维度到 Program
|
||||
if (Program.Instance != null) // 检查单例是否仍然存在
|
||||
{
|
||||
Program.Instance.RegisterDimension(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"[Dimension] Program.Instance is null during Dimension Awake. Cannot register dimension.", this);
|
||||
}
|
||||
|
||||
// 4. 注册此维度到 EntityManage
|
||||
if (EntityManage.Instance != null)
|
||||
{
|
||||
EntityManage.Instance.RegisterDimension(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"[Dimension] EntityManage.Instance is null during Dimension Awake. Cannot register dimension.",
|
||||
this);
|
||||
}
|
||||
|
||||
// 5. 处理 defaultOpen 逻辑,设置Program的焦点维度
|
||||
// 确保在自身注册到 Program 之后再设置焦点,这样 Program 内部才能找到它
|
||||
if (defaultOpen && Program.Instance != null)
|
||||
{
|
||||
Program.Instance.SetFocusedDimension(_dimensionId);
|
||||
// Program 现在会通过 SetFocusedDimension (string) 方法将 FocusedDimension 属性设置为这个 Dimension 实例。
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 当 Dimension 对象被销毁时(例如,场景卸载),从 Program 和 EntityManage 注销
|
||||
if (Program.Instance != null) // 检查单例是否仍然存在
|
||||
{
|
||||
Program.Instance.UnregisterDimension(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 在应用程序退出时,Program.Instance 可能已经为null,这通常是正常的,不打LogError
|
||||
Debug.Log(
|
||||
"[Dimension] Program.Instance is null during OnDestroy. Skipping unregister for Dimension: " +
|
||||
DimensionId, this);
|
||||
}
|
||||
|
||||
if (EntityManage.Instance != null) // 检查单例是否仍然存在
|
||||
{
|
||||
EntityManage.Instance.UnregisterDimension(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 同上,但在清理阶段,这个可能也已为null,只打Log
|
||||
Debug.Log(
|
||||
"[Dimension] EntityManage.Instance is null during OnDestroy. Skipping unregister for Dimension: " +
|
||||
DimensionId, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,134 @@
|
||||
using Logging;
|
||||
using System.Collections.Generic;
|
||||
using Logging; // Assuming 'Logging' is a standard or project-specific namespace.
|
||||
using Map; // Assuming 'Map' is a standard or project-specific namespace.
|
||||
using UnityEngine;
|
||||
using Utils; // Assuming 'Utils' is a standard or project-specific namespace.
|
||||
|
||||
public class Program : Utils.Singleton<Program>
|
||||
/// <summary>
|
||||
/// Program 类作为单例模式的核心管理器,负责维护和管理游戏或应用中的维度(Dimension)实例和焦点状态。
|
||||
/// 它提供了维度注册、注销、获取以及焦点维度设置的功能。
|
||||
/// </summary>
|
||||
public class Program : Singleton<Program>
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前聚焦的实体对象,可能为空。
|
||||
/// </summary>
|
||||
public Entity.Entity focusedEntity = null;
|
||||
public bool needLoad = true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前活跃焦点的维度唯一标识符,可能为空。
|
||||
/// </summary>
|
||||
public string focuseDimensionId = null;
|
||||
|
||||
/// <summary>
|
||||
/// 当前聚焦的维度对象实例。当 <see cref="focuseDimensionId"/> 不为空时,此属性指向对应的维度实例。
|
||||
/// </summary>
|
||||
public Dimension FocusedDimension { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 维护所有已注册的维度实例的字典,键是维度的唯一标识符 (ID)。
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, Dimension> _registeredDimensions = new Dictionary<string, Dimension>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已注册的维度。返回一个只读字典副本,防止外部直接修改。
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, Dimension> RegisteredDimensions => _registeredDimensions;
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个维度实例到 Program。
|
||||
/// </summary>
|
||||
/// <param name="dimension">要注册的维度实例。</param>
|
||||
public void RegisterDimension(Dimension dimension)
|
||||
{
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogError("[Program] Attempted to register a null Dimension.");
|
||||
return;
|
||||
}
|
||||
string id = dimension.DimensionId;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
Debug.LogError($"[Program] Attempted to register a Dimension with an empty or null ID: {dimension.name}");
|
||||
return;
|
||||
}
|
||||
if (_registeredDimensions.ContainsKey(id))
|
||||
{
|
||||
Debug.LogWarning($"[Program] Dimension with ID '{id}' is already registered. Skipping duplicate registration for {dimension.name}.");
|
||||
return;
|
||||
}
|
||||
_registeredDimensions.Add(id, dimension);
|
||||
Debug.Log($"[Program] Dimension '{id}' registered successfully.");
|
||||
|
||||
if (focuseDimensionId == id)
|
||||
{
|
||||
FocusedDimension = dimension;
|
||||
Debug.Log($"[Program] FocusedDimension updated to '{id}'.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 Program 注销一个维度实例。
|
||||
/// </summary>
|
||||
/// <param name="dimension">要注销的维度实例。</param>
|
||||
public void UnregisterDimension(Dimension dimension)
|
||||
{
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogWarning("[Program] Attempted to unregister a null Dimension. Skipping.");
|
||||
return;
|
||||
}
|
||||
string id = dimension.DimensionId;
|
||||
if (_registeredDimensions.Remove(id))
|
||||
{
|
||||
Debug.Log($"[Program] Dimension '{id}' unregistered successfully.");
|
||||
|
||||
if (focuseDimensionId == id)
|
||||
{
|
||||
focuseDimensionId = null;
|
||||
FocusedDimension = null;
|
||||
Debug.Log($"[Program] Focused dimension '{id}' was unregistered and has been cleared.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[Program] Attempted to unregister Dimension '{id}' which was not found in the registered list.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取一个已注册的维度。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <returns>对应的Dimension实例,如果未找到则返回null。</returns>
|
||||
public Dimension GetDimension(string dimensionId)
|
||||
{
|
||||
_registeredDimensions.TryGetValue(dimensionId, out Dimension dimension);
|
||||
return dimension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前聚焦的维度。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要设置为焦点的维度的唯一标识符。如果传入null或空字符串,将清除当前焦点维度。</param>
|
||||
public void SetFocusedDimension(string dimensionId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dimensionId))
|
||||
{
|
||||
focuseDimensionId = null;
|
||||
FocusedDimension = null;
|
||||
Debug.Log("[Program] Focused dimension cleared.");
|
||||
return;
|
||||
}
|
||||
if (_registeredDimensions.TryGetValue(dimensionId, out Dimension newFocusedDimension))
|
||||
{
|
||||
focuseDimensionId = dimensionId;
|
||||
FocusedDimension = newFocusedDimension;
|
||||
Debug.Log($"[Program] Focused dimension set to '{dimensionId}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[Program] Attempted to set focus to unregistered dimension ID: '{dimensionId}'. Focus not changed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
|
||||
|
||||
public class TestDefine : MonoBehaviour
|
||||
{
|
||||
public EntityManage entityManager;
|
||||
void Awake()
|
||||
{
|
||||
Managers.DefineManager.Instance.Init();
|
||||
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var chicken = Managers.DefineManager.Instance.FindDefine<CharacterDef>("testPawn");
|
||||
entityManager.GenerateEntity(chicken,Vector3.zero);
|
||||
entityManager.GenerateDefaultEntity(Vector3.down);
|
||||
Debug.Log(chicken);
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 147e91e6929d90a4fb877c0b0a6b608c
|
@ -154,7 +154,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GenerateEntity(entityDef, Utils.MousePosition.GetWorldPosition());
|
||||
Managers.EntityManage.Instance.GenerateEntity(Program.Instance.focuseDimensionId,entityDef, Utils.MousePosition.GetWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{entityDef.label}\n描述:{entityDef.description}";
|
||||
entityPlacementUI.snapEnabled = false;
|
||||
@ -165,7 +165,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GenerateBuildingEntity(def, Utils.MousePosition.GetSnappedWorldPosition());
|
||||
Managers.EntityManage.Instance.GenerateBuildingEntity(Program.Instance.focuseDimensionId,def, Utils.MousePosition.GetSnappedWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{def.label}\n描述:{def.description}";
|
||||
entityPlacementUI.snapEnabled = true;
|
||||
|
Reference in New Issue
Block a user