640 lines
21 KiB
C#
640 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using AOT.PostProcess.BlackArea;
|
|
using Gameplay.Building;
|
|
using Gameplay.Building.Data;
|
|
using Gameplay.Building.Impl;
|
|
using Gameplay.Bullet.NormalAttack;
|
|
using Gameplay.PostProcess.BlackArea;
|
|
using Gameplay.Summon;
|
|
using Gameplay.Unit;
|
|
using Gameplay.Unit.Data;
|
|
using Gameplay.Vehicle.Impl;
|
|
using PhxhSDK;
|
|
using UnityEngine;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace Gameplay.Bullet
|
|
{
|
|
using cfg.BuffCfg;
|
|
using cfg.BulletCfg;
|
|
using Character;
|
|
using Framework;
|
|
using Buff;
|
|
using Skill;
|
|
using Area;
|
|
using Common;
|
|
using cfg.SkillCfg;
|
|
|
|
public class BulletManager
|
|
{
|
|
|
|
private static BulletManager _instance;
|
|
|
|
public static BulletManager instance
|
|
{
|
|
get
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
DebugUtil.LogError("Bullet尚未初始化,请先初始化");
|
|
return null;
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
public static BulletManager CreateInstance()
|
|
{
|
|
if (_instance != null)
|
|
{
|
|
DebugUtil.LogError("请勿重复创建BulletManager");
|
|
return _instance;
|
|
}
|
|
_instance = new BulletManager();
|
|
return _instance;
|
|
}
|
|
|
|
public GameObject rootObj;
|
|
|
|
private readonly List<ViewBullet> _bullets;
|
|
private readonly List<ViewBullet> _readyBullets;
|
|
private readonly Dictionary<string, int> _loadCount;
|
|
|
|
private readonly List<NormalAttackBullet> _flyingNormalAttackBullets = new List<NormalAttackBullet>();
|
|
private Dictionary<int, Stack<NormalAttackBullet>> _normalAttackBulletPool = new Dictionary<int, Stack<NormalAttackBullet>>();
|
|
|
|
private BulletManager()
|
|
{
|
|
_bullets = new List<ViewBullet>();
|
|
_readyBullets = new List<ViewBullet>();
|
|
_loadCount = new Dictionary<string, int>();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
rootObj = new GameObject("BulletManager");
|
|
}
|
|
|
|
public void RecordLoadCount(string path)
|
|
{
|
|
if (_loadCount.ContainsKey(path))
|
|
{
|
|
_loadCount[path]++;
|
|
}
|
|
else
|
|
{
|
|
_loadCount.Add(path, 1);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
// 进行资源清理
|
|
foreach (var kv in _loadCount)
|
|
{
|
|
var path = kv.Key;
|
|
var count = kv.Value;
|
|
for (var i = 0; i < count; ++i)
|
|
{
|
|
AssetManager.Instance.Unload(path);
|
|
}
|
|
}
|
|
|
|
foreach (var kv in _normalAttackBulletPool)
|
|
{
|
|
var stack = kv.Value;
|
|
while (stack.Count > 0)
|
|
{
|
|
var bullet = stack.Pop();
|
|
bullet.Dispose();
|
|
}
|
|
}
|
|
_normalAttackBulletPool.Clear();
|
|
|
|
for (int i = 0; i < _flyingNormalAttackBullets.Count; i++)
|
|
{
|
|
var bullet = _flyingNormalAttackBullets[i];
|
|
bullet.Dispose();
|
|
}
|
|
|
|
_bullets.Clear();
|
|
_readyBullets.Clear();
|
|
_loadCount.Clear();
|
|
|
|
rootObj.Destroy();
|
|
_instance = null;
|
|
}
|
|
|
|
public void LogicUpdate(float dt)
|
|
{
|
|
for (var i = 0; i < _readyBullets.Count; ++i)
|
|
{
|
|
var bullet = _readyBullets[i];
|
|
bullet.Init();
|
|
}
|
|
|
|
_bullets.AddRange(_readyBullets);
|
|
_readyBullets.Clear();
|
|
|
|
for (var i = 0; i < _bullets.Count; ++i)
|
|
{
|
|
var bullet = _bullets[i];
|
|
|
|
// 技能特殊暂停
|
|
if (SkillManager.instance.needSkillPaused
|
|
&& bullet.sender != SkillManager.instance.inSkillingUnit)
|
|
{
|
|
continue;
|
|
}
|
|
bullet.LogicUpdate(dt);
|
|
|
|
if (bullet.needRemove)
|
|
{
|
|
_bullets.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < _flyingNormalAttackBullets.Count; ++i)
|
|
{
|
|
var bullet = _flyingNormalAttackBullets[i];
|
|
bullet.LogicUpdate(dt);
|
|
if (bullet.isFinished)
|
|
{
|
|
_ReturnNormalAttackBullet(bullet);
|
|
_flyingNormalAttackBullets.RemoveAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void _ReturnNormalAttackBullet(NormalAttackBullet bullet)
|
|
{
|
|
bullet.Hide();
|
|
if (!_normalAttackBulletPool.TryGetValue(bullet.effectId, out var stack))
|
|
{
|
|
stack = new Stack<NormalAttackBullet>();
|
|
_normalAttackBulletPool.Add(bullet.effectId, stack);
|
|
}
|
|
stack.Push(bullet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 普通攻击走特殊处理
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="receiver"></param>
|
|
public void DoNormalShoot(GameUnit sender,
|
|
GameUnit receiver, bool noMiss)
|
|
{
|
|
// _ = AudioManager.Instance.PlayAudio(1001, sender.transData.pos);
|
|
// 理论上可以走立马销毁的子弹逻辑,但是这里处理成直接addbuff,减少性能消耗
|
|
|
|
// // 造成伤害
|
|
// var normalDamageBuff = BuffManager.instance.CreateBuffById(sender, InnerBuffID.NORMAL_DAMAGE);
|
|
// BuffManager.instance.AddBuff(receiver, normalDamageBuff);
|
|
//
|
|
// // 打击士气
|
|
// var moraleDamageBuff = BuffManager.instance.CreateBuffById(sender, InnerBuffID.MORALE_DAMAGE);
|
|
// BuffManager.instance.AddBuff(receiver, moraleDamageBuff);
|
|
|
|
NormalAttackBullet bullet = null;
|
|
switch (sender.gameunitType)
|
|
{
|
|
case EGameUnitType.Character:
|
|
bullet = _HandleCharacterNormalAttack(sender, receiver, noMiss);
|
|
break;
|
|
case EGameUnitType.Vehicle:
|
|
bullet = _HandleVehicleNormalAttack(sender, receiver, noMiss);
|
|
break;
|
|
case EGameUnitType.Building:
|
|
bullet = _HandleBuildNormalAttack(sender, receiver, noMiss);
|
|
break;
|
|
default:
|
|
DebugUtil.LogError("暂未支持的GameUnitType:{0}", sender.gameunitType);
|
|
break;
|
|
}
|
|
|
|
if (bullet == null)
|
|
{
|
|
DebugUtil.LogError("普通攻击子弹创建失败");
|
|
return;
|
|
}
|
|
|
|
_TriggerNormalAttack(sender);
|
|
|
|
bullet.Show();
|
|
_flyingNormalAttackBullets.Add(bullet);
|
|
}
|
|
|
|
private void _TriggerNormalAttack(GameUnit sender)
|
|
{
|
|
var skillManager = SkillManager.instance.GetUnitSkillManager(sender);
|
|
if (skillManager != null)
|
|
{
|
|
skillManager.TriggerNormalAttack();
|
|
}
|
|
|
|
var buffManager = sender.buffManager;
|
|
if (buffManager != null)
|
|
{
|
|
buffManager.TriggerNormalAttack();
|
|
}
|
|
}
|
|
|
|
private NormalAttackBullet _HandleBuildNormalAttack(GameUnit unit, GameUnit targetUnit, bool noMiss)
|
|
{
|
|
var unitBuild = unit as UnitBuild;
|
|
if (unitBuild == null)
|
|
{
|
|
DebugUtil.LogError("非UnitBuild类型,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
switch (unitBuild.buildType)
|
|
{
|
|
case EFightBuildType.Pillbox:
|
|
return _HandleBuildPillBoxNormalAttack(unit, targetUnit, noMiss);
|
|
default:
|
|
DebugUtil.LogError("暂未支持的Build类型:{0}", unitBuild.buildType);
|
|
break;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private NormalAttackBullet _HandleVehicleNormalAttack(GameUnit unit, GameUnit targetUnit, bool noMiss)
|
|
{
|
|
var unitVehicle = unit as NormalVehicle;
|
|
if (unitVehicle == null)
|
|
{
|
|
DebugUtil.LogError("非Vehicle类型,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
var weaponConfig = unitVehicle.battery.rawConfig;
|
|
if (weaponConfig == null)
|
|
{
|
|
DebugUtil.LogError("载具炮台配置为空,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
|
|
return _CommonCreateBullet(unit,
|
|
targetUnit,
|
|
noMiss,
|
|
weaponConfig.FlyEffectId,
|
|
weaponConfig.BulletFlySpeed,
|
|
weaponConfig.MissHitGroundEffectId,
|
|
weaponConfig.CriticalHitEffectId,
|
|
weaponConfig.HitEffectId);
|
|
}
|
|
|
|
private NormalAttackBullet _HandleBuildPillBoxNormalAttack(GameUnit unit,
|
|
GameUnit targetUnit,
|
|
bool noMiss)
|
|
{
|
|
var unitBuildPillBox = unit as BuildPillBox;
|
|
if (unitBuildPillBox == null)
|
|
{
|
|
DebugUtil.LogError("非BuildPillBox类型,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
|
|
var weaponConfig = unitBuildPillBox.weaponConfig;
|
|
if (weaponConfig == null)
|
|
{
|
|
DebugUtil.LogError("武器配置为空,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
|
|
return _CommonCreateBullet(unit,
|
|
targetUnit,
|
|
noMiss,
|
|
weaponConfig.FlyEffectId,
|
|
weaponConfig.BulletFlySpeed,
|
|
weaponConfig.MissHitGroundEffectId,
|
|
weaponConfig.CriticalHitEffectId,
|
|
weaponConfig.HitEffectId);
|
|
}
|
|
|
|
private NormalAttackBullet _HandleCharacterNormalAttack(GameUnit unit, GameUnit targetUnit, bool noMiss)
|
|
{
|
|
var unitCharacter = unit as Character;
|
|
if (unitCharacter == null)
|
|
{
|
|
DebugUtil.LogError("非Character类型,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
var weaponConfig = unitCharacter.weapon.rawConfig;
|
|
if (weaponConfig == null)
|
|
{
|
|
DebugUtil.LogError("武器配置为空,无法进行普通攻击");
|
|
return null;
|
|
}
|
|
|
|
return _CommonCreateBullet(unit,
|
|
targetUnit,
|
|
noMiss,
|
|
weaponConfig.FlyEffectId,
|
|
weaponConfig.BulletFlySpeed,
|
|
weaponConfig.MissHitGroundEffectId,
|
|
weaponConfig.CriticalHitEffectId,
|
|
weaponConfig.HitEffectId);
|
|
}
|
|
|
|
private NormalAttackBullet _CommonCreateBullet(GameUnit sender,
|
|
GameUnit targetUnit,
|
|
bool noMiss,
|
|
int flyEffectId,
|
|
float flySpeed,
|
|
int hitGroundEffectId,
|
|
int criticalHitEffectId,
|
|
int normalHitEffectId)
|
|
{
|
|
var bulletEffectId = flyEffectId;
|
|
var bullet = _GetOrCreateNormalAttackBullet(bulletEffectId);
|
|
var emitTrans = NormalAttackHelper.GetEmitTrans(sender);
|
|
bullet.SetBaseInfo(sender, flySpeed, emitTrans);
|
|
|
|
var distanceToTarget = AreaManager.instance.GetDistance(sender.transData.cellIndex, targetUnit.transData.cellIndex);
|
|
var hitRate = sender.fightData.GetAttackRate(distanceToTarget);
|
|
var randomValue = UnityEngine.Random.Range(0f, 1f);
|
|
if (randomValue > hitRate && !noMiss)
|
|
{
|
|
// miss
|
|
var missPos = NormalAttackHelper.GetMissHitPos(targetUnit);
|
|
bullet.SetMiss(missPos, hitGroundEffectId);
|
|
}
|
|
else
|
|
{
|
|
var criticalRate = sender.fightData.criticalRate;
|
|
var randomCritical = UnityEngine.Random.Range(0f, 1f);
|
|
if (randomCritical < criticalRate)
|
|
{
|
|
// 暴击
|
|
var hitTrans = NormalAttackHelper.GetCriticalHitTrans(targetUnit);
|
|
bullet.SetTarget(targetUnit, hitTrans, criticalHitEffectId, EBulletHitType.Critical);
|
|
}
|
|
else
|
|
{
|
|
// 普通攻击
|
|
var hitTrans = NormalAttackHelper.GetCriticalHitTrans(targetUnit);
|
|
bullet.SetTarget(targetUnit, hitTrans, normalHitEffectId, EBulletHitType.Normal);
|
|
}
|
|
}
|
|
return bullet;
|
|
}
|
|
|
|
private NormalAttackBullet _GetOrCreateNormalAttackBullet(int effectId)
|
|
{
|
|
if (_normalAttackBulletPool.TryGetValue(effectId, out var stack))
|
|
{
|
|
if (stack.Count > 0)
|
|
{
|
|
return stack.Pop();
|
|
}
|
|
}
|
|
var bullet = new NormalAttackBullet(effectId);
|
|
bullet.CreateView(rootObj.transform);
|
|
return bullet;
|
|
}
|
|
|
|
public ViewBullet EmitBullet(DataBullet bulletConfig,
|
|
GameUnit targetUnit,
|
|
GameUnit sender)
|
|
{
|
|
ViewBullet result = null;
|
|
switch (bulletConfig.BulletType)
|
|
{
|
|
case EBulletType.DIRECT_DAMAGE:
|
|
// 直接造成伤害
|
|
AddBuffs2Unit(targetUnit, sender, bulletConfig);
|
|
break;
|
|
case EBulletType.POS_LOCK:
|
|
// 锁地板
|
|
result = _EmitLockPosBullet(targetUnit.transData.cellIndex, sender, bulletConfig);
|
|
break;
|
|
case EBulletType.UNIT_LOCK:
|
|
// 锁单位
|
|
result = _EmitLockUnitBullet(targetUnit, sender, bulletConfig);
|
|
break;
|
|
default:
|
|
DebugUtil.LogError("暂未实现的子弹类型:{0}", bulletConfig.BulletType);
|
|
break;
|
|
}
|
|
|
|
if (result != null)
|
|
{
|
|
_readyBullets.Add(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public ViewBullet EmitBullet(int bulletId,
|
|
GameUnit targetUnit,
|
|
GameUnit sender)
|
|
{
|
|
var bulletConfig = GetConfigById(bulletId);
|
|
if (bulletConfig == null)
|
|
{
|
|
DebugUtil.LogError("不存在的子弹配置:{0}", bulletId);
|
|
return null;
|
|
}
|
|
|
|
return EmitBullet(bulletConfig, targetUnit, sender);
|
|
}
|
|
|
|
public ViewBullet EmitBulletFromSummon(int bulletId, int cellIndex, BaseSummon summon)
|
|
{
|
|
return EmitBullet(bulletId, cellIndex, summon.creator);
|
|
}
|
|
|
|
public ViewBullet EmitBulletFromSummon(int bulletId, GameUnit target, BaseSummon summon)
|
|
{
|
|
return EmitBullet(bulletId, target, summon.creator);
|
|
}
|
|
|
|
public ViewBullet EmitBullet(int bulletId,
|
|
int cellIndex,
|
|
GameUnit sender)
|
|
{
|
|
var bulletConfig = GetConfigById(bulletId);
|
|
if (bulletConfig == null)
|
|
{
|
|
DebugUtil.LogError("不存在的子弹配置:{0}", bulletId);
|
|
return null;
|
|
}
|
|
|
|
ViewBullet result = null;
|
|
switch (bulletConfig.BulletType)
|
|
{
|
|
case EBulletType.DIRECT_DAMAGE:
|
|
// 直接造成伤害
|
|
AddBuffs2CellIndex(cellIndex, sender, bulletConfig);
|
|
break;
|
|
case EBulletType.POS_LOCK:
|
|
// 锁地板
|
|
result = _EmitLockPosBullet(cellIndex, sender, bulletConfig);
|
|
break;
|
|
case EBulletType.UNIT_LOCK:
|
|
// 锁单位
|
|
result = _EmitLockUnitBullet(cellIndex, sender, bulletConfig);
|
|
break;
|
|
default:
|
|
DebugUtil.LogError("暂未实现的子弹类型:{0}", bulletConfig.BulletType);
|
|
break;
|
|
}
|
|
|
|
if (result != null)
|
|
{
|
|
_readyBullets.Add(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private ViewBullet _EmitLockUnitBullet(GameUnit targetUnit,
|
|
GameUnit sender,
|
|
DataBullet bulletConfig)
|
|
{
|
|
var bullet = new ViewBullet(bulletConfig, sender);
|
|
if (!BulletManagerHelper.CheckBulletFilter(sender, targetUnit, bulletConfig))
|
|
{
|
|
bullet.LockPos(targetUnit.transData.cellIndex);
|
|
return bullet;
|
|
}
|
|
bullet.LockUnit(targetUnit);
|
|
return bullet;
|
|
}
|
|
|
|
private ViewBullet _EmitLockUnitBullet(int targetCellIndex,
|
|
GameUnit sender,
|
|
DataBullet bulletConfig)
|
|
{
|
|
var bullet = new ViewBullet(bulletConfig, sender);
|
|
var units = AreaManager.instance.GetUnitsByCellIndex(targetCellIndex);
|
|
var isLocked = false;
|
|
for (var i = 0; i < units.Count; ++i)
|
|
{
|
|
var unit = units[i];
|
|
if (!BulletManagerHelper.CheckBulletFilter(sender, unit, bulletConfig))
|
|
{
|
|
continue;
|
|
}
|
|
bullet.LockUnit(unit);
|
|
isLocked = true;
|
|
break;
|
|
}
|
|
if (!isLocked)
|
|
{
|
|
// 如果没有目标,锁地板
|
|
DebugUtil.LogError("目标错误.保护处理,锁定当前地板");
|
|
bullet.LockPos(targetCellIndex);
|
|
}
|
|
return bullet;
|
|
}
|
|
|
|
public void PreSelectUnits(int cellIndex,
|
|
int bulletId, GameUnit sender,
|
|
List<GameUnit> selectUnits)
|
|
{
|
|
var bulletConfig = GetConfigById(bulletId);
|
|
if (bulletConfig == null) return;
|
|
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
|
|
for (var i = 0; i < units.Count; ++i)
|
|
{
|
|
var unit = units[i];
|
|
if (BulletManagerHelper.CheckBulletFilter(sender, unit, bulletConfig))
|
|
{
|
|
selectUnits.Add(unit);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ViewBullet _EmitLockPosBullet(int targetCellIndex,
|
|
GameUnit creator,
|
|
DataBullet bulletConfig)
|
|
{
|
|
var bullet = new ViewBullet(bulletConfig, creator);
|
|
bullet.LockPos(targetCellIndex);
|
|
return bullet;
|
|
}
|
|
|
|
public void AddBuffs2Unit(GameUnit unit,
|
|
GameUnit creator,
|
|
DataBullet bulletConfig)
|
|
{
|
|
var sender = creator;
|
|
if (!BulletManagerHelper.CheckBulletFilter(sender, unit, bulletConfig))
|
|
{
|
|
return;
|
|
}
|
|
_CreateNoLineBullet(bulletConfig, creator, unit);
|
|
|
|
if (bulletConfig.LightDuring > 0)
|
|
{
|
|
var cellIndex = unit.transData.cellIndex;
|
|
BlackAreaManager.instance.LightUpCell(cellIndex, bulletConfig.LightDuring);
|
|
}
|
|
}
|
|
|
|
public void AddBuffs2CellIndex(int targetCellIndex,
|
|
GameUnit creator,
|
|
DataBullet bulletConfig)
|
|
{
|
|
var areaId = bulletConfig.AreaId;
|
|
if (areaId == -1)
|
|
{
|
|
// 做全局处理
|
|
var units = GameUnitManager.instance.infightUnits.unitList;
|
|
for (var i = 0; i < units.Count; ++i)
|
|
{
|
|
var unit = units[i];
|
|
AddBuffs2Unit(unit, creator, bulletConfig);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var cellIndexs = AreaManager.instance.GetCellIndexs(targetCellIndex, areaId);
|
|
for (var i = 0; i < cellIndexs.Count; ++i)
|
|
{
|
|
var cellIndex = cellIndexs[i];
|
|
// 根据filterType 和 cellIndex进行处理
|
|
var units = AreaManager.instance.GetUnitsByCellIndex(cellIndex);
|
|
for (var j = 0; j < units.Count; ++j)
|
|
{
|
|
var unit = units[j];
|
|
AddBuffs2Unit(unit, creator, bulletConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void _CreateNoLineBullet(DataBullet bulletConfig,
|
|
GameUnit creator,
|
|
GameUnit beHitter)
|
|
{
|
|
var buffIdList = bulletConfig.BuffList;
|
|
for (var i = 0; i < buffIdList.Count; ++i)
|
|
{
|
|
var buffId = buffIdList[i];
|
|
var addBuff = BuffManager.instance.CreateBuffById(creator, buffId);
|
|
if (addBuff == null)
|
|
{
|
|
continue;
|
|
}
|
|
BuffManager.instance.AddBuff(beHitter, addBuff);
|
|
}
|
|
}
|
|
|
|
public DataBullet GetConfigById(int id)
|
|
{
|
|
return TableManager.Instance.Tables.BulletConfig.Get(id);
|
|
}
|
|
|
|
}
|
|
}
|