diff --git a/Assets/GameMain/DataTables/tbentityconfig.bytes b/Assets/GameMain/DataTables/tbentityconfig.bytes index ceb9e6f..0734001 100644 --- a/Assets/GameMain/DataTables/tbentityconfig.bytes +++ b/Assets/GameMain/DataTables/tbentityconfig.bytes @@ -1,3 +1,4 @@ - FollowCameraFollowCamera.prefabPlayer Player.prefab + FollowCameraFollowCamera.prefabPlayer Player.prefab MeleeEnemyMeleeEnemy.prefab RemoteEnemyRemoteEnemy.prefab WeaponKnifeWeaponKnife.prefab WeaponHandgunWeaponHandgun.prefab WeaponSlashWeaponSlash.prefabWeaponLightningWeaponLightning.prefab WeaponLanceWeaponLance.prefab -CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab \ No newline at end of file +CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab +ProjectileProjectile.prefab \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbentityconfig.json b/Assets/GameMain/DataTables/tbentityconfig.json index 10629f4..017781d 100644 --- a/Assets/GameMain/DataTables/tbentityconfig.json +++ b/Assets/GameMain/DataTables/tbentityconfig.json @@ -42,5 +42,9 @@ { "assetName": "ExpEntity", "prefabPath": "ExpEntity.prefab" + }, + { + "assetName": "Projectile", + "prefabPath": "Projectile.prefab" } ] \ No newline at end of file diff --git a/Assets/GameMain/DataTables/tbglobalconfig.bytes b/Assets/GameMain/DataTables/tbglobalconfig.bytes index 6135738..87dcf6a 100644 Binary files a/Assets/GameMain/DataTables/tbglobalconfig.bytes and b/Assets/GameMain/DataTables/tbglobalconfig.bytes differ diff --git a/Assets/GameMain/DataTables/tbglobalconfig.json b/Assets/GameMain/DataTables/tbglobalconfig.json index 6dbd88b..108db4d 100644 --- a/Assets/GameMain/DataTables/tbglobalconfig.json +++ b/Assets/GameMain/DataTables/tbglobalconfig.json @@ -2,14 +2,28 @@ { "weaponCapacity": 6, "propMaxCount": -1, + "playerEntity": "Player", "weaponRecycleRate": 300, "shopGoodsCount": 4, "shopPriceRandomPercent": 50, "shopMinPrice": 0, "levelUpRefreshPriceBase": 2, "levelUpRewardCount": 4, - "enemyGroupName": "Enemy", "spawnEnemyMaxCount": 5000, - "spawnDistanceFromPlayer": 20000 + "enemySpawnDistanceMin": 14000, + "enemySpawnDistanceMax": 18000, + "weaponRotateSpeed": 5000, + "playerGroupName": "Player", + "enemyGroupName": "Enemy", + "projectileGroupName": "Projectile", + "dropGroupName": "Drop", + "effectGroupNmae": "Effect", + "minDamage": 1, + "maxDodgeRate": 900, + "minSpawnRateScale": 100, + "projectileLifeTime": 3000, + "projectileHeightOffset": 600, + "projectileSpeed": 12000, + "projectileEntity": "Projectile" } ] \ No newline at end of file diff --git a/Assets/GameMain/Entities/BulletHandgun.prefab b/Assets/GameMain/Entities/Projectile.prefab similarity index 99% rename from Assets/GameMain/Entities/BulletHandgun.prefab rename to Assets/GameMain/Entities/Projectile.prefab index 12ee07d..ddd4194 100644 --- a/Assets/GameMain/Entities/BulletHandgun.prefab +++ b/Assets/GameMain/Entities/Projectile.prefab @@ -115,7 +115,7 @@ GameObject: m_Component: - component: {fileID: 8253353784175897923} m_Layer: 0 - m_Name: BulletHandgun + m_Name: Projectile m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 diff --git a/Assets/GameMain/Entities/BulletHandgun.prefab.meta b/Assets/GameMain/Entities/Projectile.prefab.meta similarity index 100% rename from Assets/GameMain/Entities/BulletHandgun.prefab.meta rename to Assets/GameMain/Entities/Projectile.prefab.meta diff --git a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyData.cs b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyData.cs index abc4afc..5fc1a4a 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyData.cs +++ b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyData.cs @@ -8,55 +8,47 @@ namespace SepCore.Entity [Serializable] public class EnemyData : TargetableObjectData { - private readonly EnemyConfig _enemyConfig; + public EnemyConfig Config { get; } public EnemyData(int entityId, EnemyConfig enemyConfig, int level) : base(enemyConfig.Prefab, entityId, CampType.Enemy) { - _enemyConfig = enemyConfig; + Config = enemyConfig; int effectiveLevel = Mathf.Max(1, level); MaxHealthBase = enemyConfig.MaxHealth + enemyConfig.HpAddPerLevel * (effectiveLevel - 1); } - public EnemyType EnemyType => _enemyConfig.EnemyType; + public EnemyType EnemyType => Config.EnemyType; - public string LogicType => _enemyConfig.LogicType; + public string LogicType => Config.LogicType; public override int MaxHealthBase { get; } - public int AttackDamage => _enemyConfig.AttackDamage; + public int AttackDamage => Config.AttackDamage; - public float AttackCooldown => _enemyConfig.AttackCooldown; + public float AttackCooldown => Config.AttackCooldown; - public float AttackRange => _enemyConfig.AttackRange; + public float AttackRange => Config.AttackRange; - public float SpeedBase => _enemyConfig.Speed; + public float SpeedBase => Config.Speed; - public int DropCoin => _enemyConfig.CoinDrop; + public int DropCoin => Config.CoinDrop; - public int DropExp => _enemyConfig.ExpDrop; + public int DropExp => Config.ExpDrop; - public float DropPercent => _enemyConfig.DropPercent; + public float DropPercent => Config.DropPercent; - public float Scale => _enemyConfig.Scale; + public float Scale => Config.Scale; public Color Color { get { // 空/非法颜色值视为无染色(乘以白色)。 - return ColorUtility.TryParseHtmlString(_enemyConfig.Color, out Color color) + return ColorUtility.TryParseHtmlString(Config.Color, out Color color) ? color : Color.white; } } - - public IReadOnlyDictionary Params => null; - - public bool TryGetParam(string key, out string value) - { - value = null; - return false; - } } } diff --git a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs index d8da270..ffd7711 100644 --- a/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs +++ b/Assets/GameMain/Scripts/Base/EntityData/Enemy/EnemyProjectileData.cs @@ -8,7 +8,7 @@ namespace SepCore.Entity public class EnemyProjectileData : EntityDataBase { public EnemyProjectileData(int entityId, int ownerEntityId, CampType ownerCamp, int attackDamage, - float speed, float lifeTime, Vector3 direction, string assetName = "BulletHandgun") + float speed, float lifeTime, Vector3 direction, string assetName = "Projectile") : base(assetName, entityId) { OwnerEntityId = ownerEntityId; diff --git a/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs new file mode 100644 index 0000000..047a284 --- /dev/null +++ b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs @@ -0,0 +1,46 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + + +namespace SepCore.Definition +{ + /// + /// 敌人状态 + /// + public enum EnemyStateType : byte + { + /// + /// 无 + /// + None = 0, + /// + /// 攻击 + /// + Attack = 1, + /// + /// 待机 + /// + Idle = 2, + /// + /// 搜索 + /// + Seek = 3, + /// + /// 追逐 + /// + Chase = 4, + /// + /// 死亡 + /// + Dead = 5, + } + +} + diff --git a/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta new file mode 100644 index 0000000..08f905d --- /dev/null +++ b/Assets/GameMain/Scripts/Base/Gen/EnemyStateType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d55e30fdec251d141bab19b1b87806b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs index 47c990d..6ffec1a 100644 --- a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs @@ -18,15 +18,31 @@ public sealed partial class GlobalConfig : Luban.BeanBase { WeaponCapacity = _buf.ReadInt(); PropMaxCount = _buf.ReadInt(); + PlayerEntity = _buf.ReadString(); + PlayerEntity_Ref = null; WeaponRecycleRate = _buf.ReadInt(); ShopGoodsCount = _buf.ReadInt(); ShopPriceRandomPercent = _buf.ReadInt(); ShopMinPrice = _buf.ReadInt(); LevelUpRefreshPriceBase = _buf.ReadInt(); LevelUpRewardCount = _buf.ReadInt(); - EnemyGroupName = _buf.ReadString(); SpawnEnemyMaxCount = _buf.ReadInt(); - SpawnDistanceFromPlayer = _buf.ReadInt(); + EnemySpawnDistanceMin = _buf.ReadInt(); + EnemySpawnDistanceMax = _buf.ReadInt(); + WeaponRotateSpeed = _buf.ReadInt(); + PlayerGroupName = _buf.ReadString(); + EnemyGroupName = _buf.ReadString(); + ProjectileGroupName = _buf.ReadString(); + DropGroupName = _buf.ReadString(); + EffectGroupNmae = _buf.ReadString(); + MinDamage = _buf.ReadInt(); + MaxDodgeRate = _buf.ReadInt(); + MinSpawnRateScale = _buf.ReadInt(); + ProjectileLifeTime = _buf.ReadInt(); + ProjectileHeightOffset = _buf.ReadInt(); + ProjectileSpeed = _buf.ReadInt(); + ProjectileEntity = _buf.ReadString(); + ProjectileEntity_Ref = null; } public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf) @@ -43,6 +59,11 @@ public sealed partial class GlobalConfig : Luban.BeanBase /// public readonly int PropMaxCount; /// + /// 玩家实体 + /// + public readonly string PlayerEntity; + public EntityConfig PlayerEntity_Ref; + /// /// 武器回收价比例 /// public readonly int WeaponRecycleRate; @@ -67,17 +88,70 @@ public sealed partial class GlobalConfig : Luban.BeanBase /// public readonly int LevelUpRewardCount; /// - /// 敌人对象池名 - /// - public readonly string EnemyGroupName; - /// /// 最大敌人数量 /// public readonly int SpawnEnemyMaxCount; /// - /// 敌人生成距离 + /// 敌人生成最小距离 /// - public readonly int SpawnDistanceFromPlayer; + public readonly int EnemySpawnDistanceMin; + /// + /// 敌人生成最大距离 + /// + public readonly int EnemySpawnDistanceMax; + /// + /// 武器旋转速度 + /// + public readonly int WeaponRotateSpeed; + /// + /// 玩家对象组名 + /// + public readonly string PlayerGroupName; + /// + /// 敌人对象组名 + /// + public readonly string EnemyGroupName; + /// + /// 投射物对象组名 + /// + public readonly string ProjectileGroupName; + /// + /// 掉落物对象组名 + /// + public readonly string DropGroupName; + /// + /// 特效对象组名 + /// + public readonly string EffectGroupNmae; + /// + /// 最小伤害 + /// + public readonly int MinDamage; + /// + /// 闪避率上限 + /// + public readonly int MaxDodgeRate; + /// + /// 最小刷怪速度倍数 + /// + public readonly int MinSpawnRateScale; + /// + /// 投射物生命周期 + /// + public readonly int ProjectileLifeTime; + /// + /// 投射物生成高度 + /// + public readonly int ProjectileHeightOffset; + /// + /// 投射物移动速度 + /// + public readonly int ProjectileSpeed; + /// + /// 投射物实体 + /// + public readonly string ProjectileEntity; + public EntityConfig ProjectileEntity_Ref; public const int __ID__ = -958250779; public override int GetTypeId() => __ID__; @@ -86,6 +160,7 @@ public sealed partial class GlobalConfig : Luban.BeanBase { + PlayerEntity_Ref = tables.TbEntityConfig.GetOrDefault(PlayerEntity); @@ -95,6 +170,19 @@ public sealed partial class GlobalConfig : Luban.BeanBase + + + + + + + + + + + + + ProjectileEntity_Ref = tables.TbEntityConfig.GetOrDefault(ProjectileEntity); } public override string ToString() @@ -102,15 +190,29 @@ public sealed partial class GlobalConfig : Luban.BeanBase return "{ " + "weaponCapacity:" + WeaponCapacity + "," + "propMaxCount:" + PropMaxCount + "," + + "playerEntity:" + PlayerEntity + "," + "weaponRecycleRate:" + WeaponRecycleRate + "," + "shopGoodsCount:" + ShopGoodsCount + "," + "shopPriceRandomPercent:" + ShopPriceRandomPercent + "," + "shopMinPrice:" + ShopMinPrice + "," + "levelUpRefreshPriceBase:" + LevelUpRefreshPriceBase + "," + "levelUpRewardCount:" + LevelUpRewardCount + "," - + "enemyGroupName:" + EnemyGroupName + "," + "spawnEnemyMaxCount:" + SpawnEnemyMaxCount + "," - + "spawnDistanceFromPlayer:" + SpawnDistanceFromPlayer + "," + + "enemySpawnDistanceMin:" + EnemySpawnDistanceMin + "," + + "enemySpawnDistanceMax:" + EnemySpawnDistanceMax + "," + + "weaponRotateSpeed:" + WeaponRotateSpeed + "," + + "playerGroupName:" + PlayerGroupName + "," + + "enemyGroupName:" + EnemyGroupName + "," + + "projectileGroupName:" + ProjectileGroupName + "," + + "dropGroupName:" + DropGroupName + "," + + "effectGroupNmae:" + EffectGroupNmae + "," + + "minDamage:" + MinDamage + "," + + "maxDodgeRate:" + MaxDodgeRate + "," + + "minSpawnRateScale:" + MinSpawnRateScale + "," + + "projectileLifeTime:" + ProjectileLifeTime + "," + + "projectileHeightOffset:" + ProjectileHeightOffset + "," + + "projectileSpeed:" + ProjectileSpeed + "," + + "projectileEntity:" + ProjectileEntity + "," + "}"; } } diff --git a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs index a992005..4c6b621 100644 --- a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs @@ -39,6 +39,10 @@ public partial class TbGlobalConfig /// public int PropMaxCount => _data.PropMaxCount; /// + /// 玩家实体 + /// + public string PlayerEntity => _data.PlayerEntity; + /// /// 武器回收价比例 /// public int WeaponRecycleRate => _data.WeaponRecycleRate; @@ -63,17 +67,69 @@ public partial class TbGlobalConfig /// public int LevelUpRewardCount => _data.LevelUpRewardCount; /// - /// 敌人对象池名 - /// - public string EnemyGroupName => _data.EnemyGroupName; - /// /// 最大敌人数量 /// public int SpawnEnemyMaxCount => _data.SpawnEnemyMaxCount; /// - /// 敌人生成距离 + /// 敌人生成最小距离 /// - public int SpawnDistanceFromPlayer => _data.SpawnDistanceFromPlayer; + public int EnemySpawnDistanceMin => _data.EnemySpawnDistanceMin; + /// + /// 敌人生成最大距离 + /// + public int EnemySpawnDistanceMax => _data.EnemySpawnDistanceMax; + /// + /// 武器旋转速度 + /// + public int WeaponRotateSpeed => _data.WeaponRotateSpeed; + /// + /// 玩家对象组名 + /// + public string PlayerGroupName => _data.PlayerGroupName; + /// + /// 敌人对象组名 + /// + public string EnemyGroupName => _data.EnemyGroupName; + /// + /// 投射物对象组名 + /// + public string ProjectileGroupName => _data.ProjectileGroupName; + /// + /// 掉落物对象组名 + /// + public string DropGroupName => _data.DropGroupName; + /// + /// 特效对象组名 + /// + public string EffectGroupNmae => _data.EffectGroupNmae; + /// + /// 最小伤害 + /// + public int MinDamage => _data.MinDamage; + /// + /// 闪避率上限 + /// + public int MaxDodgeRate => _data.MaxDodgeRate; + /// + /// 最小刷怪速度倍数 + /// + public int MinSpawnRateScale => _data.MinSpawnRateScale; + /// + /// 投射物生命周期 + /// + public int ProjectileLifeTime => _data.ProjectileLifeTime; + /// + /// 投射物生成高度 + /// + public int ProjectileHeightOffset => _data.ProjectileHeightOffset; + /// + /// 投射物移动速度 + /// + public int ProjectileSpeed => _data.ProjectileSpeed; + /// + /// 投射物实体 + /// + public string ProjectileEntity => _data.ProjectileEntity; public void ResolveRef(Tables tables) { diff --git a/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs b/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs index 3e5c26b..792637f 100644 --- a/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/WeaponConfig.cs @@ -62,7 +62,7 @@ public sealed partial class WeaponConfig : Luban.BeanBase /// public readonly int Cooldown; /// - /// 范围 + /// 攻击范围 /// public readonly int AttackRange; /// diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index 832ecd5..1c418a6 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -6,6 +6,7 @@ using SepCore.EnemyManager; using SepCore.Simulation; using SepCore.Timer; using UnityEngine; +using UnityGameFramework.Runtime; namespace SepCore.Procedure { @@ -86,16 +87,9 @@ namespace SepCore.Procedure // 停止玩家逻辑 Player.Enable = false; - // 隐藏所有掉落物实体 - var entities = GameEntry.Entity.GetEntityGroup("Drop").GetAllEntities(); - foreach (var entity in entities) - { - GameEntry.Entity.HideEntity(entity.Id); - } - - HideEntityGroup("Bullet"); - HideEntityGroup("Projectile"); - HideEntityGroup("EnemyProjectile"); + // 隐藏所有掉落物与投射物实体 + HideEntityGroup(GameEntry.Luban.Global.DropGroupName); + HideEntityGroup(GameEntry.Luban.Global.ProjectileGroupName); } public override void OnDestroy() @@ -106,13 +100,14 @@ namespace SepCore.Procedure private static void HideEntityGroup(string groupName) { - var entityGroup = GameEntry.Entity.GetEntityGroup(groupName); - var entities = entityGroup?.GetAllEntities(); - if (entities == null) + if (!GameEntry.Entity.HasEntityGroup(groupName)) { + Log.Error("Entity group '{0}' does not exist.", groupName); return; } + var entityGroup = GameEntry.Entity.GetEntityGroup(groupName); + var entities = entityGroup.GetAllEntities(); foreach (var entity in entities) { GameEntry.Entity.HideEntity(entity.Id); diff --git a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs index 473497f..bdd5474 100644 --- a/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs +++ b/Assets/GameMain/Scripts/Runtime/BuiltinComponent/AsyncTask/EntityAsyncExtension.cs @@ -5,6 +5,7 @@ using Cysharp.Threading.Tasks; using SepCore.CustomUtility; using SepCore.Definition; using SepCore.Entity; +using UnityEngine; using UnityGameFramework.Runtime; using HideEntityCompleteEventArgs = UnityGameFramework.Runtime.HideEntityCompleteEventArgs; using ShowEntityFailureEventArgs = UnityGameFramework.Runtime.ShowEntityFailureEventArgs; @@ -149,7 +150,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(Player), "Player", + return ShowEntityByDataAsync(entityComponent, typeof(Player), GameEntry.Luban.Global.PlayerGroupName, Constant.AssetPriority.PlayerAsset, data, timeout, cancellationToken); } @@ -165,7 +166,7 @@ namespace SepCore.AsyncTask EntityConfig entityConfig = GameEntry.Luban.Get(data.AssetName); return entityComponent.ShowEntityAsync( - data.Id, enemyType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), "Enemy", + data.Id, enemyType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), GameEntry.Luban.Global.EnemyGroupName, Constant.AssetPriority.EnemyAsset, data, timeout, cancellationToken) .ContinueWith(entity => entity.Logic as EnemyBase); } @@ -182,7 +183,7 @@ namespace SepCore.AsyncTask EntityConfig entityConfig = GameEntry.Luban.Get(data.AssetName); return entityComponent.ShowEntityAsync( - data.Id, weaponType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), "Weapon", + data.Id, weaponType, AssetUtility.GetEntityAsset(entityConfig.PrefabPath), GameEntry.Luban.Global.PlayerGroupName, Constant.AssetPriority.WeaponAsset, data, timeout, cancellationToken) .ContinueWith(entity => entity.Logic as WeaponBase); } @@ -195,7 +196,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(Effect), "Effect", + return ShowEntityByDataAsync(entityComponent, typeof(Effect), GameEntry.Luban.Global.EffectGroupNmae, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } @@ -207,7 +208,7 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(CoinEntity), "Drop", + return ShowEntityByDataAsync(entityComponent, typeof(CoinEntity), GameEntry.Luban.Global.DropGroupName, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } @@ -219,10 +220,76 @@ namespace SepCore.AsyncTask float timeout = 30f, CancellationToken cancellationToken = default) { - return ShowEntityByDataAsync(entityComponent, typeof(ExpEntity), "Drop", + return ShowEntityByDataAsync(entityComponent, typeof(ExpEntity), GameEntry.Luban.Global.DropGroupName, Constant.AssetPriority.EffectAsset, data, timeout, cancellationToken); } + /// + /// 异步显示投射物实体。 + /// + public static UniTask ShowProjectileAsync(this EntityComponent entityComponent, + EnemyProjectileData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return ShowEntityByDataAsync(entityComponent, typeof(EnemyProjectile), + GameEntry.Luban.Global.ProjectileGroupName, + Constant.AssetPriority.BulletAsset, data, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体(直接入参快捷方法)。 + /// + public static UniTask ShowProjectileAsync(this EntityComponent entityComponent, + int ownerEntityId, + CampType ownerCamp, + int attackDamage, + Vector3 position, + Vector3 direction, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + int projectileEntityId = entityComponent.NextId(); + float speed = GameEntry.Luban.Global.ProjectileSpeed / 1000f; + float lifeTime = GameEntry.Luban.Global.ProjectileLifeTime / 1000f; + string assetName = GameEntry.Luban.Global.ProjectileEntity; + + var projectileData = new EnemyProjectileData(projectileEntityId, ownerEntityId, ownerCamp, + attackDamage, speed, lifeTime, direction, assetName) + { + Position = position, + Rotation = Quaternion.LookRotation(direction, Vector3.up) + }; + + return entityComponent.ShowProjectileAsync(projectileData, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体。 + /// + public static UniTask ShowProjectile(this EntityComponent entityComponent, + EnemyProjectileData data, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return entityComponent.ShowProjectileAsync(data, timeout, cancellationToken); + } + + /// + /// 异步显示投射物实体(直接入参快捷方法)。 + /// + public static UniTask ShowProjectile(this EntityComponent entityComponent, + int ownerEntityId, + CampType ownerCamp, + int attackDamage, + Vector3 position, + Vector3 direction, + float timeout = 30f, + CancellationToken cancellationToken = default) + { + return entityComponent.ShowProjectileAsync(ownerEntityId, ownerCamp, attackDamage, position, direction, timeout, cancellationToken); + } + /// /// 异步隐藏实体 /// diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs index 0ab45c6..4e87b5d 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemyManagerComponent.cs @@ -22,7 +22,6 @@ namespace SepCore.EnemyManager public IReadOnlyCollection Enemies => _enemyRegistry.Enemies; private int _spawnEnemyMaxCount; - private int _spawnDistanceFromPlayer; private int _currentSpawnEnemyId; private int _currentLevel; @@ -73,11 +72,18 @@ namespace SepCore.EnemyManager return; } + if (!_entity.HasEntityGroup(EnemyGroupName)) + { + Log.Error("Entity group '{0}' does not exist.", EnemyGroupName); + return; + } + _isStopped = false; _spawnEnemyMaxCount = GameEntry.Luban.Global.SpawnEnemyMaxCount; - _spawnDistanceFromPlayer = GameEntry.Luban.Global.SpawnDistanceFromPlayer / 1000; + float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin / 1000f; + float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax / 1000f; _player = player.CachedTransform; - _spawnPositionStrategy = new RandomCircleSpawnStrategy(_spawnDistanceFromPlayer); + _spawnPositionStrategy = new RandomCircleSpawnStrategy(minDistance, maxDistance); _baseDuration = level.Duration; _duration = _baseDuration; diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs index bdfc862..91f2333 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/EnemySpawnScheduler.cs @@ -8,7 +8,7 @@ namespace SepCore.EnemyManager { public class EnemySpawnScheduler { - private const float MinSpawnRateScale = 0.1f; + private static float MinSpawnRateScale => GameEntry.Luban.Global.MinSpawnRateScale / 1000f; private float _elapsedTime; private float[] _baseIntervals; diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs index 12a31fc..47740b7 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/EnemyManager/SpawnStrategy/RandomCircleSpawnStrategy.cs @@ -5,19 +5,22 @@ namespace SepCore.EnemyManager { public class RandomCircleSpawnStrategy : ISpawnPositionStrategy { - private readonly float _spawnDistance; + private readonly float _minDistance; + private readonly float _maxDistance; - public RandomCircleSpawnStrategy(float spawnDistance) + public RandomCircleSpawnStrategy(float minDistance, float maxDistance) { - _spawnDistance = spawnDistance; + _minDistance = minDistance; + _maxDistance = maxDistance; } public Vector3 GetSpawnPosition(Transform player) { float x = Random.Range(-1f, 1f); float z = Random.Range(-1f, 1f); - Vector3 dir = new Vector3(x, 0, z).normalized; - return player.position + dir * _spawnDistance; + Vector3 dir = new Vector3(x, 0f, z).normalized; + float distance = Random.Range(_minDistance, _maxDistance); + return player.position + dir * distance; } } } diff --git a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs index bc156e7..9a77e7a 100644 --- a/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs +++ b/Assets/GameMain/Scripts/Runtime/CustomComponent/Simulation/SimulationWorld.EntitySync.cs @@ -9,11 +9,9 @@ namespace SepCore.Simulation // Bridges entity show/hide events into simulation state registration. public sealed class EntitySync { - private const string EnemyGroupName = "Enemy"; - private const string DropGroupName = "Drop"; - private const string BulletGroupName = "Bullet"; - private const string ProjectileGroupName = "Projectile"; - private const string EnemyProjectileGroupName = "EnemyProjectile"; + private static string EnemyGroupName => GameEntry.Luban.Global.EnemyGroupName; + private static string DropGroupName => GameEntry.Luban.Global.DropGroupName; + private static string ProjectileGroupName => GameEntry.Luban.Global.ProjectileGroupName; private readonly SimulationWorld _world; private bool _isEventSubscribed; @@ -74,8 +72,7 @@ namespace SepCore.Simulation return; } - if ((groupName == BulletGroupName || groupName == ProjectileGroupName || - groupName == EnemyProjectileGroupName) && + if (groupName == ProjectileGroupName && args.Entity.Logic is EntityBase projectileEntity) { _world.RegisterProjectileLifecycle(projectileEntity, args.UserData); @@ -101,8 +98,7 @@ namespace SepCore.Simulation return; } - if (groupName == BulletGroupName || groupName == ProjectileGroupName || - groupName == EnemyProjectileGroupName) + if (groupName == ProjectileGroupName) { _world.UnregisterProjectileLifecycle(args.EntityId); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs index 4f27cfd..7e6a417 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyBase.cs @@ -1,61 +1,124 @@ -using Cysharp.Threading.Tasks; +using Cysharp.Threading.Tasks; using SepCore.AsyncTask; +using SepCore.Components; using SepCore.Definition; -using SepCore.Entity; using UnityEngine; +using UnityGameFramework.Runtime; -public abstract class EnemyBase : TargetableObject +namespace SepCore.Entity { - protected Transform _target; - - public abstract override ImpactData GetImpactData(); - public virtual float AttackRange => 1f; - - public virtual void SetTarget(Transform target) => _target = target; - - protected EnemyData _enemyData; - - protected override void OnShow(object userData) + public abstract class EnemyBase : TargetableObject { - base.OnShow(userData); + protected Transform _target; - if (userData is EnemyData enemyData) + public EnemyData EnemyData { get; private set; } + + public MovementComponent Movement { get; private set; } + + protected override TargetableObjectData _targetableObjectData => EnemyData; + + public virtual float AttackRange => EnemyData.AttackRange; + + public float AttackCooldown => EnemyData.AttackCooldown; + + public int AttackDamage => EnemyData.AttackDamage; + + public override ImpactData GetImpactData() { - ApplyVisualParams(enemyData); + return new ImpactData(EnemyData.Camp, EnemyData.AttackDamage); } - } - protected override void OnDead(EntityBase attacker) - { - if (Random.value < _enemyData.DropPercent) + public virtual void SetTarget(Transform target) => _target = target; + + #region Lifecycle + + protected override void OnInit(object userData) { - var data = new CoinData(_enemyData.DropCoin, GameEntry.Entity.NextId()) + base.OnInit(userData); + + Movement = GetComponent(); + _healthComponent = GetComponent(); + } + + protected override void OnShow(object userData) + { + base.OnShow(userData); + + if (userData is EnemyData enemyData) { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowCoinAsync(data).Forget(); - } + EnemyData = enemyData; + _healthComponent.OnInit(enemyData.MaxHealthBase); + Movement.OnInit(enemyData.SpeedBase, CachedTransform, null, true); + Movement.SetMove(true); - if (Random.value < _enemyData.DropPercent) - { - var data = new ExpData(_enemyData.DropExp, GameEntry.Entity.NextId()) + StartAttackCooldown(enemyData.AttackCooldown); + CachedTransform.position = enemyData.Position; + ApplyVisualParams(enemyData); + } + else { - Position = this.CachedTransform.position - }; - GameEntry.Entity.ShowExpAsync(data).Forget(); + Log.Error("Invalid enemy data type: {0}", userData?.GetType()); + } } - base.OnDead(attacker); - } + protected override void OnHide(bool isShutdown, object userData) + { + Movement.OnReset(); + _healthComponent.OnReset(); + CancelAttackCooldown(); + _target = null; - private void ApplyVisualParams(EnemyData data) - { - CachedTransform.localScale = Vector3.one * Mathf.Max(0.1f, data.Scale); + base.OnHide(isShutdown, userData); + } - var renderer = GetComponentInChildren(); - var block = new MaterialPropertyBlock(); - block.SetColor("_BaseColor", data.Color); - renderer.SetPropertyBlock(block); + protected override void OnDead(EntityBase attacker) + { + if (Random.value < EnemyData.DropPercent) + { + var data = new CoinData(EnemyData.DropCoin, GameEntry.Entity.NextId()) + { + Position = CachedTransform.position + }; + GameEntry.Entity.ShowCoinAsync(data).Forget(); + } + + if (Random.value < EnemyData.DropPercent) + { + var data = new ExpData(EnemyData.DropExp, GameEntry.Entity.NextId()) + { + Position = CachedTransform.position + }; + GameEntry.Entity.ShowExpAsync(data).Forget(); + } + + base.OnDead(attacker); + } + + #endregion + + protected Vector3 GetTargetDirection() + { + if (_target == null) + { + return Vector3.zero; + } + + return new Vector3( + _target.position.x - CachedTransform.position.x, + 0f, + _target.position.z - CachedTransform.position.z + ).normalized; + } + + private void ApplyVisualParams(EnemyData data) + { + CachedTransform.localScale = Vector3.one * data.Scale; + + var renderer = GetComponentInChildren(); + var block = new MaterialPropertyBlock(); + block.SetColor("_BaseColor", data.Color); + renderer.SetPropertyBlock(block); + } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs index afac194..a267618 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/EnemyProjectile.cs @@ -1,5 +1,4 @@ using SepCore.Definition; -using SepCore.Entity; using UnityEngine; using UnityGameFramework.Runtime; diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs index 304d4ef..904200a 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/MeleeEnemy.cs @@ -1,83 +1,24 @@ -using SepCore.Components; using SepCore.CustomUtility; using SepCore.Definition; using UnityEngine; -using UnityGameFramework.Runtime; namespace SepCore.Entity { public class MeleeEnemy : EnemyBase { - private enum AttackStateType - { - Idle, - Check_OutRange, - Check_InRange, - Attack - } - - private MovementComponent _movementComponent; - - private float _attackRange = 1f; - private float _attackCooldown = 1f; - private int _attackDamage = 1; - private float _sqrAttackRange; - - private AttackStateType _attackState = AttackStateType.Idle; - - private EnemyData _meleeEnemyData - { - get => _enemyData; - set => _enemyData = value; - } - + private EnemyStateType enemyState = EnemyStateType.Idle; private TargetableObject _targetableTarget; - protected override TargetableObjectData _targetableObjectData => _meleeEnemyData; - public override float AttackRange => _attackRange; - - public override ImpactData GetImpactData() - { - return new ImpactData(_meleeEnemyData.Camp, _attackDamage); - } - - #region FSM - - protected override void OnInit(object userData) - { - base.OnInit(userData); - - _movementComponent = GetComponent(); - _healthComponent = GetComponent(); - } + #region Lifecycle protected override void OnShow(object userData) { base.OnShow(userData); - if (userData is EnemyData enemyData) - { - _meleeEnemyData = enemyData; - _healthComponent.OnInit(enemyData.MaxHealthBase); - _movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform, null, true); - _movementComponent.SetMove(true); - - _attackRange = Mathf.Max(0.1f, _meleeEnemyData.AttackRange); - _attackCooldown = Mathf.Max(0.01f, _meleeEnemyData.AttackCooldown); - _attackDamage = Mathf.Max(1, _meleeEnemyData.AttackDamage); - _sqrAttackRange = _attackRange * _attackRange; - - StartAttackCooldown(_attackCooldown); - _attackState = AttackStateType.Idle; - _targetableTarget = null; - - this.CachedTransform.position = enemyData.Position; - } - else - { - Log.Error($"Invalid data type. Data type: {userData?.GetType()}"); - } + _sqrAttackRange = AttackRange * AttackRange; + enemyState = EnemyStateType.Idle; + _targetableTarget = null; } protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) @@ -89,11 +30,8 @@ namespace SepCore.Entity protected override void OnHide(bool isShutdown, object userData) { - _movementComponent.OnReset(); - _healthComponent.OnReset(); _targetableTarget = null; - CancelAttackCooldown(); - _attackState = AttackStateType.Idle; + enemyState = EnemyStateType.Idle; base.OnHide(isShutdown, userData); } @@ -108,74 +46,73 @@ namespace SepCore.Entity private void UpdateAttackState(float elapseSeconds) { - switch (_attackState) + switch (enemyState) { - case AttackStateType.Idle: - SetMove(false); + case EnemyStateType.Idle: + Movement.SetMove(false); if (HasValidTarget()) { - TransitionTo(AttackStateType.Check_OutRange); + TransitionTo(EnemyStateType.Seek); } break; - case AttackStateType.Check_OutRange: + case EnemyStateType.Seek: if (!HasValidTarget()) { - TransitionTo(AttackStateType.Idle); + TransitionTo(EnemyStateType.Idle); return; } if (IsTargetInRange()) { - TransitionTo(AttackStateType.Check_InRange); + TransitionTo(EnemyStateType.Chase); return; } - SetMove(true); - _movementComponent.SetDirection(GetTargetDirection()); + Movement.SetMove(true); + Movement.SetDirection(GetTargetDirection()); break; - case AttackStateType.Check_InRange: + case EnemyStateType.Chase: if (!HasValidTarget()) { - TransitionTo(AttackStateType.Idle); + TransitionTo(EnemyStateType.Idle); return; } - SetMove(false); + Movement.SetMove(false); if (!IsTargetInRange()) { - TransitionTo(AttackStateType.Check_OutRange); + TransitionTo(EnemyStateType.Seek); return; } if (_canAttack) { - ResetAttackCooldown(_attackCooldown); - TransitionTo(AttackStateType.Attack); + ResetAttackCooldown(AttackCooldown); + TransitionTo(EnemyStateType.Attack); } break; } } - private void TransitionTo(AttackStateType newState) + private void TransitionTo(EnemyStateType newState) { - _attackState = newState; + enemyState = newState; - if (_attackState == AttackStateType.Check_InRange) + if (enemyState == EnemyStateType.Chase) { - SetMove(false); + Movement.SetMove(false); } - if (_attackState == AttackStateType.Attack) + if (enemyState == EnemyStateType.Attack) { - SetMove(false); + Movement.SetMove(false); ExecuteAttack(); - TransitionTo(AttackStateType.Check_InRange); + TransitionTo(EnemyStateType.Chase); } } - private bool HasValidTarget() { if (_target == null) return false; @@ -192,7 +129,7 @@ namespace SepCore.Entity { if (_target == null) return false; - Vector3 delta = _target.position - this.CachedTransform.position; + Vector3 delta = _target.position - CachedTransform.position; delta.y = 0f; return delta.sqrMagnitude <= _sqrAttackRange; } @@ -208,31 +145,9 @@ namespace SepCore.Entity return; } - int damage = AIUtility.CalcDamageHP(_attackDamage, null, targetImpactData.DefenseStat, + int damage = AIUtility.CalcDamageHP(AttackDamage, null, targetImpactData.DefenseStat, targetImpactData.DodgeStat); _targetableTarget.ApplyDamage(this, damage); } - - private void SetMove(bool value) - { - if (_movementComponent != null) - { - _movementComponent.SetMove(value); - } - } - - private Vector3 GetTargetDirection() - { - if (_target == null) - { - return Vector3.zero; - } - - return new Vector3( - _target.position.x - this.CachedTransform.position.x, - 0f, - _target.position.z - this.CachedTransform.position.z - ).normalized; - } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs index 6b72e8a..c2f6a90 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Enemy/RemoteEnemy.cs @@ -1,8 +1,7 @@ -using SepCore.Components; +using Cysharp.Threading.Tasks; using SepCore.AsyncTask; using SepCore.CustomUtility; using SepCore.Definition; -using Cysharp.Threading.Tasks; using UnityEngine; using UnityGameFramework.Runtime; @@ -10,83 +9,13 @@ namespace SepCore.Entity { public class RemoteEnemy : EnemyBase { - private const string EnemyProjectileGroupName = "Bullet"; - private const float EnemyProjectileGroupAutoReleaseInterval = 60f; - private const int EnemyProjectileGroupCapacity = 64; - private const float EnemyProjectileGroupExpireTime = 60f; - private const int EnemyProjectileGroupPriority = 0; - - private const string ProjectileSpeedParamKey = "ProjectileSpeed"; - private const string ProjectileLifeTimeParamKey = "ProjectileLifeTime"; - private const string ProjectileSpawnForwardOffsetParamKey = "ProjectileSpawnForwardOffset"; - private const string ProjectileSpawnHeightOffsetParamKey = "ProjectileSpawnHeightOffset"; - private const string ProjectileAssetNameParamKey = "ProjectileAssetName"; - - private MovementComponent _movementComponent; - private float _attackRange = 1f; private float _attackRangeSquared; - private float _attackCooldown = 1f; - private int _attackDamage = 1; - - [SerializeField] private float _projectileSpeed = 12f; - [SerializeField] private float _projectileLifeTime = 3f; - [SerializeField] private float _projectileSpawnForwardOffset = 0.7f; - [SerializeField] private float _projectileSpawnHeightOffset = 0.6f; - [SerializeField] private string _projectileAssetName = "BulletHandgun"; - - private EnemyData _remoteEnemyData - { - get => _enemyData; - set => _enemyData = value; - } - - protected override TargetableObjectData _targetableObjectData => _remoteEnemyData; - public override float AttackRange => _attackRange; - - public override ImpactData GetImpactData() - { - return new ImpactData(_remoteEnemyData.Camp, _attackDamage); - } - - protected override void OnInit(object userData) - { - base.OnInit(userData); - - _movementComponent = GetComponent(); - _healthComponent = GetComponent(); - } protected override void OnShow(object userData) { base.OnShow(userData); - if (userData is EnemyData enemyData) - { - _remoteEnemyData = enemyData; - _healthComponent.OnInit(enemyData.MaxHealthBase); - _movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform, null, true); - _movementComponent.SetMove(true); - - _attackRange = Mathf.Max(0.1f, _remoteEnemyData.AttackRange); - _attackRangeSquared = _attackRange * _attackRange; - _attackCooldown = Mathf.Max(0.01f, _remoteEnemyData.AttackCooldown); - _attackDamage = Mathf.Max(1, _remoteEnemyData.AttackDamage); - - _projectileSpeed = ReadPositiveParam(ProjectileSpeedParamKey, _projectileSpeed); - _projectileLifeTime = ReadPositiveParam(ProjectileLifeTimeParamKey, _projectileLifeTime); - _projectileSpawnForwardOffset = ReadPositiveParam(ProjectileSpawnForwardOffsetParamKey, - _projectileSpawnForwardOffset); - _projectileSpawnHeightOffset = ReadPositiveParam(ProjectileSpawnHeightOffsetParamKey, - _projectileSpawnHeightOffset); - _projectileAssetName = ReadStringParam(ProjectileAssetNameParamKey, _projectileAssetName); - - StartAttackCooldown(_attackCooldown); - this.CachedTransform.position = enemyData.Position; - } - else - { - Log.Error($"Invalid data type. Data type: {userData?.GetType()}"); - } + _attackRangeSquared = AttackRange * AttackRange; } protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) @@ -95,50 +24,45 @@ namespace SepCore.Entity if (_target == null) { - _movementComponent.SetMove(false); + Movement.SetMove(false); return; } - Vector3 toTarget = _target.position - this.CachedTransform.position; + Vector3 toTarget = _target.position - CachedTransform.position; toTarget.y = 0f; float distanceSquared = toTarget.sqrMagnitude; if (distanceSquared <= _attackRangeSquared) { - _movementComponent.SetMove(false); + Movement.SetMove(false); if (_canAttack) { TryFireProjectile(); - ResetAttackCooldown(_attackCooldown); + ResetAttackCooldown(AttackCooldown); } } else { - _movementComponent.SetMove(true); - _movementComponent.SetDirection(GetTargetDirection()); + Movement.SetMove(true); + Movement.SetDirection(GetTargetDirection()); } } - protected override void OnHide(bool isShutdown, object userData) - { - _movementComponent.OnReset(); - _healthComponent.OnReset(); - CancelAttackCooldown(); - - base.OnHide(isShutdown, userData); - } - private void TryFireProjectile() { - if (!EnsureEnemyProjectileGroup()) return; + string groupName = GameEntry.Luban.Global.ProjectileGroupName; + if (!GameEntry.Entity.HasEntityGroup(groupName)) + { + Log.Error("Entity group '{0}' does not exist.", groupName); + return; + } - Vector3 spawnPosition = this.CachedTransform.position + - this.CachedTransform.forward * _projectileSpawnForwardOffset + - Vector3.up * _projectileSpawnHeightOffset; + float spawnHeight = GameEntry.Luban.Global.ProjectileHeightOffset / 1000f; + Vector3 spawnPosition = CachedTransform.position + Vector3.up * spawnHeight; Vector3 direction = _target.position - spawnPosition; direction.y = 0f; if (direction.sqrMagnitude <= Mathf.Epsilon) { - direction = this.CachedTransform.forward; + direction = CachedTransform.forward; direction.y = 0f; } @@ -151,84 +75,7 @@ namespace SepCore.Entity direction.Normalize(); } - int projectileEntityId = GameEntry.Entity.NextId(); - var projectileData = new EnemyProjectileData(projectileEntityId, Id, _remoteEnemyData.Camp, - _attackDamage, _projectileSpeed, _projectileLifeTime, direction) - { - Position = spawnPosition, - Rotation = Quaternion.LookRotation(direction, Vector3.up) - }; - - GameEntry.Entity.ShowEntityAsync( - projectileEntityId, - AssetUtility.GetEntityAsset(_projectileAssetName), - EnemyProjectileGroupName, - Constant.AssetPriority.BulletAsset, - projectileData).Forget(); - } - - private static bool EnsureEnemyProjectileGroup() - { - var entityComponent = GameEntry.Entity; - if (entityComponent == null) return false; - - if (entityComponent.HasEntityGroup(EnemyProjectileGroupName)) - { - return true; - } - - bool addResult = entityComponent.AddEntityGroup( - EnemyProjectileGroupName, - EnemyProjectileGroupAutoReleaseInterval, - EnemyProjectileGroupCapacity, - EnemyProjectileGroupExpireTime, - EnemyProjectileGroupPriority); - - if (!addResult) - { - Log.Warning("Can not create entity group '{0}'.", EnemyProjectileGroupName); - return false; - } - - return true; - } - - private float ReadPositiveParam(string paramName, float defaultValue) - { - if (_remoteEnemyData != null && - _remoteEnemyData.TryGetParam(paramName, out string rawValue) && - float.TryParse(rawValue, out float parsedValue)) - { - return Mathf.Max(0.01f, parsedValue); - } - - return Mathf.Max(0.01f, defaultValue); - } - - private string ReadStringParam(string paramName, string defaultValue) - { - if (_remoteEnemyData != null && - _remoteEnemyData.TryGetParam(paramName, out string rawValue) && - !string.IsNullOrWhiteSpace(rawValue)) - { - return rawValue; - } - - return defaultValue; - } - - private Vector3 GetTargetDirection() - { - if (_target == null) - { - return Vector3.zero; - } - - return new Vector3( - _target.position.x - this.CachedTransform.position.x, - 0f, - _target.position.z - this.CachedTransform.position.z - ).normalized; + GameEntry.Entity.ShowProjectile(Id, EnemyData.Camp, AttackDamage, spawnPosition, direction).Forget(); } } } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs index b662234..c376e6b 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponBase.cs @@ -18,9 +18,7 @@ namespace SepCore.Entity [SerializeField] protected bool _isEnabled = false; public WeaponData WeaponData { get; protected set; } - - public float AttackCooldown => WeaponData.Cooldown; - + public bool IsAttacking => _isAttacking; protected ITargetSelector TargetSelector { get; private set; } @@ -29,7 +27,7 @@ namespace SepCore.Entity protected EntityBase _target; - protected float _sqrRange; + private float _sqrRange; public EntityBase Target => _target; @@ -40,8 +38,9 @@ namespace SepCore.Entity private StatComponent _attackStatComponent; private System.Action _attackStatCallback; - [SerializeField] protected float _rotateSpeed = 5f; - [SerializeField] protected LayerMask _hitMask = ~0; + private static float _rotateSpeed => GameEntry.Luban.Global.WeaponRotateSpeed; + + protected LayerMask _hitMask; protected Quaternion _cachedRotation; #region Lifecycle @@ -57,8 +56,12 @@ namespace SepCore.Entity { base.OnShow(userData); - WeaponData = RequireWeaponData(userData); - if (WeaponData == null) return; + WeaponData = (WeaponData)userData; + if (WeaponData == null) + { + Log.Error("WeaponData is Invalid!"); + return; + } StartAttackCooldown(WeaponData.Cooldown); _sqrRange = WeaponData.AttackRange * WeaponData.AttackRange; @@ -229,18 +232,7 @@ namespace SepCore.Entity protected virtual void OnEnabledChanged(bool enabled) { } - - protected T RequireWeaponData(object userData) where T : WeaponData - { - var data = userData as T; - if (data == null) - { - Log.Error("{0} data is invalid.", typeof(T).Name); - } - - return data; - } - + protected IEnumerable GetCandidates() { return GameEntry.EnemyManager.Enemies; diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs index e0c0b5b..fcf5bbe 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponHandgun/WeaponHandgunAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs index 999a18e..c9c8db1 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponKnife/WeaponKnifeAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs index 6de10c1..d8955b3 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLance/WeaponLanceAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs index 8c51a07..aee7a0a 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponLightning/WeaponLightningAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs index f8080e4..98e3645 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Weapon/WeaponSlash/WeaponSlashAttackState.cs @@ -15,7 +15,7 @@ namespace SepCore.Entity public override void OnEnter() { - _weapon.ResetAttackCooldown(_weapon.AttackCooldown); + _weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown); _weapon.Attack(); } diff --git a/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs b/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs index 8ea18dc..7fdc539 100644 --- a/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs +++ b/Assets/GameMain/Scripts/Runtime/Utility/AIUtility.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using SepCore.Definition; @@ -239,10 +239,11 @@ namespace SepCore.CustomUtility public static int CalcDamageHP(int attack, StatProperty attackStat, StatProperty defenseStat, StatProperty dodgeStat) { - // 1. 处理闪避(闪避率取值 (0, 0.9),不允许拉满闪避) + // 1. 处理闪避(从全局表读取闪避率上限) + float maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f; if (dodgeStat != null) { - if (Random.value < Mathf.Clamp(dodgeStat.Value, 0, 0.9f)) return 0; + if (Random.value < Mathf.Clamp(dodgeStat.Value, 0f, maxDodge)) return 0; } // 2. 处理攻击加成 最终伤害 = (基础伤害 + 伤害提升固定值) * 伤害提升率 @@ -258,9 +259,10 @@ namespace SepCore.CustomUtility damage = (damage - defenseStat.Value) / defenseStat.Percent; } - // 将伤害限制为正整数,避免堆防御导致无法造成伤害 - if (damage < 1) return 1; - else return Mathf.CeilToInt(damage); + // 4. 将伤害限制为保底伤害,避免堆防御导致无法造成伤害 + int minDamage = GameEntry.Luban.Global.MinDamage; + if (damage < minDamage) return minDamage; + return Mathf.CeilToInt(damage); } [StructLayout(LayoutKind.Auto)] diff --git a/Assets/Launcher.unity b/Assets/Launcher.unity index a4d3075..cf47dca 100644 --- a/Assets/Launcher.unity +++ b/Assets/Launcher.unity @@ -574,28 +574,28 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.size - value: 6 + value: 5 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[0].m_Name - value: Weapon + value: Projectile objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[1].m_Name - value: Bullet - objectReference: {fileID: 0} - - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[2].m_Name value: Effect objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[3].m_Name + propertyPath: m_EntityGroups.Array.data[2].m_Name value: Enemy objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[4].m_Name + propertyPath: m_EntityGroups.Array.data[3].m_Name value: Player objectReference: {fileID: 0} + - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} + propertyPath: m_EntityGroups.Array.data[4].m_Name + value: Drop + objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[5].m_Name value: Drop @@ -614,7 +614,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[0].m_InstanceCapacity - value: 10 + value: 16 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[1].m_InstanceCapacity @@ -622,15 +622,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceCapacity - value: 16 - objectReference: {fileID: 0} - - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} - propertyPath: m_EntityGroups.Array.data[3].m_InstanceCapacity value: 10000 objectReference: {fileID: 0} + - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} + propertyPath: m_EntityGroups.Array.data[3].m_InstanceCapacity + value: 10 + objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceCapacity - value: 1 + value: 100 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[5].m_InstanceCapacity @@ -658,11 +658,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceExpireTime - value: 60 + value: 120 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[3].m_InstanceExpireTime - value: 120 + value: 60 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceExpireTime @@ -694,11 +694,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[2].m_InstanceAutoReleaseInterval - value: 60 + value: 30 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[3].m_InstanceAutoReleaseInterval - value: 30 + value: 60 objectReference: {fileID: 0} - target: {fileID: 11494652, guid: adb3eb1c35fcff14f89fba7b05c9d71c, type: 3} propertyPath: m_EntityGroups.Array.data[4].m_InstanceAutoReleaseInterval diff --git a/数据表/Datas/EntityConfig_实体配置.xlsx b/数据表/Datas/EntityConfig_实体配置.xlsx index b6fb23d..64a95e2 100644 Binary files a/数据表/Datas/EntityConfig_实体配置.xlsx and b/数据表/Datas/EntityConfig_实体配置.xlsx differ diff --git a/数据表/Datas/GlobalConfig_全局配置.xlsx b/数据表/Datas/GlobalConfig_全局配置.xlsx index 9fff49a..3c16206 100644 Binary files a/数据表/Datas/GlobalConfig_全局配置.xlsx and b/数据表/Datas/GlobalConfig_全局配置.xlsx differ diff --git a/数据表/Datas/__enums__.xlsx b/数据表/Datas/__enums__.xlsx index c821c30..7d54eab 100644 Binary files a/数据表/Datas/__enums__.xlsx and b/数据表/Datas/__enums__.xlsx differ