diff --git a/DungeonShooting_Godot/prefab/role/Player.tscn b/DungeonShooting_Godot/prefab/role/Player.tscn index 5786938..7154560 100644 --- a/DungeonShooting_Godot/prefab/role/Player.tscn +++ b/DungeonShooting_Godot/prefab/role/Player.tscn @@ -24,4 +24,4 @@ [node name="AnimatedSprite" parent="." index="2"] material = SubResource( 2 ) -frame = 2 +frame = 1 diff --git a/DungeonShooting_Godot/scene/Main.tscn b/DungeonShooting_Godot/scene/Main.tscn index 35a4274..aff7b6d 100644 --- a/DungeonShooting_Godot/scene/Main.tscn +++ b/DungeonShooting_Godot/scene/Main.tscn @@ -24,7 +24,6 @@ [node name="Main" type="Node2D"] script = ExtResource( 3 ) -Debug = true CursorPack = ExtResource( 4 ) RoomPath = NodePath("ViewCanvas/ViewportContainer/Viewport/Room") ViewportPath = NodePath("ViewCanvas/ViewportContainer/Viewport") diff --git a/DungeonShooting_Godot/src/game/event/EventBinder.cs b/DungeonShooting_Godot/src/game/event/EventBinder.cs new file mode 100644 index 0000000..1912fd7 --- /dev/null +++ b/DungeonShooting_Godot/src/game/event/EventBinder.cs @@ -0,0 +1,35 @@ + +using System; + +/// +/// 事件绑定对象 +/// +public class EventBinder +{ + /// + /// 事件类型 + /// + public readonly EventEnum EventType; + /// + /// 事件回调函数 + /// + public readonly Action Callback; + /// + /// 该监听事件是否被移除 + /// + public bool IsDiscard; + + public EventBinder(EventEnum eventType, Action callback) + { + EventType = eventType; + Callback = callback; + } + + /// + /// 移除当前监听事件 + /// + public void RemoveEventListener() + { + EventManager.RemoveEventListener(this); + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/event/EventEnum.cs b/DungeonShooting_Godot/src/game/event/EventEnum.cs new file mode 100644 index 0000000..ec25a87 --- /dev/null +++ b/DungeonShooting_Godot/src/game/event/EventEnum.cs @@ -0,0 +1,8 @@ + +/// +/// 事件类型枚举 +/// +public enum EventEnum +{ + Test, +} diff --git a/DungeonShooting_Godot/src/game/event/EventManager.cs b/DungeonShooting_Godot/src/game/event/EventManager.cs new file mode 100644 index 0000000..14008c5 --- /dev/null +++ b/DungeonShooting_Godot/src/game/event/EventManager.cs @@ -0,0 +1,115 @@ + +using System; +using System.Collections.Generic; + +/// +/// 事件管理器 +/// +public static class EventManager +{ + + private static readonly Dictionary> _eventMap = + new Dictionary>(); + + /// + /// 添加监听事件, 并返回事件绑定对象 + /// + /// 监听事件类型 + /// 回调函数 + public static EventBinder AddEventListener(EventEnum eventType, Action callback) + { + EventBinder binder; + if (!_eventMap.TryGetValue(eventType, out var list)) + { + list = new List(); + _eventMap.Add(eventType, list); + binder = new EventBinder(eventType, callback); + list.Add(binder); + return binder; + } + + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item.Callback == callback) + { + return item; + } + } + + binder = new EventBinder(eventType, callback); + list.Add(binder); + return binder; + } + + /// + /// 派发事件 + /// + /// 事件类型 + /// 传递参数 + public static void EmitEvent(EventEnum eventType, object arg = null) + { + if (_eventMap.TryGetValue(eventType, out var list)) + { + var binders = list.ToArray(); + for (var i = 0; i < binders.Length; i++) + { + var binder = binders[i]; + if (!binder.IsDiscard) + { + binder.Callback(arg); + } + } + } + } + + /// + /// 根据事件绑定对象移除一个监听事件 + /// + public static void RemoveEventListener(EventBinder binder) + { + if (_eventMap.TryGetValue(binder.EventType, out var list)) + { + if (list.Remove(binder)) + { + binder.IsDiscard = true; + if (list.Count == 0) + { + _eventMap.Remove(binder.EventType); + } + } + } + } + + /// + /// 移除指定类型的所有事件 + /// + public static void RemoveAllEventListener(EventEnum eventType) + { + if (_eventMap.TryGetValue(eventType, out var list)) + { + foreach (var binder in list) + { + binder.IsDiscard = true; + } + + _eventMap.Remove(eventType); + } + } + + /// + /// 移除所有监听事件 + /// + public static void ClearAllEventListener() + { + foreach (var kv in _eventMap) + { + foreach (var binder in kv.Value) + { + binder.IsDiscard = true; + } + } + + _eventMap.Clear(); + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs index 6eb52b6..914722a 100644 --- a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs +++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs @@ -26,7 +26,7 @@ private float MaxDistance; // 子弹飞行速度 - private float FlySpeed = 450; + private float FlySpeed = 350; //当前子弹已经飞行的距离 private float CurrFlyDistance = 0; diff --git a/DungeonShooting_Godot/src/game/role/StateController.cs b/DungeonShooting_Godot/src/game/role/StateController.cs index f12bca1..d3c40b4 100644 --- a/DungeonShooting_Godot/src/game/role/StateController.cs +++ b/DungeonShooting_Godot/src/game/role/StateController.cs @@ -61,6 +61,19 @@ } /// + /// 获取指定状态对应的实例对象 + /// + public StateBase GetState(S state) + { + if (_states.ContainsKey(state)) + { + return _states[state]; + } + + return null; + } + + /// /// 立即切换到下一个指定状态, 并且这一帧会被调用 PhysicsProcess /// public void ChangeState(S next, params object[] arg) diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index 9016553..3c3e222 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -11,6 +11,7 @@ #endregion +using System.Collections.Generic; using Godot; /// @@ -23,7 +24,9 @@ /// 公共属性, 是否找到玩家, 如果找到玩家, 则所有敌人都会知道玩家的位置 /// public static bool IsFindPlayer { get; set; } - + + private static readonly List _enemies = new List(); + /// /// 敌人身上的状态机控制器 /// @@ -85,6 +88,7 @@ StateController.Register(new AiNormalState()); StateController.Register(new AiProbeState()); StateController.Register(new AiTailAfterState()); + StateController.Register(new AIAttackState()); } public override void _Ready() @@ -96,6 +100,19 @@ NavigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition); } + public override void _EnterTree() + { + if (!_enemies.Contains(this)) + { + _enemies.Add(this); + } + } + + public override void _ExitTree() + { + _enemies.Remove(this); + } + public override void _PhysicsProcess(float delta) { base._PhysicsProcess(delta); @@ -104,6 +121,19 @@ } /// + /// 更新敌人视野 + /// + public static void UpdateEnemiesView() + { + for (var i = 0; i < _enemies.Count; i++) + { + var enemy = _enemies[i]; + + //enemy.StateController.GetState() + } + } + + /// /// Ai触发的攻击 /// public void EnemyAttack() diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AIAttackState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AIAttackState.cs new file mode 100644 index 0000000..ffc2b6e --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AIAttackState.cs @@ -0,0 +1,28 @@ + +using Godot; + +/// +/// 目标在视野范围内, 发起攻击 +/// +public class AIAttackState : StateBase +{ + public AIAttackState() : base(AIStateEnum.AIAttack) + { + } + + public override void Enter(AIStateEnum prev, params object[] args) + { + + } + + public override void PhysicsProcess(float delta) + { + + } + + public override void DebugDraw() + { + var playerPos = GameApplication.Instance.Room.Player.GlobalPosition; + Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Red); + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AIStateEnum.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AIStateEnum.cs index 9553cbd..6e94ffa 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AIStateEnum.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AIStateEnum.cs @@ -13,4 +13,8 @@ /// 发现目标, 并且知道位置 /// AITailAfter, + /// + /// 目标在视野内, 发起攻击 + /// + AIAttack, } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs index 35e44bd..8dbf8e4 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs @@ -36,7 +36,7 @@ _againstWallNormalAngle = 0; _pauseTimer = 0; } - + public override void PhysicsProcess(float delta) { @@ -77,7 +77,7 @@ if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点 { - _pauseTimer = 1; + _pauseTimer = Utils.RandRange(0.3f, 2f); _isMoveOver = true; } } diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs index 909715f..c088acf 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs @@ -6,16 +6,20 @@ /// public class AiTailAfterState : StateBase { + /// + /// 目标是否在视野半径内 + /// + public bool IsInViewRange; + + /// + /// 是否在视野内 + /// + public bool IsInView; + //导航目标点刷新计时器 private float _navigationUpdateTimer = 0; private float _navigationInterval = 0.3f; - //目标是否在视野半径内 - private bool _isInViewRange; - - //是否在视野内 - private bool _isInView; - //目标从视野消失时已经过去的时间 private float _viewTimer; @@ -25,8 +29,8 @@ public override void Enter(AIStateEnum prev, params object[] args) { - _isInViewRange = true; - _isInView = true; + IsInViewRange = true; + IsInView = true; _navigationUpdateTimer = 0; _viewTimer = 0; } @@ -62,27 +66,27 @@ if (masterPos.DistanceSquaredTo(playerPos) <= Master.TailAfterViewRange * Master.TailAfterViewRange) { - _isInView = !Master.TestViewRayCast(playerPos); - if (_isInView) + IsInView = !Master.TestViewRayCast(playerPos); + if (IsInView) { - _isInViewRange = true; + IsInViewRange = true; } else { - _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange; + IsInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange; } } else { - _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange; - _isInView = false; + IsInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange; + IsInView = false; } - if (_isInViewRange) + if (IsInViewRange) { _viewTimer = 0; - if (_isInView) + if (IsInView) { //攻击 Master.EnemyAttack(); @@ -104,11 +108,11 @@ public override void DebugDraw() { var playerPos = GameApplication.Instance.Room.Player.GlobalPosition; - if (_isInView) + if (IsInView) { Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Red); } - else if (_isInViewRange) + else if (IsInViewRange) { Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Orange); } diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs index 9643006..66265a7 100644 --- a/DungeonShooting_Godot/src/game/room/RoomManager.cs +++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs @@ -112,6 +112,7 @@ public override void _Process(float delta) { + Enemy.UpdateEnemiesView(); if (GameApplication.Instance.Debug) { Update();