123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using XGame;
- using XGame.Framework;
- namespace FL.Battle
- {
- /// <summary>
- /// 目标选择组件
- /// </summary>
- public class TargetSelector : ITargetSelector
- {
- struct FindTargetArgs
- {
- public EEntityType targetType;
- public ETargetFindType findType;
- /// <summary>
- /// 目标选择回调函数,同步查找目标时该方法无效
- /// <选中的目标>
- /// </summary>
- public Action<ITarget> onSelected;
- }
- private Dictionary<EEntityType, HashSet<ITarget>> _typeToTargetsMap;
- private Dictionary<long, ITarget> _idToTargetMap;
- private Dictionary<ITarget, FindTargetArgs> _waitingMap;
- private List<ITarget> _tempLst = new List<ITarget>();
- private long _playerEntityId;
- public void Register(ITarget target)
- {
- if (_typeToTargetsMap == null)
- {
- _typeToTargetsMap = new Dictionary<EEntityType, HashSet<ITarget>>();
- _idToTargetMap = new Dictionary<long, ITarget>();
- }
- var type = target.Entity.EntityType;
- if (!_typeToTargetsMap.TryGetValue(type, out var targets))
- {
- targets = new HashSet<ITarget>();
- _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<ITarget, bool> 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<ITarget, bool> customFilter)
- {
- return FindTarget(finder, targetType, findType, customFilter);
- }
- bool ITargetSelector.FindTargets(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func<ITarget, bool> customFilter, ref List<ITarget> targets)
- {
- if (!TryGetTargets(targetType, out var preTargets))
- {
- Log.Debug($"没有可选目标 Finder:{finder.uid} TargetType:{targetType}");
- return false;
- }
- if (targets == null)
- targets = new List<ITarget>();
- 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<ITarget, bool> customFilter, ref List<ITarget> targets)
- {
- if (!TryGetTargets(targetType, out var preTargets))
- {
- Log.Debug($"没有可选目标 Rect:{rect} TargetType:{targetType}");
- return false;
- }
- if (targets == null)
- targets = new List<ITarget>();
- 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<ITarget> onSelected)
- {
- if (_waitingMap == null)
- {
- _waitingMap = new Dictionary<ITarget, FindTargetArgs>();
- }
- _waitingMap[finder] = new FindTargetArgs()
- {
- targetType = targetType,
- findType = findType,
- onSelected = onSelected
- };
- }
- public Vector3 RandomPosition(EEntityType targetType, bool isCloseToTarget, Func<ITarget, bool> 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();
- }
- /// <summary>
- /// 查找目标
- /// </summary>
- /// <param name="finder"></param>
- /// <param name="targetType"></param>
- /// <param name="findType"></param>
- /// <param name="customFilter">过滤器,不能选择时返回true</param>
- /// <returns></returns>
- private ITarget FindTarget(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func<ITarget, bool> 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<ITarget, bool> customFilter)
- {
- if (!TryGetTargets(targetType, out var targets))
- {
- Log.Debug($"没有可选目标 TargetType: {targetType}");
- return null;
- }
- var list = ListPool.Acquire<ITarget>();
- 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;
- }
- /// <summary>
- /// 目标是否可选择
- /// </summary>
- /// <param name="target"></param>
- /// <param name="isAlive">True: 除了死亡状态,其他状态都可选</param>
- /// <returns></returns>
- 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<ITarget> 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;
- }
- }
- }
|