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