diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preinstall.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preinstall.json index c43c818..7a68eda 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preinstall.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preinstall.json @@ -1 +1 @@ -[{"Name":"Preinstall1","Weight":100,"Remark":"","AutoFill":true,"WaveList":[[{"Position":{"X":39,"Y":8},"Size":{"X":0,"Y":0},"SpecialMarkType":1,"DelayTime":0,"MarkList":[]},{"Position":{"X":-16,"Y":-18},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"weapon0001","Weight":100,"Attr":{"CurrAmmon":"30","ResidueAmmo":"210"},"Altitude":8,"VerticalSpeed":0}]},{"Position":{"X":52,"Y":65},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"$RandomProp","Weight":100,"Attr":null,"Altitude":16,"VerticalSpeed":0}]}],[{"Position":{"X":59,"Y":-5},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":1,"MarkList":[{"Id":"$RandomGun","Weight":100,"Attr":null,"Altitude":16,"VerticalSpeed":0}]},{"Position":{"X":11,"Y":-46},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"$RandomProp","Weight":100,"Attr":null,"Altitude":8,"VerticalSpeed":0}]},{"Position":{"X":72,"Y":-39},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":2,"MarkList":[{"Id":"$RandomEnemy","Weight":100,"Attr":null,"Altitude":0,"VerticalSpeed":0}]}]]}] \ No newline at end of file +[{"Name":"Preinstall1","Weight":100,"Remark":"","AutoFill":true,"WaveList":[[{"Position":{"X":39,"Y":8},"Size":{"X":0,"Y":0},"SpecialMarkType":1,"DelayTime":0,"MarkList":[]},{"Position":{"X":-16,"Y":-18},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"weapon0001","Weight":100,"Attr":{"CurrAmmon":"30","ResidueAmmo":"210"},"Altitude":8,"VerticalSpeed":0}]}]]}] \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/common/SeedRandom.cs b/DungeonShooting_Godot/src/framework/common/SeedRandom.cs index 11acb6f..20e88c5 100644 --- a/DungeonShooting_Godot/src/framework/common/SeedRandom.cs +++ b/DungeonShooting_Godot/src/framework/common/SeedRandom.cs @@ -192,77 +192,73 @@ /// /// 返回指定区域内的随机坐标点, 该函数比较慢, 请谨慎调用 /// - /// 碰撞区域数据 - /// 需要随机点的数量 - public Vector2[] GetRandomPositionInPolygon(List polygonDataList, int count) + public Vector2[] GetRandomPositionInPolygon(List vertices, List polygons, int count) { + if (vertices.Count == 0 || polygons.Count == 0) + { + return Vector2.Zero.MakeArray(count); + } var minX = int.MaxValue; var maxX = int.MinValue; var minY = int.MaxValue; var maxY = int.MinValue; - - var outCount = 0; // 遍历多边形的顶点,找到最小和最大的x、y坐标 - foreach (var navigationPolygonData in polygonDataList) + foreach (var vertex in vertices) { - if (navigationPolygonData.Type == NavigationPolygonType.Out) + if (vertex.X < minX) { - outCount++; + minX = Mathf.CeilToInt(vertex.X); } - foreach (var vertex in navigationPolygonData.GetPoints()) + else if (vertex.X > maxX) { - if (vertex.X < minX) - { - minX = Mathf.CeilToInt(vertex.X); - } - else if (vertex.X > maxX) - { - maxX = Mathf.FloorToInt(vertex.X); - } - if (vertex.Y < minY) - { - minY = Mathf.CeilToInt(vertex.Y); - } - else if (vertex.Y > maxY) - { - maxY = Mathf.FloorToInt(vertex.Y); - } + maxX = Mathf.FloorToInt(vertex.X); + } + if (vertex.Y < minY) + { + minY = Mathf.CeilToInt(vertex.Y); + } + else if (vertex.Y > maxY) + { + maxY = Mathf.FloorToInt(vertex.Y); } } var list = new List(); + var tryCount = 0; while (true) { - var flag = outCount == 0; - var point = new Vector2(RandomRangeInt(minX, maxX), RandomRangeInt(minY, maxY)); - - foreach (var navigationPolygonData in polygonDataList) + if (tryCount >= 2000) //尝试2000次后放弃 { - if (navigationPolygonData.Type == NavigationPolygonType.Out) + while (list.Count < count) { - if (!flag && Utils.IsPointInPolygon(navigationPolygonData.GetPoints(), point)) - { - flag = true; - } + list.Add(Vector2.Zero); } - else - { - if (flag && Utils.IsPointInPolygon(navigationPolygonData.GetPoints(), point)) - { - flag = false; - } - } + break; } - if (flag) + tryCount++; + var point = new Vector2(RandomRangeInt(minX, maxX), RandomRangeInt(minY, maxY)); + foreach (var ps in polygons) { - list.Add(point); - if (list.Count >= count) + var arr = new List(); + foreach (var i in ps) { - return list.ToArray(); + arr.Add(vertices[i]); + } + + if (Utils.IsPointInPolygon(arr, point)) + { + tryCount = 0; + list.Add(point); + if (list.Count >= count) + { + return list.ToArray(); + } } } } + + return list.ToArray(); } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/common/Utils.cs b/DungeonShooting_Godot/src/framework/common/Utils.cs index da9d36d..04657df 100644 --- a/DungeonShooting_Godot/src/framework/common/Utils.cs +++ b/DungeonShooting_Godot/src/framework/common/Utils.cs @@ -330,6 +330,27 @@ return isInside; } + + /// + /// 返回一个点是否在 Polygon 内部 + /// + /// 多边形顶点 + /// 目标点 + public static bool IsPointInPolygon(List polygon, Vector2 point) + { + var isInside = false; + for (int i = 0, j = polygon.Count - 1; i < polygon.Count; j = i++) + { + if ((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y) && + point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) + + polygon[i].X) + { + isInside = !isInside; + } + } + + return isInside; + } /// /// 根据法线翻转向量 diff --git a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs index 23dd77f..55c9ed3 100644 --- a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs +++ b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs @@ -191,36 +191,56 @@ } //自动填充操作 - if (RoomPreinstallInfo.AutoFill) + if (RoomPreinstallInfo.AutoFill && RoomInfo.RoomType == DungeonRoomType.Battle) { var count = world.Random.RandomRangeInt(3, 10); - //world.Random.GetRandomPositionInPolygon(RoomInfo.RoomSplit.TileInfo.NavigationVertices) + var tileInfo = RoomInfo.RoomSplit.TileInfo; + var serializeVector2s = tileInfo.NavigationVertices; + var vertices = new List(); + foreach (var sv2 in serializeVector2s) + { + vertices.Add(sv2.AsVector2()); + } + var positionArray = world.Random.GetRandomPositionInPolygon(vertices, tileInfo.NavigationPolygon, count); var arr = new ActivityType[] { ActivityType.Enemy, ActivityType.Weapon, ActivityType.Prop }; - for (int i = 0; i < count; i++) + var weight = new int[] { 15, 2, 1 }; + for (var i = 0; i < count; i++) { var tempWave = GetOrCreateWave(world.Random.RandomRangeInt(0, 2)); var mark = new ActivityMark(); - var activityType = world.Random.RandomChoose(arr); - if (activityType == ActivityType.Enemy) + mark.Attr = new Dictionary(); + var index = world.Random.RandomWeight(weight); + var activityType = arr[index]; + if (activityType == ActivityType.Enemy) //敌人 { mark.Id = world.RandomPool.GetRandomEnemy().Id; + mark.Attr.Add("Face", "0"); + mark.DerivedAttr = new Dictionary(); + mark.DerivedAttr.Add("Face", world.Random.RandomChoose((int)FaceDirection.Left, (int)FaceDirection.Right).ToString()); //链朝向 + if (world.Random.RandomBoolean(0.8f)) //手持武器 + { + var weapon = world.RandomPool.GetRandomWeapon(); + var weaponAttribute = Weapon.GetWeaponAttribute(weapon.Id); + mark.Attr.Add("Weapon", weapon.Id); //武器id + mark.Attr.Add("CurrAmmon", weaponAttribute.AmmoCapacity.ToString()); //弹夹弹药量 + mark.Attr.Add("ResidueAmmo", weaponAttribute.AmmoCapacity.ToString()); //剩余弹药量 + } } - else if (activityType == ActivityType.Weapon) + else if (activityType == ActivityType.Weapon) //武器 { mark.Id = world.RandomPool.GetRandomWeapon().Id; } - else if (activityType == ActivityType.Prop) + else if (activityType == ActivityType.Prop) //道具 { mark.Id = world.RandomPool.GetRandomProp().Id; } mark.ActivityType = activityType; mark.MarkType = SpecialMarkType.Normal; - mark.Attr = null; mark.VerticalSpeed = 0; - mark.Altitude = 8; - mark.DelayTime = 0; - mark.Position = RoomInfo.ToGlobalPosition(Vector2.Zero); + mark.Altitude = activityType == ActivityType.Enemy ? 0 : 8; + mark.DelayTime = i * 0.3f; + mark.Position = RoomInfo.ToGlobalPosition(positionArray[i]); tempWave.Add(mark); } } @@ -521,7 +541,7 @@ else if (activityMark.ActivityType == ActivityType.Enemy) //敌人类型 { var role = (Role)activityObject; - if (role is Enemy enemy && activityMark.Attr.TryGetValue("Weapon", out var weaponId)) //使用的武器 + if (role.WeaponPack.Capacity > 0 && role is Enemy enemy && activityMark.Attr.TryGetValue("Weapon", out var weaponId)) //使用的武器 { if (!string.IsNullOrEmpty(weaponId)) { diff --git a/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs b/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs index 7207dcb..e257b69 100644 --- a/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs +++ b/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs @@ -135,6 +135,10 @@ /// public Vector2I Waypoints { get; set; } + /// + /// 导航网格对象 + /// + public NavigationRegion2D NavigationRegion { get; set; } public bool IsDestroyed { get; private set; } private bool _openDoorFlag = true; diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs index f3ee163..937f133 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs @@ -14,6 +14,7 @@ { base.OnInit(); NoWeaponAttack = true; + WeaponPack.SetCapacity(0); AnimationPlayer.AnimationFinished += OnAnimationFinished; _brushData = LiquidBrushManager.GetBrush("0002"); diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs index 16f626f..032bc8d 100644 --- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs +++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs @@ -405,6 +405,7 @@ navigationPolygon.Name = "NavigationRegion" + (GetChildCount() + 1); navigationPolygon.NavigationPolygon = polygonData; World.NavigationRoot.AddChild(navigationPolygon); + roomInfo.NavigationRegion = navigationPolygon; } //创建门