using System; using System.Collections.Generic; using UnityEngine; using XGame; using XGame.Framework; namespace FL.Battle { /// /// 目标选择组件 /// public class TargetSelector : ITargetSelector { struct FindTargetArgs { public EEntityType targetType; public ETargetFindType findType; /// /// 目标选择回调函数,同步查找目标时该方法无效 /// <选中的目标> /// public Action onSelected; } private Dictionary> _typeToTargetsMap; private Dictionary _idToTargetMap; private Dictionary _waitingMap; private List _tempLst = new List(); private long _playerEntityId; public void Register(ITarget target) { if (_typeToTargetsMap == null) { _typeToTargetsMap = new Dictionary>(); _idToTargetMap = new Dictionary(); } var type = target.Entity.EntityType; if (!_typeToTargetsMap.TryGetValue(type, out var targets)) { targets = new HashSet(); _typeToTargetsMap.Add(type, targets); } targets.Add(target); _idToTargetMap.Add(target.Entity.EntityId, target); if (target.Entity.EntityType == EEntityType.Player) { _playerEntityId = target.Entity.EntityId; } } public void Unregister(ITarget target) { if (_typeToTargetsMap == null) return; foreach (var targets in _typeToTargetsMap.Values) { if (targets.Contains(target)) { targets.Remove(target); break; } } _idToTargetMap.Remove(target.Entity.EntityId); _waitingMap?.Remove(target); if (target.Entity.EntityId == _playerEntityId) { _playerEntityId = 0; } } public ITarget GetTarget(long entityId) { ITarget target = null; _idToTargetMap?.TryGetValue(entityId, out target); return target; } public ITarget GetPlayer() { return GetTarget(_playerEntityId); } ITarget ITargetSelector.FindSync(ITarget finder, EEntityType targetType, ETargetFindType findType, Func customFilter) { var info = new FinderInfo() { uid = finder.Entity.EntityId, position = finder.Position, radius = finder.Entity.Attr.Radius }; return FindTarget(info, targetType, findType, customFilter); } ITarget ITargetSelector.FindSync(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter) { return FindTarget(finder, targetType, findType, customFilter); } bool ITargetSelector.FindTargets(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter, ref List targets) { if (!TryGetTargets(targetType, out var preTargets)) { Log.Debug($"没有可选目标 Finder:{finder.uid} TargetType:{targetType}"); return false; } if (targets == null) targets = new List(); foreach (var target in preTargets) { if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive)) continue; if (customFilter.SafeInvoke(target)) { continue; } if (IsInRange(finder, target, out var distance)) { targets.Add(target); } } return targets.Count > 0; } bool ITargetSelector.FindTargets(Rect rect, EEntityType targetType, ETargetFindType findType, Func customFilter, ref List targets) { if (!TryGetTargets(targetType, out var preTargets)) { Log.Debug($"没有可选目标 Rect:{rect} TargetType:{targetType}"); return false; } if (targets == null) targets = new List(); foreach (var target in preTargets) { if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive)) continue; if (customFilter.SafeInvoke(target)) { continue; } if (rect.Contains(target.Position)) { targets.Add(target); } } return targets.Count > 0; } void ITargetSelector.FindAsync(ITarget finder, EEntityType targetType, ETargetFindType findType, Action onSelected) { if (_waitingMap == null) { _waitingMap = new Dictionary(); } _waitingMap[finder] = new FindTargetArgs() { targetType = targetType, findType = findType, onSelected = onSelected }; } public Vector3 RandomPosition(EEntityType targetType, bool isCloseToTarget, Func customFilter = null) { if (isCloseToTarget) { var tmpTarget = RandomTarget(targetType, customFilter); if (tmpTarget != null) { // 目标坐标(-1, 1)内随机 var result = tmpTarget.Position; var radius = tmpTarget.Entity.Attr.Radius; result.x += UnityEngine.Random.Range(-radius, radius); result.y += UnityEngine.Random.Range(-radius, radius); //Log.Debug($"RandomPosition Target:{tmpTarget.Position} Result:{result}"); return result; } } var tarPosition = GetPlayer().Position; //玩家的坐标和本节战斗的位置重叠 if (targetType == EEntityType.Monster) {// 目标是怪物,怪物的战斗位置在玩家坐标上移8 // TODO 转成配置 tarPosition.y += 8; } tarPosition.x += UnityEngine.Random.Range(-4, 5); tarPosition.y += UnityEngine.Random.Range(-2, 2); return tarPosition; } public void OnUpdate(int times) { var count = _waitingMap?.Count ?? 0; if (count == 0) return; _tempLst.AddRange(_waitingMap.Keys); for (var i = 0; i < count; i++) { var finder = _tempLst[i]; if (!IsCanSelect(finder, false)) continue; if (!_waitingMap.TryGetValue(finder, out var args)) { continue; } var info = new FinderInfo() { uid = finder.Entity.EntityId, position = finder.Position, radius = finder.Entity.Attr.Radius }; var target = FindTarget(info, args.targetType, args.findType, null); if (target != null) { // 找到目标了才回调,并删除数据 args.onSelected.SafeInvoke(target); _waitingMap.Remove(finder); } } _tempLst.Clear(); } /// /// 查找目标 /// /// /// /// /// 过滤器,不能选择时返回true /// private ITarget FindTarget(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter) { if (!TryGetTargets(targetType, out var targets)) { Log.Debug($"没有可选目标 Finder:{finder.uid} TargetType:{targetType}"); return null; } ITarget result = null; var distanceMin = float.MaxValue; foreach (var target in targets) { if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive)) continue; if (customFilter.SafeInvoke(target)) { continue; } var isInRange = IsInRange(finder, target, out var distance); if (findType is ETargetFindType.Nearest or ETargetFindType.NearestAlive) { if (result == null || distanceMin > distance) { result = target; distanceMin = distance; } } else if (isInRange) { result = target; break; } } return result; } private ITarget RandomTarget(EEntityType targetType, Func customFilter) { if (!TryGetTargets(targetType, out var targets)) { Log.Debug($"没有可选目标 TargetType: {targetType}"); return null; } var list = ListPool.Acquire(); foreach (var target in targets) { var random = UnityEngine.Random.Range(0, 1000) % 2; if (random == 0) list.Insert(0, target); else list.Add(target); } ITarget result = null; foreach (var target in list) { if (!IsCanSelect(target, true)) continue; if (customFilter.SafeInvoke(target)) { continue; } result = target; break; } ListPool.Recycle(list); return result; } /// /// 目标是否可选择 /// /// /// True: 除了死亡状态,其他状态都可选 /// private bool IsCanSelect(ITarget target, bool isAlive) { if (target.IsDead) return false; if (target.Entity.IsState(EEntityState.Moving)) return isAlive; return isAlive || target.IsMoving == false; //return target.Entity.State switch //{ // EEntityState.Idle or EEntityState.Battle => isAlive || target.IsMoving == false, // EEntityState.Dead => false, // _ => isAlive, //}; } private bool TryGetTargets(EEntityType entityType, out HashSet targets) { if (_typeToTargetsMap != null && _typeToTargetsMap.TryGetValue(entityType, out targets)) { return true; } targets = null; return false; } private bool IsInRange(FinderInfo finder, ITarget target, out float distance) { distance = Vector3.Distance(target.Position, finder.position); distance -= (finder.radius + target.Entity.Attr.Radius); // 去掉双方的半径 return distance <= 0; } public void Clear() { _typeToTargetsMap?.Clear(); _idToTargetMap?.Clear(); _waitingMap?.Clear(); _tempLst?.Clear(); _playerEntityId = 0; } } }