123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- using FL.Battle.Actions;
- using FL.Battle.Skills;
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using XGame;
- using XGame.Database;
- using XGame.Framework;
- using XGame.Framework.Components;
- namespace FL.Battle.Components.AI
- {
- public interface IBulletAIContext
- {
- long UID { get; }
- long MasterId { get; }
- int TableId { get; }
- ITargetSelector Selector { get; }
- Vector3 Position { get; }
- VfxComponent Vfx { get; }
- MoveComponent Move { get; }
- }
- /// <summary>
- /// AI组件和具体的EntityView绑定,其他Component最好不要尝试获取AI组件
- /// </summary>
- public class BulletAI : Component<IBulletAIContext>
- {
- /// <summary>
- /// 已攻击过的目标id
- /// </summary>
- private HashSet<long> _lastTargetIds = new HashSet<long>();
- private SkillTable _skill;
- private SkillVfxsTable _skillVfxs;
- protected override void OnDispose()
- {
- _skill = null;
- _skillVfxs = null;
- _lastTargetIds.Clear();
- }
- public void Start(long targetId, Vector3 targetPosition, EEntityType targetType, int delay)
- {
- _skill = SkillTableRepo.Get(Context.TableId);
- _skillVfxs = SkillVfxsTableRepo.Get(Context.TableId);
- var bulletType = (ESkillVfxType)_skill.SkillVfxType;
- _lastTargetIds.Clear();
- // 子弹的移动方向
- var direction = Vector3.Normalize(targetPosition - Context.Position);
- var moveArgs = new MoveArgs()
- {
- target = targetPosition,
- speed = _skillVfxs.SkillVfxSpeed,
- timeScale = 1,
- delay = delay,
- moveType = (EMoveType)(_skillVfxs.SkillVfxMoveType - 1),
- onComplete = () =>
- {
- if (bulletType == ESkillVfxType.BulletMortar)
- {
- MortarExplode(targetPosition, targetType);
- End();
- return;
- }
- var target = Context.Selector.GetTarget(targetId);
- if (target == null || target.IsDead)
- {
- //Log.Debug($"子弹移动到目标位置, 目标已死亡. Target:{targetId} Bullet:{Context.TableId}");
- End();
- return;
- }
- PlayHitVfx(_skillVfxs.HitVfx, targetPosition);
- target.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
- switch (bulletType)
- {
- case ESkillVfxType.BulletSplit:
- // 分裂
- _lastTargetIds.Add(targetId);
- Split(direction, target.Entity.EntityType);
- End();
- break;
- case ESkillVfxType.BulletJump:
- // 弹跳
- _lastTargetIds.Add(targetId);
- Jump(target.Entity.EntityType, 1);
- break;
- default:
- End();
- break;
- }
- }
- };
- if (Enum.TryParse<DG.Tweening.Ease>(_skillVfxs.SkillVfxMoveEase, true, out var ease))
- {
- moveArgs.ease = ease;
- }
- Context.Move.MoveTo(moveArgs);
- }
- /// <summary>
- /// 受击特效
- /// </summary>
- /// <param name="hitVfx"></param>
- /// <param name="targetPosition"></param>
- private void PlayHitVfx(string hitVfx, Vector3 targetPosition)
- {
- var actPosition = ObjectPool.Acquire<PositionAction>();
- actPosition.position = targetPosition;
- var hitArgs = ObjectPool.Acquire<VfxArgs>();
- hitArgs.vfxName = hitVfx;
- hitArgs.duration = _skillVfxs.HitVfxTime;
- hitArgs.followType = EVfxFollowType.World;
- hitArgs.action = actPosition;
- Context.Vfx.Play(hitArgs);
- }
- /// <summary>
- /// 子弹分裂
- /// DamageArgs[数量,夹角(为零则就近原则找目标),缩放万分比]
- /// DamageArgs[1]大于零时,以子弹的初始移动方向为中心,按顺序(先左后右)累加角度获取分裂后子弹的方向
- /// 怪物在该方向上则命中,否则子弹按直线飞出屏幕
- /// </summary>
- /// <param name="direction">子弹的移动方向</param>
- /// <param name="targetType">目标类型</param>
- private void Split(Vector3 direction, EEntityType targetType)
- {
- var childCount = _skill.DamageArgs[0];
- var childAngle = _skill.DamageArgs[1];
- var childScale = _skill.DamageArgs[2];
- var from = Context.Position;
- var finderInfo = new FinderInfo()
- {
- uid = Context.UID,
- position = from,
- radius = 0,
- };
- for (var i = 0; i < childCount; i++)
- {
- var target = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.Nearest, (a) =>
- {
- if (_lastTargetIds.Contains(a.Entity.EntityId))
- {
- return true;
- }
- if (childAngle == 0)
- return false;
- var tarAngle = ((i + 1) / 2) * childAngle;
- var tempDir = Vector3.Normalize(a.Position - from);
- // angle:[0, 180]
- var angle = Vector3.Angle(direction, tempDir);
- //Log.Debug($"Bullet Split index:{i} direction:{direction} temp:{tempDir} angle:{angle} tar:{tarAngle}");
- if (Mathf.Abs(angle - tarAngle) < 20)
- { //角度范围-5到5
- if (i == 0)
- return false;
- //cross.z > 0 目标向量在方向向量的左侧
- var cross = Vector3.Cross(direction, tempDir);
- if (i % 2 == 0)
- { // i为偶数在右边
- return cross.z > 0;
- }
- return cross.z < 0;
- }
- return true;
- });
- if (target == null && childAngle == 0)
- {
- continue;
- }
- Vector3 to;
- if (target != null)
- {
- _lastTargetIds.Add(target.Entity.EntityId);
- to = target.Position;
- }
- else
- {
- if (i == 0)
- {
- to = from + direction * 12;
- }
- else
- {
- // i为偶数在右边 负数
- var sign = i % 2 == 0 ? -1 : 1;
- var tarAngle = ((i + 1) / 2) * childAngle;
- //var tempDir = direction.RotateLeft(sign * tarAngle);
- //var angle = Vector3.Angle(direction, tempDir);
- //Log.Debug($"Bullet Split index:{i} direction:{direction} temp:{tempDir} angle:{angle} tar:{tarAngle} {Vector3.Angle(direction, Vector3.up)} {Vector3.Angle(tempDir, Vector3.up)}");
- to = from + direction.RotateLeft(sign * tarAngle) * 12;
- }
- }
- //子弹移动
- var duration = Mathf.CeilToInt(Vector3.Distance(from, to) * 1000 / _skillVfxs.SkillVfxSpeed);
- if (duration == 0)
- { // 位置重合,直接结算伤害
- Log.Debug($"子弹分裂目标重合: from:{from} to:{to} speed:{_skillVfxs.SkillVfxSpeed}");
- target?.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
- continue;
- }
- var actMove = ObjectPool.Acquire<MoveLineAction>();
- actMove.args = new MoveActionArgs()
- {
- from = from,
- to = to,
- duration = duration
- };
- //子弹特效
- var bulletArgs = ObjectPool.Acquire<VfxArgs>();
- bulletArgs.vfxName = _skillVfxs.SkillVfx;
- bulletArgs.duration = duration;
- bulletArgs.followType = EVfxFollowType.World;
- bulletArgs.action = actMove;
- if (target != null)
- {
- var skill = _skill;
- var masterId = Context.MasterId;
- bulletArgs.onFinish = () =>
- {
- if (target == null || target.IsDead)
- {
- return;
- }
- target.Calculation.Damage(-skill.Damage, masterId, skill);
- };
- //受击特效位置
- var actPosition = ObjectPool.Acquire<PositionAction>();
- actPosition.position = to;
- //受击特效
- var hitArgs = ObjectPool.Acquire<VfxArgs>();
- hitArgs.vfxName = _skillVfxs.HitVfx;
- hitArgs.duration = _skillVfxs.HitVfxTime;
- hitArgs.followType = EVfxFollowType.World;
- hitArgs.action = actPosition;
- bulletArgs.next = hitArgs;
- }
- Context.Vfx.Play(bulletArgs);
- }
- }
- /// <summary>
- /// 弹跳
- /// DamageArgs[弹跳次数,伤害递减万分比]
- /// </summary>
- /// <param name="targetType">目标类型</param>
- /// <param name="jumpTimes">弹跳次数</param>
- private void Jump(EEntityType targetType, int jumpTimes)
- {
- var jumpLimit = _skill.DamageArgs[0];
- var hurtReduce = _skill.DamageArgs[1];
- var finderInfo = new FinderInfo()
- {
- uid = Context.UID,
- position = Context.Position,
- radius = 0,
- };
- var target = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.Nearest, (a) =>
- {
- if (_lastTargetIds.Contains(a.Entity.EntityId))
- {
- return true;
- }
- return false;
- });
- if (target == null)
- {
- End();
- return;
- }
- var targetId = target.Entity.EntityId;
- _lastTargetIds.Add(targetId);
- var tarPosition = target.Position;
- var moveArgs = new MoveArgs()
- {
- target = tarPosition,
- speed = _skillVfxs.SkillVfxSpeed * 0.8f,
- timeScale = 1,
- onComplete = () =>
- {
- target = Context.Selector.GetTarget(targetId);
- if (target == null || target.IsDead)
- {
- Log.Debug($"子弹移动到目标位置, 目标已死亡. Target:{targetId} Bullet:{Context.TableId}");
- End();
- return;
- }
- // 受击特效
- PlayHitVfx(_skillVfxs.HitVfx, tarPosition);
- // 计算受击伤害
- target.Calculation.Damage(-JumpHurt(_skill.Damage, jumpTimes, hurtReduce), Context.MasterId, _skill);
- jumpTimes++;
- if (jumpTimes > jumpLimit)
- {
- End();
- return;
- }
- Jump(targetType, jumpTimes);
- }
- };
- Context.Move.MoveTo(moveArgs);
- }
- private int JumpHurt(int baseVal, int jumpTimes, int hurtReduce)
- {
- float hurtVal = baseVal;
- for (var i = 0; i < jumpTimes; i++)
- {
- hurtVal *= (1 - hurtReduce * 0.0001f);
- }
- return Mathf.RoundToInt(hurtVal);
- }
- /// <summary>
- /// 迫击炮爆炸
- /// </summary>
- /// <param name="targetPosition"></param>
- /// <param name="targetType"></param>
- private void MortarExplode(Vector3 targetPosition, EEntityType targetType)
- {
- var hitRadius = _skill.Ranges[0];
- var explodeRadius = _skill.Ranges[1];
- var explodeHurt = _skill.DamageArgs[0];
- PlayHitVfx(_skillVfxs.HitVfx, targetPosition);
- // 查找受击目标
- var finderInfo = new FinderInfo()
- {
- uid = Context.UID,
- position = Context.Position,
- radius = hitRadius,
- };
- var hitTarget = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.NearestAlive);
- if (hitTarget != null)
- {
- hitTarget.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
- }
- // 查找爆炸受击目标
- finderInfo.radius = explodeRadius;
- var targets = new List<ITarget>();
- if (Context.Selector.FindTargets(finderInfo, targetType, ETargetFindType.InRangeAlive, null, ref targets))
- {
- foreach (var explodeTar in targets)
- {
- explodeTar.Calculation.Damage(-explodeHurt, Context.MasterId, _skill);
- }
- }
- }
- /// <summary>
- /// 结束
- /// </summary>
- private void End()
- {
- _skill = null;
- _skillVfxs = null;
- _lastTargetIds.Clear();
- EventSingle.Instance.Notify(EventDefine.GameMainMapRemoveSimpleEntity, Context.UID);
- }
- }
- }
|