diff --git a/DungeonShooting_Godot/DungeonShooting.sln.DotSettings.user b/DungeonShooting_Godot/DungeonShooting.sln.DotSettings.user
index 5ef2d603bf2edccd70570d103af2b3822c78a2b4..66f7988671771065ec70957cf2f6465966408246 100644
--- a/DungeonShooting_Godot/DungeonShooting.sln.DotSettings.user
+++ b/DungeonShooting_Godot/DungeonShooting.sln.DotSettings.user
@@ -1,2 +1,5 @@
- WARNING
\ No newline at end of file
+ WARNING
+ <AssemblyExplorer>
+ <Assembly Path="D:\GameProject\DungeonShooting\DungeonShooting_Godot\.mono\assemblies\Debug\GodotSharp.dll" />
+</AssemblyExplorer>
\ No newline at end of file
diff --git a/DungeonShooting_Godot/prefab/role/Enemy.tscn b/DungeonShooting_Godot/prefab/role/Enemy.tscn
index f4e9b41d52df0b32784bc282f7477d64e505fed1..ae7b5d5bfc6e9a91de895a295fb14ff892bfae00 100644
--- a/DungeonShooting_Godot/prefab/role/Enemy.tscn
+++ b/DungeonShooting_Godot/prefab/role/Enemy.tscn
@@ -24,6 +24,7 @@ material = SubResource( 1 )
[node name="AnimatedSprite" parent="." index="2"]
material = SubResource( 2 )
+frame = 1
[node name="Collision" parent="." index="3"]
position = Vector2( 0, -8 )
diff --git a/DungeonShooting_Godot/prefab/role/Player.tscn b/DungeonShooting_Godot/prefab/role/Player.tscn
index 71545604b6587483ee3b11fab09d46b0014bb03c..5786938067f160bfcb1d512627b6852e75e6c3b9 100644
--- a/DungeonShooting_Godot/prefab/role/Player.tscn
+++ b/DungeonShooting_Godot/prefab/role/Player.tscn
@@ -24,4 +24,4 @@ material = SubResource( 1 )
[node name="AnimatedSprite" parent="." index="2"]
material = SubResource( 2 )
-frame = 1
+frame = 2
diff --git a/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3 b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..09faa04ee4a91df4f676da9f28729eea6ede9ece
Binary files /dev/null and b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3 differ
diff --git a/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3.import b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3.import
new file mode 100644
index 0000000000000000000000000000000000000000..bad57ba98c71155be9946f249c68ad43ba0638ad
--- /dev/null
+++ b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet2.mp3.import
@@ -0,0 +1,15 @@
+[remap]
+
+importer="mp3"
+type="AudioStreamMP3"
+path="res://.import/ordinaryBullet2.mp3-363566e6f98fe28405c9762713f94f39.mp3str"
+
+[deps]
+
+source_file="res://resource/sound/sfx/ordinaryBullet2.mp3"
+dest_files=[ "res://.import/ordinaryBullet2.mp3-363566e6f98fe28405c9762713f94f39.mp3str" ]
+
+[params]
+
+loop=false
+loop_offset=0
diff --git a/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3 b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..06b18e6fd80b789333d10b6a9e77fc424d4ec2be
Binary files /dev/null and b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3 differ
diff --git a/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3.import b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3.import
new file mode 100644
index 0000000000000000000000000000000000000000..8ff20f60acd4fe846d2b470c8713f89cd0552358
--- /dev/null
+++ b/DungeonShooting_Godot/resource/sound/sfx/ordinaryBullet3.mp3.import
@@ -0,0 +1,15 @@
+[remap]
+
+importer="mp3"
+type="AudioStreamMP3"
+path="res://.import/ordinaryBullet3.mp3-071f70567ddf76a565a2dbe75703e424.mp3str"
+
+[deps]
+
+source_file="res://resource/sound/sfx/ordinaryBullet3.mp3"
+dest_files=[ "res://.import/ordinaryBullet3.mp3-071f70567ddf76a565a2dbe75703e424.mp3str" ]
+
+[params]
+
+loop=false
+loop_offset=0
diff --git a/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3 b/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..586e77210fb19c432eb9f2cafb8a18be0cc183dd
Binary files /dev/null and b/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3 differ
diff --git a/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3.import b/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3.import
new file mode 100644
index 0000000000000000000000000000000000000000..d9f86f95fde7b6a3b942db3078deba9e01c1fc99
--- /dev/null
+++ b/DungeonShooting_Godot/resource/sound/sfx/reloading.mp3.import
@@ -0,0 +1,15 @@
+[remap]
+
+importer="mp3"
+type="AudioStreamMP3"
+path="res://.import/reloading.mp3-a718918dc3dc57c0c17a3c31a9e33c3f.mp3str"
+
+[deps]
+
+source_file="res://resource/sound/sfx/reloading.mp3"
+dest_files=[ "res://.import/reloading.mp3-a718918dc3dc57c0c17a3c31a9e33c3f.mp3str" ]
+
+[params]
+
+loop=false
+loop_offset=0.0
diff --git a/DungeonShooting_Godot/scene/Room.tscn b/DungeonShooting_Godot/scene/Room.tscn
index 284fa8c8802fb673bbd62f3487dca95a8cb356e3..67cf6471b903f8f6bd1f1ad0a392a3a78e58021b 100644
--- a/DungeonShooting_Godot/scene/Room.tscn
+++ b/DungeonShooting_Godot/scene/Room.tscn
@@ -1,14 +1,9 @@
-[gd_scene load_steps=6 format=2]
+[gd_scene load_steps=5 format=2]
[ext_resource path="res://resource/map/dungeon_test.tmx" type="PackedScene" id=2]
[ext_resource path="res://src/game/room/RoomManager.cs" type="Script" id=3]
[ext_resource path="res://src/game/camera/GameCamera.cs" type="Script" id=5]
-[sub_resource type="NavigationPolygon" id=2]
-vertices = PoolVector2Array( 531, 412, 588, 411, 587, 588, 342, 588, 525, 412, 343, 520, 344, 508, 393, 265, 587, 263, 586, 345, 531, 345, 343, 201, 341, 67, 588, 68, 376, 201, 283, 508, 284, 520, 284, 588, 53, 589, 53, 411, 108, 412, 115, 413, 55, 71, 283, 69, 282, 200, 249, 200, 56, 199, 232, 200, 109, 348, 53, 347, 55, 264, 232, 265, 114, 348, 249, 265, 524, 345, 376, 265, 587, 201, 393, 201, 345, 412, 280, 412 )
-polygons = [ PoolIntArray( 0, 1, 2, 3 ), PoolIntArray( 4, 0, 3, 5, 6 ), PoolIntArray( 7, 8, 9, 10 ), PoolIntArray( 11, 12, 13, 14 ), PoolIntArray( 15, 16, 17, 18, 19, 20, 21 ), PoolIntArray( 22, 23, 24, 25, 26 ), PoolIntArray( 27, 26, 25 ), PoolIntArray( 28, 29, 30, 31, 32 ), PoolIntArray( 27, 25, 33, 31 ), PoolIntArray( 10, 0, 4, 34 ), PoolIntArray( 7, 10, 34 ), PoolIntArray( 7, 34, 32, 35 ), PoolIntArray( 13, 36, 37 ), PoolIntArray( 14, 13, 37 ), PoolIntArray( 35, 14, 37, 7 ), PoolIntArray( 33, 35, 32 ), PoolIntArray( 31, 33, 32 ), PoolIntArray( 6, 38, 4 ), PoolIntArray( 15, 6, 5, 16 ), PoolIntArray( 28, 32, 21, 20 ), PoolIntArray( 15, 21, 39 ) ]
-outlines = [ PoolVector2Array( 55, 71, 56, 199, 232, 200, 232, 265, 55, 264, 53, 347, 109, 348, 108, 412, 53, 411, 53, 589, 284, 588, 284, 520, 343, 520, 342, 588, 587, 588, 588, 411, 531, 412, 531, 345, 586, 345, 587, 263, 393, 265, 393, 201, 587, 201, 588, 68, 341, 67, 343, 201, 376, 201, 376, 265, 249, 265, 249, 200, 282, 200, 283, 69 ), PoolVector2Array( 114, 348, 115, 413, 280, 412, 283, 508, 344, 508, 345, 412, 525, 412, 524, 345 ) ]
-
[sub_resource type="Environment" id=1]
background_mode = 4
glow_enabled = true
@@ -16,11 +11,6 @@ glow_enabled = true
[node name="Room" type="Navigation2D"]
script = ExtResource( 3 )
-[node name="NavigationPolygonInstance" type="NavigationPolygonInstance" parent="."]
-visible = false
-navpoly = SubResource( 2 )
-enabled = false
-
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource( 1 )
diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs
index 089ca37dac6d7fb816b6f131b2a24958f9b787b3..effd4614b692f383fb81eb051949a5309ed20078 100644
--- a/DungeonShooting_Godot/src/framework/ActivityObject.cs
+++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs
@@ -9,6 +9,11 @@ using Plugin;
///
public abstract class ActivityObject : KinematicBody2D
{
+ ///
+ /// 是否是调试模式
+ ///
+ public static bool IsDebug { get; set; }
+
///
/// 当前物体类型id, 用于区分是否是同一种物体, 如果不是通过 ActivityObject.Create() 函数创建出来的对象那么 ItemId 为 null
///
@@ -230,6 +235,13 @@ public abstract class ActivityObject : KinematicBody2D
{
}
+ ///
+ /// 如果开启 debug, 则每帧调用该函数, 可用于绘制文字线段等
+ ///
+ protected virtual void DebugDraw()
+ {
+ }
+
///
/// 拾起一个 node 节点
///
@@ -408,9 +420,10 @@ public abstract class ActivityObject : KinematicBody2D
var temp = arr[i].Value;
if (temp != null && temp.ActivityObject == this && temp.Enable)
{
- if (!temp.IsStart)
+ if (!temp.IsReady)
{
temp.Ready();
+ temp.IsReady = true;
}
temp.Process(delta);
@@ -500,30 +513,14 @@ public abstract class ActivityObject : KinematicBody2D
//计算阴影
CalcShadow();
}
-
- }
-
- ///
- /// 重新计算物体阴影的位置和旋转信息, 无论是否显示阴影
- ///
- public void CalcShadow()
- {
- //缩放
- ShadowSprite.Scale = AnimatedSprite.Scale;
- //阴影角度
- ShadowSprite.GlobalRotationDegrees = GlobalRotationDegrees;
- //阴影位置计算
- var pos = AnimatedSprite.GlobalPosition;
- if (_throwData != null && !_throwData.IsOver)
- {
- ShadowSprite.GlobalPosition = new Vector2(pos.x + ShadowOffset.x, pos.y + ShadowOffset.y + _throwData.Y);
- }
- else
+
+ //调试绘制
+ if (IsDebug)
{
- ShadowSprite.GlobalPosition = pos + ShadowOffset;
+ Update();
}
}
-
+
public override void _PhysicsProcess(float delta)
{
//更新组件
@@ -536,9 +533,10 @@ public abstract class ActivityObject : KinematicBody2D
var temp = arr[i].Value;
if (temp != null && temp.ActivityObject == this && temp.Enable)
{
- if (!temp.IsStart)
+ if (!temp.IsReady)
{
temp.Ready();
+ temp.IsReady = true;
}
temp.PhysicsProcess(delta);
@@ -547,6 +545,46 @@ public abstract class ActivityObject : KinematicBody2D
}
}
+ public override void _Draw()
+ {
+ if (IsDebug)
+ {
+ DebugDraw();
+ var arr = _components.ToArray();
+ for (int i = 0; i < arr.Length; i++)
+ {
+ if (IsDestroyed) return;
+ var temp = arr[i].Value;
+ if (temp != null && temp.ActivityObject == this && temp.Enable)
+ {
+ temp.DebugDraw();
+ }
+ }
+ }
+ }
+
+ ///
+ /// 重新计算物体阴影的位置和旋转信息, 无论是否显示阴影
+ ///
+ public void CalcShadow()
+ {
+ //缩放
+ ShadowSprite.Scale = AnimatedSprite.Scale;
+ //阴影角度
+ ShadowSprite.GlobalRotationDegrees = GlobalRotationDegrees;
+ //阴影位置计算
+ var pos = AnimatedSprite.GlobalPosition;
+ if (_throwData != null && !_throwData.IsOver)
+ {
+ ShadowSprite.GlobalPosition = new Vector2(pos.x + ShadowOffset.x, pos.y + ShadowOffset.y + _throwData.Y);
+ }
+ else
+ {
+ ShadowSprite.GlobalPosition = pos + ShadowOffset;
+ }
+ }
+
+
///
/// 销毁物体
///
diff --git a/DungeonShooting_Godot/src/framework/Component.cs b/DungeonShooting_Godot/src/framework/Component.cs
index f81a60023dc2535e9fb4ced0c5b005cca527e170..2f2264a91d2fd4886f1e47f19366af40c71d0180 100644
--- a/DungeonShooting_Godot/src/framework/Component.cs
+++ b/DungeonShooting_Godot/src/framework/Component.cs
@@ -142,8 +142,10 @@ public abstract class Component : IProcess, IDestroy
///
public bool IsDestroyed { get; private set; }
- //是否调用过 start 函数
- internal bool IsStart = false;
+ ///
+ /// 是否调用过 Ready 函数
+ ///
+ public bool IsReady { get; set; }
///
/// 第一次调用 Process 或 PhysicsProcess 之前调用
@@ -202,6 +204,13 @@ public abstract class Component : IProcess, IDestroy
public virtual void OnDisable()
{
}
+
+ ///
+ /// 如果开启 debug, 则每帧调用该函数, 可用于绘制文字线段等
+ ///
+ public virtual void DebugDraw()
+ {
+ }
///
/// 当组件销毁
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index f53ca90760eefc2d88fdff7e2895c8f0fb68ff21..c2aec781ab7b417076c841596213f5e629725a1c 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -61,6 +61,9 @@ public class GameApplication : Node2D
public override void _EnterTree()
{
+ GD.Randomize();
+ ActivityObject.IsDebug = Debug;
+
GlobalNodeRoot = GetNode(GlobalNodeRootPath);
// 初始化鼠标
Cursor = CursorPack.Instance();
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
index 7a394cadb00831b3049c2ffbed22a3064975e46b..e7834ecbc0a1d8a1b4874269dfec5e0b4fd8d697 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
@@ -108,7 +108,7 @@ public class Gun : Weapon
GameCamera.Main.ProcessDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
}
//播放射击音效
- SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet_ogg, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), 6f);
+ SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet2_mp3, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), -8);
}
protected override void OnShoot(float fireRotation)
diff --git a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
index 51d66af2e3fa51737f8643c261d1b83ddb32129c..2bd3d8140f6d396f7cc09663b72054726afdd940 100644
--- a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
+++ b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
@@ -30,13 +30,16 @@ public class ResourcePath
public const string resource_effects_KnifeHit1_tres = "res://resource/effects/KnifeHit1.tres";
public const string resource_font_cn_font_12_tres = "res://resource/font/cn_font_12.tres";
public const string resource_font_cn_font_18_tres = "res://resource/font/cn_font_18.tres";
- public const string resource_font_cn_font_35_tres = "res://resource/font/cn_font_35.tres";
+ public const string resource_font_cn_font_36_tres = "res://resource/font/cn_font_36.tres";
public const string resource_map_dungeon_test_tmx = "res://resource/map/dungeon_test.tmx";
public const string resource_map_itchioDungeonTileset4_tsx = "res://resource/map/itch-io-DungeonTileset4.tsx";
public const string resource_materlal_Blend_gdshader = "res://resource/materlal/Blend.gdshader";
public const string resource_materlal_Blend_tres = "res://resource/materlal/Blend.tres";
public const string resource_sound_bgm_Intro_ogg = "res://resource/sound/bgm/Intro.ogg";
public const string resource_sound_sfx_ordinaryBullet_ogg = "res://resource/sound/sfx/ordinaryBullet.ogg";
+ public const string resource_sound_sfx_ordinaryBullet2_mp3 = "res://resource/sound/sfx/ordinaryBullet2.mp3";
+ public const string resource_sound_sfx_ordinaryBullet3_mp3 = "res://resource/sound/sfx/ordinaryBullet3.mp3";
+ public const string resource_sound_sfx_reloading_mp3 = "res://resource/sound/sfx/reloading.mp3";
public const string resource_sprite_bullet_arrow_png = "res://resource/sprite/bullet/arrow.png";
public const string resource_sprite_bullet_bullet_png = "res://resource/sprite/bullet/bullet.png";
public const string resource_sprite_bullet_bullet2_png = "res://resource/sprite/bullet/bullet2.png";
@@ -114,6 +117,8 @@ public class ResourcePath
public const string scene_Room_tscn = "res://scene/Room.tscn";
public const string scene_test_TestCommpont_tscn = "res://scene/test/TestCommpont.tscn";
public const string scene_test_TestNavigation_tscn = "res://scene/test/TestNavigation.tscn";
+ public const string scene_test_TestNavigation2_tscn = "res://scene/test/TestNavigation2.tscn";
+ public const string scene_test_TestNavigationPolygon_tscn = "res://scene/test/TestNavigationPolygon.tscn";
public const string default_bus_layout_tres = "res://default_bus_layout.tres";
public const string default_env_tres = "res://default_env.tres";
public const string icon_png = "res://icon.png";
diff --git a/DungeonShooting_Godot/src/game/role/PathSign.cs b/DungeonShooting_Godot/src/game/role/PathSign.cs
index a3487edf705f619b849cc63498f863b0ac3696ae..cf8f654e12b5273c0f99af65b4257be8a87954a4 100644
--- a/DungeonShooting_Godot/src/game/role/PathSign.cs
+++ b/DungeonShooting_Godot/src/game/role/PathSign.cs
@@ -221,3 +221,66 @@ public class PathSign : Node2D, IDestroy
}
}
}
+
+#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
diff --git a/DungeonShooting_Godot/src/game/role/IState.cs b/DungeonShooting_Godot/src/game/role/StateBase.cs
similarity index 30%
rename from DungeonShooting_Godot/src/game/role/IState.cs
rename to DungeonShooting_Godot/src/game/role/StateBase.cs
index 0a7c2b0348c4d1f605a4241c928b3ccb8d73b984..c4d2d17bcc8932b9256c721428ec54ade6a1a1da 100644
--- a/DungeonShooting_Godot/src/game/role/IState.cs
+++ b/DungeonShooting_Godot/src/game/role/StateBase.cs
@@ -1,44 +1,92 @@
-using System;///
-/// 状态接口
+using System;
+
+///
+/// 状态基类
///
-public interface IState where T : ActivityObject where S : Enum
+public abstract class StateBase where T : ActivityObject where S : Enum
{
///
- /// 当前状态对象对应的状态枚举类型
+ /// 当前活跃的状态对象实例
+ ///
+ public StateBase CurrStateBase => StateController.CurrStateBase;
+
+ ///
+ /// 当前对象对应的状态枚举
///
- S StateType { get; }
+ public S State { get; }
///
/// 当前状态对象挂载的角色对象
///
- T Master { get; set; }
+ public T Master { get; set; }
///
/// 当前状态对象所处的状态机对象
///
- StateController StateController { get; set; }
+ public StateController StateController { get; set; }
+ public StateBase(S state)
+ {
+ State = state;
+ }
+
///
/// 当从其他状态进入到当前状态时调用
///
/// 上一个状态类型
/// 切换当前状态时附带的参数
- void Enter(S prev, params object[] args);
+ public virtual void Enter(S prev, params object[] args)
+ {
+
+ }
///
- /// 物理帧每帧更新
+ /// 如果当前状态已被激活, 物理帧每帧更新
///
- void PhysicsProcess(float delta);
+ public virtual void PhysicsProcess(float delta)
+ {
+
+ }
///
- /// 是否允许切换至下一个状态
+ /// 是否允许切换至下一个状态, 该函数由状态机控制器调用, 不需要手动调用
///
/// 下一个状态类型
- bool CanChangeState(S next);
+ public virtual bool CanChangeState(S next)
+ {
+ return true;
+ }
///
/// 从当前状态退出时调用
///
/// 下一个状态类型
- void Exit(S next);
+ public virtual void Exit(S next)
+ {
+
+ }
+
+ ///
+ /// 当启用 debug 后调用该函数, 调试绘制, 需要调用 Master 身上的绘制函数
+ ///
+ public virtual void DebugDraw()
+ {
+
+ }
+
+ ///
+ /// 立即切换到下一个指定状态, 并且这一帧会被调用 PhysicsProcess
+ ///
+ public void ChangeState(S next, params object[] args)
+ {
+ StateController.ChangeState(next, args);
+ }
+
+ ///
+ /// 切换到下一个指定状态, 下一帧才会调用 PhysicsProcess
+ ///
+ public void ChangeStateLate(S next, params object[] args)
+ {
+ StateController.ChangeStateLate(next, args);
+ }
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/StateController.cs b/DungeonShooting_Godot/src/game/role/StateController.cs
index 18943c00ffbbf20971da3790ec1ffcbc49ae9ab7..f12bca1855bce3eed6f01407dd45a060c7d51e8d 100644
--- a/DungeonShooting_Godot/src/game/role/StateController.cs
+++ b/DungeonShooting_Godot/src/game/role/StateController.cs
@@ -8,27 +8,26 @@ using System.Collections.Generic;
public class StateController : Component where T : ActivityObject where S : Enum
{
///
- /// 当前活跃的状态
+ /// 当前活跃的状态对象实例
///
- public IState CurrState => _currState;
- private IState _currState;
-
+ public StateBase CurrStateBase { get; private set; }
+
///
/// 负责存放状态实例对象
///
- private readonly Dictionary> _states = new Dictionary>();
+ private readonly Dictionary> _states = new Dictionary>();
///
/// 记录下当前帧是否有改变的状态
///
private bool _isChangeState;
-
+
public override void PhysicsProcess(float delta)
{
_isChangeState = false;
- if (CurrState != null)
+ if (CurrStateBase != null)
{
- CurrState.PhysicsProcess(delta);
+ CurrStateBase.PhysicsProcess(delta);
//判断当前帧是否有改变的状态, 如果有, 则重新调用 PhysicsProcess() 方法
if (_isChangeState)
{
@@ -37,19 +36,28 @@ public class StateController : Component where T : ActivityObject where S
}
}
+ public override void DebugDraw()
+ {
+ if (CurrStateBase != null)
+ {
+ CurrStateBase.DebugDraw();
+ }
+ }
+
///
- /// 往状态机力注册一个新的状态
+ /// 往状态机里注册一个新的状态实例
///
- public void Register(IState state)
+ public void Register(StateBase stateBase)
{
- if (GetStateInstance(state.StateType) != null)
+ if (GetStateInstance(stateBase.State) != null)
{
- GD.PrintErr("当前状态已经在状态机中注册:", state);
+ GD.PrintErr("当前状态已经在状态机中注册:", stateBase);
return;
}
- state.Master = ActivityObject as T;
- state.StateController = this;
- _states.Add(state.StateType, state);
+
+ stateBase.Master = ActivityObject as T;
+ stateBase.StateController = this;
+ _states.Add(stateBase.State, stateBase);
}
///
@@ -71,7 +79,7 @@ public class StateController : Component where T : ActivityObject where S
///
/// 根据状态类型获取相应的状态对象
///
- private IState GetStateInstance(S stateType)
+ private StateBase GetStateInstance(S stateType)
{
_states.TryGetValue(stateType, out var v);
return v;
@@ -82,29 +90,31 @@ public class StateController : Component where T : ActivityObject where S
///
private void _changeState(bool late, S next, params object[] arg)
{
- if (_currState != null && _currState.StateType.Equals(next))
+ if (CurrStateBase != null && CurrStateBase.State.Equals(next))
{
return;
}
+
var newState = GetStateInstance(next);
if (newState == null)
{
GD.PrintErr("当前状态机未找到相应状态:" + next);
return;
}
- if (_currState == null)
+
+ if (CurrStateBase == null)
{
- _currState = newState;
+ CurrStateBase = newState;
newState.Enter(default, arg);
}
- else if (_currState.CanChangeState(next))
+ else if (CurrStateBase.CanChangeState(next))
{
_isChangeState = !late;
- var prev = _currState.StateType;
- _currState.Exit(next);
+ var prev = CurrStateBase.State;
+ CurrStateBase.Exit(next);
GD.Print("nextState => " + next);
- _currState = newState;
- _currState.Enter(prev, arg);
+ CurrStateBase = newState;
+ CurrStateBase.Enter(prev, arg);
}
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
index 538935d7f2459318d6065b025da85868985061df..90165538404674450b52e29cc783589a18307a1d 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
@@ -18,69 +18,73 @@ using Godot;
///
public class Enemy : Role
{
-
+
+ ///
+ /// 公共属性, 是否找到玩家, 如果找到玩家, 则所有敌人都会知道玩家的位置
+ ///
+ public static bool IsFindPlayer { get; set; }
+
///
/// 敌人身上的状态机控制器
///
public StateController StateController { get; }
-
+
///
- /// 视野半径, 单位像素
+ /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
///
- public float ViewRange { get; set; } = 200;
+ public float ViewRange { get; set; } = 250;
+
+ ///
+ /// 发现玩家后的视野半径
+ ///
+ public float TailAfterViewRange { get; set; } = 400;
///
/// 背后的视野半径, 单位像素
///
public float BackViewRange { get; set; } = 50;
-
+
///
/// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
///
public RayCast2D ViewRay { get; }
- //------------------- 寻路相关 ---------------------------
-
///
- /// 移动目标标记
+ /// 导航代理
///
- public PathSign PathSign { get; }
+ public NavigationAgent2D NavigationAgent2D { get; }
///
- /// 寻路标记线段总长度
+ /// 导航代理中点
///
- public float PathSignLength { get; set; } = 500;
+ public Position2D NavigationPoint { get; }
- //-------------------------------------------------------
-
- private Position2D _navigationPoint;
- private NavigationAgent2D _navigationAgent2D;
- private float _navigationUpdateTimer = 0;
+ private float _enemyAttackTimer = 0;
public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn)
{
StateController = new StateController();
AddComponent(StateController);
-
+
AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Props | PhysicsLayer.Player;
Camp = CampEnum.Camp2;
MoveSpeed = 30;
-
+
Holster.SlotList[2].Enable = true;
Holster.SlotList[3].Enable = true;
-
+
//视野射线
ViewRay = GetNode("ViewRay");
- _navigationPoint = GetNode("NavigationPoint");
- _navigationAgent2D = _navigationPoint.GetNode("NavigationAgent2D");
-
- PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player);
-
+ NavigationPoint = GetNode("NavigationPoint");
+ NavigationAgent2D = NavigationPoint.GetNode("NavigationAgent2D");
+
+ //PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player);
+
//注册Ai状态机
- StateController.Register(new AINormalState());
- StateController.Register(new AIProbeState());
- StateController.Register(new AITailAfterState());
+ StateController.Register(new AiNormalState());
+ StateController.Register(new AiProbeState());
+ StateController.Register(new AiTailAfterState());
}
public override void _Ready()
@@ -88,58 +92,73 @@ public class Enemy : Role
base._Ready();
//默认状态
StateController.ChangeState(AIStateEnum.AINormal);
-
- _navigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition);
- }
-
- public override void _Process(float delta)
- {
- base._Process(delta);
- if (GameApplication.Instance.Debug)
- {
- PathSign.Scale = new Vector2((int)Face, 1);
- Update();
- }
+
+ NavigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition);
}
public override void _PhysicsProcess(float delta)
{
base._PhysicsProcess(delta);
- if (_navigationAgent2D.IsNavigationFinished())
- {
- return;
- }
- var playerGlobalPosition = GameApplication.Instance.Room.Player.GlobalPosition;
- //临时处理, 让敌人跟随玩家
- if (_navigationUpdateTimer <= 0)
+ _enemyAttackTimer -= delta;
+ }
+
+ ///
+ /// Ai触发的攻击
+ ///
+ public void EnemyAttack()
+ {
+ var weapon = Holster.ActiveWeapon;
+ if (weapon != null)
{
- _navigationUpdateTimer = 0.2f;
- if (_navigationAgent2D.GetTargetLocation() != playerGlobalPosition)
+ if (weapon.Attribute.ContinuousShoot) //连发
{
- _navigationAgent2D.SetTargetLocation(playerGlobalPosition);
+ Attack();
+ }
+ else //单发
+ {
+ if (_enemyAttackTimer <= 0)
+ {
+ _enemyAttackTimer = 60f / weapon.Attribute.StartFiringSpeed;
+ Attack();
+ }
}
}
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- var nextPos = _navigationAgent2D.GetNextLocation();
- LookTargetPosition(playerGlobalPosition);
- AnimatedSprite.Animation = AnimatorNames.Run;
- Velocity = (nextPos - GlobalPosition - _navigationPoint.Position).Normalized() * MoveSpeed;
- CalcMove(delta);
}
- public override void _Draw()
+ ///
+ /// 返回目标点是否在视野范围内
+ ///
+ public bool IsInViewRange(Vector2 target)
{
- if (GameApplication.Instance.Debug)
+ var isForward = IsPositionInForward(target);
+ if (isForward)
{
- if (PathSign != null)
+ if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
{
- DrawLine(Vector2.Zero,ToLocal(PathSign.GlobalPosition), Colors.Red);
+ return true;
}
}
+
+ return false;
+ }
+
+ ///
+ /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回被挡住视野的物体对象, 视野无阻则返回 null
+ ///
+ public bool TestViewRayCast(Vector2 target)
+ {
+ ViewRay.Enabled = true;
+ ViewRay.CastTo = ViewRay.ToLocal(target);
+ ViewRay.ForceRaycastUpdate();
+ return ViewRay.IsColliding();
+ }
+
+ ///
+ /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
+ ///
+ public void TestViewRayCastOver()
+ {
+ ViewRay.Enabled = false;
}
}
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs
deleted file mode 100644
index 5733bec0f0de569979c1462840e2902b8319bb6b..0000000000000000000000000000000000000000
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-
-///
-/// AI 正常状态
-///
-public class AINormalState : IState
-{
- public AIStateEnum StateType { get; } = AIStateEnum.AINormal;
- public Enemy Master { get; set; }
- 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;
- }
- }
-
- }
-
- public bool CanChangeState(AIStateEnum next)
- {
- return true;
- }
-
- public void Exit(AIStateEnum next)
- {
-
- }
-}
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs
deleted file mode 100644
index 9a4efcac7e03bc62a18dda805262c856c61f67b7..0000000000000000000000000000000000000000
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-///
-/// Ai 不确定玩家位置
-///
-public class AIProbeState : IState
-{
- public AIStateEnum StateType { get; } = AIStateEnum.AIProbe;
- public Enemy Master { get; set; }
- public StateController StateController { get; set; }
- public void Enter(AIStateEnum prev, params object[] args)
- {
-
- }
-
- public void PhysicsProcess(float delta)
- {
-
- }
-
- public bool CanChangeState(AIStateEnum next)
- {
- return true;
- }
-
- public void Exit(AIStateEnum next)
- {
-
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs
deleted file mode 100644
index 0cfa70c63b67dcc768a934cc994a1e0b2f3c324b..0000000000000000000000000000000000000000
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-
-///
-/// AI 发现玩家
-///
-public class AITailAfterState : IState
-{
- public AIStateEnum StateType { get; } = AIStateEnum.AITailAfter;
- public Enemy Master { get; set; }
- 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.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);
- }
- }
- }
- }
-
- public bool CanChangeState(AIStateEnum next)
- {
- return true;
- }
-
- public void Exit(AIStateEnum next)
- {
-
- }
-}
\ 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
new file mode 100644
index 0000000000000000000000000000000000000000..35e44bdfe3afb3c866d16d8fa7c1a81ad0e20719
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs
@@ -0,0 +1,125 @@
+
+using Godot;
+
+///
+/// AI 正常状态
+///
+public class AiNormalState : StateBase
+{
+ //是否发现玩家
+ private bool _isFindPlayer;
+
+ //下一个运动的角度
+ private Vector2 _nextPos;
+
+ //是否移动结束
+ private bool _isMoveOver;
+
+ //上一次移动是否撞墙
+ private bool _againstWall;
+
+ //撞墙法线角度
+ private float _againstWallNormalAngle;
+
+ //移动停顿计时器
+ private float _pauseTimer;
+
+ public AiNormalState() : base(AIStateEnum.AINormal)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ _isFindPlayer = false;
+ _isMoveOver = true;
+ _againstWall = false;
+ _againstWallNormalAngle = 0;
+ _pauseTimer = 0;
+ }
+
+ public override void PhysicsProcess(float delta)
+ {
+
+ if (_isFindPlayer) //已经找到玩家了
+ {
+ //现临时处理, 直接切换状态
+ ChangeStateLate(AIStateEnum.AITailAfter);
+ }
+ else //没有找到玩家
+ {
+ //检测玩家
+ var player = GameApplication.Instance.Room.Player;
+ //玩家中心点坐标
+ var playerPos = player.MountPoint.GlobalPosition;
+
+ if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家
+ {
+ //发现玩家
+ _isFindPlayer = true;
+ }
+ else if (_pauseTimer >= 0)
+ {
+ _pauseTimer -= delta;
+ }
+ else if (_isMoveOver) //没发现玩家, 且已经移动完成
+ {
+ RunOver();
+ _isMoveOver = false;
+ }
+ else //移动中
+ {
+ //计算移动
+ var nextPos = Master.NavigationAgent2D.GetNextLocation();
+ Master.AnimatedSprite.Animation = AnimatorNames.Run;
+ Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() *
+ Master.MoveSpeed;
+ Master.CalcMove(delta);
+
+ if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
+ {
+ _pauseTimer = 1;
+ _isMoveOver = true;
+ }
+ }
+
+ Master.TestViewRayCastOver();
+ }
+ }
+
+ //移动结束
+ private void RunOver()
+ {
+ float angle;
+ if (_againstWall)
+ {
+ angle = Utils.RandRange(_againstWallNormalAngle - Mathf.Pi * 0.5f,
+ _againstWallNormalAngle + Mathf.Pi * 0.5f);
+ }
+ else
+ {
+ angle = Utils.RandRange(0, Mathf.Pi * 2f);
+ }
+
+ var len = Utils.RandRangeInt(30, 200);
+ _nextPos = new Vector2(len, 0).Rotated(angle) + Master.GlobalPosition;
+ //获取射线碰到的坐标
+ if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
+ {
+ _nextPos = Master.ViewRay.GetCollisionPoint();
+ _againstWall = true;
+ _againstWallNormalAngle = Master.ViewRay.GetCollisionNormal().Angle();
+ }
+ else
+ {
+ _againstWall = false;
+ }
+
+ Master.NavigationAgent2D.SetTargetLocation(_nextPos);
+ Master.LookTargetPosition(_nextPos);
+ }
+
+ public override void DebugDraw()
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(_nextPos), Colors.Green);
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4b1d6ca93b30b53ac09a7f00c0152fc88527845b
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeState.cs
@@ -0,0 +1,10 @@
+
+///
+/// Ai 不确定玩家位置
+///
+public class AiProbeState : StateBase
+{
+ public AiProbeState() : base(AIStateEnum.AIProbe)
+ {
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..909715fcd8a1e62970c85b99aa8c80a511b57eef
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs
@@ -0,0 +1,120 @@
+
+using Godot;
+
+///
+/// AI 发现玩家
+///
+public class AiTailAfterState : StateBase
+{
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 0.3f;
+
+ //目标是否在视野半径内
+ private bool _isInViewRange;
+
+ //是否在视野内
+ private bool _isInView;
+
+ //目标从视野消失时已经过去的时间
+ private float _viewTimer;
+
+ public AiTailAfterState() : base(AIStateEnum.AITailAfter)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ _isInViewRange = true;
+ _isInView = true;
+ _navigationUpdateTimer = 0;
+ _viewTimer = 0;
+ }
+
+ public override void PhysicsProcess(float delta)
+ {
+ var masterPos = Master.GlobalPosition;
+ var playerPos = GameApplication.Instance.Room.Player.GlobalPosition;
+
+ //更新玩家位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ if (Master.NavigationAgent2D.GetTargetLocation() != playerPos)
+ {
+ Master.NavigationAgent2D.SetTargetLocation(playerPos);
+ }
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ //计算移动
+ var nextPos = Master.NavigationAgent2D.GetNextLocation();
+ Master.LookTargetPosition(playerPos);
+ Master.AnimatedSprite.Animation = AnimatorNames.Run;
+ Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed;
+ Master.CalcMove(delta);
+
+ //检测玩家是否在视野内, 此时视野可穿墙, 直接检测距离即可
+
+ if (masterPos.DistanceSquaredTo(playerPos) <= Master.TailAfterViewRange * Master.TailAfterViewRange)
+ {
+ _isInView = !Master.TestViewRayCast(playerPos);
+ if (_isInView)
+ {
+ _isInViewRange = true;
+ }
+ else
+ {
+ _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange;
+ }
+ }
+ else
+ {
+ _isInViewRange = masterPos.DistanceSquaredTo(playerPos) <= Master.ViewRange * Master.ViewRange;
+ _isInView = false;
+ }
+
+ if (_isInViewRange)
+ {
+ _viewTimer = 0;
+
+ if (_isInView)
+ {
+ //攻击
+ Master.EnemyAttack();
+ }
+ }
+ else //超出视野
+ {
+ if (_viewTimer > 10) //10秒
+ {
+ ChangeStateLate(AIStateEnum.AINormal);
+ }
+ else
+ {
+ _viewTimer += delta;
+ }
+ }
+ }
+
+ public override void DebugDraw()
+ {
+ var playerPos = GameApplication.Instance.Room.Player.GlobalPosition;
+ if (_isInView)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Red);
+ }
+ else if (_isInViewRange)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Orange);
+ }
+ else
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(playerPos), Colors.Blue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs
index 06539e6849131e63594138e979d4758d208b4dc7..9643006e4334288d26f51de7bf1d1a98813adaa3 100644
--- a/DungeonShooting_Godot/src/game/room/RoomManager.cs
+++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs
@@ -13,6 +13,11 @@ public class RoomManager : Navigation2D
///
public Role Player { get; private set; }
+ ///
+ /// 导航区域形状
+ ///
+ public NavigationPolygonInstance NavigationPolygon { get; private set; }
+
//对象根节点
private Node2D _objectRoot;
@@ -21,8 +26,6 @@ public class RoomManager : Navigation2D
private Node2D _mapRoot;
- private NavigationPolygonInstance _navigationPolygon;
-
//可行走区域的tileId
private List _wayIds = new List(new[] { 129 });
@@ -32,6 +35,8 @@ public class RoomManager : Navigation2D
//导航区域数据
private List _polygonDataList = new List();
+ private TileMap _tileMap;
+
public override void _EnterTree()
{
Input.MouseMode = Input.MouseModeEnum.Hidden;
@@ -39,13 +44,14 @@ public class RoomManager : Navigation2D
_sortRoot = GetNode("SortRoot");
_objectRoot = GetNode("ObjectRoot");
- //_navigationPolygon = GetNode("NavigationPolygonInstance");
- _navigationPolygon = new NavigationPolygonInstance();
- AddChild(_navigationPolygon);
+ NavigationPolygon = new NavigationPolygonInstance();
+ AddChild(NavigationPolygon);
//初始化地图
_mapRoot = GetNode("MapRoot");
- var node = _mapRoot.GetChild(0).GetNode("Config");
+ var child = _mapRoot.GetChild(0);
+ _tileMap = child.GetNode("Wall");
+ var node = child.GetNode("Config");
Color color = (Color)node.GetMeta("ClearColor");
VisualServer.SetDefaultClearColor(color);
@@ -69,22 +75,28 @@ public class RoomManager : Navigation2D
polygon.AddOutline(polygonData.Points.ToArray());
}
polygon.MakePolygonsFromOutlines();
- _navigationPolygon.Navpoly = polygon;
+ NavigationPolygon.Navpoly = polygon;
//播放bgm
SoundManager.PlayMusic(ResourcePath.resource_sound_bgm_Intro_ogg, -17f);
var enemy1 = new Enemy();
enemy1.Name = "Enemy";
- enemy1.PutDown(new Vector2(150, 150));
+ enemy1.PutDown(new Vector2(150, 300));
enemy1.PickUpWeapon(WeaponManager.GetGun("1003"));
enemy1.PickUpWeapon(WeaponManager.GetGun("1001"));
var enemy2 = new Enemy();
enemy2.Name = "Enemy2";
- enemy2.PutDown(new Vector2(190, 150));
+ enemy2.PutDown(new Vector2(540, 100));
enemy2.PickUpWeapon(WeaponManager.GetGun("1002"));
enemy2.PickUpWeapon(WeaponManager.GetGun("1004"));
enemy2.PickUpWeapon(WeaponManager.GetGun("1003"));
+
+ var enemy3 = new Enemy();
+ enemy3.Name = "Enemy3";
+ enemy3.PutDown(new Vector2(540, 300));
+ enemy3.PickUpWeapon(WeaponManager.GetGun("1003"));
+ enemy3.PickUpWeapon(WeaponManager.GetGun("1002"));
WeaponManager.GetGun("1001").PutDown(new Vector2(80, 100));
WeaponManager.GetGun("1001").PutDown(new Vector2(80, 80));
@@ -131,15 +143,32 @@ public class RoomManager : Navigation2D
return useYSort ? _sortRoot : _objectRoot;
}
+ ///
+ /// 返回指定位置的Tile是否为可以行走
+ ///
+ public bool IsWayTile(int x, int y)
+ {
+ var cellId = _tileMap.GetCell(x, y);
+ return cellId != -1 && _wayIds.Contains(cellId);
+ }
+
+ ///
+ /// 返回指定坐标下对应的Tile是否为可以行走
+ ///
+ public bool IsWayPosition(float x, float y)
+ {
+ var tileMapCellSize = _tileMap.CellSize;
+ return IsWayTile((int)(x / tileMapCellSize.x), (int)(y / tileMapCellSize.y));
+ }
+
///
/// 自动生成导航区域
///
private void GenerateNavigationPolygon()
{
- var tileMap = _mapRoot.GetChild(0).GetNode("Wall");
- var size = tileMap.CellSize;
+ var size = _tileMap.CellSize;
- var rect = tileMap.GetUsedRect();
+ var rect = _tileMap.GetUsedRect();
var x = (int)rect.Position.x;
var y = (int)rect.Position.y;
@@ -150,20 +179,19 @@ public class RoomManager : Navigation2D
{
for (int i = x; i < w; i++)
{
- var tileId = tileMap.GetCell(i, j);
- if (IsWayCell(tileId))
+ if (IsWayTile(i, j))
{
if (!_usePoints.Contains(new Vector2(i, j)))
{
NavigationPolygonData polygonData = null;
- if (!IsWayCell(tileMap.GetCell(i, j - 1)))
+ if (!IsWayTile(i, j - 1))
{
- polygonData = CalcOutline(i, j, tileMap, size);
+ polygonData = CalcOutline(i, j, _tileMap, size);
}
- else if (!IsWayCell(tileMap.GetCell(i, j + 1)))
+ else if (!IsWayTile(i, j + 1))
{
- polygonData = CalcInline(i, j, tileMap, size);
+ polygonData = CalcInline(i, j, _tileMap, size);
}
if (polygonData != null)
@@ -196,7 +224,7 @@ public class RoomManager : Navigation2D
{
case 0: //右
{
- if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //先向上找
+ if (IsWayTile(tempI, tempJ - 1)) //先向上找
{
dir = 3;
@@ -212,7 +240,7 @@ public class RoomManager : Navigation2D
tempJ--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //再向右找
+ else if (IsWayTile(tempI + 1, tempJ)) //再向右找
{
if (points.Count == 0)
{
@@ -229,7 +257,7 @@ public class RoomManager : Navigation2D
tempI++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //向下找
+ else if (IsWayTile(tempI, tempJ + 1)) //向下找
{
dir = 1;
@@ -250,7 +278,7 @@ public class RoomManager : Navigation2D
}
case 1: //下
{
- if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //先向右找
+ if (IsWayTile(tempI + 1, tempJ)) //先向右找
{
dir = 0;
@@ -266,7 +294,7 @@ public class RoomManager : Navigation2D
tempI++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //再向下找
+ else if (IsWayTile(tempI, tempJ + 1)) //再向下找
{
if (points.Count == 0)
{
@@ -283,7 +311,7 @@ public class RoomManager : Navigation2D
tempJ++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //向左找
+ else if (IsWayTile(tempI - 1, tempJ)) //向左找
{
dir = 2;
@@ -304,7 +332,7 @@ public class RoomManager : Navigation2D
}
case 2: //左
{
- if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //先向下找
+ if (IsWayTile(tempI, tempJ + 1)) //先向下找
{
dir = 1;
@@ -320,7 +348,7 @@ public class RoomManager : Navigation2D
tempJ++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //再向左找
+ else if (IsWayTile(tempI - 1, tempJ)) //再向左找
{
if (points.Count == 0)
{
@@ -337,7 +365,7 @@ public class RoomManager : Navigation2D
tempI--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //向上找
+ else if (IsWayTile(tempI, tempJ - 1)) //向上找
{
dir = 3;
@@ -358,7 +386,7 @@ public class RoomManager : Navigation2D
}
case 3: //上
{
- if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //先向左找
+ if (IsWayTile(tempI - 1, tempJ)) //先向左找
{
dir = 2;
@@ -374,7 +402,7 @@ public class RoomManager : Navigation2D
tempI--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //再向上找
+ else if (IsWayTile(tempI, tempJ - 1)) //再向上找
{
if (points.Count == 0)
{
@@ -391,7 +419,7 @@ public class RoomManager : Navigation2D
tempJ--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //向右找
+ else if (IsWayTile(tempI + 1, tempJ)) //向右找
{
dir = 0;
@@ -434,7 +462,7 @@ public class RoomManager : Navigation2D
{
case 0: //右
{
- if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //向下找
+ if (IsWayTile(tempI, tempJ + 1)) //向下找
{
dir = 1;
@@ -450,7 +478,7 @@ public class RoomManager : Navigation2D
tempJ++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //再向右找
+ else if (IsWayTile(tempI + 1, tempJ)) //再向右找
{
if (points.Count == 0)
{
@@ -467,7 +495,7 @@ public class RoomManager : Navigation2D
tempI++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //先向上找
+ else if (IsWayTile(tempI, tempJ - 1)) //先向上找
{
dir = 3;
@@ -488,7 +516,7 @@ public class RoomManager : Navigation2D
}
case 1: //下
{
- if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //向左找
+ if (IsWayTile(tempI - 1, tempJ)) //向左找
{
dir = 2;
@@ -504,7 +532,7 @@ public class RoomManager : Navigation2D
tempI--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //再向下找
+ else if (IsWayTile(tempI, tempJ + 1)) //再向下找
{
if (points.Count == 0)
{
@@ -521,7 +549,7 @@ public class RoomManager : Navigation2D
tempJ++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //先向右找
+ else if (IsWayTile(tempI + 1, tempJ)) //先向右找
{
dir = 0;
@@ -542,7 +570,7 @@ public class RoomManager : Navigation2D
}
case 2: //左
{
- if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //向上找
+ if (IsWayTile(tempI, tempJ - 1)) //向上找
{
dir = 3;
@@ -558,7 +586,7 @@ public class RoomManager : Navigation2D
tempJ--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //再向左找
+ else if (IsWayTile(tempI - 1, tempJ)) //再向左找
{
if (points.Count == 0)
{
@@ -575,7 +603,7 @@ public class RoomManager : Navigation2D
tempI--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ + 1))) //先向下找
+ else if (IsWayTile(tempI, tempJ + 1)) //先向下找
{
dir = 1;
@@ -596,7 +624,7 @@ public class RoomManager : Navigation2D
}
case 3: //上
{
- if (IsWayCell(tileMap.GetCell(tempI + 1, tempJ))) //向右找
+ if (IsWayTile(tempI + 1, tempJ)) //向右找
{
dir = 0;
@@ -612,7 +640,7 @@ public class RoomManager : Navigation2D
tempI++;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI, tempJ - 1))) //再向上找
+ else if (IsWayTile(tempI, tempJ - 1)) //再向上找
{
if (points.Count == 0)
{
@@ -629,7 +657,7 @@ public class RoomManager : Navigation2D
tempJ--;
break;
}
- else if (IsWayCell(tileMap.GetCell(tempI - 1, tempJ))) //先向左找
+ else if (IsWayTile(tempI - 1, tempJ)) //先向左找
{
dir = 2;
@@ -661,9 +689,4 @@ public class RoomManager : Navigation2D
_usePoints.Add(pos);
}
-
- private bool IsWayCell(int cellId)
- {
- return cellId != -1 && _wayIds.Contains(cellId);
- }
}
\ No newline at end of file