using Godot; /// <summary> /// 寻路标记, 记录下Role移动过的位置, 用于Ai寻路 /// </summary> public class PathSign : Node2D, IDestroy { public bool IsDestroyed { get; private set; } /// <summary> /// 当前标记在整个链上的索引 /// </summary> public int Index { get; } /// <summary> /// 监视的对象 /// </summary> public Role Target { get; } /// <summary> /// 视野半径 /// </summary> public float ViewRadius { get; } /// <summary> /// 射线对象 /// </summary> public RayCast2D RayCast { get; } /// <summary> /// 连接的下一个 PathSign /// </summary> public PathSign Next { get; set; } /// <summary> /// 是否启用标记路径, 如果禁用, 将会清空所有标记 /// </summary> 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; } } /// <summary> /// 目标出现过的位置 /// </summary> public Vector2 TargetPosition { get => _targetPos; set => _targetPos = value; } //是否发现过目标 private bool _isDiscoverTarget = false; //目标在视野范围内出现过的位置 private Vector2 _targetPos; //射线是否碰撞到目标 private bool _isCollision; //目标是否在范围内 private bool _isInRange; //是否启用 private bool _enable = false; /// <summary> /// 创建标记 /// </summary> /// <param name="root">挂载节点</param> /// <param name="viewRadius">视野半径</param> /// <param name="target">监视对象</param> public PathSign(Node2D root, float viewRadius, Role target) : this(root, Vector2.Zero, viewRadius, target, 0) { } private PathSign(Node2D root, Vector2 pos, float viewRadius, Role target, int index) { Index = index; Target = target; ViewRadius = viewRadius; root.AddChild(this); Position = pos; //目前只检测墙壁碰撞 RayCast = new RayCast2D(); RayCast.CollisionMask = PhysicsLayer.Wall; AddChild(RayCast); //绘制箭头 if (GameApplication.Instance.Debug) { var sprite = new Sprite(); sprite.Texture = ResourceManager.Load<Texture>(ResourcePath.resource_effects_debug_arrows_png); sprite.Position = new Vector2(0, -sprite.Texture.GetHeight() * 0.5f); AddChild(sprite); } } public override void _Process(float delta) { base._Process(delta); if (GameApplication.Instance.Debug) { Update(); } } public override void _PhysicsProcess(float delta) { if (!_enable) { 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; Next.Enable = true; } } else //没有碰到墙 { if (Next != null) { Next.Destroy(); Next = null; } _targetPos = nowTargetPos; _isDiscoverTarget = true; } _isCollision = isCollision; } else { _isCollision = false; } _isInRange = nowIsInRange; } /// <summary> /// 检测射线是否碰到墙壁 /// </summary> /// <returns></returns> 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() { if (IsDestroyed) { return; } IsDestroyed = true; if (Next != null) { Next.Destroy(); } QueueFree(); } public override void _Draw() { if (GameApplication.Instance.Debug && _isDiscoverTarget) { if (Next != null) { DrawLine(Vector2.Zero, ToLocal(Next.GlobalPosition), Colors.Blue); } else if (_isInRange && !_isCollision) { var pos = ToLocal(_targetPos); DrawString(ResourceManager.Load<Font>(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<Font>(ResourcePath.resource_font_cn_font_12_tres), new Vector2(-6, 12), "0"); DrawLine(Vector2.Zero, pos, Colors.Yellow); } } } } #region 备份代码 /* 第一个绑定在Role身上的点, 需要每一帧更新 if (Master.PathSign.Enable) { var targetSign = master.PathSign; var enemyPos = master.GlobalPosition; if (targetSign.Next == null) { var targetPosition = targetSign.TargetPosition; if (enemyPos.DistanceSquaredTo(targetPosition) <= master.Velocity.LengthSquared() * delta) //移动到下一个节点了, 还是没有找到目标, 变为第二状态 { StateController.ChangeStateLate(AIStateEnum.AINormal); } else //继续移动 { master.LookTargetPosition(targetPosition); master.AnimatedSprite.Animation = AnimatorNames.Run; master.Velocity = (targetPosition - enemyPos).Normalized() * master.MoveSpeed; master.CalcMove(delta); } } else { var nextPos = targetSign.Next.GlobalPosition; if (enemyPos.DistanceSquaredTo(nextPos) <= master.Velocity.LengthSquared() * delta) //已经移动到下一个节点了, 删除下一个节点, 后面的接上 { var nextNext = targetSign.Next.Next; var tempPos = targetSign.Next.TargetPosition; targetSign.Next.Next = null; targetSign.Next.Destroy(); targetSign.Next = nextNext; if (nextNext != null) //下一个点继续移动 { nextPos = nextNext.GlobalPosition; master.LookTargetPosition(nextPos); master.AnimatedSprite.Animation = AnimatorNames.Run; master.Velocity = (nextPos - enemyPos).Normalized() * master.MoveSpeed; master.CalcMove(delta); } else { targetSign.TargetPosition = tempPos; } } else //继续移动 { master.LookTargetPosition(nextPos); master.AnimatedSprite.Animation = AnimatorNames.Run; master.Velocity = (nextPos - enemyPos).Normalized() * master.MoveSpeed; master.CalcMove(delta); } } } */ #endregion