diff --git a/DungeonShooting_Godot/src/game/role/PathSign.cs b/DungeonShooting_Godot/src/game/role/PathSign.cs index 3925315..cec2ea9 100644 --- a/DungeonShooting_Godot/src/game/role/PathSign.cs +++ b/DungeonShooting_Godot/src/game/role/PathSign.cs @@ -7,7 +7,7 @@ public class PathSign : Node2D, IDestroy { public bool IsDestroyed { get; private set; } - + /// /// 当前标记在整个链上的索引 /// @@ -27,37 +27,65 @@ /// 射线对象 /// public RayCast2D RayCast { get; } - - /// - /// 连接的上一个 PathSign - /// - public PathSign Prev { get; set; } - + /// /// 连接的下一个 PathSign /// public PathSign Next { get; set; } - private Vector2 _prevTargetPos; - + /// + /// 是否启用标记路径, 如果禁用, 将会清空所有标记 + /// + public bool Enable + { + get => _enable; + set + { + if (_enable && !value && Next != null) + { + Next.Destroy(); + Next = null; + } + + if (!value) + { + _isInRange = false; + _isCollision = false; + _targetPos = Vector2.Zero; + _isDiscoverTarget = false; + } + _enable = value; + } + } + + //是否发现过目标 + private bool _isDiscoverTarget = false; + //目标在视野范围内出现过的位置 + private Vector2 _targetPos; + //射线是否碰撞到目标 + private bool _isCollision; + //目标是否在范围内 + private bool _isInRange; + //是否启用 + private bool _enable = false; + /// /// 创建标记 /// - /// 坐标 + /// 挂载节点 /// 视野半径 /// 监视对象 - public PathSign(Vector2 pos, float viewRadius, Role target) : this(pos, viewRadius, target, 0, null) + public PathSign(Node2D root, float viewRadius, Role target) : this(root, Vector2.Zero, viewRadius, target, 0) { } - private PathSign(Vector2 pos, float viewRadius, Role target, int index, PathSign prev) + private PathSign(Node2D root, Vector2 pos, float viewRadius, Role target, int index) { Index = index; - Prev = prev; Target = target; ViewRadius = viewRadius; - GameApplication.Instance.Room.GetRoot(false).AddChild(this); - GlobalPosition = pos; + root.AddChild(this); + Position = pos; //目前只检测墙壁碰撞 RayCast = new RayCast2D(); @@ -85,24 +113,62 @@ public override void _PhysicsProcess(float delta) { - if (Next == null) + if (!_enable) { - //监视目标 - var targetPos = Target.GlobalPosition; - if (GlobalPosition.DistanceSquaredTo(targetPos) <= ViewRadius * ViewRadius) //在视野范围内 - { - RayCast.Enabled = true; - RayCast.CastTo = RayCast.ToLocal(targetPos); - RayCast.ForceRaycastUpdate(); - - if (RayCast.IsColliding()) - { - Next = new PathSign(_prevTargetPos, ViewRadius, Target, Index + 1, this); - } - RayCast.Enabled = false; - _prevTargetPos = targetPos; - } + return; } + //监视目标 + var nowTargetPos = Target.GlobalPosition; + var distanceSquared = GlobalPosition.DistanceSquaredTo(nowTargetPos); + var nowIsInRange = distanceSquared <= ViewRadius * ViewRadius; + + if (nowIsInRange) //在视野范围内 + { + var isCollision = Detect(nowTargetPos); + + if (isCollision) //碰到墙 + { + if (_isInRange && !_isCollision && Next == null) //如果上一帧就在视野内, 才能创建新的折点 + { + var distance = Mathf.Sqrt(distanceSquared); + Next = new PathSign(GameApplication.Instance.Room.GetRoot(false), _targetPos, ViewRadius - distance, Target, Index + 1); + Next._targetPos = nowTargetPos; + } + } + else //没有碰到墙 + { + if (Next != null) + { + Next.Destroy(); + Next = null; + } + _targetPos = nowTargetPos; + _isDiscoverTarget = true; + } + + _isCollision = isCollision; + } + else + { + _isCollision = false; + } + + _isInRange = nowIsInRange; + } + + /// + /// 检测射线是否碰到墙壁 + /// + /// + private bool Detect(Vector2 pos) + { + RayCast.Enabled = true; + RayCast.CastTo = RayCast.ToLocal(pos); + RayCast.ForceRaycastUpdate(); + + var flag = RayCast.IsColliding(); + RayCast.Enabled = false; + return flag; } public void Destroy() @@ -124,11 +190,23 @@ public override void _Draw() { - if (GameApplication.Instance.Debug) + if (GameApplication.Instance.Debug && _isDiscoverTarget) { if (Next != null) { - DrawLine(Vector2.Zero,ToLocal(Next.GlobalPosition), Colors.Red); + DrawLine(Vector2.Zero, ToLocal(Next.GlobalPosition), Colors.Blue); + } + else if (_isInRange && !_isCollision) + { + var pos = ToLocal(_targetPos); + DrawString(ResourceManager.Load(ResourcePath.resource_font_cn_font_12_tres), new Vector2(-6, 12), (ViewRadius - GlobalPosition.DistanceTo(_targetPos)).ToString()); + DrawLine(Vector2.Zero, pos, Colors.Red); + } + else + { + var pos = ToLocal(_targetPos); + DrawString(ResourceManager.Load(ResourcePath.resource_font_cn_font_12_tres), new Vector2(-6, 12), "0"); + DrawLine(Vector2.Zero, pos, Colors.Yellow); } } } diff --git a/DungeonShooting_Godot/src/game/role/Role.cs b/DungeonShooting_Godot/src/game/role/Role.cs index 24ecc4c..dfc1877 100644 --- a/DungeonShooting_Godot/src/game/role/Role.cs +++ b/DungeonShooting_Godot/src/game/role/Role.cs @@ -303,7 +303,7 @@ /// /// 使角色看向指定的坐标, - /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 会每帧更新玩家视野位置 + /// 注意, 调用该函数会清空 LookTarget, 因为拥有 LookTarget 时也会每帧更新玩家视野位置 /// /// public void LookTargetPosition(Vector2 pos) diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index 1a93b89..142ee21 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -41,11 +41,16 @@ //------------------- 寻路相关 --------------------------- - - private PathSign _pathSign; - //上一帧玩家位置 - private Vector2 _prevPlayerPos; - + /// + /// 移动目标标记 + /// + public PathSign TargetSign { get; } + + /// + /// 寻路标记线段总长度 + /// + public float PathSignLength { get; set; } = 500; + //------------------------------------------------------- public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn) @@ -66,6 +71,8 @@ StateController.Register(new AIRunState()); //默认状态 StateController.ChangeState(StateEnum.Idle); + + TargetSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player); } public override void _Process(float delta) @@ -84,52 +91,39 @@ var player = GameApplication.Instance.Room.Player; //玩家中心点坐标 var playerPos = player.MountPoint.GlobalPosition; - - //检测是否在视野内 - var pos = GlobalPosition; - + //玩家是否在前方 var isForward = IsPositionInForward(playerPos); if (isForward) //脸朝向玩家 { - if (pos.DistanceSquaredTo(playerPos) <= ViewRange * ViewRange) //没有超出视野半径 - { - //射线检测墙体 - ViewRay.Enabled = true; - var localPos = ViewRay.ToLocal(playerPos); - ViewRay.CastTo = localPos; - ViewRay.ForceRaycastUpdate(); - - if (ViewRay.IsColliding()) //在视野范围内, 但是碰到墙壁 - { - if (_pathSign == null) //路径标记 - { - _pathSign = new PathSign(_prevPlayerPos, ViewRange, player); - } - LookTarget = null; - StateController.ChangeState(StateEnum.Idle); - } - else //视野无阻 - { - if (_pathSign != null) //删除路径标记 - { - _pathSign.Destroy(); - _pathSign = null; - } - - LookTarget = player; - StateController.ChangeState(StateEnum.Run); - } - - ViewRay.Enabled = false; - } - else //超出视野半径 - { - LookTarget = null; - StateController.ChangeState(StateEnum.Idle); - } - _prevPlayerPos = playerPos; + // if (GlobalPosition.DistanceSquaredTo(playerPos) <= ViewRange * ViewRange) //没有超出视野半径 + // { + // //射线检测墙体 + // ViewRay.Enabled = true; + // var localPos = ViewRay.ToLocal(playerPos); + // ViewRay.CastTo = localPos; + // ViewRay.ForceRaycastUpdate(); + // + // if (ViewRay.IsColliding()) //在视野范围内, 但是碰到墙壁 + // { + // LookTarget = null; + // StateController.ChangeState(StateEnum.Idle); + // } + // else //视野无阻 + // { + // LookTarget = player; + // StateController.ChangeState(StateEnum.Run); + // } + // + // ViewRay.Enabled = false; + // } + // else //超出视野半径 + // { + // LookTarget = null; + // StateController.ChangeState(StateEnum.Idle); + // } + // //_prevPlayerPos = playerPos; } } @@ -137,9 +131,9 @@ { if (GameApplication.Instance.Debug) { - if (_pathSign != null) + if (TargetSign != null) { - DrawLine(Vector2.Zero,ToLocal(_pathSign.GlobalPosition), Colors.Red); + DrawLine(Vector2.Zero,ToLocal(TargetSign.GlobalPosition), Colors.Red); } } } diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs index a3c9331..35e709c 100644 --- a/DungeonShooting_Godot/src/game/room/RoomManager.cs +++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs @@ -61,9 +61,9 @@ /// /// 获取房间根节点 /// - /// + /// 是否获取 YSort 节点 /// - public Node2D GetRoot(bool useYSort) + public Node2D GetRoot(bool useYSort = false) { return useYSort ? SortRoot : ObjectRoot; }