diff --git a/DungeonShooting_Godot/prefab/role/Role0001.tscn b/DungeonShooting_Godot/prefab/role/Role0001.tscn index 71e4939..11cf968 100644 --- a/DungeonShooting_Godot/prefab/role/Role0001.tscn +++ b/DungeonShooting_Godot/prefab/role/Role0001.tscn @@ -24,7 +24,7 @@ [sub_resource type="AnimationLibrary" id="AnimationLibrary_ka171"] [node name="Role0001" node_paths=PackedStringArray("HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_10c2n")] -collision_layer = 8 +collision_layer = 24 script = ExtResource("2_6xwnt") HurtArea = NodePath("HurtArea") HurtCollision = NodePath("HurtArea/HurtCollision") diff --git a/DungeonShooting_Godot/prefab/role/enemy/Enemy0001.tscn b/DungeonShooting_Godot/prefab/role/enemy/Enemy0001.tscn index 3e85288..b616e02 100644 --- a/DungeonShooting_Godot/prefab/role/enemy/Enemy0001.tscn +++ b/DungeonShooting_Godot/prefab/role/enemy/Enemy0001.tscn @@ -44,12 +44,14 @@ "query": ExtResource("7_e37p2") } -[node name="Enemy0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_2vqwe")] +[node name="Enemy0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "ViewArea", "ViewAreaCollision", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_2vqwe")] script = ExtResource("2_0pcq3") ViewRay = NodePath("ViewRay") NavigationAgent2D = NodePath("NavigationPoint/NavigationAgent2D") NavigationPoint = NodePath("NavigationPoint") FirePoint = NodePath("FirePoint") +ViewArea = NodePath("MountPoint/ViewArea") +ViewAreaCollision = NodePath("MountPoint/ViewArea/ViewAreaCollision") HurtArea = NodePath("HurtArea") HurtCollision = NodePath("HurtArea/HurtCollision") InteractiveArea = NodePath("InteractiveArea") diff --git a/DungeonShooting_Godot/prefab/role/enemy/Enemy0002.tscn b/DungeonShooting_Godot/prefab/role/enemy/Enemy0002.tscn index 6829646..cfc530c 100644 --- a/DungeonShooting_Godot/prefab/role/enemy/Enemy0002.tscn +++ b/DungeonShooting_Godot/prefab/role/enemy/Enemy0002.tscn @@ -9,7 +9,6 @@ [ext_resource type="Animation" uid="uid://cmje7jsgrhgmx" path="res://resource/animation/enemy/Enemy_query.res" id="7_h4cls"] [ext_resource type="Animation" uid="uid://16rxpnsgj5tl" path="res://resource/animation/enemy/Enemy_notify.res" id="8_0688j"] - [sub_resource type="ShaderMaterial" id="ShaderMaterial_y5nia"] resource_local_to_scene = true shader = ExtResource("2_yunbp") @@ -85,12 +84,14 @@ "query": ExtResource("7_h4cls") } -[node name="Enemy0002" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_fanet")] +[node name="Enemy0002" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "ViewArea", "ViewAreaCollision", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_fanet")] script = ExtResource("2_3an4s") ViewRay = NodePath("ViewRay") NavigationAgent2D = NodePath("NavigationPoint/NavigationAgent2D") NavigationPoint = NodePath("NavigationPoint") FirePoint = NodePath("FirePoint") +ViewArea = NodePath("MountPoint/ViewArea") +ViewAreaCollision = NodePath("MountPoint/ViewArea/ViewAreaCollision") HurtArea = NodePath("HurtArea") HurtCollision = NodePath("HurtArea/HurtCollision") InteractiveArea = NodePath("InteractiveArea") diff --git a/DungeonShooting_Godot/prefab/role/shopBoss/ShopBoss0001.tscn b/DungeonShooting_Godot/prefab/role/shopBoss/ShopBoss0001.tscn index d0f6ea5..3638fac 100644 --- a/DungeonShooting_Godot/prefab/role/shopBoss/ShopBoss0001.tscn +++ b/DungeonShooting_Godot/prefab/role/shopBoss/ShopBoss0001.tscn @@ -29,13 +29,15 @@ shader_parameter/outline_use_blend = true shader_parameter/grey = 0.0 -[node name="ShopBoss0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_25fpq")] +[node name="ShopBoss0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "ViewArea", "ViewAreaCollision", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_25fpq")] collision_layer = 1024 script = ExtResource("2_2ng7e") ViewRay = NodePath("ViewRay") NavigationAgent2D = NodePath("NavigationPoint/NavigationAgent2D") NavigationPoint = NodePath("NavigationPoint") FirePoint = NodePath("FirePoint") +ViewArea = NodePath("MountPoint/ViewArea") +ViewAreaCollision = NodePath("MountPoint/ViewArea/ViewAreaCollision") HurtArea = NodePath("HurtArea") HurtCollision = NodePath("HurtArea/HurtCollision") InteractiveArea = NodePath("InteractiveArea") diff --git a/DungeonShooting_Godot/prefab/role/template/AiTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/AiTemplate.tscn index 23667b3..3866ed8 100644 --- a/DungeonShooting_Godot/prefab/role/template/AiTemplate.tscn +++ b/DungeonShooting_Godot/prefab/role/template/AiTemplate.tscn @@ -33,6 +33,13 @@ position = Vector2(0, -8) enabled = false +[node name="ViewArea" type="Area2D" parent="MountPoint" index="1"] +collision_layer = 0 +collision_mask = 16 +monitorable = false + +[node name="ViewAreaCollision" type="CollisionPolygon2D" parent="MountPoint/ViewArea" index="0"] + [node name="FirePoint" type="Marker2D" parent="." index="8"] [node name="NavigationPoint" type="Marker2D" parent="." index="9"] diff --git a/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn index 102bf01..ad4f584 100644 --- a/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn +++ b/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn @@ -46,7 +46,7 @@ shape = SubResource("CircleShape2D_5pj80") [node name="HurtArea" type="Area2D" parent="."] -collision_layer = 0 +collision_layer = 1024 collision_mask = 0 monitoring = false script = ExtResource("2_2eey0") diff --git a/DungeonShooting_Godot/project.godot b/DungeonShooting_Godot/project.godot index 9c42853..1822292 100644 --- a/DungeonShooting_Godot/project.godot +++ b/DungeonShooting_Godot/project.godot @@ -246,13 +246,13 @@ 2d_physics/layer_2="bullet" 2d_physics/layer_3="prop" 2d_physics/layer_4="player" -2d_physics/layer_5="enemy" +2d_physics/layer_5="role" 2d_physics/layer_6="affiliation" 2d_physics/layer_7="onHand" 2d_physics/layer_8="debris" 2d_physics/layer_9="throwing" 2d_physics/layer_10="obstacle" -2d_physics/layer_11="npc" +2d_physics/layer_11="hurtArea" 2d_physics/layer_14="ui_mouse" [mono] diff --git a/DungeonShooting_Godot/resource/map/tileMaps/GroupConfig.json b/DungeonShooting_Godot/resource/map/tileMaps/GroupConfig.json index 93e8149..fd96875 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/GroupConfig.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/GroupConfig.json @@ -30,8 +30,12 @@ ], "InletList": [ { - "ErrorType": 0, + "ErrorType": 2, "Path": "resource/map/tileMaps/Test1/inlet/Start" + }, + { + "ErrorType": 0, + "Path": "resource/map/tileMaps/Test1/inlet/Start2" } ], "OutletList": [ diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preview.png b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preview.png index 65b6fc2..95f53df 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preview.png +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/Preview.png Binary files differ diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/RoomInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/RoomInfo.json index 6091345..f4b132a 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/RoomInfo.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/RoomInfo.json @@ -1 +1 @@ -{"Position":{"X":-9,"Y":-11},"Size":{"X":20,"Y":21},"DoorAreaInfos":[{"Direction":2,"Start":48,"End":176},{"Direction":3,"Start":0,"End":256},{"Direction":0,"Start":0,"End":208},{"Direction":1,"Start":0,"End":224}],"GroupName":"Test1","RoomType":2,"RoomName":"Start","Weight":100,"Remark":""} \ No newline at end of file +{"Position":{"X":-10,"Y":-12},"Size":{"X":22,"Y":23},"DoorAreaInfos":[{"Direction":2,"Start":48,"End":176},{"Direction":3,"Start":0,"End":256},{"Direction":0,"Start":0,"End":208},{"Direction":1,"Start":0,"End":224}],"GroupName":"Test1","RoomType":2,"RoomName":"Start","Weight":100,"Remark":""} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/TileInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/TileInfo.json index 04f62db..881c982 100644 --- a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/TileInfo.json +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start/TileInfo.json @@ -1 +1 @@ -{"NavigationVertices":[{"X":134,"Y":38},{"X":138,"Y":-122},{"X":138,"Y":138},{"X":134,"Y":90},{"X":134,"Y":-6},{"X":90,"Y":-6},{"X":86,"Y":-58},{"X":86,"Y":-102},{"X":-106,"Y":-122},{"X":-38,"Y":-102},{"X":74,"Y":10},{"X":74,"Y":54},{"X":58,"Y":90},{"X":-26,"Y":38},{"X":-26,"Y":-6},{"X":90,"Y":10},{"X":-38,"Y":-58},{"X":118,"Y":54},{"X":118,"Y":38},{"X":134,"Y":138},{"X":58,"Y":138},{"X":-58,"Y":138},{"X":-58,"Y":106},{"X":-86,"Y":38},{"X":-106,"Y":106},{"X":-86,"Y":-6}],"NavigationPolygon":[[0,1,2,3],[1,0,4],[1,4,5,6,7],[8,1,7,9],[10,11,12,13,14],[6,5,15,10,14,16],[17,18,0,3],[2,19,3],[17,3,12,11],[13,12,20,21,22],[23,13,22,24],[23,24,8,25],[25,8,9,16],[14,25,16]],"Floor":[-3,-4,0,3,-2,-4,0,3,-2,-3,0,3,-3,-3,0,3,-7,-2,0,3,-7,-1,0,3,-7,0,0,3,-7,1,0,3,-7,2,0,3,-7,3,0,3,-7,4,0,3,-7,5,0,3,-6,-3,0,3,-6,-2,0,3,-6,-1,0,3,-6,0,0,3,-6,1,0,3,-6,2,0,3,-6,3,0,3,-6,4,0,3,-6,5,0,3,-5,-3,0,3,-5,-2,0,3,-5,2,0,3,-5,3,0,3,-5,4,0,3,-5,5,0,3,-4,-3,0,3,-4,-2,0,3,-4,2,0,3,-4,3,0,3,-4,4,0,3,-4,5,0,3,-3,-2,0,3,-3,2,0,3,-3,3,0,3,-3,4,0,3,-3,5,0,3,-2,-2,0,3,-2,-1,0,3,-2,0,0,3,-2,1,0,3,-2,2,0,3,-2,3,0,3,-2,4,0,3,-2,5,0,3,-4,6,0,3,-4,7,0,3,-3,6,0,3,-3,7,0,3,-2,6,0,3,-2,7,0,3,-1,3,0,3,-1,4,0,3,-1,7,0,3,0,3,0,3,0,4,0,3,0,7,0,3,1,3,0,3,1,4,0,3,1,5,0,3,1,6,0,3,1,7,0,3,2,3,0,3,2,4,0,3,2,5,0,3,2,6,0,3,2,7,0,3,3,3,0,3,3,4,0,3,3,5,0,3,3,6,0,3,-1,-4,0,3,-1,-3,0,3,-1,-2,0,3,-1,-1,0,3,-1,0,0,3,0,-4,0,3,0,-3,0,3,0,-2,0,3,0,-1,0,3,0,0,0,3,1,-4,0,3,1,-3,0,3,1,-2,0,3,1,-1,0,3,1,0,0,3,2,-4,0,3,2,-3,0,3,2,-2,0,3,2,-1,0,3,2,0,0,3,3,-4,0,3,3,-3,0,3,3,-2,0,3,3,-1,0,3,3,0,0,3,4,-4,0,3,4,-3,0,3,4,-2,0,3,4,-1,0,3,4,0,0,3,5,-4,0,3,5,-3,0,3,5,-1,0,3,3,1,0,3,3,2,0,3,4,1,0,3,4,2,0,3,4,3,0,3,4,4,0,3,5,3,0,3,5,4,0,3,6,3,0,3,6,4,0,3,6,-3,0,3,7,-3,0,3,7,-2,0,3,7,2,0,3,7,3,0,3,7,4,0,3,8,-3,0,3,8,-2,0,3,8,-1,0,3,8,0,0,3,8,1,0,3,8,2,0,3,8,3,0,3,8,4,0,3,-1,2,0,3,0,2,0,3,1,2,0,3,2,2,0,3,2,1,0,3,1,1,0,3,0,1,0,3,-1,1,0,3,-1,5,0,3,0,5,0,3,0,6,0,3,-1,6,0,3,-7,-3,0,3,6,-2,0,3,5,-2,0,3,8,5,0,3,8,6,0,3,8,7,0,3,-3,-5,0,3,-3,-6,0,3,5,-6,0,3,5,-5,0,3,5,-7,0,3,-3,-7,0,3,5,-8,0,3,4,-8,0,3,3,-8,0,3,2,-8,0,3,1,-8,0,3,0,-8,0,3,-1,-8,0,3,-2,-8,0,3,-3,-8,0,3,3,7,0,3,-7,-8,0,3,-7,-7,0,3,-7,-6,0,3,-7,-5,0,3,-7,-4,0,3,-6,-8,0,3,-6,-7,0,3,-6,-6,0,3,-6,-5,0,3,-6,-4,0,3,-5,-8,0,3,-5,-7,0,3,-5,-6,0,3,-5,-5,0,3,-5,-4,0,3,-4,-8,0,3,-4,-7,0,3,-4,-6,0,3,-4,-5,0,3,-4,-4,0,3,6,-8,0,3,6,-7,0,3,6,-6,0,3,6,-5,0,3,6,-4,0,3,7,-8,0,3,7,-7,0,3,7,-6,0,3,7,-5,0,3,7,-4,0,3,8,-8,0,3,8,-7,0,3,8,-6,0,3,8,-5,0,3,8,-4,0,3],"CustomFloor1":[-4,2,1,7,14,-3,3,1,8,15,-4,3,1,7,15,-5,3,1,6,15,6,4,1,8,15,5,4,1,7,15,-5,2,1,6,14,4,3,1,6,14,4,4,1,6,15,6,3,1,8,14,5,3,1,7,14,-3,2,1,8,14],"CustomFloor2":[],"CustomFloor3":[],"CustomMiddle1":[-3,4,1,10,5,-3,5,1,10,6,2,4,1,10,5,2,5,1,10,6],"CustomMiddle2":[5,-4,1,6,0,5,-3,1,6,1,-2,-1,1,5,0,-2,0,1,5,1],"CustomTop":[]} \ No newline at end of file +{"NavigationVertices":[{"X":134,"Y":38},{"X":138,"Y":-122},{"X":138,"Y":138},{"X":134,"Y":90},{"X":134,"Y":-6},{"X":90,"Y":-6},{"X":86,"Y":-58},{"X":86,"Y":-102},{"X":-106,"Y":-122},{"X":-38,"Y":-102},{"X":74,"Y":10},{"X":74,"Y":54},{"X":58,"Y":90},{"X":-26,"Y":38},{"X":-26,"Y":-6},{"X":90,"Y":10},{"X":-38,"Y":-58},{"X":118,"Y":54},{"X":118,"Y":38},{"X":134,"Y":138},{"X":58,"Y":138},{"X":-58,"Y":138},{"X":-58,"Y":106},{"X":-86,"Y":38},{"X":-106,"Y":106},{"X":-86,"Y":-6}],"NavigationPolygon":[[0,1,2,3],[1,0,4],[1,4,5,6,7],[8,1,7,9],[10,11,12,13,14],[6,5,15,10,14,16],[17,18,0,3],[2,19,3],[17,3,12,11],[13,12,20,21,22],[23,13,22,24],[23,24,8,25],[25,8,9,16],[14,25,16]],"Floor":[-3,-4,0,3,-2,-4,0,3,-2,-3,0,3,-3,-3,0,3,-7,-2,0,3,-7,-1,0,3,-7,0,0,3,-7,1,0,3,-7,2,0,3,-7,3,0,3,-7,4,0,3,-7,5,0,3,-6,-3,0,3,-6,-2,0,3,-6,-1,0,3,-6,0,0,3,-6,1,0,3,-6,2,0,3,-6,3,0,3,-6,4,0,3,-6,5,0,3,-5,-3,0,3,-5,-2,0,3,-5,2,0,3,-5,3,0,3,-5,4,0,3,-5,5,0,3,-4,-3,0,3,-4,-2,0,3,-4,2,0,3,-4,3,0,3,-4,4,0,3,-4,5,0,3,-3,-2,0,3,-3,2,0,3,-3,3,0,3,-3,4,0,3,-3,5,0,3,-2,-2,0,3,-2,-1,0,3,-2,0,0,3,-2,1,0,3,-2,2,0,3,-2,3,0,3,-2,4,0,3,-2,5,0,3,-4,6,0,3,-4,7,0,3,-3,6,0,3,-3,7,0,3,-2,6,0,3,-2,7,0,3,-1,3,0,3,-1,4,0,3,-1,7,0,3,0,3,0,3,0,4,0,3,0,7,0,3,1,3,0,3,1,4,0,3,1,5,0,3,1,6,0,3,1,7,0,3,2,3,0,3,2,4,0,3,2,5,0,3,2,6,0,3,2,7,0,3,3,3,0,3,3,4,0,3,3,5,0,3,3,6,0,3,-1,-4,0,3,-1,-3,0,3,-1,-2,0,3,-1,-1,0,3,-1,0,0,3,0,-4,0,3,0,-3,0,3,0,-2,0,3,0,-1,0,3,0,0,0,3,1,-4,0,3,1,-3,0,3,1,-2,0,3,1,-1,0,3,1,0,0,3,2,-4,0,3,2,-3,0,3,2,-2,0,3,2,-1,0,3,2,0,0,3,3,-4,0,3,3,-3,0,3,3,-2,0,3,3,-1,0,3,3,0,0,3,4,-4,0,3,4,-3,0,3,4,-2,0,3,4,-1,0,3,4,0,0,3,5,-4,0,3,5,-3,0,3,5,-1,0,3,3,1,0,3,3,2,0,3,4,1,0,3,4,2,0,3,4,3,0,3,4,4,0,3,5,3,0,3,5,4,0,3,6,3,0,3,6,4,0,3,6,-3,0,3,7,-3,0,3,7,-2,0,3,7,2,0,3,7,3,0,3,7,4,0,3,8,-3,0,3,8,-2,0,3,8,-1,0,3,8,0,0,3,8,1,0,3,8,2,0,3,8,3,0,3,8,4,0,3,-1,2,0,3,0,2,0,3,1,2,0,3,2,2,0,3,2,1,0,3,1,1,0,3,0,1,0,3,-1,1,0,3,-1,5,0,3,0,5,0,3,0,6,0,3,-1,6,0,3,-7,-3,0,3,6,-2,0,3,5,-2,0,3,8,5,0,3,8,6,0,3,8,7,0,3,-3,-5,0,3,-3,-6,0,3,5,-6,0,3,5,-5,0,3,5,-7,0,3,-3,-7,0,3,5,-8,0,3,4,-8,0,3,3,-8,0,3,2,-8,0,3,1,-8,0,3,0,-8,0,3,-1,-8,0,3,-2,-8,0,3,-3,-8,0,3,3,7,0,3,-7,-8,0,3,-7,-7,0,3,-7,-6,0,3,-7,-5,0,3,-7,-4,0,3,-6,-8,0,3,-6,-7,0,3,-6,-6,0,3,-6,-5,0,3,-6,-4,0,3,-5,-8,0,3,-5,-7,0,3,-5,-6,0,3,-5,-5,0,3,-5,-4,0,3,-4,-8,0,3,-4,-7,0,3,-4,-6,0,3,-4,-5,0,3,-4,-4,0,3,6,-8,0,3,6,-7,0,3,6,-6,0,3,6,-5,0,3,6,-4,0,3,7,-8,0,3,7,-7,0,3,7,-6,0,3,7,-5,0,3,7,-4,0,3,8,-8,0,3,8,-7,0,3,8,-6,0,3,8,-5,0,3,8,-4,0,3,-2,-6,0,3],"CustomFloor1":[-4,2,1,7,14,-3,3,1,8,15,-4,3,1,7,15,-5,3,1,6,15,6,4,1,8,15,5,4,1,7,15,-5,2,1,6,14,4,3,1,6,14,4,4,1,6,15,6,3,1,8,14,5,3,1,7,14,-3,2,1,8,14],"CustomFloor2":[],"CustomFloor3":[],"CustomMiddle1":[-3,4,1,10,5,-3,5,1,10,6,2,4,1,10,5,2,5,1,10,6],"CustomMiddle2":[5,-4,1,6,0,5,-3,1,6,1,-2,-1,1,5,0,-2,0,1,5,1],"CustomTop":[]} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preinstall.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preinstall.json new file mode 100644 index 0000000..155c178 --- /dev/null +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preinstall.json @@ -0,0 +1 @@ +[{"Name":"Preinstall1","Weight":100,"Remark":"","AutoFill":true,"WaveList":[[{"Position":{"X":-81,"Y":25},"Size":{"X":0,"Y":0},"SpecialMarkType":1,"DelayTime":0,"MarkList":[]},{"Position":{"X":21,"Y":31},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"enemy0001","Weight":100,"Attr":{"Face":"1","Weapon":"weapon0003","CurrAmmon":"12","ResidueAmmo":"12"},"Altitude":0,"VerticalSpeed":5.551115E-14}]},{"Position":{"X":110,"Y":37},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"enemy0001","Weight":100,"Attr":{"Face":"-1","Weapon":"weapon0002","CurrAmmon":"7","ResidueAmmo":"7"},"Altitude":0,"VerticalSpeed":5.551115E-14}]},{"Position":{"X":-54,"Y":20},"Size":{"X":16,"Y":16},"SpecialMarkType":0,"DelayTime":0,"MarkList":[{"Id":"weapon0001","Weight":100,"Attr":{"CurrAmmon":"30","ResidueAmmo":"210"},"Altitude":8,"VerticalSpeed":5.551115E-14}]}]]}] \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png new file mode 100644 index 0000000..a1a56f6 --- /dev/null +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png Binary files differ diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png.import b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png.import new file mode 100644 index 0000000..e160238 --- /dev/null +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/Preview.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cpx43bg5na8f4" +path="res://.godot/imported/Preview.png-a179680c55da0fe1cc56e9132032b6e3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/map/tileMaps/Test1/inlet/Start2/Preview.png" +dest_files=["res://.godot/imported/Preview.png-a179680c55da0fe1cc56e9132032b6e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/RoomInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/RoomInfo.json new file mode 100644 index 0000000..1b4a534 --- /dev/null +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/RoomInfo.json @@ -0,0 +1 @@ +{"Position":{"X":-9,"Y":-7},"Size":{"X":19,"Y":16},"DoorAreaInfos":[],"GroupName":"Test1","RoomType":2,"RoomName":"Start2","Weight":100,"Remark":""} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/TileInfo.json b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/TileInfo.json new file mode 100644 index 0000000..d48b637 --- /dev/null +++ b/DungeonShooting_Godot/resource/map/tileMaps/Test1/inlet/Start2/TileInfo.json @@ -0,0 +1 @@ +{"NavigationVertices":[{"X":6,"Y":86},{"X":122,"Y":122},{"X":-106,"Y":122},{"X":-22,"Y":86},{"X":-106,"Y":-58},{"X":-22,"Y":-22},{"X":122,"Y":-58},{"X":6,"Y":-22}],"NavigationPolygon":[[0,1,2,3],[3,2,4,5],[5,4,6,7],[6,1,0,7]],"Floor":[-3,-4,0,3,-3,-3,0,3,-3,-2,0,3,-3,-1,0,3,-3,0,0,3,-3,1,0,3,-3,2,0,3,-3,3,0,3,-3,4,0,3,-3,5,0,3,-3,6,0,3,-2,-4,0,3,-2,-3,0,3,-2,-2,0,3,-2,0,0,3,-2,1,0,3,-2,2,0,3,-2,3,0,3,-2,4,0,3,-2,5,0,3,-2,6,0,3,-1,-4,0,3,-1,-3,0,3,-1,5,0,3,-1,6,0,3,0,-4,0,3,0,-3,0,3,0,-2,0,3,0,-1,0,3,0,0,0,3,0,1,0,3,0,2,0,3,0,3,0,3,0,4,0,3,0,5,0,3,0,6,0,3,1,-4,0,3,1,-3,0,3,1,-2,0,3,1,-1,0,3,1,0,0,3,1,1,0,3,1,2,0,3,1,3,0,3,1,4,0,3,1,5,0,3,1,6,0,3,2,-4,0,3,2,-3,0,3,2,-2,0,3,2,-1,0,3,2,0,0,3,2,1,0,3,2,2,0,3,2,3,0,3,2,4,0,3,2,5,0,3,2,6,0,3,3,-4,0,3,3,-3,0,3,3,-2,0,3,3,-1,0,3,3,0,0,3,3,1,0,3,3,2,0,3,3,3,0,3,3,4,0,3,3,5,0,3,3,6,0,3,4,-4,0,3,4,-3,0,3,4,-2,0,3,4,-1,0,3,4,0,0,3,4,1,0,3,4,2,0,3,4,3,0,3,4,4,0,3,4,5,0,3,4,6,0,3,5,-4,0,3,5,-3,0,3,5,-2,0,3,5,-1,0,3,5,0,0,3,5,1,0,3,5,2,0,3,5,3,0,3,5,4,0,3,5,5,0,3,5,6,0,3,6,-4,0,3,6,-3,0,3,6,-2,0,3,6,-1,0,3,6,0,0,3,6,1,0,3,6,2,0,3,6,3,0,3,6,4,0,3,6,5,0,3,6,6,0,3,7,-4,0,3,7,-3,0,3,7,-2,0,3,7,-1,0,3,7,0,0,3,7,1,0,3,7,2,0,3,7,3,0,3,7,4,0,3,7,5,0,3,7,6,0,3,-4,6,0,3,-4,5,0,3,-4,4,0,3,-4,3,0,3,-4,2,0,3,-4,1,0,3,-4,0,0,3,-4,-1,0,3,-4,-2,0,3,-4,-3,0,3,-4,-4,0,3,-5,-4,0,3,-5,-3,0,3,-5,-2,0,3,-5,-1,0,3,-5,0,0,3,-5,1,0,3,-5,2,0,3,-5,3,0,3,-5,4,0,3,-5,5,0,3,-5,6,0,3,-6,6,0,3,-6,5,0,3,-6,4,0,3,-6,3,0,3,-6,2,0,3,-6,1,0,3,-6,0,0,3,-6,-1,0,3,-6,-2,0,3,-6,-3,0,3,-6,-4,0,3,-7,-4,0,3,-7,-3,0,3,-7,-2,0,3,-7,-1,0,3,-7,0,0,3,-7,1,0,3,-7,2,0,3,-7,3,0,3,-7,4,0,3,-7,5,0,3,-7,6,0,3,-2,-1,0,3],"CustomFloor1":[],"CustomFloor2":[],"CustomFloor3":[],"CustomMiddle1":[],"CustomMiddle2":[],"CustomTop":[]} \ No newline at end of file diff --git a/DungeonShooting_Godot/resource/material/Sawtooth.tres b/DungeonShooting_Godot/resource/material/Sawtooth.tres index 73c3175..68a3ed6 100644 --- a/DungeonShooting_Godot/resource/material/Sawtooth.tres +++ b/DungeonShooting_Godot/resource/material/Sawtooth.tres @@ -2,6 +2,5 @@ [ext_resource type="Shader" path="res://resource/shader/Sawtooth.gdshader" id="1_nrhtr"] - [resource] shader = ExtResource("1_nrhtr") diff --git a/DungeonShooting_Godot/resource/shader/Sawtooth.gdshader b/DungeonShooting_Godot/resource/shader/Sawtooth.gdshader index cbbc062..09c8dd0 100644 --- a/DungeonShooting_Godot/resource/shader/Sawtooth.gdshader +++ b/DungeonShooting_Godot/resource/shader/Sawtooth.gdshader @@ -1,18 +1,178 @@ shader_type canvas_item; +bool checkLB(float len, sampler2D tex, vec2 uv) { + int c = 0; + int c2 = 0; + if (texture(tex, uv + vec2(-len, 0.0)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(-len * 2.0, 0.0)).a > 0.0) { + c++; + c2 += 2; + } + if (c > 0) { + if (texture(tex, uv + vec2(0.0, -len)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(0.0, -len * 2.0)).a > 0.0) { + c++; + c2 += 2; + } + } + return c >= 2 && c2 <= 3; +} + +bool checkLT(float len, sampler2D tex, vec2 uv) { + int c = 0; + int c2 = 0; + if (texture(tex, uv + vec2(-len, 0.0)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(-len * 2.0, 0.0)).a > 0.0) { + c++; + c2 += 2; + } + if (c > 0) { + if (texture(tex, uv + vec2(0.0, len)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(0.0, len * 2.0)).a > 0.0) { + c++; + c2 += 2; + } + } + return c >= 2 && c2 <= 3; +} + +bool checkRB(float len, sampler2D tex, vec2 uv) { + int c = 0; + int c2 = 0; + if (texture(tex, uv + vec2(len, 0.0)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(len * 2.0, 0.0)).a > 0.0) { + c++; + c2 += 2; + } + if (c > 0) { + if (texture(tex, uv + vec2(0.0, -len)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(0.0, -len * 2.0)).a > 0.0) { + c++; + c2 += 2; + } + } + return c >= 2 && c2 <= 3; +} + +bool checkRT(float len, sampler2D tex, vec2 uv) { + int c = 0; + int c2 = 0; + if (texture(tex, uv + vec2(len, 0.0)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(len * 2.0, 0.0)).a > 0.0) { + c++; + c2 += 2; + } + if (c > 0) { + if (texture(tex, uv + vec2(0.0, len)).a > 0.0) { + c++; + c2 += 1; + } else if (texture(tex, uv + vec2(0.0, len * 2.0)).a > 0.0) { + c++; + c2 += 2; + } + } + return c >= 2 && c2 <= 3; +} + + void vertex() { VERTEX = VERTEX * vec2(4.0); } void fragment() { - vec2 pixel_size = 1.0 / vec2(textureSize(TEXTURE, 0)); - vec4 color = vec4(0.0); - for (int x = -1; x <= 1; x++) { - for (int y = -1; y <= 1; y++) { - color += texture(TEXTURE, UV + vec2(float(x), float(y)) * pixel_size / 2.0); - } - } - color /= 9.0; - COLOR = color; -} + if (COLOR.a <= 0.0) { + int c = 0; + vec4 cf = vec4(0.0, 0.0, 0.0, 0.0); + vec4 cL = texture(TEXTURE, UV + vec2(-TEXTURE_PIXEL_SIZE.x, 0.0)); + vec4 cr = texture(TEXTURE, UV + vec2(TEXTURE_PIXEL_SIZE.x, 0.0)); + vec4 ct = texture(TEXTURE, UV + vec2(0.0, TEXTURE_PIXEL_SIZE.y)); + vec4 cb = texture(TEXTURE, UV + vec2(0.0, -TEXTURE_PIXEL_SIZE.y)); + if (cL.a > 0.0) { + c++; + cf += cL; + } + if (cr.a > 0.0) { + c++; + cf += cr; + } + if (ct.a > 0.0) { + c++; + cf += ct; + } + if (cb.a > 0.0) { + c++; + cf += cb; + } + if (c >= 2) { + if (cb.a > 0.0 && cL.a > 0.0) { + if (checkLB(TEXTURE_PIXEL_SIZE.y / 4.0, TEXTURE, UV)) { + cf /= float(c); + COLOR = cf; + } + } + if (ct.a > 0.0 && cL.a > 0.0) { + if (checkLT(TEXTURE_PIXEL_SIZE.y / 4.0, TEXTURE, UV)) { + cf /= float(c); + COLOR = cf; + } + } + if (cb.a > 0.0 && cr.a > 0.0) { + if (checkRB(TEXTURE_PIXEL_SIZE.y / 4.0, TEXTURE, UV)) { + cf /= float(c); + COLOR = cf; + } + } + if (ct.a > 0.0 && cr.a > 0.0) { + if (checkRT(TEXTURE_PIXEL_SIZE.y / 4.0, TEXTURE, UV)) { + cf /= float(c); + COLOR = cf; + } + } + } + } else { + int c = 0; + float tempV = TEXTURE_PIXEL_SIZE.x / 4.0; + vec4 cf = vec4(0.0, 0.0, 0.0, 0.0); + vec4 clt = texture(TEXTURE, UV + vec2(-tempV, tempV)); + vec4 crt = texture(TEXTURE, UV + vec2(tempV, tempV)); + vec4 clb = texture(TEXTURE, UV + vec2(-tempV, -tempV)); + vec4 crb = texture(TEXTURE, UV + vec2(tempV, -tempV)); + + if (clt.a > 0.0) { + c++; + cf += clt; + } + if (crt.a > 0.0) { + c++; + cf += crt; + } + if (clb.a > 0.0) { + c++; + cf += clb; + } + if (crb.a > 0.0) { + c++; + cf += crb; + } + if (c > 0 && c < 2) { + COLOR = vec4(0.0, 0.0, 0.0, 0.0); + } else { + COLOR = cf / float(c); + } + } +} diff --git a/DungeonShooting_Godot/resource/sprite/brush/Temp.png b/DungeonShooting_Godot/resource/sprite/brush/Temp.png new file mode 100644 index 0000000..1c50613 --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/brush/Temp.png Binary files differ diff --git a/DungeonShooting_Godot/resource/sprite/brush/Temp.png.import b/DungeonShooting_Godot/resource/sprite/brush/Temp.png.import new file mode 100644 index 0000000..d3e4a73 --- /dev/null +++ b/DungeonShooting_Godot/resource/sprite/brush/Temp.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6iklkty6p8sx" +path="res://.godot/imported/Temp.png-eb88d17e172de4ef1d192f84370ff0de.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://resource/sprite/brush/Temp.png" +dest_files=["res://.godot/imported/Temp.png-eb88d17e172de4ef1d192f84370ff0de.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/DungeonShooting_Godot/scene/test/TestCreateSector.tscn b/DungeonShooting_Godot/scene/test/TestCreateSector.tscn index 795fc2e..255bb1e 100644 --- a/DungeonShooting_Godot/scene/test/TestCreateSector.tscn +++ b/DungeonShooting_Godot/scene/test/TestCreateSector.tscn @@ -1,13 +1,91 @@ -[gd_scene load_steps=4 format=3 uid="uid://d4axmwaqk1f55"] +[gd_scene load_steps=10 format=3 uid="uid://d4axmwaqk1f55"] [ext_resource type="Script" path="res://src/test/TestCreateSector.cs" id="1_ieqp4"] [ext_resource type="Texture2D" uid="uid://uhhfgdhpk7i4" path="res://icon.png" id="2_73jok"] +[ext_resource type="Texture2D" uid="uid://b6iklkty6p8sx" path="res://resource/sprite/brush/Temp.png" id="3_mchtg"] +[ext_resource type="Material" uid="uid://cca0ka64xmrrd" path="res://resource/material/Sawtooth.tres" id="3_p1gue"] +[ext_resource type="Texture2D" uid="uid://jnx4wfn5hu3u" path="res://resource/sprite/brush/Brush3.png" id="5_1h5h3"] +[ext_resource type="Texture2D" uid="uid://betgai078na4v" path="res://resource/map/tileSet/TileSet1/Main.png" id="6_22ncj"] + +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_on1ir"] +texture = ExtResource("6_22ncj") +0:0/0 = 0 +1:0/0 = 0 +2:0/0 = 0 +3:0/0 = 0 +4:0/0 = 0 +5:0/0 = 0 +6:0/0 = 0 +7:0/0 = 0 +8:0/0 = 0 +9:0/0 = 0 +10:0/0 = 0 +11:0/0 = 0 +0:1/0 = 0 +1:1/0 = 0 +2:1/0 = 0 +3:1/0 = 0 +4:1/0 = 0 +5:1/0 = 0 +6:1/0 = 0 +7:1/0 = 0 +8:1/0 = 0 +9:1/0 = 0 +11:1/0 = 0 +0:2/0 = 0 +1:2/0 = 0 +2:2/0 = 0 +3:2/0 = 0 +4:2/0 = 0 +5:2/0 = 0 +6:2/0 = 0 +7:2/0 = 0 +8:2/0 = 0 +9:2/0 = 0 +10:2/0 = 0 +11:2/0 = 0 +0:3/0 = 0 +1:3/0 = 0 +2:3/0 = 0 +3:3/0 = 0 +4:3/0 = 0 +5:3/0 = 0 +6:3/0 = 0 +7:3/0 = 0 +8:3/0 = 0 +9:3/0 = 0 +10:3/0 = 0 +11:3/0 = 0 +0:4/0 = 0 +1:4/0 = 0 +2:4/0 = 0 +3:4/0 = 0 +4:4/0 = 0 +5:4/0 = 0 +6:4/0 = 0 +7:4/0 = 0 +8:4/0 = 0 +9:4/0 = 0 +10:4/0 = 0 +0:5/0 = 0 +1:5/0 = 0 +2:5/0 = 0 +3:5/0 = 0 +4:5/0 = 0 + +[sub_resource type="TileSet" id="TileSet_5gh5n"] +sources/0 = SubResource("TileSetAtlasSource_on1ir") [sub_resource type="CircleShape2D" id="CircleShape2D_42m3w"] [node name="TestCreateSector" type="Node2D"] script = ExtResource("1_ieqp4") +[node name="TileMap" type="TileMap" parent="."] +tile_set = SubResource("TileSet_5gh5n") +format = 2 +layer_0/tile_data = PackedInt32Array(-65535, 0, 4, 0, 0, 4, 65536, 0, 4, 131072, 0, 4, 131073, 0, 4, 131074, 0, 4, 65538, 0, 4, 2, 0, 4, 1, 0, 4, 65537, 0, 4, 196609, 0, 4, 196610, 0, 4, 65539, 0, 4, 3, 0, 4, 131075, 0, 4, 196611, 0, 4, 262147, 0, 4, 65540, 0, 4, 4, 0, 4, 131076, 0, 4, 196612, 0, 4, 262148, 0, 4, 262146, 0, 4, 262145, 0, 4, 196608, 0, 4, 262144, 0, 4) + [node name="Area2D" type="Area2D" parent="."] position = Vector2(959, 505) @@ -20,3 +98,14 @@ [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D2"] shape = SubResource("CircleShape2D_42m3w") + +[node name="Sprite2D" type="Sprite2D" parent="."] +material = ExtResource("3_p1gue") +position = Vector2(5, -19) +texture = ExtResource("3_mchtg") +centered = false + +[node name="Sprite2D2" type="Sprite2D" parent="."] +position = Vector2(32, 43) +texture = ExtResource("5_1h5h3") +centered = false diff --git a/DungeonShooting_Godot/src/framework/activity/hurt/HurtArea.cs b/DungeonShooting_Godot/src/framework/activity/hurt/HurtArea.cs index 8631faa..b83bd6b 100644 --- a/DungeonShooting_Godot/src/framework/activity/hurt/HurtArea.cs +++ b/DungeonShooting_Godot/src/framework/activity/hurt/HurtArea.cs @@ -1,17 +1,19 @@ using Godot; +/// +/// 可被子弹击中的区域 +/// [Tool] public partial class HurtArea : Area2D, IHurt { - public delegate void HurtDelegate(ActivityObject target, int damage, float angle); + /// + /// 所属角色 + /// + public Role Master { get; private set; } - public event HurtDelegate OnHurtEvent; - - public ActivityObject ActivityObject { get; private set; } - - public void InitActivityObject(ActivityObject activityObject) + public void InitRole(Role role) { - ActivityObject = activityObject; + Master = role; } public override void _Ready() @@ -19,11 +21,19 @@ Monitoring = false; } + public bool CanHurt(CampEnum targetCamp) + { + //无敌状态 + if (Master.Invincible) + { + return true; + } + + return Master.IsEnemy(targetCamp); + } + public void Hurt(ActivityObject target, int damage, float angle) { - if (OnHurtEvent != null) - { - OnHurtEvent(target, damage, angle); - } + Master.CallDeferred(nameof(Master.HurtHandler), target, damage, angle); } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/activity/hurt/IHurt.cs b/DungeonShooting_Godot/src/framework/activity/hurt/IHurt.cs index fd27abc..da56662 100644 --- a/DungeonShooting_Godot/src/framework/activity/hurt/IHurt.cs +++ b/DungeonShooting_Godot/src/framework/activity/hurt/IHurt.cs @@ -4,6 +4,12 @@ public interface IHurt { /// + /// 返回是否可以造成伤害 + /// + /// 攻击目标所属层级 + bool CanHurt(CampEnum targetCamp); + + /// /// 受到伤害 /// /// 触发伤害的对象, 为 null 表示不存在对象或者对象已经被销毁 diff --git a/DungeonShooting_Godot/src/framework/common/NodeExtend.cs b/DungeonShooting_Godot/src/framework/common/NodeExtend.cs index e04437d..50d1ad9 100644 --- a/DungeonShooting_Godot/src/framework/common/NodeExtend.cs +++ b/DungeonShooting_Godot/src/framework/common/NodeExtend.cs @@ -21,7 +21,7 @@ if (hurt is HurtArea hurtArea) { - return hurtArea.ActivityObject; + return hurtArea.Master; } return null; diff --git a/DungeonShooting_Godot/src/framework/common/Utils.cs b/DungeonShooting_Godot/src/framework/common/Utils.cs index 416b796..54f7ba1 100644 --- a/DungeonShooting_Godot/src/framework/common/Utils.cs +++ b/DungeonShooting_Godot/src/framework/common/Utils.cs @@ -67,6 +67,17 @@ return angle; } + + /// + /// 判断a和b是否在同一梯度下 + /// + /// + /// + /// 梯度间距 + public static bool IsSameGradient(float a, float b, float gradient) + { + return (int)(a / gradient) == (int)(b / gradient); + } /// /// 根据步长吸附值 diff --git a/DungeonShooting_Godot/src/framework/map/AffiliationArea.cs b/DungeonShooting_Godot/src/framework/map/AffiliationArea.cs index 10f775d..3c9123d 100644 --- a/DungeonShooting_Godot/src/framework/map/AffiliationArea.cs +++ b/DungeonShooting_Godot/src/framework/map/AffiliationArea.cs @@ -64,7 +64,7 @@ Monitoring = true; Monitorable = false; CollisionLayer = PhysicsLayer.None; - CollisionMask = PhysicsLayer.Prop | PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Debris | PhysicsLayer.Throwing; + CollisionMask = PhysicsLayer.Prop | PhysicsLayer.Role | PhysicsLayer.Debris | PhysicsLayer.Throwing | PhysicsLayer.Obstacle; BodyEntered += OnBodyEntered; BodyExited += OnBodyExited; diff --git a/DungeonShooting_Godot/src/framework/map/DungeonGenerator.cs b/DungeonShooting_Godot/src/framework/map/DungeonGenerator.cs index 3f82e5d..b439592 100644 --- a/DungeonShooting_Godot/src/framework/map/DungeonGenerator.cs +++ b/DungeonShooting_Godot/src/framework/map/DungeonGenerator.cs @@ -207,14 +207,14 @@ if (errorCode == GenerateRoomErrorCode.OutArea) { _failCount++; - Debug.Log("超出区域失败次数: " + _failCount); + //Debug.Log("超出区域失败次数: " + _failCount); if (_failCount >= _maxFailCount) { //_enableLimitRange = false; _failCount = 0; _rangeX += 50; _rangeY += 50; - Debug.Log("生成房间失败次数过多, 增大区域"); + //Debug.Log("生成房间失败次数过多, 增大区域"); } } diff --git a/DungeonShooting_Godot/src/framework/map/liquid/LiquidCanvas.cs b/DungeonShooting_Godot/src/framework/map/liquid/LiquidCanvas.cs index 1b1ab92..67007d2 100644 --- a/DungeonShooting_Godot/src/framework/map/liquid/LiquidCanvas.cs +++ b/DungeonShooting_Godot/src/framework/map/liquid/LiquidCanvas.cs @@ -318,6 +318,7 @@ } else { + var oldA = imagePixel.Color.A; imagePixel.Color.A -= imagePixel.Material.WriteOffSpeed * (_runTime - imagePixel.TempTime); if (imagePixel.Color.A <= 0) //完全透明了 @@ -328,7 +329,7 @@ imagePixel.IsUpdate = false; return true; } - else + else if (!Utils.IsSameGradient(oldA, imagePixel.Color.A, GameConfig.LiquidGradient)) //同一渐变梯度下才会有颜色变化 { _changeFlag = true; _image.SetPixel(imagePixel.X, imagePixel.Y, imagePixel.Color); diff --git a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs index 2a63757..4e398ae 100644 --- a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs +++ b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs @@ -317,7 +317,7 @@ foreach (var preloadData in _readyList) { //有敌人 - if (!hasEnemy && preloadData.ActivityObject.CollisionWithMask(PhysicsLayer.Enemy)) + if (!hasEnemy && preloadData.ActivityObject is Role role && role.IsEnemyWithPlayer()) { hasEnemy = true; } @@ -332,7 +332,7 @@ if (!hasEnemy) { hasEnemy = RoomInfo.AffiliationArea.ExistIncludeItem( - activityObject => activityObject.CollisionWithMask(PhysicsLayer.Enemy) + activityObject => activityObject is Role role && role.IsEnemyWithPlayer() ); } diff --git a/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs b/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs index 740846d..3459432 100644 --- a/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs +++ b/DungeonShooting_Godot/src/framework/map/room/RoomInfo.cs @@ -474,7 +474,7 @@ //房间内有敌人, 或者会刷新敌人才会关门 var hasEnemy = false; - if (AffiliationArea.ExistEnterItem(activityObject => activityObject.CollisionWithMask(PhysicsLayer.Enemy))) //先判断房间里面是否有敌人 + if (AffiliationArea.ExistEnterItem(activityObject => activityObject is Role role && role.IsEnemyWithPlayer())) //先判断房间里面是否有敌人 { hasEnemy = true; } diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs index 0c2112e..53a5b93 100644 --- a/DungeonShooting_Godot/src/game/GameApplication.cs +++ b/DungeonShooting_Godot/src/game/GameApplication.cs @@ -134,7 +134,7 @@ //固定帧率 //Engine.MaxFps = TargetFps; //调试绘制开关 - ActivityObject.IsDebug = false; + ActivityObject.IsDebug = true; //Engine.TimeScale = 0.2f; //调整窗口分辨率 OnWindowSizeChanged(); diff --git a/DungeonShooting_Godot/src/game/GameConfig.cs b/DungeonShooting_Godot/src/game/GameConfig.cs index 1874314..8a36f3c 100644 --- a/DungeonShooting_Godot/src/game/GameConfig.cs +++ b/DungeonShooting_Godot/src/game/GameConfig.cs @@ -16,6 +16,11 @@ // ----------------------- 常量 ----------------------- + + /// + /// 液体画布渐变梯度 + /// + public const float LiquidGradient = 0.1f; /// /// 连接房间的过道宽度 diff --git a/DungeonShooting_Godot/src/game/World.cs b/DungeonShooting_Godot/src/game/World.cs deleted file mode 100644 index 34add95..0000000 --- a/DungeonShooting_Godot/src/game/World.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Godot; - -/// -/// 游戏世界 -/// -public partial class World : CanvasModulate, ICoroutine -{ - /// - /// 当前的游戏世界对象 - /// - public static World Current => GameApplication.Instance.DungeonManager.CurrWorld; - - /// - /// 当前操作的玩家 - /// - public Player Player { get; private set; } - - /// - /// //对象根节点 - /// - public Node2D NormalLayer; - - /// - /// 对象根节点, 带y轴排序功能 - /// - public Node2D YSortLayer; - - /// - /// 地图根节点 - /// - public TileMap TileRoot; - - public Node2D StaticSpriteRoot; - public Node2D AffiliationAreaRoot; - public Node2D FogMaskRoot; - public Node2D NavigationRoot; - - /// - /// 是否暂停 - /// - public bool Pause - { - get => _pause; - set - { - if (_pause != value) - { - _pause = value; - if (value) - { - ProcessMode = ProcessModeEnum.WhenPaused; - } - else - { - ProcessMode = ProcessModeEnum.Inherit; - } - } - } - } - - /// - /// 所有被扔在地上的武器 - /// - public HashSet Weapon_UnclaimedWeapons { get; } = new HashSet(); - - /// - /// 记录所有存活的敌人 - /// - public List Enemy_InstanceList { get; } = new List(); - - /// - /// 随机数对象 - /// - public SeedRandom Random { get; private set; } - - /// - /// 随机对象池 - /// - public RandomPool RandomPool { get; private set; } - - /// - /// 角色死亡事件 - /// - public event Action OnRoleDieEvent; - - private bool _pause = false; - private List _coroutineList; - - public override void _Ready() - { - //TileRoot.YSortEnabled = false; - NormalLayer = GetNode("TileRoot/NormalLayer"); - YSortLayer = GetNode("TileRoot/YSortLayer"); - TileRoot = GetNode("TileRoot"); - StaticSpriteRoot = GetNode("TileRoot/StaticSpriteRoot"); - FogMaskRoot = GetNode("TileRoot/FogMaskRoot"); - NavigationRoot = GetNode("TileRoot/NavigationRoot"); - AffiliationAreaRoot = GetNode("TileRoot/AffiliationAreaRoot"); - } - - public override void _Process(double delta) - { - //协程更新 - ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta); - } - - /// - /// 获取指定层级根节点 - /// - public Node2D GetRoomLayer(RoomLayerEnum layerEnum) - { - switch (layerEnum) - { - case RoomLayerEnum.NormalLayer: - return NormalLayer; - case RoomLayerEnum.YSortLayer: - return YSortLayer; - } - - return null; - } - - /// - /// 设置当前操作的玩家对象 - /// - public void SetCurrentPlayer(Player player) - { - Player = player; - //设置相机和鼠标跟随玩家 - GameCamera.Main.SetFollowTarget(player); - GameApplication.Instance.Cursor.SetMountRole(player); - } - - /// - /// 通知其他敌人发现目标了 - /// - /// 发送通知的角色 - /// 目标 - public void NotifyEnemyTarget(Role self, ActivityObject target) - { - foreach (var role in Enemy_InstanceList) - { - if (role != self && !role.IsDestroyed && role.AffiliationArea == self.AffiliationArea) - { - //将未发现目标的敌人状态置为惊讶状态 - var controller = role.StateController; - //延时通知效果 - role.CallDelay(Utils.Random.RandomRangeFloat(0.2f, 1f), () => - { - if (controller.CurrState == AIStateEnum.AiNormal) - { - controller.ChangeState(AIStateEnum.AiLeaveFor, target); - } - }); - } - } - } - - public long StartCoroutine(IEnumerator able) - { - return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able); - } - - public void StopCoroutine(long coroutineId) - { - ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId); - } - - public bool IsCoroutineOver(long coroutineId) - { - return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId); - } - - public void StopAllCoroutine() - { - ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList); - } - - /// - /// 初始化随机池 - /// - public void InitRandomPool(SeedRandom random) - { - Random = random; - RandomPool = new RandomPool(this); - } - - /// - /// 角色死亡 - /// - public void OnRoleDie(Role role) - { - if (OnRoleDieEvent != null) - { - OnRoleDieEvent(role); - } - } -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs index 17edff3..52af65a 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs @@ -26,21 +26,26 @@ public CircleShape2D CircleShape { get; private set; } /// - /// 爆炸攻击的层级 - /// - public uint AttackLayer { get; private set; } - - /// /// 产生爆炸的子弹数据 /// public BulletData BulletData { get; private set; } + + /// + /// 所属阵营 + /// + public CampEnum Camp { get; private set; } private bool _init = false; private float _hitRadius; private int _harm; private float _repelledRadius; private float _maxRepelled; - + + public override void _Ready() + { + CollisionMask = Role.AttackLayer; + } + public void Destroy() { if (IsDestroyed) @@ -56,12 +61,12 @@ /// 初始化爆炸数据 /// /// 产生爆炸的子弹数据 - /// 攻击的层级 + /// 所属阵营 /// 伤害半径 /// 造成的伤害 /// 击退半径 /// 最大击退速度 - public void Init(BulletData bulletData, uint attackLayer, float hitRadius, int harm, float repelledRadius, float maxRepelled) + public void Init(BulletData bulletData, CampEnum camp, float hitRadius, int harm, float repelledRadius, float maxRepelled) { if (!_init) { @@ -74,13 +79,12 @@ BodyEntered += OnBodyEntered; } + Camp = camp; BulletData = bulletData; - AttackLayer = attackLayer; _hitRadius = hitRadius; _harm = harm; _repelledRadius = repelledRadius; _maxRepelled = maxRepelled; - CollisionMask = attackLayer | PhysicsLayer.Prop | PhysicsLayer.Debris; CircleShape.Radius = Mathf.Max(hitRadius, maxRepelled); //冲击波 @@ -162,18 +166,22 @@ var len = temp.Length(); var angle = temp.Angle(); - if (len <= _hitRadius) //在伤害半径内 + if (hurt.CanHurt(Camp)) { - hurt.Hurt(BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, _harm, angle); - } - - if (len <= _repelledRadius) //击退半径内 - { - var o = hurt.GetActivityObject(); - if (o != null) + var target = BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole; + if (len <= _hitRadius) //在伤害半径内 { - var repelled = (_repelledRadius - len) / _repelledRadius * _maxRepelled; - o.AddRepelForce(Vector2.FromAngle(angle) * repelled); + hurt.Hurt(target, _harm, angle); + } + + if (len <= _repelledRadius) //击退半径内 + { + var o = hurt.GetActivityObject(); + if (o != null) + { + var repelled = (_repelledRadius - len) / _repelledRadius * _maxRepelled; + o.AddRepelForce(Vector2.FromAngle(angle) * repelled); + } } } } diff --git a/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs b/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs index ebfb3f2..af0a9c4 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/laser/Laser.cs @@ -24,18 +24,14 @@ public Sprite2D LineSprite { get; private set; } public RectangleShape2D Shape { get; private set; } + public CampEnum Camp { get; set; } + public event Action OnReclaimEvent; public event Action OnLeavePoolEvent; public bool IsRecycled { get; set; } public string Logotype { get; set; } - public uint AttackLayer - { - get => CollisionMask; - set => CollisionMask = value; - } - public BulletData BulletData { get; private set; } public BulletStateEnum State { get; protected set; } = BulletStateEnum.Normal; @@ -49,12 +45,17 @@ private Tween _tween; private bool _init = false; - public void InitData(BulletData data, uint attackLayer) + public override void _Ready() { - InitData(data, attackLayer, LaserDefaultWidth); + CollisionMask = Role.AttackLayer; } - public void InitData(BulletData data, uint attackLayer, float width) + public void InitData(BulletData data, CampEnum camp) + { + InitData(data, camp, LaserDefaultWidth); + } + + public void InitData(BulletData data, CampEnum camp, float width) { if (!_init) { @@ -70,9 +71,9 @@ _init = true; } + Camp = camp; ZIndex = 1; BulletData = data; - AttackLayer = attackLayer; Position = data.Position; Rotation = data.Rotation; @@ -103,7 +104,7 @@ LineSprite.Scale = new Vector2(0, width * _pixelScale); //如果子弹会对玩家造成伤害, 则显示成红色 - if (BulletData.World.Player.CollisionWithMask(attackLayer)) + if (BulletData.TriggerRole != null && BulletData.TriggerRole.IsEnemyWithPlayer()) { LineSprite.Modulate = new Color(2.5f, 0.5f, 0.5f); } @@ -191,7 +192,7 @@ bulletData.BounceCount -= 1; bulletData.MaxDistance = newDistance; bulletData.Rotation = rotation; - FireManager.ShootBullet(bulletData, AttackLayer); + FireManager.ShootBullet(bulletData, Camp); } } } @@ -214,17 +215,21 @@ private void HandlerCollision(IHurt hurt) { - if (BulletData.Repel != 0) + if (hurt.CanHurt(Camp)) { - var o = hurt.GetActivityObject(); - if (o != null && o is not Player) //目标不是玩家才会触发击退 + if (BulletData.Repel != 0) { - o.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel); + var o = hurt.GetActivityObject(); + if (o != null && o is not Player) //目标不是玩家才会触发击退 + { + o.AddRepelForce(Vector2.FromAngle(Rotation) * BulletData.Repel); + } } + + //造成伤害 + var target = BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole; + hurt.Hurt(target, BulletData.Harm, Rotation); } - - //造成伤害 - hurt.Hurt(BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation); } public long StartCoroutine(IEnumerator able) diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/Arrow.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/Arrow.cs index 3f1ad52..dc53286 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/Arrow.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/Arrow.cs @@ -10,9 +10,9 @@ [Export, ExportFillNode] public AnimatedSprite2D HalfSprite { get; set; } - public override void InitData(BulletData data, uint attackLayer) + public override void InitData(BulletData data, CampEnum camp) { - base.InitData(data, attackLayer); + base.InitData(data, camp); SetEnableMovement(true); EnableVerticalMotion = false; DefaultLayer = RoomLayerEnum.NormalLayer; diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs index 79174e7..129e855 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/BoomBullet.cs @@ -21,9 +21,12 @@ public override void OnCollisionTarget(IHurt o) { - State = BulletStateEnum.CollisionTarget; - PlayBoom(); - LogicalFinish(); + if (o.CanHurt(Camp)) + { + State = BulletStateEnum.CollisionTarget; + PlayBoom(); + LogicalFinish(); + } } public override void OnMoveCollision(KinematicCollision2D lastSlideCollision) @@ -59,7 +62,7 @@ explode.Position = pos; explode.RotationDegrees = Utils.Random.RandomRangeInt(0, 360); explode.AddToActivityRootDeferred(RoomLayerEnum.YSortLayer); - explode.Init(BulletData, AttackLayer, 25, BulletData.Harm, 50, BulletData.Repel); + explode.Init(BulletData, Camp, 25, BulletData.Harm, 50, BulletData.Repel); explode.RunPlay(BulletData.TriggerRole); if (AffiliationArea != null) { diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs index 5ce3d20..d96b193 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs @@ -12,9 +12,12 @@ { public event Action OnReclaimEvent; public event Action OnLeavePoolEvent; + public bool IsRecycled { get; set; } public string Logotype { get; set; } + public CampEnum Camp { get; set; } + /// /// 子弹伤害碰撞区域 /// @@ -34,15 +37,6 @@ public Array Particles2D { get; set; } /// - /// 攻击的层级 - /// - public uint AttackLayer - { - get => CollisionArea.CollisionMask; - set => CollisionArea.CollisionMask = value; - } - - /// /// 子弹使用的数据 /// public BulletData BulletData { get; private set; } @@ -77,9 +71,11 @@ base.OnInit(); OutlineColor = new Color(2.5f, 0, 0); SetBlendColor(new Color(2.5f, 2.5f, 2.5f)); + + CollisionArea.CollisionMask = Role.AttackLayer; } - public virtual void InitData(BulletData data, uint attackLayer) + public virtual void InitData(BulletData data, CampEnum camp) { if (!_init) { @@ -87,13 +83,13 @@ CollisionArea.BodyEntered += OnBodyEntered; _init = true; } - + + Camp = camp; CurrentBounce = 0; CurrentPenetration = 0; CurrFlyDistance = 0; BulletData = data; - AttackLayer = attackLayer; Rotation = data.Rotation; var triggerRole = data.TriggerRole; @@ -120,7 +116,7 @@ MoveController.AddForce(new Vector2(data.FlySpeed, 0).Rotated(Rotation)); //如果子弹会对玩家造成伤害, 则显示红色描边 - if (World.Player != null && World.Player.CollisionWithMask(attackLayer)) + if (triggerRole != null && triggerRole.IsEnemyWithPlayer()) { if (!IsEnemyBullet) { @@ -193,25 +189,29 @@ /// public virtual void OnCollisionTarget(IHurt hurt) { - OnPlayDisappearEffect(); - if (BulletData.Repel != 0) + if (hurt.CanHurt(Camp)) { - var o = hurt.GetActivityObject(); - if (o != null && o is not Player) //目标不是玩家才会触发击退 + OnPlayDisappearEffect(); + if (BulletData.Repel != 0) { - o.AddRepelForce(Velocity.Normalized() * BulletData.Repel); + var o = hurt.GetActivityObject(); + if (o != null && o is not Player) //目标不是玩家才会触发击退 + { + o.AddRepelForce(Velocity.Normalized() * BulletData.Repel); + } } - } - - //造成伤害 - hurt.Hurt(BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation); - - //穿透次数 - CurrentPenetration++; - if (CurrentPenetration > BulletData.Penetration) - { - State = BulletStateEnum.CollisionTarget; - CallDeferred(nameof(LogicalFinish)); + + //造成伤害 + var target = BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole; + hurt.Hurt(target, BulletData.Harm, Rotation); + + //穿透次数 + CurrentPenetration++; + if (CurrentPenetration > BulletData.Penetration) + { + State = BulletStateEnum.CollisionTarget; + CallDeferred(nameof(LogicalFinish)); + } } } diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/IBullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/IBullet.cs index a642541..0e2df83 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/IBullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/IBullet.cs @@ -14,9 +14,10 @@ event Action OnLeavePoolEvent; /// - /// 攻击的层级 + /// 子弹所在阵营 /// - uint AttackLayer { get; set; } + CampEnum Camp { get; set; } + /// /// 子弹数据 /// @@ -30,7 +31,8 @@ /// /// 初始化子弹数据 /// - void InitData(BulletData data, uint attackLayer); + void InitData(BulletData data, CampEnum camp); + /// /// 子弹运行逻辑执行完成 /// diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/TrailBullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/TrailBullet.cs index 67f6da9..d459b56 100644 --- a/DungeonShooting_Godot/src/game/activity/bullet/normal/TrailBullet.cs +++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/TrailBullet.cs @@ -8,9 +8,9 @@ private static Color EnemyTerrainColor = new Color(1.5f, 0, 0, 0.7f); private Trail trail; - public override void InitData(BulletData data, uint attackLayer) + public override void InitData(BulletData data, CampEnum camp) { - base.InitData(data, attackLayer); + base.InitData(data, camp); trail = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_common_Trail0001_tscn); trail.SetTarget(AnimatedSprite); diff --git a/DungeonShooting_Godot/src/game/activity/item/ObstacleObject.cs b/DungeonShooting_Godot/src/game/activity/item/ObstacleObject.cs index 46ed568..f49703d 100644 --- a/DungeonShooting_Godot/src/game/activity/item/ObstacleObject.cs +++ b/DungeonShooting_Godot/src/game/activity/item/ObstacleObject.cs @@ -7,6 +7,11 @@ [Tool] public partial class ObstacleObject : ActivityObject, IHurt { + public virtual bool CanHurt(CampEnum targetCamp) + { + return true; + } + public virtual void Hurt(ActivityObject target, int damage, float angle) { } diff --git a/DungeonShooting_Godot/src/game/activity/role/CampEnum.cs b/DungeonShooting_Godot/src/game/activity/role/CampEnum.cs index 6954070..6ba2bb7 100644 --- a/DungeonShooting_Godot/src/game/activity/role/CampEnum.cs +++ b/DungeonShooting_Godot/src/game/activity/role/CampEnum.cs @@ -1,10 +1,28 @@ public enum CampEnum { - // 阵营1, 玩家 + /// + /// 无阵营, 所有角色都视为敌人 + /// + None, + /// + /// 和平阵营, 不会被攻击 + /// + Peace, + /// + /// 阵营1, 玩家 + /// Camp1, - // 阵营2, 敌人 + /// + /// 阵营2, 敌人 + /// Camp2, - // 阵营3, 中立单位 - Camp3 + /// + /// 阵营3, 敌人2 + /// + Camp3, + /// + /// 阵营4, 敌人3 + /// + Camp4, } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs index 850cba3..7b36d4d 100644 --- a/DungeonShooting_Godot/src/game/activity/role/Role.cs +++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs @@ -11,6 +11,11 @@ public abstract partial class Role : ActivityObject { /// + /// 攻击目标的碰撞器所属层级, 数据源自于: + /// + public const uint AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Obstacle | PhysicsLayer.HurtArea; + + /// /// 当前角色对其他角色造成伤害时对回调 /// 参数1为目标角色 /// 参数2为造成对伤害值 @@ -28,11 +33,6 @@ public RoleState RoleState { get; private set; } /// - /// 默认攻击对象层级 - /// - public const uint DefaultAttackLayer = PhysicsLayer.Player | PhysicsLayer.Enemy | PhysicsLayer.Obstacle; - - /// /// 伤害区域 /// [Export, ExportFillNode] @@ -47,17 +47,7 @@ /// /// 所属阵营 /// - public CampEnum Camp; - - /// - /// 攻击目标的碰撞器所属层级, 数据源自于: - /// - public uint AttackLayer { get; set; } = PhysicsLayer.Wall | PhysicsLayer.Obstacle; - - /// - /// 该角色敌对目标的碰撞器所属层级, 数据源自于: - /// - public uint EnemyLayer { get; set; } = PhysicsLayer.Enemy; + public CampEnum Camp { get; set; } /// /// 携带的被动道具列表 @@ -282,20 +272,11 @@ { if (value) //无敌状态 { - if (HurtArea != null) - { - HurtArea.CollisionLayer = _currentLayer; - } - _flashingInvincibleTimer = -1; _flashingInvincibleFlag = false; } else //正常状态 { - if (HurtArea != null) - { - HurtArea.CollisionLayer = _currentLayer; - } SetBlendModulate(new Color(1, 1, 1, 1)); } } @@ -335,7 +316,6 @@ private Vector2 _startScale; //当前可互动的物体 private CheckInteractiveResult _currentResultData; - private uint _currentLayer; //闪烁计时器 private float _flashingInvincibleTimer = -1; //闪烁状态 @@ -471,6 +451,20 @@ { } + public override void EnterTree() + { + if (!World.Role_InstanceList.Contains(this)) + { + World.Role_InstanceList.Add(this); + } + } + + public override void ExitTree() + { + World.Role_InstanceList.Remove(this); + } + + public override void OnInit() { RoleState = OnCreateRoleState(); @@ -479,15 +473,7 @@ _startScale = Scale; - HurtArea.InitActivityObject(this); - HurtArea.CollisionLayer = CollisionLayer; - HurtArea.CollisionMask = PhysicsLayer.None; - _currentLayer = HurtArea.CollisionLayer; - //CollisionLayer = PhysicsLayer.None; - HurtArea.OnHurtEvent += (target, damage, angle) => - { - CallDeferred(nameof(HurtHandler), target, damage, angle); - }; + HurtArea.InitRole(this); Face = FaceDirection.Right; @@ -857,7 +843,7 @@ /// 触发伤害的对象, 为 null 表示不存在对象或者对象已经被销毁 /// 伤害的量 /// 伤害角度(弧度制) - protected virtual void HurtHandler(ActivityObject target, int damage, float angle) + public virtual void HurtHandler(ActivityObject target, int damage, float angle) { //受伤闪烁, 无敌状态 if (Invincible) @@ -1016,6 +1002,33 @@ return this == World.Player; } + + /// + /// 返回指定角色是否是敌人 + /// + public bool IsEnemy(Role other) + { + if (other.Camp == Camp || other.Camp == CampEnum.Peace || Camp == CampEnum.Peace) + { + return false; + } + + return true; + } + + /// + /// 返回指定阵营是否是敌人 + /// + public bool IsEnemy(CampEnum otherCamp) + { + if (otherCamp == Camp || otherCamp == CampEnum.Peace || Camp == CampEnum.Peace) + { + return false; + } + + return true; + } + /// /// 是否是玩家的敌人 /// @@ -1025,7 +1038,7 @@ { return false; } - return CollisionWithMask(World.Player.EnemyLayer); + return IsEnemy(World.Player); } /// @@ -1413,8 +1426,7 @@ } else if (body is Bullet bullet) //攻击子弹 { - var attackLayer = bullet.AttackLayer; - if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹 + if (IsEnemy(bullet.BulletData.TriggerRole)) { bullet.OnPlayDisappearEffect(); bullet.Destroy(); @@ -1424,21 +1436,24 @@ private void HandlerCollision(IHurt hurt, Weapon activeWeapon) { - var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange); - damage = RoleState.CalcDamage(damage); - - var o = hurt.GetActivityObject(); - var pos = hurt.GetPosition(); - if (o != null && o is not Player) //不是玩家才能被击退 + if (hurt.CanHurt(Camp)) { - var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute; - var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange); - var position = pos - MountPoint.GlobalPosition; - var v2 = position.Normalized() * repel; - o.AddRepelForce(v2); + var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange); + damage = RoleState.CalcDamage(damage); + + var o = hurt.GetActivityObject(); + var pos = hurt.GetPosition(); + if (o != null && o is not Player) //不是玩家才能被击退 + { + var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute; + var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange); + var position = pos - MountPoint.GlobalPosition; + var v2 = position.Normalized() * repel; + o.AddRepelForce(v2); + } + + hurt.Hurt(this, damage, (pos - GlobalPosition).Angle()); } - - hurt.Hurt(this, damage, (pos - GlobalPosition).Angle()); } protected override void OnDestroy() diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/AIStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/ai/AIStateEnum.cs new file mode 100644 index 0000000..9b923d5 --- /dev/null +++ b/DungeonShooting_Godot/src/game/activity/role/ai/AIStateEnum.cs @@ -0,0 +1,40 @@ + +public enum AIStateEnum +{ + /// + /// Ai 状态, 正常, 未发现目标 + /// + AiNormal, + /// + /// 找到玩家,准备通知其他敌人 + /// + AiNotify, + /// + /// 惊讶状态 + /// + AiAstonished, + /// + /// 收到其他敌人通知, 前往发现目标的位置 + /// + AiLeaveFor, + /// + /// 发现目标, 目标不在视野内, 但是知道位置 + /// + AiTailAfter, + /// + /// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火 + /// + AiFollowUp, + /// + /// 距离足够近, 在目标附近随机移动 + /// + AiSurround, + /// + /// Ai 寻找弹药 + /// + AiFindAmmo, + /// + /// Ai攻击 + /// + AiAttack, +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs b/DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs index 2eba496..2935087 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs @@ -1,4 +1,5 @@ +using System.Collections.Generic; using AiState; using Godot; @@ -42,6 +43,18 @@ public Marker2D FirePoint { get; set; } /// + /// 视野区域 + /// + [Export, ExportFillNode] + public Area2D ViewArea { get; set; } + + /// + /// 视野区域碰撞器形状 + /// + [Export, ExportFillNode] + public CollisionPolygon2D ViewAreaCollision { get; set; } + + /// /// 当前敌人所看向的对象, 也就是枪口指向的对象 /// public ActivityObject LookTarget { get; set; } @@ -55,11 +68,27 @@ /// 锁定目标已经走过的时间 /// public float LockTargetTime { get; set; } = 0; - + /// /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙 /// - public float ViewRange { get; set; } = 250; + public float ViewRange + { + get => _viewRange; + set + { + if (_viewRange != value) + { + _viewRange = value; + if (ViewAreaCollision != null) + { + ViewAreaCollision.Polygon = Utils.CreateSectorPolygon(0, _viewRange, 120, 4); + } + } + } + } + + private float _viewRange = -1; /// /// 发现玩家后跟随玩家的视野半径 @@ -80,11 +109,25 @@ /// 当前Ai是否有攻击欲望 /// public bool HasAttackDesire { get; private set; } = true; + + /// + /// 是否有移动欲望, 仅在 AiNormal 状态下有效, 其他状态都可以移动 + /// + public bool HasMoveDesire { get; private set; } = true; + + /// + /// 临时存储攻击目标, 获取该值请调用 GetAttackTarget() 函数 + /// + private Role AttackTarget { get; set; } = null; + + private HashSet _viewTargets = new HashSet(); public override void OnInit() { base.OnInit(); IsAi = true; + ViewArea.BodyEntered += OnViewAreaBodyEntered; + ViewArea.BodyExited += OnViewAreaBodyExited; StateController = AddComponent>(); @@ -108,9 +151,32 @@ /// /// 获取攻击的目标对象, 当 HasAttackDesire 为 true 时才会调用 /// - public virtual Role GetAttackTarget() + /// 上一次发现的角色在本次检测中是否开启视野透视 + public Role GetAttackTarget(bool perspective = true) { - return World.Player; + //目标丢失 + if (AttackTarget == null || AttackTarget.IsDestroyed || !IsEnemy(AttackTarget)) + { + AttackTarget = RefreshAttackTargets(AttackTarget); + return AttackTarget; + } + + if (!perspective) + { + //被墙阻挡 + if (TestViewRayCast(AttackTarget.GetCenterPosition())) + { + AttackTarget = RefreshAttackTargets(AttackTarget); + TestViewRayCastOver(); + return AttackTarget; + } + else + { + TestViewRayCastOver(); + } + } + + return AttackTarget; } /// @@ -118,7 +184,7 @@ /// public bool CheckUsableWeaponInUnclaimed() { - foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons) + foreach (var unclaimedWeapon in World.Weapon_UnclaimedList) { //判断是否能拾起武器, 条件: 相同的房间 if (unclaimedWeapon.AffiliationArea == AffiliationArea) @@ -158,7 +224,7 @@ { Weapon target = null; var position = Position; - foreach (var weapon in World.Weapon_UnclaimedWeapons) + foreach (var weapon in World.Weapon_UnclaimedList) { //判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间 if (weapon.AffiliationArea == AffiliationArea) @@ -405,6 +471,52 @@ } } + /// + /// 设置Ai是否有移动欲望 + /// + public void SetMoveDesire(bool v) + { + HasMoveDesire = v; + } + + private void OnViewAreaBodyEntered(Node2D node) + { + if (node is Role role) + { + _viewTargets.Add(role); + } + } + + private void OnViewAreaBodyExited(Node2D node) + { + if (node is Role role) + { + _viewTargets.Remove(role); + } + } + + private Role RefreshAttackTargets(Role prevRole) + { + if (_viewTargets.Count == 0) + { + return null; + } + foreach (var attackTarget in _viewTargets) + { + if (prevRole != attackTarget && !attackTarget.IsDestroyed && IsEnemy(attackTarget)) + { + if (!TestViewRayCast(attackTarget.GetCenterPosition())) + { + TestViewRayCastOver(); + return attackTarget; + } + } + } + + TestViewRayCastOver(); + return null; + } + // private void OnVelocityComputed(Vector2 velocity) // { // if (Mathf.Abs(velocity.X) >= 0.01f && Mathf.Abs(velocity.Y) >= 0.01f) diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAstonishedState.cs index 7e8f382..d3f446f 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAstonishedState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAstonishedState.cs @@ -7,13 +7,10 @@ /// public class AiAstonishedState : StateBase { - /// - /// 下一个状态 - /// - public AIStateEnum NextState; - private float _timer; private object[] _args; + //用于判断是否进入过惊讶状态 + private bool _flag = false; public AiAstonishedState() : base(AIStateEnum.AiAstonished) { @@ -28,9 +25,15 @@ return; } + if (_flag) + { + ChangeNextState(args); + return; + } + + _flag = true; _args = args; - NextState = (AIStateEnum)args[0]; _timer = 0.6f; //播放惊讶表情 @@ -46,14 +49,19 @@ _timer -= delta; if (_timer <= 0) { - if (_args.Length == 1) - { - ChangeState(NextState); - } - else if (_args.Length == 2) - { - ChangeState(NextState, _args[1]); - } + ChangeNextState(_args); + } + } + + private void ChangeNextState(object[] args) + { + if (args.Length == 1) + { + ChangeState((AIStateEnum)args[0]); + } + else if (args.Length == 2) + { + ChangeState((AIStateEnum)args[0], args[1]); } } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAttackState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAttackState.cs index 12acf2e..417a039 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAttackState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiAttackState.cs @@ -43,7 +43,9 @@ { if (Master.LookTarget == null) { - throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有攻击目标!"); + ChangeState(AIStateEnum.AiNormal); + return; + //throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有攻击目标!"); } var weapon = Master.WeaponPack.ActiveItem; @@ -83,6 +85,12 @@ public override void Process(float delta) { + if (Master.LookTarget == null || Master.LookTarget.IsDestroyed || (Master.LookTarget is Role role && !Master.IsEnemy(role))) //更改攻击状态 + { + ChangeState(AIStateEnum.AiNormal); + return; + } + //更新标记位置 Master.UpdateMarkTargetPosition(); diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFindAmmoState.cs index c776dc0..adc289d 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFindAmmoState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFindAmmoState.cs @@ -81,17 +81,11 @@ var attackTarget = Master.GetAttackTarget(); if (attackTarget != null) { - var targetPos = attackTarget.GetCenterPosition(); - if (Master.IsInViewRange(targetPos) && !Master.TestViewRayCast(targetPos)) //发现玩家 - { - //关闭射线检测 - Master.TestViewRayCastOver(); - //发现玩家 - Master.LookTarget = attackTarget; - //进入惊讶状态, 然后再进入通知状态 - ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiFindAmmo, TargetWeapon); - return; - } + //发现玩家 + Master.LookTarget = attackTarget; + //进入惊讶状态, 然后再进入通知状态 + ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiFindAmmo, TargetWeapon); + return; } } diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFollowUpState.cs index c231a9a..679ed22 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFollowUpState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiFollowUpState.cs @@ -21,7 +21,9 @@ { if (Master.LookTarget == null) { - throw new Exception("进入 AIAdvancedStateEnum.AiFollowUp 状态时角色没有攻击目标!"); + ChangeState(AIStateEnum.AiNormal); + return; + //throw new Exception("进入 AIAdvancedStateEnum.AiFollowUp 状态时角色没有攻击目标!"); } _navigationUpdateTimer = 0; diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiLeaveForState.cs index 4f118db..5bec8fc 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiLeaveForState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiLeaveForState.cs @@ -108,27 +108,11 @@ var attackTarget = Master.GetAttackTarget(); if (attackTarget != null) { - var targetPos = attackTarget.GetCenterPosition(); - //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态 - if (Master.IsInTailAfterViewRange(targetPos)) - { - if (!Master.TestViewRayCast(targetPos)) //看到玩家 - { - //关闭射线检测 - Master.TestViewRayCastOver(); - //切换成发现目标状态 - Master.LookTarget = attackTarget; - ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiFollowUp); - return; - } - else - { - //关闭射线检测 - Master.TestViewRayCastOver(); - } - } + //切换成发现目标状态 + Master.LookTarget = attackTarget; + ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiFollowUp); + return; } - //移动到目标掉了, 还没发现目标 if (Master.NavigationAgent2D.IsNavigationFinished()) diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNormalState.cs index dfdc704..7844ea7 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNormalState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNormalState.cs @@ -49,33 +49,27 @@ if (Master.HasAttackDesire) //有攻击欲望 { //获取攻击目标 - var attackTarget = Master.GetAttackTarget(); + var attackTarget = Master.GetAttackTarget(false); if (attackTarget != null) { - //玩家中心点坐标 - var targetPos = attackTarget.GetCenterPosition(); - - if (Master.IsInViewRange(targetPos) && !Master.TestViewRayCast(targetPos)) //发现目标 + //发现玩家 + Master.LookTarget = attackTarget; + //判断是否进入通知状态 + if (Master.World.Role_InstanceList.FindIndex(role => + role is AiRole enemy && + enemy != Master && !enemy.IsDie && enemy.AffiliationArea == Master.AffiliationArea && + enemy.StateController.CurrState == AIStateEnum.AiNormal) != -1) { - //关闭射线检测 - Master.TestViewRayCastOver(); - //发现玩家 - Master.LookTarget = attackTarget; - //判断是否进入通知状态 - if (Master.World.Enemy_InstanceList.FindIndex(enemy => - enemy != Master && !enemy.IsDie && enemy.AffiliationArea == Master.AffiliationArea && - enemy.StateController.CurrState == AIStateEnum.AiNormal) != -1) - { - //进入惊讶状态, 然后再进入通知状态 - ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiNotify); - } - else - { - //进入惊讶状态, 然后再进入跟随状态 - ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiTailAfter); - } - return; + //进入惊讶状态, 然后再进入通知状态 + ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiNotify); } + else + { + //进入惊讶状态, 然后再进入跟随状态 + ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiTailAfter); + } + + return; } } @@ -89,7 +83,7 @@ RunOver(); _isMoveOver = false; } - else //移动中 + else if (Master.HasMoveDesire) //移动中 { if (_lockTimer >= 1) //卡在一个点超过一秒 { diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNotifyState.cs index b1e0b3f..fac6cc8 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNotifyState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiNotifyState.cs @@ -17,7 +17,9 @@ { if (Master.LookTarget == null) { - throw new Exception("进入 AIAdvancedStateEnum.AiNotify 没有攻击目标!"); + ChangeState(AIStateEnum.AiNormal); + return; + //throw new Exception("进入 AIAdvancedStateEnum.AiNotify 没有攻击目标!"); } _timer = 1.2f; //通知其它角色 diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiSurroundState.cs index 94afce7..4760836 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiSurroundState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiSurroundState.cs @@ -32,7 +32,9 @@ { if (Master.LookTarget == null) { - throw new Exception("进入 AIAdvancedStateEnum.AiSurround 状态时角色没有攻击目标!"); + ChangeState(AIStateEnum.AiNormal); + return; + //throw new Exception("进入 AIAdvancedStateEnum.AiSurround 状态时角色没有攻击目标!"); } Master.TargetInView = true; @@ -43,6 +45,12 @@ public override void Process(float delta) { + if (Master.LookTarget == null) + { + ChangeState(AIStateEnum.AiNormal); + return; + } + //先检查弹药是否打光 if (Master.IsAllWeaponTotalAmmoEmpty()) { diff --git a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiTailAfterState.cs index 05a5d9e..00706f3 100644 --- a/DungeonShooting_Godot/src/game/activity/role/ai/state/AiTailAfterState.cs +++ b/DungeonShooting_Godot/src/game/activity/role/ai/state/AiTailAfterState.cs @@ -29,7 +29,9 @@ { if (Master.LookTarget == null) { - throw new Exception("进入 AIAdvancedStateEnum.AiTailAfter 状态时角色没有攻击目标!"); + ChangeState(AIStateEnum.AiNormal); + return; + //throw new Exception("进入 AIAdvancedStateEnum.AiTailAfter 状态时角色没有攻击目标!"); } _isInViewRange = true; @@ -51,7 +53,12 @@ public override void Process(float delta) { //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽 - + + if (Master.LookTarget == null) + { + ChangeState(AIStateEnum.AiNormal); + return; + } var playerPos = Master.LookTarget.GetCenterPosition(); //更新玩家位置 diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs deleted file mode 100644 index 9b923d5..0000000 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs +++ /dev/null @@ -1,40 +0,0 @@ - -public enum AIStateEnum -{ - /// - /// Ai 状态, 正常, 未发现目标 - /// - AiNormal, - /// - /// 找到玩家,准备通知其他敌人 - /// - AiNotify, - /// - /// 惊讶状态 - /// - AiAstonished, - /// - /// 收到其他敌人通知, 前往发现目标的位置 - /// - AiLeaveFor, - /// - /// 发现目标, 目标不在视野内, 但是知道位置 - /// - AiTailAfter, - /// - /// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火 - /// - AiFollowUp, - /// - /// 距离足够近, 在目标附近随机移动 - /// - AiSurround, - /// - /// Ai 寻找弹药 - /// - AiFindAmmo, - /// - /// Ai攻击 - /// - AiAttack, -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs index 14c0bc5..1f8dd9f 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs @@ -63,9 +63,6 @@ public override void OnInit() { base.OnInit(); - - AttackLayer = PhysicsLayer.Obstacle | PhysicsLayer.Player; - EnemyLayer = PhysicsLayer.Player; Camp = CampEnum.Camp2; RoleState.MoveSpeed = 20; @@ -94,20 +91,7 @@ roleState.Gold = Mathf.Max(0, Utils.Random.RandomConfigRange(enemyBase.Gold)); return roleState; } - - public override void EnterTree() - { - if (!World.Enemy_InstanceList.Contains(this)) - { - World.Enemy_InstanceList.Add(this); - } - } - - public override void ExitTree() - { - World.Enemy_InstanceList.Remove(this); - } - + protected override void OnDie() { var effPos = Position + new Vector2(0, -Altitude); @@ -144,20 +128,27 @@ //看向目标 if (LookTarget != null && MountLookTarget) { - var pos = LookTarget.Position; - LookPosition = pos; - //脸的朝向 - var gPos = Position; - if (pos.X > gPos.X && Face == FaceDirection.Left) + if (LookTarget.IsDestroyed) { - Face = FaceDirection.Right; + LookTarget = null; } - else if (pos.X < gPos.X && Face == FaceDirection.Right) + else { - Face = FaceDirection.Left; + var pos = LookTarget.Position; + LookPosition = pos; + //脸的朝向 + var gPos = Position; + if (pos.X > gPos.X && Face == FaceDirection.Left) + { + Face = FaceDirection.Right; + } + else if (pos.X < gPos.X && Face == FaceDirection.Right) + { + Face = FaceDirection.Left; + } + //枪口跟随目标 + MountPoint.SetLookAt(pos); } - //枪口跟随目标 - MountPoint.SetLookAt(pos); } if (RoleState.CanPickUpWeapon) @@ -184,7 +175,8 @@ { LookTarget = target; //判断是否进入通知状态 - if (World.Enemy_InstanceList.FindIndex(enemy => + if (World.Role_InstanceList.FindIndex(role => + role is AiRole enemy && enemy != this && !enemy.IsDie && enemy.AffiliationArea == AffiliationArea && enemy.StateController.CurrState == AIStateEnum.AiNormal) != -1) { diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs index 89d9ccc..3dd5f63 100644 --- a/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs +++ b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs @@ -56,7 +56,7 @@ var data = bulletData.Clone(); var tempPos = new Vector2(targetPosition.X + Utils.Random.RandomRangeInt(-30, 30), targetPosition.Y + Utils.Random.RandomRangeInt(-30, 30)); FireManager.SetParabolaTarget(data, tempPos); - FireManager.ShootBullet(data, AttackLayer); + FireManager.ShootBullet(data, Camp); } } diff --git a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs index 342d205..ca2e85f 100644 --- a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs +++ b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs @@ -37,8 +37,6 @@ IsAi = false; StateController = AddComponent>(); - AttackLayer = PhysicsLayer.Obstacle | PhysicsLayer.Enemy; - EnemyLayer = EnemyLayer = PhysicsLayer.Enemy; Camp = CampEnum.Camp1; MaxHp = 6; @@ -205,6 +203,15 @@ else if (InputManager.UseActiveProp) //使用道具 { UseActiveProp(); + + foreach (var role in World.Role_InstanceList) + { + if (IsEnemy(role)) + { + role.Camp = Camp; + break; + } + } } else if (InputManager.ExchangeProp) //切换道具 { @@ -223,10 +230,14 @@ } else if (Input.IsKeyPressed(Key.O)) //测试用, 消灭房间内所有敌人 { - var enemyList = AffiliationArea.FindIncludeItems(o => o.CollisionWithMask(PhysicsLayer.Enemy)); + var enemyList = AffiliationArea.FindIncludeItems(o => o is Role role && role.IsEnemyWithPlayer()); foreach (var enemy in enemyList) { - ((Enemy)enemy).HurtArea.Hurt(this, 1000, 0); + var hurt = ((Enemy)enemy).HurtArea; + if (hurt.CanHurt(Camp)) + { + hurt.Hurt(this, 1000, 0); + } } } // //测试用 @@ -253,7 +264,7 @@ } //测试刷地 - //DrawLiquid(_brushData2); + DrawLiquid(_brushData2); } protected override void OnAffiliationChange(AffiliationArea prevArea) diff --git a/DungeonShooting_Godot/src/game/activity/role/shop/ShopBoss.cs b/DungeonShooting_Godot/src/game/activity/role/shop/ShopBoss.cs index f481376..02360d5 100644 --- a/DungeonShooting_Godot/src/game/activity/role/shop/ShopBoss.cs +++ b/DungeonShooting_Godot/src/game/activity/role/shop/ShopBoss.cs @@ -10,7 +10,8 @@ public override void OnInit() { base.OnInit(); - SetAttackDesire(false); + SetAttackDesire(false); //默认不攻击 + SetMoveDesire(false); //默认不攻击 } protected override RoleState OnCreateRoleState() diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs index edb144a..2ec849b 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs @@ -27,11 +27,6 @@ private ExcelConfig.WeaponBase _playerWeaponAttribute; private ExcelConfig.WeaponBase _aiWeaponAttribute; - /// - /// 武器攻击的目标阵营 - /// - public CampEnum TargetCamp { get; set; } - public Role Master { get; set; } public int PackageIndex { get; set; } = -1; @@ -150,11 +145,6 @@ public Role TriggerRole { get; private set; } /// - /// 上一次触发改武器开火的触发开火攻击的层级, 数据源自于: - /// - public long TriggerRoleAttackLayer { get; private set; } - - /// /// 武器当前射速 /// public float CurrentFiringSpeed { get; private set; } @@ -494,13 +484,13 @@ //收集落在地上的武器 if (IsInGround()) { - World.Weapon_UnclaimedWeapons.Add(this); + World.Weapon_UnclaimedList.Add(this); } } public override void ExitTree() { - World.Weapon_UnclaimedWeapons.Remove(this); + World.Weapon_UnclaimedList.Remove(this); } protected override void Process(float delta) @@ -850,13 +840,11 @@ if (triggerRole != null) { TriggerRole = triggerRole; - TriggerRoleAttackLayer = triggerRole.AttackLayer; SetCurrentWeaponAttribute(triggerRole.IsAi ? _aiWeaponAttribute : _playerWeaponAttribute); } else if (Master != null) { TriggerRole = Master; - TriggerRoleAttackLayer = Master.AttackLayer; SetCurrentWeaponAttribute(Master.IsAi ? _aiWeaponAttribute : _playerWeaponAttribute); } @@ -1276,19 +1264,6 @@ } /// - /// 获取武器攻击的目标层级 - /// - /// - public uint GetAttackLayer() - { - if (TriggerRoleAttackLayer > 0) - { - return (uint)TriggerRoleAttackLayer; - } - return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer; - } - - /// /// 返回弹药是否到达上限 /// public bool IsAmmoFull() diff --git a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs index 11c2d30..dffce05 100644 --- a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs +++ b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs @@ -26,7 +26,7 @@ public override void OnInit() { base.OnInit(); - + //没有Master时不能触发开火 NoMasterCanTrigger = false; _hitArea = GetNode("HitArea"); @@ -40,6 +40,9 @@ _hitArea.Monitorable = false; _hitArea.BodyEntered += OnBodyEntered; _hitArea.AreaEntered += OnArea2dEntered; + + //更新碰撞层级 + _hitArea.CollisionMask = Role.AttackLayer | PhysicsLayer.Bullet; //禁用自动播放动画 IsAutoPlaySpriteFrames = false; @@ -74,8 +77,6 @@ protected override void OnFire() { Debug.Log("近战武器攻击! 蓄力时长: " + GetTriggerChargeTime() + ", 扳机按下时长: " + GetTriggerDownTime()); - //更新碰撞层级 - _hitArea.CollisionMask = GetAttackLayer() | PhysicsLayer.Bullet; //启用碰撞 _hitArea.Monitoring = true; _attackIndex = 0; @@ -130,8 +131,7 @@ } else if (body is Bullet bullet) //攻击子弹 { - var attackLayer = bullet.AttackLayer; - if (TriggerRole != null && TriggerRole.CollisionWithMask(attackLayer)) //是攻击玩家的子弹 + if (TriggerRole != null && TriggerRole.IsEnemy(bullet.BulletData.TriggerRole)) { //反弹子弹 bullet.OnPlayDisappearEffect(); @@ -143,7 +143,7 @@ } bullet.MoveController.ScaleAllVelocity(scale); bullet.Rotation += Mathf.Pi; - bullet.AttackLayer = TriggerRole.AttackLayer; + bullet.Camp = Master.Camp; bullet.RefreshBulletColor(false); } } @@ -159,42 +159,45 @@ private void HandlerCollision(IHurt hurt) { - var damage = Utils.Random.RandomConfigRange(Attribute.Bullet.HarmRange); - //计算子弹造成的伤害 - if (TriggerRole != null) + if (TriggerRole == null || hurt.CanHurt(TriggerRole.Camp)) { - damage = TriggerRole.RoleState.CalcDamage(damage); - } - //击退 - var attr = GetUseAttribute(TriggerRole); - var repel = Utils.Random.RandomConfigRange(attr.Bullet.RepelRange); - //计算击退 - if (TriggerRole != null) - { - repel = TriggerRole.RoleState.CalcBulletRepel(repel); - } - - var globalPosition = GlobalPosition; - if (repel != 0) - { - var o = hurt.GetActivityObject(); - if (o != null && o is not Player) //不是玩家才能被击退 + var damage = Utils.Random.RandomConfigRange(Attribute.Bullet.HarmRange); + //计算子弹造成的伤害 + if (TriggerRole != null) { - Vector2 position; - if (TriggerRole != null) - { - position = o.GlobalPosition - TriggerRole.MountPoint.GlobalPosition; - } - else - { - position = o.GlobalPosition - globalPosition; - } - var v2 = position.Normalized() * repel; - o.AddRepelForce(v2); + damage = TriggerRole.RoleState.CalcDamage(damage); } - } + //击退 + var attr = GetUseAttribute(TriggerRole); + var repel = Utils.Random.RandomConfigRange(attr.Bullet.RepelRange); + //计算击退 + if (TriggerRole != null) + { + repel = TriggerRole.RoleState.CalcBulletRepel(repel); + } - //造成伤害 - hurt.Hurt(TriggerRole, damage, (hurt.GetPosition() - globalPosition).Angle()); + var globalPosition = GlobalPosition; + if (repel != 0) + { + var o = hurt.GetActivityObject(); + if (o != null && o is not Player) //不是玩家才能被击退 + { + Vector2 position; + if (TriggerRole != null) + { + position = o.GlobalPosition - TriggerRole.MountPoint.GlobalPosition; + } + else + { + position = o.GlobalPosition - globalPosition; + } + var v2 = position.Normalized() * repel; + o.AddRepelForce(v2); + } + } + + //造成伤害 + hurt.Hurt(TriggerRole, damage, (hurt.GetPosition() - globalPosition).Angle()); + } } } diff --git a/DungeonShooting_Godot/src/game/buff/effect/Eff_SwapWeapon.cs b/DungeonShooting_Godot/src/game/buff/effect/Eff_SwapWeapon.cs index d2b95ae..e89e6fb 100644 --- a/DungeonShooting_Godot/src/game/buff/effect/Eff_SwapWeapon.cs +++ b/DungeonShooting_Godot/src/game/buff/effect/Eff_SwapWeapon.cs @@ -14,13 +14,12 @@ public override void OnUse() { - var list = new List(); - foreach (var enemy in Master.World.Enemy_InstanceList) + var list = new List(); + foreach (var role in Master.World.Role_InstanceList) { - if (enemy.WeaponPack.ActiveItem != null) + if (role is AiRole enemy && enemy.IsEnemy(Role) && enemy.WeaponPack.ActiveItem != null) { list.Add(enemy); - } } @@ -45,9 +44,9 @@ return false; } - foreach (var enemy in Master.World.Enemy_InstanceList) + foreach (var role in Master.World.Role_InstanceList) { - if (enemy.WeaponPack.ActiveItem != null) + if (role is AiRole enemy && enemy.IsEnemy(Role) && enemy.WeaponPack.ActiveItem != null) { return true; } diff --git a/DungeonShooting_Godot/src/game/data/property/PhysicsLayer.cs b/DungeonShooting_Godot/src/game/data/property/PhysicsLayer.cs index 0194269..cf6f7f8 100644 --- a/DungeonShooting_Godot/src/game/data/property/PhysicsLayer.cs +++ b/DungeonShooting_Godot/src/game/data/property/PhysicsLayer.cs @@ -24,9 +24,9 @@ /// public const uint Player = 0b1000; /// - /// 敌人 + /// 角色基类 /// - public const uint Enemy = 0b10000; + public const uint Role = 0b10000; /// /// 归属区域判断层级 /// @@ -48,7 +48,7 @@ /// public const uint Obstacle = 0b1000000000; /// - /// npc + /// 可被子弹击中的区域 /// - public const uint Npc = 0b10000000000; + public const uint HurtArea = 0b10000000000; } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/manager/FireManager.cs b/DungeonShooting_Godot/src/game/manager/FireManager.cs index c7b436b..db692a7 100644 --- a/DungeonShooting_Godot/src/game/manager/FireManager.cs +++ b/DungeonShooting_Godot/src/game/manager/FireManager.cs @@ -97,11 +97,13 @@ { if (bullet.Type == 1) //实体子弹 { - return ShootSolidBullet(CreateSolidBulletData(weapon, fireRotation, bullet), weapon.GetAttackLayer()); + return ShootSolidBullet(CreateSolidBulletData(weapon, fireRotation, bullet), + weapon.TriggerRole != null ? weapon.TriggerRole.Camp : CampEnum.None); } else if (bullet.Type == 2) //激光子弹 { - return ShootLaser(CreateLaserData(weapon, fireRotation, bullet), weapon.GetAttackLayer()); + return ShootLaser(CreateLaserData(weapon, fireRotation, bullet), + weapon.TriggerRole != null ? weapon.TriggerRole.Camp : CampEnum.None); } else { @@ -118,7 +120,7 @@ { if (bullet.Type == 1) //实体子弹 { - return ShootSolidBullet(CreateSolidBulletData(trigger, fireRotation, bullet), trigger.AttackLayer); + return ShootSolidBullet(CreateSolidBulletData(trigger, fireRotation, bullet), trigger.Camp); } return null; @@ -127,15 +129,15 @@ /// /// 通过 BulletData 直接发射子弹 /// - public static IBullet ShootBullet(BulletData bulletData, uint attackLayer) + public static IBullet ShootBullet(BulletData bulletData, CampEnum camp) { if (bulletData.BulletBase.Type == 1) //实体子弹 { - return ShootSolidBullet(bulletData, attackLayer); + return ShootSolidBullet(bulletData, camp); } else if (bulletData.BulletBase.Type == 2) //激光子弹 { - return ShootLaser(bulletData, attackLayer); + return ShootLaser(bulletData, camp); } else { @@ -148,23 +150,23 @@ /// /// 发射子弹的默认实现方式 /// - private static Bullet ShootSolidBullet(BulletData bulletData, uint attackLayer) + private static Bullet ShootSolidBullet(BulletData bulletData, CampEnum camp) { //创建子弹 var bulletInstance = ObjectManager.GetBullet(bulletData.BulletBase.Prefab); - bulletInstance.InitData(bulletData, attackLayer); + bulletInstance.InitData(bulletData, camp); return bulletInstance; } /// /// 发射射线的默认实现方式 /// - private static Laser ShootLaser(BulletData bulletData, uint attackLayer) + private static Laser ShootLaser(BulletData bulletData, CampEnum camp) { //创建激光 var laser = ObjectManager.GetLaser(bulletData.BulletBase.Prefab); laser.AddToActivityRoot(RoomLayerEnum.YSortLayer); - laser.InitData(bulletData, attackLayer, Laser.LaserDefaultWidth); + laser.InitData(bulletData, camp, Laser.LaserDefaultWidth); return laser; } diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs index ab52b27..eb38790 100644 --- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs +++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs @@ -858,10 +858,10 @@ if (room.IsSeclusion) { var playerAffiliationArea = CurrWorld.Player.AffiliationArea; - foreach (var enemy in CurrWorld.Enemy_InstanceList) + foreach (var role in CurrWorld.Role_InstanceList) { //不与玩家处于同一个房间 - if (!enemy.IsDestroyed && enemy.AffiliationArea != playerAffiliationArea) + if (role is AiRole enemy && !enemy.IsDestroyed && enemy.AffiliationArea != playerAffiliationArea) { if (enemy.StateController.CurrState != AIStateEnum.AiNormal) { @@ -904,7 +904,7 @@ { //房间内是否有存活的敌人 var flag = ActiveAffiliationArea.ExistEnterItem( - activityObject => activityObject.CollisionWithMask(PhysicsLayer.Enemy) + activityObject => activityObject is Role role && role.IsEnemyWithPlayer() ); //Debug.Log("当前房间存活数量: " + count); if (!flag) diff --git a/DungeonShooting_Godot/src/game/ui/roomMap/RoomMapPanel.cs b/DungeonShooting_Godot/src/game/ui/roomMap/RoomMapPanel.cs index 5ba219f..41e04fd 100644 --- a/DungeonShooting_Godot/src/game/ui/roomMap/RoomMapPanel.cs +++ b/DungeonShooting_Godot/src/game/ui/roomMap/RoomMapPanel.cs @@ -75,7 +75,7 @@ //更新敌人位置 { - var enemyList = World.Current.Enemy_InstanceList; + var enemyList = World.Current.Role_InstanceList; if (enemyList.Count == 0) //没有敌人 { foreach (var sprite in _enemySpriteList) @@ -90,8 +90,8 @@ var count = 0; //绘制数量 for (var i = 0; i < enemyList.Count; i++) { - var enemy = enemyList[i]; - if (!enemy.IsDestroyed && !enemy.IsDie && enemy.AffiliationArea != null && enemy.AffiliationArea.RoomInfo.RoomFogMask.IsExplored) + var role = enemyList[i]; + if (role is AiRole enemy && !enemy.IsDestroyed && !enemy.IsDie && enemy.AffiliationArea != null && enemy.AffiliationArea.RoomInfo.RoomFogMask.IsExplored) { count++; Sprite2D sprite; diff --git a/DungeonShooting_Godot/src/game/world/World.cs b/DungeonShooting_Godot/src/game/world/World.cs new file mode 100644 index 0000000..f5a32fd --- /dev/null +++ b/DungeonShooting_Godot/src/game/world/World.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Godot; + +/// +/// 游戏世界 +/// +public partial class World : CanvasModulate, ICoroutine +{ + /// + /// 当前的游戏世界对象 + /// + public static World Current => GameApplication.Instance.DungeonManager.CurrWorld; + + /// + /// 当前操作的玩家 + /// + public Player Player { get; private set; } + + /// + /// //对象根节点 + /// + public Node2D NormalLayer; + + /// + /// 对象根节点, 带y轴排序功能 + /// + public Node2D YSortLayer; + + /// + /// 地图根节点 + /// + public TileMap TileRoot; + + public Node2D StaticSpriteRoot; + public Node2D AffiliationAreaRoot; + public Node2D FogMaskRoot; + public Node2D NavigationRoot; + + /// + /// 是否暂停 + /// + public bool Pause + { + get => _pause; + set + { + if (_pause != value) + { + _pause = value; + if (value) + { + ProcessMode = ProcessModeEnum.WhenPaused; + } + else + { + ProcessMode = ProcessModeEnum.Inherit; + } + } + } + } + + /// + /// 所有被扔在地上的武器 + /// + public HashSet Weapon_UnclaimedList { get; } = new HashSet(); + + /// + /// 记录所有存活的角色 + /// + public List Role_InstanceList { get; } = new List(); + + /// + /// 随机数对象 + /// + public SeedRandom Random { get; private set; } + + /// + /// 随机对象池 + /// + public RandomPool RandomPool { get; private set; } + + /// + /// 角色死亡事件 + /// + public event Action OnRoleDieEvent; + + private bool _pause = false; + private List _coroutineList; + + public override void _Ready() + { + //TileRoot.YSortEnabled = false; + NormalLayer = GetNode("TileRoot/NormalLayer"); + YSortLayer = GetNode("TileRoot/YSortLayer"); + TileRoot = GetNode("TileRoot"); + StaticSpriteRoot = GetNode("TileRoot/StaticSpriteRoot"); + FogMaskRoot = GetNode("TileRoot/FogMaskRoot"); + NavigationRoot = GetNode("TileRoot/NavigationRoot"); + AffiliationAreaRoot = GetNode("TileRoot/AffiliationAreaRoot"); + } + + public override void _Process(double delta) + { + //协程更新 + ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, (float)delta); + } + + /// + /// 获取指定层级根节点 + /// + public Node2D GetRoomLayer(RoomLayerEnum layerEnum) + { + switch (layerEnum) + { + case RoomLayerEnum.NormalLayer: + return NormalLayer; + case RoomLayerEnum.YSortLayer: + return YSortLayer; + } + + return null; + } + + /// + /// 设置当前操作的玩家对象 + /// + public void SetCurrentPlayer(Player player) + { + Player = player; + //设置相机和鼠标跟随玩家 + GameCamera.Main.SetFollowTarget(player); + GameApplication.Instance.Cursor.SetMountRole(player); + } + + /// + /// 通知其他敌人发现目标了 + /// + /// 发送通知的角色 + /// 目标 + public void NotifyEnemyTarget(Role self, ActivityObject target) + { + foreach (var role in Role_InstanceList) + { + if (role != self && !role.IsDestroyed && role.AffiliationArea == self.AffiliationArea && role is AiRole enemy && !self.IsEnemy(enemy)) + { + //将未发现目标的敌人状态置为惊讶状态 + var controller = enemy.StateController; + //延时通知效果 + role.CallDelay(Utils.Random.RandomRangeFloat(0.2f, 1f), () => + { + if (controller.CurrState == AIStateEnum.AiNormal) + { + controller.ChangeState(AIStateEnum.AiLeaveFor, target); + } + }); + } + } + } + + public long StartCoroutine(IEnumerator able) + { + return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able); + } + + public void StopCoroutine(long coroutineId) + { + ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId); + } + + public bool IsCoroutineOver(long coroutineId) + { + return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId); + } + + public void StopAllCoroutine() + { + ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList); + } + + /// + /// 初始化随机池 + /// + public void InitRandomPool(SeedRandom random) + { + Random = random; + RandomPool = new RandomPool(this); + } + + /// + /// 角色死亡 + /// + public void OnRoleDie(Role role) + { + if (OnRoleDieEvent != null) + { + OnRoleDieEvent(role); + } + } +} \ No newline at end of file