diff --git a/DungeonShooting_Godot/Silver.ttf b/DungeonShooting_Godot/Silver.ttf new file mode 100644 index 0000000..9d568c4 --- /dev/null +++ b/DungeonShooting_Godot/Silver.ttf Binary files differ diff --git a/DungeonShooting_Godot/prefab/role/Enemy.tscn b/DungeonShooting_Godot/prefab/role/Enemy.tscn index 335ac3f..0315428 100644 --- a/DungeonShooting_Godot/prefab/role/Enemy.tscn +++ b/DungeonShooting_Godot/prefab/role/Enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://prefab/role/Role.tscn" type="PackedScene" id=1] [ext_resource path="res://resource/materlal/Blend.gdshader" type="Shader" id=2] @@ -15,6 +15,9 @@ shader_param/blend = Color( 1, 1, 1, 1 ) shader_param/schedule = 0.0 +[sub_resource type="CircleShape2D" id=3] +radius = 13.0 + [node name="Enemy" instance=ExtResource( 1 )] CollisionLayer = 16 @@ -23,7 +26,13 @@ [node name="AnimatedSprite" parent="." index="2"] material = SubResource( 2 ) -frame = 1 [node name="ViewRay" type="RayCast2D" parent="." index="6"] position = Vector2( 0, -8 ) + +[node name="MarginArea" type="Area2D" parent="." index="8"] +collision_layer = 0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="MarginArea" index="0"] +position = Vector2( 0, -8 ) +shape = SubResource( 3 ) diff --git a/DungeonShooting_Godot/project.godot b/DungeonShooting_Godot/project.godot index e1ec7c8..c5ef034 100644 --- a/DungeonShooting_Godot/project.godot +++ b/DungeonShooting_Godot/project.godot @@ -39,7 +39,7 @@ [gui] theme/custom="res://resource/theme/mainTheme.tres" -theme/custom_font="res://resource/font/cn_font_18.tres" +theme/custom_font="res://resource/font/cn_font_36.tres" [importer_defaults] @@ -173,6 +173,7 @@ 2d_physics/layer_3="props" 2d_physics/layer_4="player" 2d_physics/layer_5="enemy" +2d_physics/layer_6="view" [mono] diff --git a/DungeonShooting_Godot/resource/font/cn_font_12.tres b/DungeonShooting_Godot/resource/font/cn_font_12.tres index dbd8053..fd3c470 100644 --- a/DungeonShooting_Godot/resource/font/cn_font_12.tres +++ b/DungeonShooting_Godot/resource/font/cn_font_12.tres @@ -1,6 +1,6 @@ [gd_resource type="DynamicFont" load_steps=2 format=2] -[ext_resource path="res://SourceHanSerifCN-SemiBold.otf" type="DynamicFontData" id=1] +[ext_resource path="res://Silver.ttf" type="DynamicFontData" id=1] [resource] size = 12 diff --git a/DungeonShooting_Godot/resource/font/cn_font_18.tres b/DungeonShooting_Godot/resource/font/cn_font_18.tres index 032a49a..a1de47c 100644 --- a/DungeonShooting_Godot/resource/font/cn_font_18.tres +++ b/DungeonShooting_Godot/resource/font/cn_font_18.tres @@ -1,6 +1,6 @@ [gd_resource type="DynamicFont" load_steps=2 format=2] -[ext_resource path="res://SourceHanSerifCN-SemiBold.otf" type="DynamicFontData" id=1] +[ext_resource path="res://Silver.ttf" type="DynamicFontData" id=1] [resource] size = 18 diff --git a/DungeonShooting_Godot/resource/font/cn_font_35.tres b/DungeonShooting_Godot/resource/font/cn_font_35.tres deleted file mode 100644 index f4e81ad..0000000 --- a/DungeonShooting_Godot/resource/font/cn_font_35.tres +++ /dev/null @@ -1,8 +0,0 @@ -[gd_resource type="DynamicFont" load_steps=2 format=2] - -[ext_resource path="res://SourceHanSerifCN-SemiBold.otf" type="DynamicFontData" id=1] - -[resource] -size = 35 -extra_spacing_char = 1 -font_data = ExtResource( 1 ) diff --git a/DungeonShooting_Godot/resource/font/cn_font_36.tres b/DungeonShooting_Godot/resource/font/cn_font_36.tres new file mode 100644 index 0000000..b3ea792 --- /dev/null +++ b/DungeonShooting_Godot/resource/font/cn_font_36.tres @@ -0,0 +1,7 @@ +[gd_resource type="DynamicFont" load_steps=2 format=2] + +[ext_resource path="res://Silver.ttf" type="DynamicFontData" id=1] + +[resource] +size = 36 +font_data = ExtResource( 1 ) diff --git a/DungeonShooting_Godot/scene/test/TestNavigation.tscn b/DungeonShooting_Godot/scene/test/TestNavigation.tscn index d310e7d..baf5acf 100644 --- a/DungeonShooting_Godot/scene/test/TestNavigation.tscn +++ b/DungeonShooting_Godot/scene/test/TestNavigation.tscn @@ -4,7 +4,6 @@ [ext_resource path="res://icon.png" type="Texture" id=2] [ext_resource path="res://resource/sprite/environment/craftpix-net-248911/16x16.png" type="Texture" id=3] - [sub_resource type="NavigationPolygon" id=2] vertices = PoolVector2Array( 0, 0, 16, 0, 16, 16, 0, 16 ) polygons = [ PoolIntArray( 0, 1, 2, 3 ) ] @@ -48,6 +47,7 @@ 0/z_index = 0 [node name="TestNavigation" type="Node2D"] +scale = Vector2( 4, 4 ) script = ExtResource( 1 ) [node name="Position2D" type="Position2D" parent="."] diff --git a/DungeonShooting_Godot/src/game/PhysicsLayer.cs b/DungeonShooting_Godot/src/game/PhysicsLayer.cs index 36a31e5..1d4bb83 100644 --- a/DungeonShooting_Godot/src/game/PhysicsLayer.cs +++ b/DungeonShooting_Godot/src/game/PhysicsLayer.cs @@ -23,4 +23,8 @@ /// 敌人 /// public const uint Enemy = 16; + /// + /// 视野遮挡 + /// + public const uint View = 32; } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/PathSign.cs b/DungeonShooting_Godot/src/game/role/PathSign.cs index cec2ea9..a3487ed 100644 --- a/DungeonShooting_Godot/src/game/role/PathSign.cs +++ b/DungeonShooting_Godot/src/game/role/PathSign.cs @@ -58,6 +58,15 @@ } } + /// + /// 目标出现过的位置 + /// + public Vector2 TargetPosition + { + get => _targetPos; + set => _targetPos = value; + } + //是否发现过目标 private bool _isDiscoverTarget = false; //目标在视野范围内出现过的位置 @@ -133,6 +142,7 @@ 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 //没有碰到墙 diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index 626896f..7eb9c12 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -44,7 +44,7 @@ /// /// 移动目标标记 /// - public PathSign TargetSign { get; } + public PathSign PathSign { get; } /// /// 寻路标记线段总长度 @@ -52,6 +52,8 @@ public float PathSignLength { get; set; } = 500; //------------------------------------------------------- + + private Area2D _marginArea; public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn) { @@ -65,21 +67,40 @@ //视野射线 ViewRay = GetNode("ViewRay"); + _marginArea = GetNode("MarginArea"); + + PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player); //注册Ai状态机 StateController.Register(new AINormalState()); + StateController.Register(new AIProbeState()); StateController.Register(new AITailAfterState()); + } + + public override void _Ready() + { + base._Ready(); //默认状态 StateController.ChangeState(AIStateEnum.AINormal); - TargetSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player); + _marginArea.Connect("body_shape_entered", this, nameof(OnObjectEnter)); } + public void OnObjectEnter(RID id, Node node, int shapeIndex, int localShapeIndex) + { + if (node is TileMap tileMap) + { + // var tileGetShape = tileMap.TileSet.TileGetShapeTransform(shapeIndex, localShapeIndex).; + // GD.Print("enter: ", tileGetShape.GetType().FullName); + } + } + public override void _Process(float delta) { base._Process(delta); if (GameApplication.Instance.Debug) { + PathSign.Scale = new Vector2((int)Face, 1); Update(); } } @@ -88,52 +109,52 @@ { base._PhysicsProcess(delta); - var player = GameApplication.Instance.Room.Player; - //玩家中心点坐标 - var playerPos = player.MountPoint.GlobalPosition; - - //玩家是否在前方 - var isForward = IsPositionInForward(playerPos); - - if (isForward) //脸朝向玩家 - { - // 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; - } + // var player = GameApplication.Instance.Room.Player; + // //玩家中心点坐标 + // var playerPos = player.MountPoint.GlobalPosition; + // + // //玩家是否在前方 + // var isForward = IsPositionInForward(playerPos); + // + // if (isForward) //脸朝向玩家 + // { + // // 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; + // } } public override void _Draw() { if (GameApplication.Instance.Debug) { - if (TargetSign != null) + if (PathSign != null) { - DrawLine(Vector2.Zero,ToLocal(TargetSign.GlobalPosition), Colors.Red); + DrawLine(Vector2.Zero,ToLocal(PathSign.GlobalPosition), Colors.Red); } } } diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs index 664b8d6..5733bec 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs @@ -9,11 +9,38 @@ public StateController StateController { get; set; } public void Enter(AIStateEnum prev, params object[] args) { - + Master.PathSign.Enable = false; } public void PhysicsProcess(float delta) { + //检测玩家 + var player = GameApplication.Instance.Room.Player; + //玩家中心点坐标 + var playerPos = player.MountPoint.GlobalPosition; + + //玩家是否在前方 + var isForward = Master.IsPositionInForward(playerPos); + + if (isForward) //脸朝向玩家 + { + if (Master.GlobalPosition.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange) //没有超出视野半径 + { + //射线检测墙体 + Master.ViewRay.Enabled = true; + var localPos = Master.ViewRay.ToLocal(playerPos); + Master.ViewRay.CastTo = localPos; + Master.ViewRay.ForceRaycastUpdate(); + + if (!Master.ViewRay.IsColliding()) //视野无阻 + { + //发现玩家, 切换状态 + StateController.ChangeStateLate(AIStateEnum.AITailAfter); + } + + Master.ViewRay.Enabled = false; + } + } } diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs index 1a84a2d..170cb24 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs @@ -9,17 +9,62 @@ public StateController StateController { get; set; } public void Enter(AIStateEnum prev, params object[] args) { - + Master.PathSign.Enable = true; } public void PhysicsProcess(float delta) { var master = Master; - if (master.LookTarget != null) + var targetSign = master.PathSign; + var enemyPos = master.GlobalPosition; + if (targetSign.Next == null) { - master.AnimatedSprite.Animation = AnimatorNames.ReverseRun; - master.Velocity = (master.LookTarget.GlobalPosition - master.GlobalPosition).Normalized() * master.MoveSpeed; - master.CalcMove(delta); + 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); + } } }