整理武器配置和重构
This commit is contained in:
@@ -18,12 +18,55 @@ namespace SepCore.Definition
|
|||||||
{
|
{
|
||||||
public const string DefaultLayerName = "Default";
|
public const string DefaultLayerName = "Default";
|
||||||
public static readonly int DefaultLayerId = LayerMask.NameToLayer(DefaultLayerName);
|
public static readonly int DefaultLayerId = LayerMask.NameToLayer(DefaultLayerName);
|
||||||
|
public static readonly LayerMask DefaultLayerMask = 1 << DefaultLayerId;
|
||||||
|
|
||||||
|
public const string TransparentFXLayerName = "TransparentFX";
|
||||||
|
public static readonly int TransparentLayerId = LayerMask.NameToLayer(TransparentFXLayerName);
|
||||||
|
public static readonly LayerMask TransparentLayerMask = 1 << TransparentLayerId;
|
||||||
|
|
||||||
|
public const string IgnoreRaycastLayerName = "Ignore Raycast";
|
||||||
|
public static readonly int IgnoreLayerId = LayerMask.NameToLayer(IgnoreRaycastLayerName);
|
||||||
|
public static readonly LayerMask IgnoreLayerMask = 1 << IgnoreLayerId;
|
||||||
|
|
||||||
|
public const string WaterLayerName = "Water";
|
||||||
|
public static readonly int WaterLayerId = LayerMask.NameToLayer(WaterLayerName);
|
||||||
|
public static readonly LayerMask WaterLayerMask = 1 << WaterLayerId;
|
||||||
|
|
||||||
public const string UILayerName = "UI";
|
public const string UILayerName = "UI";
|
||||||
public static readonly int UILayerId = LayerMask.NameToLayer(UILayerName);
|
public static readonly int UILayerId = LayerMask.NameToLayer(UILayerName);
|
||||||
|
public static readonly LayerMask UILayerMask = 1 << UILayerId;
|
||||||
|
|
||||||
public const string TargetableObjectLayerName = "Targetable Object";
|
public const string TargetableObjectLayerName = "Targetable Object";
|
||||||
public static readonly int TargetableObjectLayerId = LayerMask.NameToLayer(TargetableObjectLayerName);
|
public static readonly int TargetableObjectLayerId = LayerMask.NameToLayer(TargetableObjectLayerName);
|
||||||
|
public static readonly LayerMask TargetableObjectLayerMask = 1 << TargetableObjectLayerId;
|
||||||
|
|
||||||
|
public const string PlayerLayerName = "Player";
|
||||||
|
public static readonly int PlayerLayerId = LayerMask.NameToLayer(PlayerLayerName);
|
||||||
|
public static readonly LayerMask PlayerLayerMask = 1 << PlayerLayerId;
|
||||||
|
|
||||||
|
public const string EnemyLayerName = "Enemy";
|
||||||
|
public static readonly int EnemyLayerId = LayerMask.NameToLayer(EnemyLayerName);
|
||||||
|
public static readonly LayerMask EnemyLayerMask = 1 << EnemyLayerId;
|
||||||
|
|
||||||
|
public const string PlayerBulletLayerName = "PlayerBullet";
|
||||||
|
public static readonly int PlayerBulletLayerId = LayerMask.NameToLayer(PlayerBulletLayerName);
|
||||||
|
public static readonly LayerMask PlayerBulletLayerMask = 1 << PlayerBulletLayerId;
|
||||||
|
|
||||||
|
public const string EnemyBulletLayerName = "EnemyBullet";
|
||||||
|
public static readonly int EnemyBulletLayerId = LayerMask.NameToLayer(EnemyBulletLayerName);
|
||||||
|
public static readonly LayerMask EnemyBulletLayerMask = 1 << EnemyBulletLayerId;
|
||||||
|
|
||||||
|
public const string PlayerWeaponLayerName = "PlayerWeapon";
|
||||||
|
public static readonly int PlayerWeaponLayerId = LayerMask.NameToLayer(PlayerWeaponLayerName);
|
||||||
|
public static readonly LayerMask PlayerWeaponLayerMask = 1 << PlayerWeaponLayerId;
|
||||||
|
|
||||||
|
public const string EnemyWeaponLayerName = "EnemyWeapon";
|
||||||
|
public static readonly int EnemyWeaponLayerId = LayerMask.NameToLayer(EnemyWeaponLayerName);
|
||||||
|
public static readonly LayerMask EnemyWeaponLayerMask = 1 << EnemyWeaponLayerId;
|
||||||
|
|
||||||
|
public const string DropLayerName = "Drop";
|
||||||
|
public static readonly int DropLayerId = LayerMask.NameToLayer(DropLayerName);
|
||||||
|
public static readonly LayerMask DropLayerMask = 1 << DropLayerId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,66 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class WeaponData : AccessoryObjectData
|
public class WeaponData : AccessoryObjectData
|
||||||
{
|
{
|
||||||
private readonly WeaponConfig _weaponConfig;
|
public WeaponConfig Config { get; }
|
||||||
|
|
||||||
public WeaponData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
public WeaponData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
||||||
: base(weaponConfig.Prefab, entityId, ownerId, ownerCamp)
|
: base(weaponConfig.Prefab, entityId, ownerId, ownerCamp)
|
||||||
{
|
{
|
||||||
_weaponConfig = weaponConfig;
|
Config = weaponConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeaponConfig Config => _weaponConfig;
|
public WeaponType WeaponType => Config.WeaponType;
|
||||||
|
|
||||||
public WeaponType WeaponType => _weaponConfig.WeaponType;
|
public string LogicType => Config.LogicType;
|
||||||
|
|
||||||
public string LogicType => _weaponConfig.LogicType;
|
public int Attack => Config.Attack;
|
||||||
|
|
||||||
public int Attack => _weaponConfig.Attack;
|
public string Title => Config.WeaponType.ToString();
|
||||||
|
|
||||||
public string Title => _weaponConfig.WeaponType.ToString();
|
public string Sprite => Config.Sprite;
|
||||||
|
|
||||||
public string IconAssetName => _weaponConfig.Sprite;
|
public Rarity Rarity => Config.Rarity;
|
||||||
|
|
||||||
public Rarity Rarity => _weaponConfig.Rarity;
|
public int Price => Config.Price;
|
||||||
|
|
||||||
public int Price => _weaponConfig.Price;
|
public float Cooldown => Config.Cooldown / 1000f;
|
||||||
|
|
||||||
public float Cooldown => _weaponConfig.Cooldown / 1000f;
|
public float AttackRange => Config.AttackRange / 1000f;
|
||||||
|
|
||||||
public float AttackRange => _weaponConfig.AttackRange / 1000f;
|
public int AttackSoundId => Config.AttackSoundId;
|
||||||
|
|
||||||
public int AttackSoundId => _weaponConfig.AttackSoundId;
|
public IReadOnlyDictionary<ParamType, int> Parameters => Config.Parameters;
|
||||||
|
|
||||||
public IReadOnlyDictionary<ParamType, int> Parameters => _weaponConfig.Parameters;
|
public IReadOnlyList<modifier> Modifiers => Config.Modifiers;
|
||||||
|
|
||||||
public IReadOnlyList<modifier> Modifiers => _weaponConfig.Modifiers;
|
public int GetParam(ParamType paramType)
|
||||||
|
|
||||||
public int GetParam(ParamType paramType, int defaultValue = 0)
|
|
||||||
{
|
{
|
||||||
return _weaponConfig.Parameters.TryGetValue(paramType, out int value) ? value : defaultValue;
|
if (Config.Parameters.TryGetValue(paramType, out int value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetParamFloat(ParamType paramType, float defaultValue = 0f)
|
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetParamFloat(ParamType paramType)
|
||||||
{
|
{
|
||||||
if (_weaponConfig.Parameters.TryGetValue(paramType, out int val))
|
if (Config.Parameters.TryGetValue(paramType, out int val))
|
||||||
{
|
{
|
||||||
return paramType == ParamType.SectorAngle ? val : val / 1000f;
|
return paramType == ParamType.SectorAngle ? val : val / 1000f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
|
||||||
|
return 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponHandgunParamsData
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponHandgunData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponHandgunParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponHandgunData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponHandgunParamsData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponKnifeParamsData
|
|
||||||
{
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponKnifeData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponKnifeParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponKnifeData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponKnifeParamsData
|
|
||||||
{
|
|
||||||
HitRadius = GetParamFloat(ParamType.HitRadius)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponLanceParamsData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 横向半宽,表示前戳矩形判定的一半宽度。
|
|
||||||
/// </summary>
|
|
||||||
public float HitHalfWidth { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 旧字段兼容,未配置 HitHalfWidth 时回退使用。
|
|
||||||
/// </summary>
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 前戳判定盒体的总高度。
|
|
||||||
/// </summary>
|
|
||||||
public float HitHeight { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判定盒体中心相对战斗平面的高度偏移。
|
|
||||||
/// </summary>
|
|
||||||
public float HitCenterYOffset { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 前刺距离,同时驱动武器位移和命中长度。
|
|
||||||
/// </summary>
|
|
||||||
public float PierceLength { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 旧字段兼容,未配置 PierceLength 时回退使用。
|
|
||||||
/// </summary>
|
|
||||||
public float ThrustDistance { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判定起点相对武器当前位置的前置偏移。
|
|
||||||
/// </summary>
|
|
||||||
public float ForwardOffset { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 追踪目标时的转向速度。
|
|
||||||
/// </summary>
|
|
||||||
public float RotateSpeed { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向前突刺阶段耗时。
|
|
||||||
/// </summary>
|
|
||||||
public float AttackDuration { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收枪返回阶段耗时。
|
|
||||||
/// </summary>
|
|
||||||
public float ReturnDuration { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class WeaponLanceData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponLanceParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponLanceData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponLanceParamsData
|
|
||||||
{
|
|
||||||
HitHalfWidth = GetParamFloat(ParamType.HitHalfWidth),
|
|
||||||
PierceLength = GetParamFloat(ParamType.PierceLength),
|
|
||||||
HitHeight = GetParamFloat(ParamType.HitHeight)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponLightningParamsData
|
|
||||||
{
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
public float HoverHeight { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponLightningData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponLightningParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponLightningData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponLightningParamsData
|
|
||||||
{
|
|
||||||
HitRadius = GetParamFloat(ParamType.HitRadius),
|
|
||||||
HoverHeight = 0f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 25b0006918fd46959c7f6b8ec1bbc8ab
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponSlashParamsData
|
|
||||||
{
|
|
||||||
public float SectorAngle { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponSlashData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponSlashParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponSlashData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponSlashParamsData
|
|
||||||
{
|
|
||||||
SectorAngle = GetParamFloat(ParamType.SectorAngle)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4aa6473525dc4c62aeee02ff562ec453
|
|
||||||
timeCreated: 1771484661
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 武器状态
|
||||||
|
/// </summary>
|
||||||
|
public enum WeaponStateType : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击
|
||||||
|
/// </summary>
|
||||||
|
Attack = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 待机
|
||||||
|
/// </summary>
|
||||||
|
Idle = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索
|
||||||
|
/// </summary>
|
||||||
|
Seek = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// 瞄准
|
||||||
|
/// </summary>
|
||||||
|
Aim = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4d1a821e8ee1a9a4b912b70b0a1616eb
|
guid: 1065e2570786f184c9553db06202a6a7
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -41,7 +41,7 @@ namespace SepCore.UI
|
|||||||
if (ItemType == ItemType.Weapon)
|
if (ItemType == ItemType.Weapon)
|
||||||
{
|
{
|
||||||
var data = rawData.WeaponData;
|
var data = rawData.WeaponData;
|
||||||
IconAssetName = data.IconAssetName;
|
IconAssetName = data.Sprite;
|
||||||
Title = data.Title;
|
Title = data.Title;
|
||||||
Description = ItemDescUtility.CreateWeaponDescription(data);
|
Description = ItemDescUtility.CreateWeaponDescription(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
return new DisplayItemContext
|
return new DisplayItemContext
|
||||||
{
|
{
|
||||||
IconAssetName = weaponBase.WeaponData.IconAssetName,
|
IconAssetName = weaponBase.WeaponData.Sprite,
|
||||||
Rarity = weaponBase.WeaponData.Rarity,
|
Rarity = weaponBase.WeaponData.Rarity,
|
||||||
IsWeapon = true
|
IsWeapon = true
|
||||||
};
|
};
|
||||||
@@ -124,7 +124,7 @@ namespace SepCore.UI
|
|||||||
ItemType.Weapon => await SpriteLoader.LoadSpriteByKeyAsync(rawData.DataRow switch
|
ItemType.Weapon => await SpriteLoader.LoadSpriteByKeyAsync(rawData.DataRow switch
|
||||||
{
|
{
|
||||||
WeaponConfig weaponConfig => weaponConfig.Sprite,
|
WeaponConfig weaponConfig => weaponConfig.Sprite,
|
||||||
WeaponData weaponData => weaponData.IconAssetName,
|
WeaponData weaponData => weaponData.Sprite,
|
||||||
_ => string.Empty
|
_ => string.Empty
|
||||||
}),
|
}),
|
||||||
ItemType.Prop => await SpriteLoader.LoadSpriteByKeyAsync(((PropConfig)rawData.DataRow).Sprite),
|
ItemType.Prop => await SpriteLoader.LoadSpriteByKeyAsync(((PropConfig)rawData.DataRow).Sprite),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using SepCore.AsyncTask;
|
using SepCore.AsyncTask;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
using SepCore.Entity;
|
using SepCore.Entity;
|
||||||
using SepCore.Timer;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public abstract class EnemyBase : TargetableObject
|
public abstract class EnemyBase : TargetableObject
|
||||||
@@ -16,29 +15,6 @@ public abstract class EnemyBase : TargetableObject
|
|||||||
|
|
||||||
protected EnemyData _enemyData;
|
protected EnemyData _enemyData;
|
||||||
|
|
||||||
protected bool _canAttack;
|
|
||||||
protected TimerHandle _attackTimerHandle;
|
|
||||||
|
|
||||||
protected void StartAttackCooldown(float cooldown)
|
|
||||||
{
|
|
||||||
_canAttack = false;
|
|
||||||
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ResetAttackCooldown(float cooldown)
|
|
||||||
{
|
|
||||||
GameEntry.Timer.Cancel(_attackTimerHandle);
|
|
||||||
_canAttack = false;
|
|
||||||
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void CancelAttackCooldown()
|
|
||||||
{
|
|
||||||
GameEntry.Timer.Cancel(_attackTimerHandle);
|
|
||||||
_attackTimerHandle = TimerHandle.Invalid;
|
|
||||||
_canAttack = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnShow(object userData)
|
protected override void OnShow(object userData)
|
||||||
{
|
{
|
||||||
base.OnShow(userData);
|
base.OnShow(userData);
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
if (_projectileData.OwnerCamp == CampType.Player)
|
if (_projectileData.OwnerCamp == CampType.Player)
|
||||||
{
|
{
|
||||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
gameObject.layer = Constant.Layer.PlayerWeaponLayerId;
|
||||||
}
|
}
|
||||||
else if (_projectileData.OwnerCamp == CampType.Enemy)
|
else if (_projectileData.OwnerCamp == CampType.Enemy)
|
||||||
{
|
{
|
||||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
gameObject.layer = Constant.Layer.EnemyWeaponLayerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetColliderEnabled(false);
|
SetColliderEnabled(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using SepCore.Entity;
|
using SepCore.Entity;
|
||||||
|
using SepCore.Timer;
|
||||||
using GameFramework;
|
using GameFramework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
@@ -9,6 +10,11 @@ namespace SepCore.Entity
|
|||||||
{
|
{
|
||||||
[SerializeField] private EntityDataBase _entityData = null;
|
[SerializeField] private EntityDataBase _entityData = null;
|
||||||
|
|
||||||
|
protected bool _canAttack;
|
||||||
|
protected TimerHandle _attackTimerHandle;
|
||||||
|
|
||||||
|
public bool CanAttack => _canAttack;
|
||||||
|
|
||||||
public int Id => Entity.Id;
|
public int Id => Entity.Id;
|
||||||
|
|
||||||
public Animation CachedAnimation { get; private set; }
|
public Animation CachedAnimation { get; private set; }
|
||||||
@@ -47,6 +53,26 @@ namespace SepCore.Entity
|
|||||||
base.OnHide(isShutdown, userData);
|
base.OnHide(isShutdown, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void StartAttackCooldown(float cooldown)
|
||||||
|
{
|
||||||
|
_canAttack = false;
|
||||||
|
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetAttackCooldown(float cooldown)
|
||||||
|
{
|
||||||
|
GameEntry.Timer.Cancel(_attackTimerHandle);
|
||||||
|
_canAttack = false;
|
||||||
|
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void CancelAttackCooldown()
|
||||||
|
{
|
||||||
|
GameEntry.Timer.Cancel(_attackTimerHandle);
|
||||||
|
_attackTimerHandle = TimerHandle.Invalid;
|
||||||
|
_canAttack = false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
|
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
|
||||||
{
|
{
|
||||||
base.OnAttached(childEntity, parentTransform, userData);
|
base.OnAttached(childEntity, parentTransform, userData);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 299f47fcfb4de4c4ea5a78d7ed206291
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 武器状态基类。
|
||||||
|
/// </summary>
|
||||||
|
public abstract class WeaponStateBase
|
||||||
|
{
|
||||||
|
public abstract WeaponStateType State { get; }
|
||||||
|
public abstract void OnInit(WeaponBase weapon);
|
||||||
|
public abstract void OnEnter();
|
||||||
|
public abstract void OnUpdate(float elapseSeconds, float realElapseSeconds);
|
||||||
|
public abstract void OnLeave();
|
||||||
|
public override string ToString() => State.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 574d105bf89bf7546a682ed407708034
|
guid: 64f43677dac08f744848382cab8588fb
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 武器状态机。负责状态注册、切换与驱动。
|
||||||
|
/// </summary>
|
||||||
|
public class WeaponStateMachine
|
||||||
|
{
|
||||||
|
private readonly WeaponBase _weapon;
|
||||||
|
private readonly Dictionary<WeaponStateType, WeaponStateBase> _states = new();
|
||||||
|
|
||||||
|
private WeaponStateBase _currentState;
|
||||||
|
|
||||||
|
public WeaponStateMachine(WeaponBase weapon)
|
||||||
|
{
|
||||||
|
_weapon = weapon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBuilt => _states.Count > 0;
|
||||||
|
|
||||||
|
public bool Contains(WeaponStateType stateType)
|
||||||
|
{
|
||||||
|
return _states.ContainsKey(stateType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(WeaponStateBase state)
|
||||||
|
{
|
||||||
|
state.OnInit(_weapon);
|
||||||
|
_states[state.State] = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryTransition(WeaponStateType stateType)
|
||||||
|
{
|
||||||
|
if (!_states.TryGetValue(stateType, out WeaponStateBase nextState))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentState?.OnLeave();
|
||||||
|
_currentState = nextState;
|
||||||
|
_currentState.OnEnter();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 54932952c9fa3fd4dbe6d21f2a484b4d
|
guid: d29897e3011246748bb0c939e54a0ca5
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using SepCore.Components;
|
|
||||||
using SepCore.CustomUtility;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public sealed class HighestHealthTargetSelector : ITargetSelector
|
|
||||||
{
|
|
||||||
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
|
|
||||||
{
|
|
||||||
if (weapon == null || candidates == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityBase target = null;
|
|
||||||
float maxHp = float.MinValue;
|
|
||||||
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
|
|
||||||
|
|
||||||
foreach (var candidate in candidates)
|
|
||||||
{
|
|
||||||
if (candidate == null || !candidate.Available) continue;
|
|
||||||
|
|
||||||
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
|
|
||||||
|
|
||||||
if (!TryGetCurrentHealth(candidate, out var hp)) continue;
|
|
||||||
|
|
||||||
if (hp <= maxHp) continue;
|
|
||||||
|
|
||||||
maxHp = hp;
|
|
||||||
target = candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetCurrentHealth(EntityBase target, out float health)
|
|
||||||
{
|
|
||||||
health = 0f;
|
|
||||||
if (target == null) return false;
|
|
||||||
|
|
||||||
var healthComponent = target.GetComponent<HealthComponent>();
|
|
||||||
if (healthComponent == null) return false;
|
|
||||||
|
|
||||||
health = healthComponent.CurrentHealth;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using SepCore.Components;
|
|
||||||
using SepCore.Definition;
|
|
||||||
using SepCore.CustomUtility;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public sealed class LowestHealthTargetSelector : ITargetSelector
|
|
||||||
{
|
|
||||||
public EntityBase SelectTarget(WeaponBase weapon, IEnumerable<EntityBase> candidates, float maxSqrRange)
|
|
||||||
{
|
|
||||||
if (weapon == null || candidates == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityBase target = null;
|
|
||||||
float minHp = float.MaxValue;
|
|
||||||
float maxSqr = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
|
|
||||||
|
|
||||||
foreach (var candidate in candidates)
|
|
||||||
{
|
|
||||||
if (candidate == null || !candidate.Available) continue;
|
|
||||||
|
|
||||||
if (maxSqrRange > 0f && AIUtility.GetSqrMagnitudeXZ(weapon, candidate) >= maxSqr) continue;
|
|
||||||
|
|
||||||
if (!TryGetHealth(candidate, out var hp)) continue;
|
|
||||||
|
|
||||||
if (hp >= minHp) continue;
|
|
||||||
|
|
||||||
minHp = hp;
|
|
||||||
target = candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetHealth(EntityBase target, out float health)
|
|
||||||
{
|
|
||||||
health = 0f;
|
|
||||||
if (target == null) return false;
|
|
||||||
|
|
||||||
var statComponent = target.GetComponent<StatComponent>();
|
|
||||||
if (statComponent == null) return false;
|
|
||||||
|
|
||||||
var hpStat = statComponent.GetStat(StatType.MaxHealth);
|
|
||||||
if (hpStat == null) return false;
|
|
||||||
|
|
||||||
health = hpStat.Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 081d67a491f5792479306d88091168e3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public enum TargetSelectorType
|
|
||||||
{
|
|
||||||
Nearest = 0,
|
|
||||||
HighestHealth = 1,
|
|
||||||
LowestHealth = 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4eb1e0b99139fdd4280c8616804a8f77
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,58 +1,48 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using SepCore.Components;
|
using SepCore.Components;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
using SepCore.Entity;
|
|
||||||
using GameFramework;
|
using GameFramework;
|
||||||
using SepCore.CustomUtility;
|
using SepCore.CustomUtility;
|
||||||
using SepCore.Timer;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
public enum WeaponStateType
|
|
||||||
{
|
|
||||||
Disabled,
|
|
||||||
Attack,
|
|
||||||
Idle,
|
|
||||||
Check_OutRange,
|
|
||||||
Check_InRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 武器类。
|
/// 武器类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class WeaponBase : EntityBase
|
public abstract class WeaponBase : EntityBase
|
||||||
{
|
{
|
||||||
protected const string AttachPoint = "Weapon Point";
|
|
||||||
|
|
||||||
protected bool _isAttacking = false;
|
protected bool _isAttacking = false;
|
||||||
|
|
||||||
[SerializeField] protected bool _isEnabled = false;
|
[SerializeField] protected bool _isEnabled = false;
|
||||||
|
|
||||||
public WeaponData WeaponData { get; protected set; }
|
public WeaponData WeaponData { get; protected set; }
|
||||||
|
|
||||||
|
public float AttackCooldown => WeaponData.Cooldown;
|
||||||
|
|
||||||
public bool IsAttacking => _isAttacking;
|
public bool IsAttacking => _isAttacking;
|
||||||
|
|
||||||
protected ITargetSelector TargetSelector { get; set; }
|
protected ITargetSelector TargetSelector { get; private set; }
|
||||||
|
|
||||||
protected Dictionary<WeaponStateType, WeaponStateBase> _states;
|
protected WeaponStateMachine StateMachine { get; private set; }
|
||||||
|
|
||||||
protected WeaponStateBase _currentState;
|
|
||||||
|
|
||||||
protected EntityBase _target;
|
protected EntityBase _target;
|
||||||
|
|
||||||
protected bool _canAttack;
|
|
||||||
protected TimerHandle _attackTimerHandle;
|
|
||||||
|
|
||||||
protected float _sqrRange;
|
protected float _sqrRange;
|
||||||
|
|
||||||
|
public EntityBase Target => _target;
|
||||||
|
|
||||||
|
public float SqrRange => _sqrRange;
|
||||||
|
|
||||||
protected StatProperty AttackStat { get; private set; } = new();
|
protected StatProperty AttackStat { get; private set; } = new();
|
||||||
|
|
||||||
private StatComponent _attackStatComponent;
|
private StatComponent _attackStatComponent;
|
||||||
private System.Action<modifier, bool> _attackStatCallback;
|
private System.Action<modifier, bool> _attackStatCallback;
|
||||||
|
|
||||||
private static readonly List<EntityBase> s_EmptyCandidates = new();
|
[SerializeField] protected float _rotateSpeed = 5f;
|
||||||
|
[SerializeField] protected LayerMask _hitMask = ~0;
|
||||||
|
protected Quaternion _cachedRotation;
|
||||||
|
|
||||||
#region Lifecycle
|
#region Lifecycle
|
||||||
|
|
||||||
@@ -60,16 +50,31 @@ namespace SepCore.Entity
|
|||||||
{
|
{
|
||||||
base.OnInit(userData);
|
base.OnInit(userData);
|
||||||
|
|
||||||
if (TargetSelector == null)
|
TargetSelector = new NearestTargetSelector();
|
||||||
{
|
|
||||||
TargetSelector = CreateSelector(DefaultTargetSelectorType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override void OnShow(object userData)
|
protected sealed override void OnShow(object userData)
|
||||||
{
|
{
|
||||||
base.OnShow(userData);
|
base.OnShow(userData);
|
||||||
|
|
||||||
|
WeaponData = RequireWeaponData<WeaponData>(userData);
|
||||||
|
if (WeaponData == null) return;
|
||||||
|
|
||||||
|
StartAttackCooldown(WeaponData.Cooldown);
|
||||||
|
_sqrRange = WeaponData.AttackRange * WeaponData.AttackRange;
|
||||||
|
_cachedRotation = CachedTransform.rotation;
|
||||||
|
|
||||||
|
if (WeaponData.OwnerCamp == CampType.Player)
|
||||||
|
{
|
||||||
|
gameObject.layer = Constant.Layer.PlayerWeaponLayerId;
|
||||||
|
_hitMask = Constant.Layer.EnemyLayerMask;
|
||||||
|
}
|
||||||
|
else if (WeaponData.OwnerCamp == CampType.Enemy)
|
||||||
|
{
|
||||||
|
gameObject.layer = Constant.Layer.EnemyWeaponLayerId;
|
||||||
|
_hitMask = Constant.Layer.PlayerLayerMask;
|
||||||
|
}
|
||||||
|
|
||||||
if (!OnWeaponShow(userData)) return;
|
if (!OnWeaponShow(userData)) return;
|
||||||
|
|
||||||
EnsureStatesBuilt();
|
EnsureStatesBuilt();
|
||||||
@@ -78,6 +83,7 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
protected sealed override void OnHide(bool isShutdown, object userData)
|
protected sealed override void OnHide(bool isShutdown, object userData)
|
||||||
{
|
{
|
||||||
|
CancelAttackCooldown();
|
||||||
ReleaseAttackStatSubscription();
|
ReleaseAttackStatSubscription();
|
||||||
OnWeaponHide(userData);
|
OnWeaponHide(userData);
|
||||||
base.OnHide(isShutdown, userData);
|
base.OnHide(isShutdown, userData);
|
||||||
@@ -107,29 +113,69 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
protected virtual void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
protected virtual void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
||||||
{
|
{
|
||||||
|
BindAttackStatFromOwner(parentEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
protected virtual void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
||||||
{
|
{
|
||||||
|
ReleaseAttackStatSubscription();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
{
|
{
|
||||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||||
|
|
||||||
if (!_isEnabled) return;
|
if (!_isEnabled || StateMachine == null) return;
|
||||||
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
|
StateMachine.Update(elapseSeconds, realElapseSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public abstract ImpactData GetImpactData();
|
public virtual ImpactData GetImpactData()
|
||||||
protected abstract void Attack();
|
{
|
||||||
protected abstract void Check();
|
return new ImpactData(WeaponData.OwnerCamp, WeaponData.Attack, AttackStat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Attack();
|
||||||
|
|
||||||
|
public virtual void Check()
|
||||||
|
{
|
||||||
|
_target = SelectTarget(_sqrRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void RotateToTarget(float elapseSeconds)
|
||||||
|
{
|
||||||
|
if (_target == null || !_target.Available) return;
|
||||||
|
|
||||||
|
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
||||||
|
directionToTarget.y = 0f;
|
||||||
|
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
||||||
|
|
||||||
|
Quaternion startRotation = CachedTransform.rotation;
|
||||||
|
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
||||||
|
CachedTransform.rotation = Quaternion.Slerp(startRotation, targetRotation, _rotateSpeed * elapseSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void RotateToOrigin(float elapseSeconds)
|
||||||
|
{
|
||||||
|
Quaternion startRotation = CachedTransform.rotation;
|
||||||
|
CachedTransform.rotation = Quaternion.Slerp(startRotation, _cachedRotation, _rotateSpeed * elapseSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void FaceTargetImmediately()
|
||||||
|
{
|
||||||
|
if (_target == null || !_target.Available) return;
|
||||||
|
|
||||||
|
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
||||||
|
directionToTarget.y = 0f;
|
||||||
|
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
||||||
|
|
||||||
|
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void BuildStates();
|
protected abstract void BuildStates();
|
||||||
|
|
||||||
protected virtual WeaponStateType InitialState => WeaponStateType.Idle;
|
protected WeaponStateType InitialState => WeaponStateType.Idle;
|
||||||
protected virtual TargetSelectorType DefaultTargetSelectorType => TargetSelectorType.Nearest;
|
|
||||||
|
|
||||||
protected void RegisterState(WeaponStateBase state)
|
protected void RegisterState(WeaponStateBase state)
|
||||||
{
|
{
|
||||||
@@ -139,38 +185,34 @@ namespace SepCore.Entity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.OnInit(this);
|
StateMachine.Register(state);
|
||||||
_states[state.State] = state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void EnsureStatesBuilt()
|
protected void EnsureStatesBuilt()
|
||||||
{
|
{
|
||||||
if (_states != null) return;
|
if (StateMachine != null) return;
|
||||||
|
|
||||||
_states = new Dictionary<WeaponStateType, WeaponStateBase>();
|
StateMachine = new WeaponStateMachine(this);
|
||||||
BuildStates();
|
BuildStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void TransitionTo(WeaponStateType newState)
|
public void TransitionTo(WeaponStateType newState)
|
||||||
{
|
{
|
||||||
if (_states == null || !_states.TryGetValue(newState, out var nextState))
|
if (!StateMachine.TryTransition(newState))
|
||||||
{
|
{
|
||||||
Log.Error("Weapon state not found: {0}", newState);
|
Log.Error("Weapon state not found: {0}", newState);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentState?.OnLeave();
|
|
||||||
_currentState = nextState;
|
|
||||||
_currentState.OnEnter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEnabled(bool value)
|
public void SetEnabled(bool value)
|
||||||
{
|
{
|
||||||
|
if (value == _isEnabled) return;
|
||||||
|
|
||||||
_isEnabled = value;
|
_isEnabled = value;
|
||||||
|
|
||||||
OnEnabledChanged(value);
|
OnEnabledChanged(value);
|
||||||
|
|
||||||
if (_states == null || _states.Count == 0) return;
|
if (StateMachine == null || !StateMachine.IsBuilt) return;
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
@@ -178,9 +220,9 @@ namespace SepCore.Entity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_states.ContainsKey(WeaponStateType.Disabled))
|
if (StateMachine.Contains(WeaponStateType.None))
|
||||||
{
|
{
|
||||||
TransitionTo(WeaponStateType.Disabled);
|
TransitionTo(WeaponStateType.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,23 +241,17 @@ namespace SepCore.Entity
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IEnumerable<EntityBase> GetCandidates()
|
protected IEnumerable<EntityBase> GetCandidates()
|
||||||
{
|
{
|
||||||
var enemyManager = GameEntry.EnemyManager;
|
return GameEntry.EnemyManager.Enemies;
|
||||||
return enemyManager != null ? enemyManager.Enemies : s_EmptyCandidates;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EntityBase SelectTarget(float maxSqrRange)
|
protected EntityBase SelectTarget(float maxSqrRange)
|
||||||
{
|
{
|
||||||
return TargetSelector != null ? TargetSelector.SelectTarget(this, GetCandidates(), maxSqrRange) : null;
|
return TargetSelector.SelectTarget(this, GetCandidates(), maxSqrRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float GetSqrDistance(EntityBase target)
|
public bool IsInRange(EntityBase target, float sqrRange)
|
||||||
{
|
|
||||||
return target != null ? AIUtility.GetSqrMagnitudeXZ(this, target) : float.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool IsInRange(EntityBase target, float sqrRange)
|
|
||||||
{
|
{
|
||||||
if (target == null) return false;
|
if (target == null) return false;
|
||||||
return AIUtility.GetSqrMagnitudeXZ(this, target) < sqrRange;
|
return AIUtility.GetSqrMagnitudeXZ(this, target) < sqrRange;
|
||||||
@@ -223,61 +259,22 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
protected bool TryQueueAreaCollisionQuery(in Vector3 center, float radius, int maxTargets = 16)
|
protected bool TryQueueAreaCollisionQuery(in Vector3 center, float radius, int maxTargets = 16)
|
||||||
{
|
{
|
||||||
var simulationWorld = GameEntry.SimulationWorld;
|
return GameEntry.SimulationWorld.TryRequestAreaCollision(Id, WeaponData.OwnerId, in center, radius,
|
||||||
if (simulationWorld == null)
|
maxTargets);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
|
|
||||||
return simulationWorld.TryRequestAreaCollision(Id, ownerEntityId, in center, radius, maxTargets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool TryQueueSectorCollisionQuery(in Vector3 center, float radius, in Vector3 direction,
|
protected bool TryQueueSectorCollisionQuery(in Vector3 center, float radius, in Vector3 direction,
|
||||||
float halfAngleDeg, int maxTargets = 16)
|
float halfAngleDeg, int maxTargets = 16)
|
||||||
{
|
{
|
||||||
var simulationWorld = GameEntry.SimulationWorld;
|
return GameEntry.SimulationWorld.TryRequestSectorCollision(Id, WeaponData.OwnerId, in center, radius,
|
||||||
if (simulationWorld == null)
|
in direction, halfAngleDeg, maxTargets);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
|
|
||||||
return simulationWorld.TryRequestSectorCollision(Id, ownerEntityId, in center, radius, in direction,
|
|
||||||
halfAngleDeg, maxTargets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool TryQueueRectangleCollisionQuery(in Vector3 center, float halfWidth, float halfLength,
|
protected bool TryQueueRectangleCollisionQuery(in Vector3 center, float halfWidth, float halfLength,
|
||||||
in Vector3 direction, int maxTargets = 16)
|
in Vector3 direction, int maxTargets = 16)
|
||||||
{
|
{
|
||||||
var simulationWorld = GameEntry.SimulationWorld;
|
return GameEntry.SimulationWorld.TryRequestRectangleCollision(Id, WeaponData.OwnerId, in center,
|
||||||
if (simulationWorld == null)
|
halfWidth, halfLength, in direction, maxTargets);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ownerEntityId = WeaponData != null ? WeaponData.OwnerId : Id;
|
|
||||||
return simulationWorld.TryRequestRectangleCollision(Id, ownerEntityId, in center, halfWidth, halfLength,
|
|
||||||
in direction, maxTargets);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetTargetSelector(TargetSelectorType selectorType)
|
|
||||||
{
|
|
||||||
TargetSelector = CreateSelector(selectorType);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static ITargetSelector CreateSelector(TargetSelectorType selectorType)
|
|
||||||
{
|
|
||||||
switch (selectorType)
|
|
||||||
{
|
|
||||||
case TargetSelectorType.HighestHealth:
|
|
||||||
return new HighestHealthTargetSelector();
|
|
||||||
case TargetSelectorType.LowestHealth:
|
|
||||||
return new LowestHealthTargetSelector();
|
|
||||||
case TargetSelectorType.Nearest:
|
|
||||||
default:
|
|
||||||
return new NearestTargetSelector();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void BindAttackStatFromOwner(EntityLogic parentEntity)
|
protected void BindAttackStatFromOwner(EntityLogic parentEntity)
|
||||||
@@ -308,35 +305,5 @@ namespace SepCore.Entity
|
|||||||
_attackStatCallback = null;
|
_attackStatCallback = null;
|
||||||
AttackStat = new StatProperty();
|
AttackStat = new StatProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void StartAttackCooldown(float cooldown)
|
|
||||||
{
|
|
||||||
_canAttack = false;
|
|
||||||
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ResetAttackCooldown(float cooldown)
|
|
||||||
{
|
|
||||||
GameEntry.Timer.Cancel(_attackTimerHandle);
|
|
||||||
_canAttack = false;
|
|
||||||
_attackTimerHandle = GameEntry.Timer.ScheduleOnce(cooldown, () => _canAttack = true, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void CancelAttackCooldown()
|
|
||||||
{
|
|
||||||
GameEntry.Timer.Cancel(_attackTimerHandle);
|
|
||||||
_attackTimerHandle = TimerHandle.Invalid;
|
|
||||||
_canAttack = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class WeaponStateBase
|
|
||||||
{
|
|
||||||
public abstract WeaponStateType State { get; }
|
|
||||||
public abstract void OnInit(WeaponBase weapon);
|
|
||||||
public abstract void OnEnter();
|
|
||||||
public abstract void OnUpdate(float elapseSeconds, float realElapseSeconds);
|
|
||||||
public abstract void OnLeave();
|
|
||||||
public override string ToString() => State.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponHandgun
|
|
||||||
{
|
|
||||||
private class AttackState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponHandgun _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Attack;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
|
|
||||||
_weapon.Attack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4e72df067466464fa3be92bfd049dd0d
|
|
||||||
timeCreated: 1771579220
|
|
||||||
-45
@@ -1,45 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponHandgun
|
|
||||||
{
|
|
||||||
private class CheckInRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponHandgun _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_InRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: bfd76c0fb7284ceaa28f78c702f595da
|
|
||||||
timeCreated: 1771579231
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponHandgun
|
|
||||||
{
|
|
||||||
private class CheckOutRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponHandgun _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_OutRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
|
|
||||||
public override void OnEnter() { }
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3a9f936d3c8643f7bf632734548f800c
|
|
||||||
timeCreated: 1771579241
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponHandgun
|
|
||||||
{
|
|
||||||
private class IdleState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponHandgun _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Idle;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponHandgun;
|
|
||||||
public override void OnEnter() { }
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToOrigin(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target != null && _weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9c5341944d1a467b8553d31bc3d81d5b
|
|
||||||
timeCreated: 1771579252
|
|
||||||
@@ -1,20 +1,12 @@
|
|||||||
using SepCore.Definition;
|
|
||||||
using SepCore.CustomUtility;
|
using SepCore.CustomUtility;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
public partial class WeaponHandgun : WeaponBase
|
public class WeaponHandgun : WeaponBase
|
||||||
{
|
{
|
||||||
#region Property
|
#region Property
|
||||||
|
|
||||||
private WeaponHandgunData _weaponData;
|
|
||||||
|
|
||||||
private Quaternion _cachedRotation;
|
|
||||||
|
|
||||||
[SerializeField] private float _rotateSpeed = 6f;
|
|
||||||
[SerializeField] private LayerMask _hitMask = ~0;
|
|
||||||
[SerializeField] private Vector3 _fireOriginOffset = Vector3.zero;
|
[SerializeField] private Vector3 _fireOriginOffset = Vector3.zero;
|
||||||
|
|
||||||
[SerializeField] private float _hitMarkerSize = 0.2f;
|
[SerializeField] private float _hitMarkerSize = 0.2f;
|
||||||
@@ -26,21 +18,15 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public override ImpactData GetImpactData()
|
|
||||||
{
|
|
||||||
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void BuildStates()
|
protected override void BuildStates()
|
||||||
{
|
{
|
||||||
RegisterState(new IdleState());
|
RegisterState(new WeaponHandgunIdleState());
|
||||||
RegisterState(new CheckOutRangeState());
|
RegisterState(new WeaponHandgunSeekState());
|
||||||
RegisterState(new CheckInRangeState());
|
RegisterState(new WeaponHandgunAimState());
|
||||||
RegisterState(new AttackState());
|
RegisterState(new WeaponHandgunAttackState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Attack()
|
public override void Attack()
|
||||||
{
|
{
|
||||||
FaceTargetImmediately();
|
FaceTargetImmediately();
|
||||||
|
|
||||||
@@ -49,43 +35,12 @@ namespace SepCore.Entity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_attackEffect?.Play(this, hitPosition, targetable, 0f);
|
_attackEffect.Play(this, hitPosition, targetable, 0f);
|
||||||
_isAttacking = true;
|
_isAttacking = true;
|
||||||
AIUtility.PerformCollision(targetable, this);
|
AIUtility.PerformCollision(targetable, this);
|
||||||
_isAttacking = false;
|
_isAttacking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Check()
|
|
||||||
{
|
|
||||||
_target = SelectTarget(_sqrRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToTarget(float elapseSeconds)
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = (_target.CachedTransform.position - CachedTransform.position).normalized;
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
|
|
||||||
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, targetRotation,
|
|
||||||
_rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToOrigin(float elapseSeconds)
|
|
||||||
{
|
|
||||||
CachedTransform.rotation = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation,
|
|
||||||
_rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FaceTargetImmediately()
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryResolveAttackTarget(out TargetableObject targetable, out Vector3 hitPosition)
|
private bool TryResolveAttackTarget(out TargetableObject targetable, out Vector3 hitPosition)
|
||||||
{
|
{
|
||||||
targetable = _target as TargetableObject;
|
targetable = _target as TargetableObject;
|
||||||
@@ -95,17 +50,12 @@ namespace SepCore.Entity
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform targetTransform = targetable.CachedTransform;
|
Vector3 targetPosition = targetable.CachedTransform.position;
|
||||||
if (targetTransform == null)
|
hitPosition = targetPosition;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hitPosition = targetTransform.position;
|
|
||||||
|
|
||||||
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
|
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
|
||||||
Vector3 directionToTarget = targetTransform.position - fireOrigin;
|
Vector3 directionToTarget = targetPosition - fireOrigin;
|
||||||
float maxDistance = Mathf.Max(0.1f, _weaponData.AttackRange);
|
float maxDistance = WeaponData.AttackRange;
|
||||||
if (directionToTarget.sqrMagnitude > Mathf.Epsilon &&
|
if (directionToTarget.sqrMagnitude > Mathf.Epsilon &&
|
||||||
Physics.Raycast(fireOrigin, directionToTarget.normalized, out RaycastHit hit, maxDistance, _hitMask,
|
Physics.Raycast(fireOrigin, directionToTarget.normalized, out RaycastHit hit, maxDistance, _hitMask,
|
||||||
QueryTriggerInteraction.Collide))
|
QueryTriggerInteraction.Collide))
|
||||||
@@ -124,46 +74,16 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
protected override bool OnWeaponShow(object userData)
|
protected override bool OnWeaponShow(object userData)
|
||||||
{
|
{
|
||||||
_weaponData = RequireWeaponData<WeaponHandgunData>(userData);
|
|
||||||
if (_weaponData == null) return false;
|
|
||||||
|
|
||||||
WeaponData = _weaponData;
|
|
||||||
StartAttackCooldown(_weaponData.Cooldown);
|
|
||||||
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
|
|
||||||
_cachedRotation = CachedTransform.rotation;
|
|
||||||
_attackEffect = new HandgunHitMarkerAttackEffect(_hitMarkerSize, _hitMarkerYOffset, _hitMarkerDuration,
|
_attackEffect = new HandgunHitMarkerAttackEffect(_hitMarkerSize, _hitMarkerYOffset, _hitMarkerDuration,
|
||||||
_hitMarkerColor);
|
_hitMarkerColor);
|
||||||
|
|
||||||
if (_weaponData.OwnerCamp == CampType.Player)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Enemy");
|
|
||||||
}
|
|
||||||
else if (_weaponData.OwnerCamp == CampType.Enemy)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Player");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponHide(object userData)
|
protected override void OnWeaponHide(object userData)
|
||||||
{
|
{
|
||||||
CancelAttackCooldown();
|
|
||||||
_attackEffect = null;
|
_attackEffect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
|
||||||
{
|
|
||||||
BindAttackStatFromOwner(parentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
|
||||||
{
|
|
||||||
ReleaseAttackStatSubscription();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手枪的瞄准状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponHandgunAimState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponHandgun _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Aim;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: da25d8e0669f0194d8f56f4283c67b38
|
guid: 71e311baa75a23448a593f2abb05999c
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手枪的攻击状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponHandgunAttackState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponHandgun _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Attack;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
_weapon.ResetAttackCooldown(_weapon.AttackCooldown);
|
||||||
|
_weapon.Attack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_weapon.IsAttacking)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f0513d1d0592c344b93c8df22f892ea0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手枪的待机状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponHandgunIdleState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponHandgun _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Idle;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToOrigin(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target != null && _weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bd6af23da5725ce4385e23b49b0d4811
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 手枪的搜寻状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponHandgunSeekState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponHandgun _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Seek;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponHandgun)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e7a9fee7c4ec5f498756d6e17b1259e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponKnife
|
|
||||||
{
|
|
||||||
private class AttackState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponKnife _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Attack;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
|
|
||||||
_weapon.Attack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
if (!_weapon._isAttacking)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 417516ffe5574e0c998105fcaa073de7
|
|
||||||
timeCreated: 1771578955
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponKnife
|
|
||||||
{
|
|
||||||
private class CheckInRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponKnife _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_InRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 59644093364644768c26261b24bdb820
|
|
||||||
timeCreated: 1771578916
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponKnife
|
|
||||||
{
|
|
||||||
private class CheckOutRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponKnife _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_OutRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d9e1a63869694e72a360afe29cc94cfb
|
|
||||||
timeCreated: 1771578994
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponKnife
|
|
||||||
{
|
|
||||||
public class IdleState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponKnife _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Idle;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToOrigin(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target != null && _weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b4d66ec44b064640aa95c6aa71ff3846
|
|
||||||
timeCreated: 1771578662
|
|
||||||
@@ -2,50 +2,36 @@ using System.Collections.Generic;
|
|||||||
using SepCore.CustomUtility;
|
using SepCore.CustomUtility;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
using DG.Tweening;
|
using DG.Tweening;
|
||||||
using SepCore.Entity;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
public partial class WeaponKnife : WeaponBase
|
public class WeaponKnife : WeaponBase
|
||||||
{
|
{
|
||||||
private WeaponKnifeData _weaponData;
|
|
||||||
|
|
||||||
private Quaternion _cachedRotation;
|
|
||||||
[SerializeField] private float _rotateSpeed = 4f;
|
|
||||||
|
|
||||||
private Sequence _attackSequence;
|
private Sequence _attackSequence;
|
||||||
private Transform _attackParent;
|
private Transform _attackParent;
|
||||||
private Vector3 _attackCenter;
|
private Vector3 _attackCenter;
|
||||||
|
|
||||||
[SerializeField] private float attackDuration = 0.15f;
|
[SerializeField] private float _attackDuration = 0.15f;
|
||||||
[SerializeField] private float returnDuration = 0.2f;
|
[SerializeField] private float _returnDuration = 0.2f;
|
||||||
|
|
||||||
[SerializeField] private LayerMask _hitMask = ~0;
|
|
||||||
[SerializeField] private int _maxHitColliders = 32;
|
[SerializeField] private int _maxHitColliders = 32;
|
||||||
|
|
||||||
private IWeaponAttackEffect _attackEffect;
|
private IWeaponAttackEffect _attackEffect;
|
||||||
private Collider[] _hitResults;
|
private Collider[] _hitResults;
|
||||||
private readonly HashSet<int> _hitEntityIds = new();
|
private readonly HashSet<int> _hitEntityIds = new();
|
||||||
|
|
||||||
private float _hitRadius;
|
public float HitRadius => WeaponData.GetParamFloat(ParamType.HitRadius);
|
||||||
private float _hitRadiusSqr;
|
|
||||||
|
|
||||||
public override ImpactData GetImpactData()
|
|
||||||
{
|
|
||||||
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void BuildStates()
|
protected override void BuildStates()
|
||||||
{
|
{
|
||||||
RegisterState(new IdleState());
|
RegisterState(new WeaponKnifeIdleState());
|
||||||
RegisterState(new CheckInRangeState());
|
RegisterState(new WeaponKnifeAimState());
|
||||||
RegisterState(new CheckOutRangeState());
|
RegisterState(new WeaponKnifeSeekState());
|
||||||
RegisterState(new AttackState());
|
RegisterState(new WeaponKnifeAttackState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Attack()
|
public override void Attack()
|
||||||
{
|
{
|
||||||
StopAttackTween(false);
|
StopAttackTween(false);
|
||||||
FaceTargetImmediately();
|
FaceTargetImmediately();
|
||||||
@@ -56,12 +42,12 @@ namespace SepCore.Entity
|
|||||||
_attackCenter = _target != null ? _target.CachedTransform.position : CachedTransform.position;
|
_attackCenter = _target != null ? _target.CachedTransform.position : CachedTransform.position;
|
||||||
CachedTransform.SetParent(null);
|
CachedTransform.SetParent(null);
|
||||||
|
|
||||||
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
|
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * WeaponData.AttackRange;
|
||||||
_attackSequence = DOTween.Sequence();
|
_attackSequence = DOTween.Sequence();
|
||||||
_attackSequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
|
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
|
||||||
_attackSequence.AppendCallback(() =>
|
_attackSequence.AppendCallback(() =>
|
||||||
{
|
{
|
||||||
_attackEffect?.Play(this, _attackCenter, null, _hitRadius);
|
_attackEffect.Play(this, _attackCenter, null, HitRadius);
|
||||||
ApplyGroundAreaDamage();
|
ApplyGroundAreaDamage();
|
||||||
if (_attackParent != null)
|
if (_attackParent != null)
|
||||||
{
|
{
|
||||||
@@ -69,7 +55,7 @@ namespace SepCore.Entity
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
_attackSequence.Append(
|
_attackSequence.Append(
|
||||||
CachedTransform.DOLocalMove(Vector3.zero, returnDuration * 0.5f).SetEase(Ease.InQuad));
|
CachedTransform.DOLocalMove(Vector3.zero, _returnDuration * 0.5f).SetEase(Ease.InQuad));
|
||||||
_attackSequence.AppendCallback(() =>
|
_attackSequence.AppendCallback(() =>
|
||||||
{
|
{
|
||||||
_isAttacking = false;
|
_isAttacking = false;
|
||||||
@@ -78,54 +64,22 @@ namespace SepCore.Entity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Check()
|
|
||||||
{
|
|
||||||
_target = SelectTarget(_sqrRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToTarget(float elapseSeconds)
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RotateToOrigin(float elapseSeconds)
|
|
||||||
{
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FaceTargetImmediately()
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyGroundAreaDamage()
|
private void ApplyGroundAreaDamage()
|
||||||
{
|
{
|
||||||
if (_hitRadius <= 0f) return;
|
float radius = HitRadius;
|
||||||
|
if (radius <= 0f) return;
|
||||||
|
|
||||||
if (TryQueueAreaCollisionQuery(_attackCenter, _hitRadius, Mathf.Max(1, _maxHitColliders)))
|
if (TryQueueAreaCollisionQuery(_attackCenter, radius, Mathf.Max(1, _maxHitColliders)))
|
||||||
{
|
{
|
||||||
_hitEntityIds.Clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hitResults == null || _hitResults.Length == 0) return;
|
if (_hitResults == null || _hitResults.Length == 0) return;
|
||||||
|
|
||||||
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, _hitRadius, _hitResults, _hitMask,
|
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, radius, _hitResults, _hitMask,
|
||||||
QueryTriggerInteraction.Collide);
|
QueryTriggerInteraction.Collide);
|
||||||
|
|
||||||
|
float radiusSqr = radius * radius;
|
||||||
_hitEntityIds.Clear();
|
_hitEntityIds.Clear();
|
||||||
for (int i = 0; i < hitCount; i++)
|
for (int i = 0; i < hitCount; i++)
|
||||||
{
|
{
|
||||||
@@ -138,7 +92,7 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
Vector3 delta = targetable.CachedTransform.position - _attackCenter;
|
Vector3 delta = targetable.CachedTransform.position - _attackCenter;
|
||||||
delta.y = 0f;
|
delta.y = 0f;
|
||||||
if (delta.sqrMagnitude > _hitRadiusSqr) continue;
|
if (delta.sqrMagnitude > radiusSqr) continue;
|
||||||
|
|
||||||
AIUtility.PerformCollision(targetable, this);
|
AIUtility.PerformCollision(targetable, this);
|
||||||
}
|
}
|
||||||
@@ -146,18 +100,6 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
protected override bool OnWeaponShow(object userData)
|
protected override bool OnWeaponShow(object userData)
|
||||||
{
|
{
|
||||||
_weaponData = RequireWeaponData<WeaponKnifeData>(userData);
|
|
||||||
if (_weaponData == null) return false;
|
|
||||||
WeaponData = _weaponData;
|
|
||||||
|
|
||||||
StartAttackCooldown(_weaponData.Cooldown);
|
|
||||||
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
|
|
||||||
_cachedRotation = CachedTransform.rotation;
|
|
||||||
|
|
||||||
float configuredHitRadius = _weaponData.ParamsData != null ? _weaponData.ParamsData.HitRadius : 0f;
|
|
||||||
_hitRadius = configuredHitRadius > 0f ? Mathf.Max(0.1f, configuredHitRadius) : _weaponData.AttackRange;
|
|
||||||
|
|
||||||
_hitRadiusSqr = _hitRadius * _hitRadius;
|
|
||||||
_attackEffect = new KnifeRangeAttackEffect();
|
_attackEffect = new KnifeRangeAttackEffect();
|
||||||
|
|
||||||
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
||||||
@@ -166,36 +108,19 @@ namespace SepCore.Entity
|
|||||||
_hitResults = new Collider[colliderCapacity];
|
_hitResults = new Collider[colliderCapacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_weaponData.OwnerCamp == CampType.Player)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Enemy");
|
|
||||||
}
|
|
||||||
else if (_weaponData.OwnerCamp == CampType.Enemy)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Player");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponHide(object userData)
|
protected override void OnWeaponHide(object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
CancelAttackCooldown();
|
|
||||||
_attackEffect = null;
|
_attackEffect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
|
||||||
{
|
|
||||||
BindAttackStatFromOwner(parentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
ReleaseAttackStatSubscription();
|
base.OnWeaponDetach(parentEntity, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnabledChanged(bool enabled)
|
protected override void OnEnabledChanged(bool enabled)
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 小刀的瞄准状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponKnifeAimState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponKnife _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Aim;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0fb5d24a4e6deac4b8c64d1acc697adc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 小刀的攻击状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponKnifeAttackState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponKnife _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Attack;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
_weapon.ResetAttackCooldown(_weapon.AttackCooldown);
|
||||||
|
_weapon.Attack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_weapon.IsAttacking)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aad966d918f5cdb45912c0275826ef5c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 小刀的待机状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponKnifeIdleState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponKnife _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Idle;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToOrigin(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target != null && _weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 393f986a051df42409d687426bb213b7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 小刀的搜寻状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponKnifeSeekState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponKnife _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Seek;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponKnife)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ac6681a70a0b3a6429a876bac61166cc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLance
|
|
||||||
{
|
|
||||||
private class AttackState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLance _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Attack;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
|
|
||||||
_weapon.Attack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
if (!_weapon._isAttacking)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d9991ee189b733b4e9d40395357f5ef2
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLance
|
|
||||||
{
|
|
||||||
private class CheckInRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLance _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_InRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 01edc8bb0ff46a14d8028e03af932b52
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLance
|
|
||||||
{
|
|
||||||
private class CheckOutRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLance _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_OutRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8009ad2c332a956438e3357ed5e30eb6
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLance
|
|
||||||
{
|
|
||||||
public class IdleState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLance _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Idle;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLance;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToOrigin(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target != null && _weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4c9e98f5381e579469dcdac7bf3e1e25
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -9,15 +9,8 @@ using UnityGameFramework.Runtime;
|
|||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
public partial class WeaponLance : WeaponBase
|
public class WeaponLance : WeaponBase
|
||||||
{
|
{
|
||||||
// 长枪专用数据,包含强类型 ParamsData。
|
|
||||||
[SerializeField] private WeaponLanceData _weaponData;
|
|
||||||
|
|
||||||
private Quaternion _cachedRotation;
|
|
||||||
// 朝向目标时的旋转速度,可被 ParamsData.RotateSpeed 覆盖。
|
|
||||||
[SerializeField] private float _rotateSpeed = 5f;
|
|
||||||
|
|
||||||
private Sequence _attackSequence;
|
private Sequence _attackSequence;
|
||||||
private Transform _attackParent;
|
private Transform _attackParent;
|
||||||
|
|
||||||
@@ -26,57 +19,46 @@ namespace SepCore.Entity
|
|||||||
// 收枪返回耗时。
|
// 收枪返回耗时。
|
||||||
[SerializeField] private float _returnDuration = 0.18f;
|
[SerializeField] private float _returnDuration = 0.18f;
|
||||||
|
|
||||||
[SerializeField] private LayerMask _hitMask = ~0;
|
|
||||||
[SerializeField] private int _maxHitColliders = 32;
|
[SerializeField] private int _maxHitColliders = 32;
|
||||||
|
|
||||||
private IWeaponAttackEffect _attackEffect;
|
private IWeaponAttackEffect _attackEffect;
|
||||||
private Collider[] _hitResults;
|
private Collider[] _hitResults;
|
||||||
private readonly HashSet<int> _hitEntityIds = new();
|
private readonly HashSet<int> _hitEntityIds = new();
|
||||||
|
|
||||||
// 前戳矩形判定的横向半宽。
|
public float HitHalfWidth => WeaponData.GetParamFloat(ParamType.HitHalfWidth);
|
||||||
private float _hitHalfWidth;
|
public float HitHeight => WeaponData.GetParamFloat(ParamType.HitHeight);
|
||||||
// 前戳矩形判定盒体的总高度。
|
public float PierceLength => WeaponData.GetParamFloat(ParamType.PierceLength);
|
||||||
private float _hitHeight;
|
public Vector3 StrikeDirection => _strikeDirection;
|
||||||
// 盒体中心相对战斗平面的高度偏移。
|
|
||||||
private float _hitCenterYOffset;
|
|
||||||
// 从判定起点向前延伸的有效刺击长度。
|
|
||||||
private float _pierceLength;
|
|
||||||
// 判定起点相对武器当前位置的前移量。
|
|
||||||
private float _forwardOffset;
|
|
||||||
// 本次攻击锁定的前戳方向,避免受位移动画中的武器位置影响。
|
// 本次攻击锁定的前戳方向,避免受位移动画中的武器位置影响。
|
||||||
private Vector3 _strikeDirection = Vector3.forward;
|
private Vector3 _strikeDirection = Vector3.forward;
|
||||||
// 本次攻击锁定的矩形判定中心。
|
// 本次攻击锁定的矩形判定中心。
|
||||||
private Vector3 _strikeCenter;
|
private Vector3 _strikeCenter;
|
||||||
|
|
||||||
public override ImpactData GetImpactData()
|
|
||||||
{
|
|
||||||
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void BuildStates()
|
protected override void BuildStates()
|
||||||
{
|
{
|
||||||
RegisterState(new IdleState());
|
RegisterState(new WeaponLanceIdleState());
|
||||||
RegisterState(new CheckInRangeState());
|
RegisterState(new WeaponLanceAimState());
|
||||||
RegisterState(new CheckOutRangeState());
|
RegisterState(new WeaponLanceSeekState());
|
||||||
RegisterState(new AttackState());
|
RegisterState(new WeaponLanceAttackState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Attack()
|
public override void Attack()
|
||||||
{
|
{
|
||||||
StopAttackTween(false);
|
StopAttackTween(false);
|
||||||
FaceTargetImmediately();
|
FaceTargetImmediately();
|
||||||
|
_attackParent = CachedTransform.parent;
|
||||||
CacheStrikeSnapshot();
|
CacheStrikeSnapshot();
|
||||||
|
|
||||||
_isAttacking = true;
|
_isAttacking = true;
|
||||||
_attackParent = CachedTransform.parent;
|
|
||||||
CachedTransform.SetParent(null);
|
CachedTransform.SetParent(null);
|
||||||
|
|
||||||
Vector3 targetPos = CachedTransform.position + _strikeDirection * _pierceLength;
|
Vector3 targetPos = CachedTransform.position + _strikeDirection * PierceLength;
|
||||||
_attackSequence = DOTween.Sequence();
|
_attackSequence = DOTween.Sequence();
|
||||||
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
|
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
|
||||||
_attackSequence.AppendCallback(() =>
|
_attackSequence.AppendCallback(() =>
|
||||||
{
|
{
|
||||||
_attackEffect?.Play(this, _strikeCenter, _target, _hitHalfWidth);
|
_attackEffect.Play(this, _strikeCenter, _target, HitHalfWidth);
|
||||||
ApplyPierceDamage();
|
ApplyPierceDamage();
|
||||||
|
|
||||||
if (_attackParent != null)
|
if (_attackParent != null)
|
||||||
@@ -93,49 +75,12 @@ namespace SepCore.Entity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Check()
|
|
||||||
{
|
|
||||||
_target = SelectTarget(_sqrRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToTarget(float elapseSeconds)
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
directionToTarget.y = 0f;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToOrigin(float elapseSeconds)
|
|
||||||
{
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FaceTargetImmediately()
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
directionToTarget.y = 0f;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CacheStrikeSnapshot()
|
private void CacheStrikeSnapshot()
|
||||||
{
|
{
|
||||||
_strikeDirection = ResolvePlanarForward();
|
_strikeDirection = ResolvePlanarForward();
|
||||||
Vector3 strikeStart = CachedTransform.position;
|
Vector3 strikeStart = CachedTransform.position;
|
||||||
strikeStart.y = ResolveStrikePlaneY();
|
strikeStart.y = ResolveStrikePlaneY();
|
||||||
strikeStart += _strikeDirection * _forwardOffset;
|
_strikeCenter = strikeStart + _strikeDirection * (PierceLength * 0.5f);
|
||||||
_strikeCenter = strikeStart + _strikeDirection * (_pierceLength * 0.5f);
|
|
||||||
_strikeCenter.y += _hitCenterYOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector3 ResolvePlanarForward()
|
private Vector3 ResolvePlanarForward()
|
||||||
@@ -153,7 +98,7 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
private void ApplyPierceDamage()
|
private void ApplyPierceDamage()
|
||||||
{
|
{
|
||||||
if (_hitHalfWidth <= 0f || _pierceLength <= 0f) return;
|
if (HitHalfWidth <= 0f || PierceLength <= 0f) return;
|
||||||
|
|
||||||
Vector3 strikeDirection = _strikeDirection;
|
Vector3 strikeDirection = _strikeDirection;
|
||||||
if (strikeDirection.sqrMagnitude <= Mathf.Epsilon) return;
|
if (strikeDirection.sqrMagnitude <= Mathf.Epsilon) return;
|
||||||
@@ -161,20 +106,15 @@ namespace SepCore.Entity
|
|||||||
Vector3 broadPhaseCenter = _strikeCenter;
|
Vector3 broadPhaseCenter = _strikeCenter;
|
||||||
Quaternion broadPhaseRotation = Quaternion.LookRotation(strikeDirection, Vector3.up);
|
Quaternion broadPhaseRotation = Quaternion.LookRotation(strikeDirection, Vector3.up);
|
||||||
|
|
||||||
if (TryQueueRectangleCollisionQuery(broadPhaseCenter, _hitHalfWidth, _pierceLength * 0.5f,
|
if (TryQueueRectangleCollisionQuery(broadPhaseCenter, HitHalfWidth, PierceLength * 0.5f,
|
||||||
strikeDirection, Mathf.Max(1, _maxHitColliders)))
|
strikeDirection, Mathf.Max(1, _maxHitColliders)))
|
||||||
{
|
{
|
||||||
_hitEntityIds.Clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int capacity = Mathf.Max(1, _maxHitColliders);
|
if (_hitResults == null || _hitResults.Length == 0) return;
|
||||||
if (_hitResults == null || _hitResults.Length != capacity)
|
|
||||||
{
|
|
||||||
_hitResults = new Collider[capacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 halfExtents = new(_hitHalfWidth, _hitHeight * 0.5f, _pierceLength * 0.5f);
|
Vector3 halfExtents = new(HitHalfWidth, HitHeight * 0.5f, PierceLength * 0.5f);
|
||||||
int hitCount = Physics.OverlapBoxNonAlloc(broadPhaseCenter, halfExtents, _hitResults, broadPhaseRotation,
|
int hitCount = Physics.OverlapBoxNonAlloc(broadPhaseCenter, halfExtents, _hitResults, broadPhaseRotation,
|
||||||
_hitMask, QueryTriggerInteraction.Collide);
|
_hitMask, QueryTriggerInteraction.Collide);
|
||||||
_hitEntityIds.Clear();
|
_hitEntityIds.Clear();
|
||||||
@@ -203,8 +143,8 @@ namespace SepCore.Entity
|
|||||||
float lateralDistance = Vector3.Dot(delta, right);
|
float lateralDistance = Vector3.Dot(delta, right);
|
||||||
float targetRadius = ResolveTargetCollisionRadius(targetable);
|
float targetRadius = ResolveTargetCollisionRadius(targetable);
|
||||||
|
|
||||||
return Mathf.Abs(forwardDistance) <= _pierceLength * 0.5f + targetRadius &&
|
return Mathf.Abs(forwardDistance) <= PierceLength * 0.5f + targetRadius &&
|
||||||
Mathf.Abs(lateralDistance) <= _hitHalfWidth + targetRadius;
|
Mathf.Abs(lateralDistance) <= HitHalfWidth + targetRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float ResolveTargetCollisionRadius(TargetableObject targetable)
|
private static float ResolveTargetCollisionRadius(TargetableObject targetable)
|
||||||
@@ -225,64 +165,11 @@ namespace SepCore.Entity
|
|||||||
return _target.CachedTransform.position.y;
|
return _target.CachedTransform.position.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_attackParent != null)
|
return _attackParent != null ? _attackParent.position.y : CachedTransform.position.y;
|
||||||
{
|
|
||||||
return _attackParent.position.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CachedTransform.parent != null)
|
|
||||||
{
|
|
||||||
return CachedTransform.parent.position.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CachedTransform.position.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnWeaponShow(object userData)
|
protected override bool OnWeaponShow(object userData)
|
||||||
{
|
{
|
||||||
_weaponData = RequireWeaponData<WeaponLanceData>(userData);
|
|
||||||
if (_weaponData == null) return false;
|
|
||||||
WeaponData = _weaponData;
|
|
||||||
|
|
||||||
StartAttackCooldown(_weaponData.Cooldown);
|
|
||||||
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
|
|
||||||
_cachedRotation = CachedTransform.rotation;
|
|
||||||
|
|
||||||
WeaponLanceParamsData paramsData = _weaponData.ParamsData;
|
|
||||||
float configuredHalfWidth = paramsData != null && paramsData.HitHalfWidth > 0f
|
|
||||||
? paramsData.HitHalfWidth
|
|
||||||
: paramsData != null && paramsData.HitRadius > 0f
|
|
||||||
? paramsData.HitRadius
|
|
||||||
: 0.45f;
|
|
||||||
_hitHalfWidth = Mathf.Max(0.1f, configuredHalfWidth);
|
|
||||||
_hitHeight = paramsData != null && paramsData.HitHeight > 0f
|
|
||||||
? Mathf.Max(0.2f, paramsData.HitHeight)
|
|
||||||
: 4f;
|
|
||||||
_hitCenterYOffset = paramsData != null ? paramsData.HitCenterYOffset : 0f;
|
|
||||||
_pierceLength = paramsData != null && paramsData.PierceLength > 0f
|
|
||||||
? paramsData.PierceLength
|
|
||||||
: paramsData != null && paramsData.ThrustDistance > 0f
|
|
||||||
? paramsData.ThrustDistance
|
|
||||||
: _weaponData.AttackRange;
|
|
||||||
_forwardOffset = paramsData != null && paramsData.ForwardOffset > 0f
|
|
||||||
? paramsData.ForwardOffset
|
|
||||||
: 0f;
|
|
||||||
|
|
||||||
if (paramsData != null && paramsData.RotateSpeed > 0f)
|
|
||||||
{
|
|
||||||
_rotateSpeed = paramsData.RotateSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paramsData != null && paramsData.AttackDuration > 0f)
|
|
||||||
{
|
|
||||||
_attackDuration = paramsData.AttackDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paramsData != null && paramsData.ReturnDuration > 0f)
|
|
||||||
{
|
|
||||||
_returnDuration = paramsData.ReturnDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
||||||
if (_hitResults == null || _hitResults.Length != colliderCapacity)
|
if (_hitResults == null || _hitResults.Length != colliderCapacity)
|
||||||
{
|
{
|
||||||
@@ -290,37 +177,19 @@ namespace SepCore.Entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
_attackEffect = new LanceThrustAttackEffect();
|
_attackEffect = new LanceThrustAttackEffect();
|
||||||
|
|
||||||
if (_weaponData.OwnerCamp == CampType.Player)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Enemy");
|
|
||||||
}
|
|
||||||
else if (_weaponData.OwnerCamp == CampType.Enemy)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Player");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponHide(object userData)
|
protected override void OnWeaponHide(object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
CancelAttackCooldown();
|
|
||||||
_attackEffect = null;
|
_attackEffect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
|
||||||
{
|
|
||||||
BindAttackStatFromOwner(parentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
ReleaseAttackStatSubscription();
|
base.OnWeaponDetach(parentEntity, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnabledChanged(bool enabled)
|
protected override void OnEnabledChanged(bool enabled)
|
||||||
@@ -351,9 +220,5 @@ namespace SepCore.Entity
|
|||||||
_attackParent = null;
|
_attackParent = null;
|
||||||
_hitEntityIds.Clear();
|
_hitEntityIds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float HitHalfWidth => _hitHalfWidth;
|
|
||||||
public float PierceLength => _pierceLength;
|
|
||||||
public Vector3 StrikeDirection => _strikeDirection;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 长枪的瞄准状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLanceAimState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLance _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Aim;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b871ce786fd5a4b44832f9437a752880
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 长枪的攻击状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLanceAttackState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLance _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Attack;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
_weapon.ResetAttackCooldown(_weapon.AttackCooldown);
|
||||||
|
_weapon.Attack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_weapon.IsAttacking)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b3529471ff522fa4dbbf7ca2005b9358
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 长枪的待机状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLanceIdleState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLance _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Idle;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToOrigin(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target != null && _weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1a33cf2ffd5d4d54194406a6d9eb68a5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 长枪的搜寻状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLanceSeekState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLance _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Seek;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLance)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 366f9a214870cfb4b90fc2682c74f127
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLightning
|
|
||||||
{
|
|
||||||
private class AttackState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLightning _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Attack;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
|
|
||||||
_weapon.Attack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
if (!_weapon._isAttacking)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 25854832a7d3416dacab67bcfef2a2fa
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLightning
|
|
||||||
{
|
|
||||||
private class CheckInRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLightning _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_InRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 25404ad172434ba38609e797b6d96919
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLightning
|
|
||||||
{
|
|
||||||
private class CheckOutRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLightning _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_OutRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f3aa479abb79491d83e8de76806faf7a
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponLightning
|
|
||||||
{
|
|
||||||
private class IdleState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponLightning _weapon;
|
|
||||||
|
|
||||||
public override WeaponStateType State => WeaponStateType.Idle;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponLightning;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToOrigin(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target != null && _weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3464037c29774c3ea326dc93fe33551e
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
+15
-96
@@ -8,13 +8,8 @@ using UnityGameFramework.Runtime;
|
|||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
public partial class WeaponLightning : WeaponBase
|
public class WeaponLightning : WeaponBase
|
||||||
{
|
{
|
||||||
private WeaponLightningData _weaponData;
|
|
||||||
|
|
||||||
private Quaternion _cachedRotation;
|
|
||||||
[SerializeField] private float _rotateSpeed = 5f;
|
|
||||||
|
|
||||||
[SerializeField] private float _takeOffDuration = 0.3f;
|
[SerializeField] private float _takeOffDuration = 0.3f;
|
||||||
[SerializeField] private float _flyToTargetDuration = 0.2f;
|
[SerializeField] private float _flyToTargetDuration = 0.2f;
|
||||||
[SerializeField] private float _strikeDuration = 0.1f;
|
[SerializeField] private float _strikeDuration = 0.1f;
|
||||||
@@ -22,7 +17,6 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
[SerializeField] private float _hoverHeight = 15f;
|
[SerializeField] private float _hoverHeight = 15f;
|
||||||
|
|
||||||
[SerializeField] private LayerMask _hitMask = ~0;
|
|
||||||
[SerializeField] private int _maxHitColliders = 32;
|
[SerializeField] private int _maxHitColliders = 32;
|
||||||
|
|
||||||
private Sequence _attackSequence;
|
private Sequence _attackSequence;
|
||||||
@@ -32,25 +26,19 @@ namespace SepCore.Entity
|
|||||||
private Collider[] _hitResults;
|
private Collider[] _hitResults;
|
||||||
private readonly HashSet<int> _hitEntityIds = new();
|
private readonly HashSet<int> _hitEntityIds = new();
|
||||||
|
|
||||||
private float _hitRadius;
|
public float HitRadius => WeaponData.GetParamFloat(ParamType.HitRadius);
|
||||||
private float _hitRadiusSqr;
|
|
||||||
|
|
||||||
private IWeaponAttackEffect _attackEffect;
|
private IWeaponAttackEffect _attackEffect;
|
||||||
|
|
||||||
public override ImpactData GetImpactData()
|
|
||||||
{
|
|
||||||
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, AttackStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void BuildStates()
|
protected override void BuildStates()
|
||||||
{
|
{
|
||||||
RegisterState(new IdleState());
|
RegisterState(new WeaponLightningIdleState());
|
||||||
RegisterState(new CheckOutRangeState());
|
RegisterState(new WeaponLightningSeekState());
|
||||||
RegisterState(new CheckInRangeState());
|
RegisterState(new WeaponLightningAimState());
|
||||||
RegisterState(new AttackState());
|
RegisterState(new WeaponLightningAttackState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Attack()
|
public override void Attack()
|
||||||
{
|
{
|
||||||
StopAttackTween(false);
|
StopAttackTween(false);
|
||||||
FaceTargetImmediately();
|
FaceTargetImmediately();
|
||||||
@@ -70,7 +58,7 @@ namespace SepCore.Entity
|
|||||||
_attackSequence.Append(CachedTransform.DOMove(_lockedStrikePoint, _strikeDuration).SetEase(Ease.InQuad));
|
_attackSequence.Append(CachedTransform.DOMove(_lockedStrikePoint, _strikeDuration).SetEase(Ease.InQuad));
|
||||||
_attackSequence.AppendCallback(() =>
|
_attackSequence.AppendCallback(() =>
|
||||||
{
|
{
|
||||||
_attackEffect?.Play(this, _lockedStrikePoint, _target, _hitRadius);
|
_attackEffect.Play(this, _lockedStrikePoint, _target, HitRadius);
|
||||||
ApplyGroundAreaDamage();
|
ApplyGroundAreaDamage();
|
||||||
|
|
||||||
if (_attackParent != null)
|
if (_attackParent != null)
|
||||||
@@ -87,11 +75,6 @@ namespace SepCore.Entity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Check()
|
|
||||||
{
|
|
||||||
_target = SelectTarget(_sqrRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3 ResolveStrikePoint()
|
private Vector3 ResolveStrikePoint()
|
||||||
{
|
{
|
||||||
if (_target != null && _target.Available)
|
if (_target != null && _target.Available)
|
||||||
@@ -104,19 +87,20 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
private void ApplyGroundAreaDamage()
|
private void ApplyGroundAreaDamage()
|
||||||
{
|
{
|
||||||
if (_hitRadius <= 0f) return;
|
float radius = HitRadius;
|
||||||
|
if (radius <= 0f) return;
|
||||||
|
|
||||||
if (TryQueueAreaCollisionQuery(_lockedStrikePoint, _hitRadius, Mathf.Max(1, _maxHitColliders)))
|
if (TryQueueAreaCollisionQuery(_lockedStrikePoint, radius, Mathf.Max(1, _maxHitColliders)))
|
||||||
{
|
{
|
||||||
_hitEntityIds.Clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hitResults == null || _hitResults.Length == 0) return;
|
if (_hitResults == null || _hitResults.Length == 0) return;
|
||||||
|
|
||||||
int hitCount = Physics.OverlapSphereNonAlloc(_lockedStrikePoint, _hitRadius, _hitResults, _hitMask,
|
int hitCount = Physics.OverlapSphereNonAlloc(_lockedStrikePoint, radius, _hitResults, _hitMask,
|
||||||
QueryTriggerInteraction.Collide);
|
QueryTriggerInteraction.Collide);
|
||||||
|
|
||||||
|
float radiusSqr = radius * radius;
|
||||||
_hitEntityIds.Clear();
|
_hitEntityIds.Clear();
|
||||||
for (int i = 0; i < hitCount; i++)
|
for (int i = 0; i < hitCount; i++)
|
||||||
{
|
{
|
||||||
@@ -129,61 +113,14 @@ namespace SepCore.Entity
|
|||||||
|
|
||||||
Vector3 delta = targetable.CachedTransform.position - _lockedStrikePoint;
|
Vector3 delta = targetable.CachedTransform.position - _lockedStrikePoint;
|
||||||
delta.y = 0f;
|
delta.y = 0f;
|
||||||
if (delta.sqrMagnitude > _hitRadiusSqr) continue;
|
if (delta.sqrMagnitude > radiusSqr) continue;
|
||||||
|
|
||||||
AIUtility.PerformCollision(targetable, this);
|
AIUtility.PerformCollision(targetable, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RotateToTarget(float elapseSeconds)
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
directionToTarget.y = 0f;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RotateToOrigin(float elapseSeconds)
|
|
||||||
{
|
|
||||||
CachedTransform.rotation =
|
|
||||||
Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FaceTargetImmediately()
|
|
||||||
{
|
|
||||||
if (_target == null || !_target.Available) return;
|
|
||||||
|
|
||||||
Vector3 directionToTarget = _target.CachedTransform.position - CachedTransform.position;
|
|
||||||
directionToTarget.y = 0f;
|
|
||||||
if (directionToTarget.sqrMagnitude <= Mathf.Epsilon) return;
|
|
||||||
|
|
||||||
CachedTransform.rotation = Quaternion.LookRotation(directionToTarget.normalized, Vector3.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnWeaponShow(object userData)
|
protected override bool OnWeaponShow(object userData)
|
||||||
{
|
{
|
||||||
_weaponData = RequireWeaponData<WeaponLightningData>(userData);
|
|
||||||
if (_weaponData == null) return false;
|
|
||||||
WeaponData = _weaponData;
|
|
||||||
|
|
||||||
StartAttackCooldown(_weaponData.Cooldown);
|
|
||||||
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
|
|
||||||
_cachedRotation = CachedTransform.rotation;
|
|
||||||
|
|
||||||
float configuredHitRadius = _weaponData.ParamsData != null ? _weaponData.ParamsData.HitRadius : 0f;
|
|
||||||
_hitRadius = configuredHitRadius > 0f ? Mathf.Max(0.1f, configuredHitRadius) : _weaponData.AttackRange;
|
|
||||||
_hitRadiusSqr = _hitRadius * _hitRadius;
|
|
||||||
float configuredHoverHeight = _weaponData.ParamsData != null ? _weaponData.ParamsData.HoverHeight : 0f;
|
|
||||||
if (configuredHoverHeight > 0f)
|
|
||||||
{
|
|
||||||
_hoverHeight = Mathf.Max(0.1f, configuredHoverHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
int colliderCapacity = Mathf.Max(1, _maxHitColliders);
|
||||||
if (_hitResults == null || _hitResults.Length != colliderCapacity)
|
if (_hitResults == null || _hitResults.Length != colliderCapacity)
|
||||||
{
|
{
|
||||||
@@ -191,37 +128,19 @@ namespace SepCore.Entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
_attackEffect = new LightningStrikeAttackEffect();
|
_attackEffect = new LightningStrikeAttackEffect();
|
||||||
|
|
||||||
if (_weaponData.OwnerCamp == CampType.Player)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Enemy");
|
|
||||||
}
|
|
||||||
else if (_weaponData.OwnerCamp == CampType.Enemy)
|
|
||||||
{
|
|
||||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
|
||||||
_hitMask = LayerMask.GetMask("Player");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponHide(object userData)
|
protected override void OnWeaponHide(object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
CancelAttackCooldown();
|
|
||||||
_attackEffect = null;
|
_attackEffect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnWeaponAttach(EntityLogic parentEntity, Transform parentTransform, object userData)
|
|
||||||
{
|
|
||||||
BindAttackStatFromOwner(parentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
||||||
{
|
{
|
||||||
StopAttackTween(true);
|
StopAttackTween(true);
|
||||||
ReleaseAttackStatSubscription();
|
base.OnWeaponDetach(parentEntity, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnabledChanged(bool enabled)
|
protected override void OnEnabledChanged(bool enabled)
|
||||||
|
|||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 闪电的瞄准状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLightningAimState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLightning _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Aim;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.CanAttack)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Attack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 472efc8d8802b4941892df6907b98d03
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 闪电的攻击状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLightningAttackState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLightning _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Attack;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
_weapon.ResetAttackCooldown(_weapon.AttackCooldown);
|
||||||
|
_weapon.Attack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_weapon.IsAttacking)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9d4b2a83cb82e1442826effabf6c9351
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 闪电的待机状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLightningIdleState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLightning _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Idle;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToOrigin(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target != null && _weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Seek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1f6fccbd2de0c494fa2f33e0fba04d4f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
|
||||||
|
namespace SepCore.Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 闪电的搜寻状态。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WeaponLightningSeekState : WeaponStateBase
|
||||||
|
{
|
||||||
|
private WeaponLightning _weapon;
|
||||||
|
|
||||||
|
public override WeaponStateType State => WeaponStateType.Seek;
|
||||||
|
|
||||||
|
public override void OnInit(WeaponBase weapon) => _weapon = (WeaponLightning)weapon;
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
_weapon.Check();
|
||||||
|
_weapon.RotateToTarget(elapseSeconds);
|
||||||
|
|
||||||
|
if (_weapon.Target == null || !_weapon.Target.Available)
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Idle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_weapon.IsInRange(_weapon.Target, _weapon.SqrRange))
|
||||||
|
{
|
||||||
|
_weapon.TransitionTo(WeaponStateType.Aim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLeave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9fe264206ae0d0d4492b94b952d6d698
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponSlash
|
|
||||||
{
|
|
||||||
private class AttackState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponSlash _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Attack;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
_weapon.ResetAttackCooldown(_weapon._weaponData.Cooldown);
|
|
||||||
_weapon.Attack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
if (!_weapon._isAttacking)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 96b136ec326941a0821f66ddf750de29
|
|
||||||
timeCreated: 1771579419
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponSlash
|
|
||||||
{
|
|
||||||
private class CheckInRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponSlash _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_InRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon._canAttack)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Attack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 57a936178b5a4ff2938df369b77631e6
|
|
||||||
timeCreated: 1771579430
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
public partial class WeaponSlash
|
|
||||||
{
|
|
||||||
private class CheckOutRangeState : WeaponStateBase
|
|
||||||
{
|
|
||||||
private WeaponSlash _weapon;
|
|
||||||
public override WeaponStateType State => WeaponStateType.Check_OutRange;
|
|
||||||
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponSlash;
|
|
||||||
|
|
||||||
public override void OnEnter()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_weapon.Check();
|
|
||||||
_weapon.RotateToTarget(elapseSeconds);
|
|
||||||
|
|
||||||
if (_weapon._target == null || !_weapon._target.Available)
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Idle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_weapon.IsInRange(_weapon._target, _weapon._sqrRange))
|
|
||||||
{
|
|
||||||
_weapon.TransitionTo(WeaponStateType.Check_InRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnLeave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user