diff --git a/DungeonShooting_Godot/excel/excelFile/ActivityBase.xlsx b/DungeonShooting_Godot/excel/excelFile/ActivityBase.xlsx index acccf7f..1c0ddcf 100644 --- a/DungeonShooting_Godot/excel/excelFile/ActivityBase.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/ActivityBase.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/ActivityMaterial.xlsx b/DungeonShooting_Godot/excel/excelFile/ActivityMaterial.xlsx index a4130c3..474fcbe 100644 --- a/DungeonShooting_Godot/excel/excelFile/ActivityMaterial.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/ActivityMaterial.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/AiAttackAttr.xlsx b/DungeonShooting_Godot/excel/excelFile/AiAttackAttr.xlsx index e635cb7..edb6abc 100644 --- a/DungeonShooting_Godot/excel/excelFile/AiAttackAttr.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/AiAttackAttr.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/BulletBase.xlsx b/DungeonShooting_Godot/excel/excelFile/BulletBase.xlsx index 9503aa3..0d2d771 100644 --- a/DungeonShooting_Godot/excel/excelFile/BulletBase.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/BulletBase.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/EnemyBase.xlsx b/DungeonShooting_Godot/excel/excelFile/EnemyBase.xlsx new file mode 100644 index 0000000..b3f6cab --- /dev/null +++ b/DungeonShooting_Godot/excel/excelFile/EnemyBase.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/Sound.xlsx b/DungeonShooting_Godot/excel/excelFile/Sound.xlsx index cf056c6..ad0d8fe 100644 --- a/DungeonShooting_Godot/excel/excelFile/Sound.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/Sound.xlsx Binary files differ diff --git a/DungeonShooting_Godot/excel/excelFile/WeaponBase.xlsx b/DungeonShooting_Godot/excel/excelFile/WeaponBase.xlsx index a859078..98e9781 100644 --- a/DungeonShooting_Godot/excel/excelFile/WeaponBase.xlsx +++ b/DungeonShooting_Godot/excel/excelFile/WeaponBase.xlsx Binary files differ diff --git a/DungeonShooting_Godot/resource/config/EnemyBase.json b/DungeonShooting_Godot/resource/config/EnemyBase.json new file mode 100644 index 0000000..968950a --- /dev/null +++ b/DungeonShooting_Godot/resource/config/EnemyBase.json @@ -0,0 +1,26 @@ +[ + { + "Id": "0001", + "__Activity": "enemy0001", + "Remark": "\u654C\u4EBA1", + "MoveSpeed": 120, + "Acceleration": 1500, + "Friction": 900, + "CanPickUpWeapon": true, + "ViewRange": 250, + "TailAfterViewRange": 400, + "BackViewRange": 50 + }, + { + "Id": "0002", + "__Activity": "enemy0002", + "Remark": "\u654C\u4EBA2", + "MoveSpeed": 120, + "Acceleration": 1500, + "Friction": 900, + "CanPickUpWeapon": false, + "ViewRange": 250, + "TailAfterViewRange": 400, + "BackViewRange": 50 + } +] \ No newline at end of file diff --git a/DungeonShooting_Godot/src/config/ExcelConfig.cs b/DungeonShooting_Godot/src/config/ExcelConfig.cs index f9d7eaf..32a1ea5 100644 --- a/DungeonShooting_Godot/src/config/ExcelConfig.cs +++ b/DungeonShooting_Godot/src/config/ExcelConfig.cs @@ -44,6 +44,15 @@ public static Dictionary BulletBase_Map { get; private set; } /// + /// EnemyBase.xlsx表数据集合, 以 List 形式存储, 数据顺序与 Excel 表相同 + /// + public static List EnemyBase_List { get; private set; } + /// + /// EnemyBase.xlsx表数据集合, 里 Map 形式存储, key 为 Id + /// + public static Dictionary EnemyBase_Map { get; private set; } + + /// /// Sound.xlsx表数据集合, 以 List 形式存储, 数据顺序与 Excel 表相同 /// public static List Sound_List { get; private set; } @@ -75,10 +84,12 @@ _InitActivityMaterialConfig(); _InitAiAttackAttrConfig(); _InitBulletBaseConfig(); + _InitEnemyBaseConfig(); _InitSoundConfig(); _InitWeaponBaseConfig(); _InitActivityBaseRef(); + _InitEnemyBaseRef(); _InitWeaponBaseRef(); } private static void _InitActivityBaseConfig() @@ -153,6 +164,24 @@ throw new Exception("初始化表'BulletBase'失败!"); } } + private static void _InitEnemyBaseConfig() + { + try + { + var text = _ReadConfigAsText("res://resource/config/EnemyBase.json"); + EnemyBase_List = new List(JsonSerializer.Deserialize>(text)); + EnemyBase_Map = new Dictionary(); + foreach (var item in EnemyBase_List) + { + EnemyBase_Map.Add(item.Id, item); + } + } + catch (Exception e) + { + GD.PrintErr(e.ToString()); + throw new Exception("初始化表'EnemyBase'失败!"); + } + } private static void _InitSoundConfig() { try @@ -209,6 +238,25 @@ } } } + private static void _InitEnemyBaseRef() + { + foreach (Ref_EnemyBase item in EnemyBase_List) + { + try + { + if (!string.IsNullOrEmpty(item.__Activity)) + { + item.Activity = ActivityBase_Map[item.__Activity]; + } + + } + catch (Exception e) + { + GD.PrintErr(e.ToString()); + throw new Exception("初始化'EnemyBase'引用其他表数据失败, 当前行id: " + item.Id); + } + } + } private static void _InitWeaponBaseRef() { foreach (Ref_WeaponBase item in WeaponBase_List) diff --git a/DungeonShooting_Godot/src/config/ExcelConfig_EnemyBase.cs b/DungeonShooting_Godot/src/config/ExcelConfig_EnemyBase.cs new file mode 100644 index 0000000..cedad80 --- /dev/null +++ b/DungeonShooting_Godot/src/config/ExcelConfig_EnemyBase.cs @@ -0,0 +1,94 @@ +using System.Text.Json.Serialization; +using System.Collections.Generic; + +namespace Config; + +public static partial class ExcelConfig +{ + public class EnemyBase + { + /// + /// 表Id + /// + [JsonInclude] + public string Id; + + /// + /// 绑定的ActivityBase表数据 + /// + public ActivityBase Activity; + + /// + /// 备注 + /// + [JsonInclude] + public string Remark; + + /// + /// 移动速度 + /// + [JsonInclude] + public float MoveSpeed; + + /// + /// 移动加速度 + /// + [JsonInclude] + public float Acceleration; + + /// + /// 移动摩擦力, 仅用于人物基础移动 + /// + [JsonInclude] + public float Friction; + + /// + /// 是否可以拾起武器 + /// + [JsonInclude] + public bool CanPickUpWeapon; + + /// + /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙 + /// + [JsonInclude] + public float ViewRange; + + /// + /// 发现玩家后跟随玩家的视野半径 + /// + [JsonInclude] + public float TailAfterViewRange; + + /// + /// 背后的视野半径, 单位像素 + /// + [JsonInclude] + public float BackViewRange; + + /// + /// 返回浅拷贝出的新对象 + /// + public EnemyBase Clone() + { + var inst = new EnemyBase(); + inst.Id = Id; + inst.Activity = Activity; + inst.Remark = Remark; + inst.MoveSpeed = MoveSpeed; + inst.Acceleration = Acceleration; + inst.Friction = Friction; + inst.CanPickUpWeapon = CanPickUpWeapon; + inst.ViewRange = ViewRange; + inst.TailAfterViewRange = TailAfterViewRange; + inst.BackViewRange = BackViewRange; + return inst; + } + } + private class Ref_EnemyBase : EnemyBase + { + [JsonInclude] + public string __Activity; + + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs index 0786aea..276b27d 100644 --- a/DungeonShooting_Godot/src/game/GameApplication.cs +++ b/DungeonShooting_Godot/src/game/GameApplication.cs @@ -91,6 +91,8 @@ InitRoomConfig(); //初始化武器数据 Weapon.InitWeaponAttribute(); + //初始化敌人数据 + Enemy.InitEnemyAttribute(); DungeonConfig = new DungeonConfig(); DungeonConfig.GroupName = RoomConfig.FirstOrDefault().Key; diff --git a/DungeonShooting_Godot/src/game/activity/package/Package.cs b/DungeonShooting_Godot/src/game/activity/package/Package.cs index 476b7e5..7c2d6e4 100644 --- a/DungeonShooting_Godot/src/game/activity/package/Package.cs +++ b/DungeonShooting_Godot/src/game/activity/package/Package.cs @@ -56,16 +56,15 @@ { capacity = 0; } - - if (capacity == Capacity) - { - return; - } - + if (ItemSlot == null) { ItemSlot = new T[capacity]; } + else if (capacity == Capacity) + { + return; + } else if (ItemSlot.Length > capacity) //删减格子 { var newArray = new T[capacity]; @@ -96,7 +95,6 @@ ItemSlot = newArray; } Capacity = capacity; - } /// diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs index f8d437d..c1daa8a 100644 --- a/DungeonShooting_Godot/src/game/activity/role/Role.cs +++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs @@ -16,7 +16,7 @@ /// /// 角色属性 /// - public RoleState RoleState { get; } = new RoleState(); + public RoleState RoleState { get; private set; } /// /// 默认攻击对象层级 @@ -339,6 +339,14 @@ ResourceManager.Load(ResourcePath.resource_spriteFrames_role_Role_tip_tres); } } + + /// + /// 创建角色的 RoleState 对象 + /// + protected virtual RoleState OnCreateRoleState() + { + return new RoleState(); + } /// /// 当血量改变时调用 @@ -449,8 +457,9 @@ public override void OnInit() { + RoleState = OnCreateRoleState(); ActivePropsPack = AddComponent>(); - ActivePropsPack.SetCapacity(1); + ActivePropsPack.SetCapacity(RoleState.CanPickUpWeapon ? 1 : 0); _startScale = Scale; diff --git a/DungeonShooting_Godot/src/game/activity/role/RoleState.cs b/DungeonShooting_Godot/src/game/activity/role/RoleState.cs index 8f96289..2af6bf3 100644 --- a/DungeonShooting_Godot/src/game/activity/role/RoleState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/RoleState.cs @@ -7,6 +7,11 @@ public class RoleState { /// + /// 是否可以拾起武器 + /// + public bool CanPickUpWeapon = false; + + /// /// 移动速度 /// public float MoveSpeed = 120f; @@ -22,21 +27,6 @@ public float Friction = 900f; /// - /// 翻滚速度 - /// - public float RollSpeed = 400f; - - /// - /// 翻滚持续时间 - /// - public float RollTime = 0.15f; - - /// - /// 翻滚冷却时间 - /// - public float RollCoolingTime = 0.5f; - - /// /// 单格护盾恢复时间, 单位: 秒 /// public float ShieldRecoveryTime = 18; diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs index c36937f..ae95977 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs @@ -11,6 +11,9 @@ #endregion +using System; +using System.Collections.Generic; +using Config; using EnemyState; using Godot; @@ -29,27 +32,6 @@ /// 敌人身上的状态机控制器 /// public StateController StateController { get; private set; } - - /// - /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙 - /// - public float ViewRange { get; set; } = 250; - - /// - /// 发现玩家后跟随玩家的视野半径 - /// - public float TailAfterViewRange { get; set; } = 400; - - /// - /// 背后的视野半径, 单位像素 - /// - public float BackViewRange { get; set; } = 50; - - /// - /// 是否可以拾起武器 - /// - [Export] - public bool CanPickUpWeapon { get; set; } = true; /// /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙 @@ -89,16 +71,66 @@ /// 锁定目标已经走过的时间 /// public float LockTargetTime { get; set; } = 0; + + /// + /// 敌人属性 + /// + public EnemyRoleState EnemyRoleState { get; private set; } + /// + /// 敌人属性 + /// + private ExcelConfig.EnemyBase _enemyAttribute; + + private static bool _init = false; + private static Dictionary _enemyAttributeMap = + new Dictionary(); + + /// + /// 初始化敌人属性数据 + /// + public static void InitEnemyAttribute() + { + if (_init) + { + return; + } + + _init = true; + foreach (var enemyAttr in ExcelConfig.EnemyBase_List) + { + if (enemyAttr.Activity != null) + { + if (!_enemyAttributeMap.TryAdd(enemyAttr.Activity.Id, enemyAttr)) + { + Debug.LogError("发现重复注册的敌人属性: " + enemyAttr.Id); + } + } + } + } + + /// + /// 根据 ActivityBase.Id 获取对应敌人的属性数据 + /// + public static ExcelConfig.EnemyBase GetEnemyAttribute(string itemId) + { + if (itemId == null) + { + return null; + } + if (_enemyAttributeMap.TryGetValue(itemId, out var attr)) + { + return attr; + } + + throw new Exception($"敌人'{itemId}'没有在 EnemyBase 表中配置属性数据!"); + } + public override void OnInit() { base.OnInit(); + IsAi = true; - - if (!CanPickUpWeapon) - { - WeaponPack.SetCapacity(0); - } StateController = AddComponent>(); @@ -126,6 +158,23 @@ StateController.ChangeStateInstant(AIStateEnum.AiNormal); } + protected override RoleState OnCreateRoleState() + { + var roleState = new EnemyRoleState(); + EnemyRoleState = roleState; + var enemyBase = GetEnemyAttribute(ActivityBase.Id).Clone(); + _enemyAttribute = enemyBase; + + roleState.CanPickUpWeapon = enemyBase.CanPickUpWeapon; + roleState.MoveSpeed = enemyBase.MoveSpeed; + roleState.Acceleration = enemyBase.Acceleration; + roleState.Friction = enemyBase.Friction; + roleState.ViewRange = enemyBase.ViewRange; + roleState.TailAfterViewRange = enemyBase.TailAfterViewRange; + roleState.BackViewRange = enemyBase.BackViewRange; + return roleState; + } + public override void EnterTree() { if (!World.Enemy_InstanceList.Contains(this)) @@ -199,7 +248,7 @@ public override bool IsAllWeaponTotalAmmoEmpty() { - if (!CanPickUpWeapon) + if (!_enemyAttribute.CanPickUpWeapon) { return false; } @@ -348,7 +397,7 @@ var isForward = IsPositionInForward(target); if (isForward) { - if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径 + if (GlobalPosition.DistanceSquaredTo(target) <= EnemyRoleState.ViewRange * EnemyRoleState.ViewRange) //没有超出视野半径 { return true; } @@ -365,7 +414,7 @@ var isForward = IsPositionInForward(target); if (isForward) { - if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径 + if (GlobalPosition.DistanceSquaredTo(target) <= EnemyRoleState.TailAfterViewRange * EnemyRoleState.TailAfterViewRange) //没有超出视野半径 { return true; } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/EnemyRoleState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/EnemyRoleState.cs new file mode 100644 index 0000000..174b52e --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/EnemyRoleState.cs @@ -0,0 +1,18 @@ + +public class EnemyRoleState : RoleState +{ + /// + /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙 + /// + public float ViewRange = 250; + + /// + /// 发现玩家后跟随玩家的视野半径 + /// + public float TailAfterViewRange = 400; + + /// + /// 背后的视野半径, 单位像素 + /// + public float BackViewRange = 50; +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs index cd8a3c4..c6ae3d6 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs @@ -72,7 +72,7 @@ } else { - inAttackRange = distanceSquared <= Mathf.Pow(Master.ViewRange * 0.7f, 2); + inAttackRange = distanceSquared <= Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2); } if (!Master.NavigationAgent2D.IsNavigationFinished()) @@ -121,7 +121,7 @@ else { //距离够近, 可以切换到环绕模式 - if (distanceSquared <= Mathf.Pow(Master.ViewRange * 0.7f, 2)) + if (distanceSquared <= Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2)) { ChangeState(AIStateEnum.AiSurround); } diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs index 968ef0d..2d3c914 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs @@ -151,7 +151,7 @@ } else { - if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.ViewRange * 0.7f, 2)) //玩家离开正常射击范围 + if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2)) //玩家离开正常射击范围 { ChangeState(AIStateEnum.AiFollowUp); } diff --git a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs index 3cacc51..72c65e9 100644 --- a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs +++ b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs @@ -18,6 +18,8 @@ /// public StateController StateController { get; private set; } + public PlayerRoleState PlayerRoleState { get; private set; } + /// /// 是否可以翻滚 /// @@ -78,6 +80,13 @@ //InitSubLine(); } + protected override RoleState OnCreateRoleState() + { + var roleState = new PlayerRoleState(); + PlayerRoleState = roleState; + return roleState; + } + protected override void Process(float delta) { base.Process(delta); @@ -352,7 +361,7 @@ /// public void OverRoll() { - _rollCoolingTimer = RoleState.RollCoolingTime; + _rollCoolingTimer = PlayerRoleState.RollCoolingTime; } // protected override void DebugDraw() diff --git a/DungeonShooting_Godot/src/game/activity/role/player/PlayerRoleState.cs b/DungeonShooting_Godot/src/game/activity/role/player/PlayerRoleState.cs new file mode 100644 index 0000000..a276605 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/player/PlayerRoleState.cs @@ -0,0 +1,18 @@ + +public class PlayerRoleState : RoleState +{ + /// + /// 翻滚速度 + /// + public float RollSpeed = 400f; + + /// + /// 翻滚持续时间 + /// + public float RollTime = 0.15f; + + /// + /// 翻滚冷却时间 + /// + public float RollCoolingTime = 0.5f; +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/player/state/PlayerRollState.cs b/DungeonShooting_Godot/src/game/activity/role/player/state/PlayerRollState.cs index 5817928..f108c44 100644 --- a/DungeonShooting_Godot/src/game/activity/role/player/state/PlayerRollState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/player/state/PlayerRollState.cs @@ -31,7 +31,7 @@ //翻滚移动方向 _moveDir = InputManager.MoveAxis; - Master.BasisVelocity = _moveDir * Master.RoleState.RollSpeed; + Master.BasisVelocity = _moveDir * Master.PlayerRoleState.RollSpeed; } public override void Exit(PlayerStateEnum next) @@ -46,7 +46,7 @@ public override void Process(float delta) { - Master.BasisVelocity = _moveDir * Master.RoleState.RollSpeed; + Master.BasisVelocity = _moveDir * Master.PlayerRoleState.RollSpeed; } //翻滚逻辑处理 @@ -55,7 +55,7 @@ Master.AnimationPlayer.Play(AnimatorNames.Roll); var time = 0f; var time2 = 0f; - while (time < Master.RoleState.RollTime) + while (time < Master.PlayerRoleState.RollTime) { var delta = (float)Master.GetProcessDeltaTime(); time += delta; diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs index b54c0b3..62cb32e 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs @@ -279,7 +279,7 @@ return attr; } - throw new Exception($"武器'{itemId}'没有在 Weapon 表中配置属性数据!"); + throw new Exception($"武器'{itemId}'没有在 WeaponBase 表中配置属性数据!"); } public override void OnInit() @@ -291,7 +291,7 @@ /// /// 初始化武器属性 /// - public void InitWeapon(ExcelConfig.WeaponBase attribute) + private void InitWeapon(ExcelConfig.WeaponBase attribute) { _playerWeaponAttribute = attribute; _weaponAttribute = attribute;