From cc4b91797dd67add5f300671782e338a2b4dd8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=A8=E8=90=BD=E5=9F=BA=E5=9B=B4=E8=99=BE?= <3161880837@qq.com> Date: Wed, 18 Mar 2026 22:39:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AD=90=E5=BC=B9=E7=B3=BB=E7=BB=9F):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0QKSword=E5=AD=90=E5=BC=B9=E5=8F=8A=E5=85=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现QKSword子弹类型,包括子弹行为、生成逻辑和命中效果 - 新增QKSwordBullet类,实现追踪目标功能 - 新增QKSwordHandler处理命中后的子弹生成 - 修改ParryBall使其命中时生成QKSword子弹 - 调整CycleTimer的子弹偏移计算 - 为BulletBase添加afterSpawn回调 --- components/Bullets/QKSword.tscn | 139 ++++++++++++++++++ components/Bullets/QKSwordHandler.tscn | 13 ++ scripts/Contents/Bullets/ParryBall.gd | 11 ++ scripts/Contents/Bullets/QKSword.gd | 10 ++ scripts/Contents/Bullets/QKSword.gd.uid | 1 + scripts/Contents/Bullets/QKSwordHandler.gd | 14 ++ .../Contents/Bullets/QKSwordHandler.gd.uid | 1 + scripts/Statemachine/BulletBase.gd | 3 + scripts/Statemachine/CycleTimer.gd | 2 +- 9 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 components/Bullets/QKSword.tscn create mode 100644 components/Bullets/QKSwordHandler.tscn create mode 100644 scripts/Contents/Bullets/QKSword.gd create mode 100644 scripts/Contents/Bullets/QKSword.gd.uid create mode 100644 scripts/Contents/Bullets/QKSwordHandler.gd create mode 100644 scripts/Contents/Bullets/QKSwordHandler.gd.uid diff --git a/components/Bullets/QKSword.tscn b/components/Bullets/QKSword.tscn new file mode 100644 index 0000000..aacd244 --- /dev/null +++ b/components/Bullets/QKSword.tscn @@ -0,0 +1,139 @@ +[gd_scene load_steps=9 format=3 uid="uid://bhp1ru3rjwkbf"] + +[ext_resource type="PackedScene" uid="uid://crtdkysmnkith" path="res://components/Abstracts/BulletBase.tscn" id="1_1ke2b"] +[ext_resource type="Texture2D" uid="uid://ctmxadaowx5ps" path="res://resources/weapons/Volcano.webp" id="2_q2yg1"] +[ext_resource type="Script" uid="uid://chyrnrbgcudmk" path="res://scripts/Contents/Bullets/QKSword.gd" id="2_x26jp"] + +[sub_resource type="SpriteFrames" id="SpriteFrames_x26jp"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": ExtResource("2_q2yg1") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[sub_resource type="Animation" id="Animation_q2yg1"] +resource_name = "spawn" +step = 0.1 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0, 2), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.16666667, 0, 0, 0), +"times": PackedFloat32Array(0, 1) +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:scale:y") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(2, 2), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.16666667, 0, 0, 0), +"times": PackedFloat32Array(0, 1) +} +tracks/2/type = "bezier" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:rotation") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"handle_modes": PackedInt32Array(2, 0), +"points": PackedFloat32Array(-4.71, -0.25, 0, 0.25, 0, 0.78, -0.15, 0, 0, 0), +"times": PackedFloat32Array(0, 1) +} +tracks/3/type = "bezier" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath(".:modulate:a") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"handle_modes": PackedInt32Array(2, 2), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.16666667, 0, 0, 0), +"times": PackedFloat32Array(0, 1) +} + +[sub_resource type="Animation" id="Animation_x26jp"] +length = 0.001 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:scale:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:scale:y") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/2/type = "bezier" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:rotation") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} +tracks/3/type = "bezier" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath(".:modulate:a") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_evtlt"] +_data = { +&"RESET": SubResource("Animation_x26jp"), +&"spawn": SubResource("Animation_q2yg1") +} + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_q2yg1"] +size = Vector2(94, 12) + +[node name="QKSword" instance=ExtResource("1_1ke2b")] +script = ExtResource("2_x26jp") +speed = 20.0 +lifeTime = 2000.0 +autoSpawnAnimation = true + +[node name="texture" parent="." index="0"] +modulate = Color(1, 1, 1, 0) +scale = Vector2(1e-05, 1e-05) +sprite_frames = SubResource("SpriteFrames_x26jp") + +[node name="animator" parent="texture" index="0"] +libraries = { +&"": SubResource("AnimationLibrary_evtlt") +} + +[node name="hitbox" parent="." index="1"] +shape = SubResource("RectangleShape2D_q2yg1") diff --git a/components/Bullets/QKSwordHandler.tscn b/components/Bullets/QKSwordHandler.tscn new file mode 100644 index 0000000..b215efb --- /dev/null +++ b/components/Bullets/QKSwordHandler.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=4 format=3 uid="uid://cocn5lqxetw2u"] + +[ext_resource type="PackedScene" uid="uid://crtdkysmnkith" path="res://components/Abstracts/BulletBase.tscn" id="1_ixcon"] +[ext_resource type="Script" uid="uid://cmph2bim5oro5" path="res://scripts/Contents/Bullets/QKSwordHandler.gd" id="2_y2oc8"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_ixcon"] +radius = 50.0 + +[node name="QKSwordHandler" instance=ExtResource("1_ixcon")] +script = ExtResource("2_y2oc8") + +[node name="hitbox" parent="." index="1"] +shape = SubResource("CircleShape2D_ixcon") diff --git a/scripts/Contents/Bullets/ParryBall.gd b/scripts/Contents/Bullets/ParryBall.gd index 28ac3cb..443b498 100644 --- a/scripts/Contents/Bullets/ParryBall.gd +++ b/scripts/Contents/Bullets/ParryBall.gd @@ -9,3 +9,14 @@ func spawn(): func ai(): PresetBulletAI.selfRotate(self , 5) hitbox.disabled = !launcher.sprinting +func succeedToHit(_dmg: float, entity: EntityBase): + for bullet in BulletBase.generate( + ComponentManager.getBullet("QKSword"), + launcher, + entity.position, + 0 + ): + if bullet is QKSwordBullet: + bullet.position = entity.texture.global_position + MathTool.sampleInRing(50, 200) + bullet.tracer = entity + bullet.look_at(entity.position) diff --git a/scripts/Contents/Bullets/QKSword.gd b/scripts/Contents/Bullets/QKSword.gd new file mode 100644 index 0000000..bf65edb --- /dev/null +++ b/scripts/Contents/Bullets/QKSword.gd @@ -0,0 +1,10 @@ +extends BulletBase +class_name QKSwordBullet + +var tracer: EntityBase + +func ai(): + if is_instance_valid(tracer): + look_at(tracer.position) + if timeLived() > 1000: + PresetBulletAI.forward(self , rotation) diff --git a/scripts/Contents/Bullets/QKSword.gd.uid b/scripts/Contents/Bullets/QKSword.gd.uid new file mode 100644 index 0000000..179dc25 --- /dev/null +++ b/scripts/Contents/Bullets/QKSword.gd.uid @@ -0,0 +1 @@ +uid://chyrnrbgcudmk diff --git a/scripts/Contents/Bullets/QKSwordHandler.gd b/scripts/Contents/Bullets/QKSwordHandler.gd new file mode 100644 index 0000000..fcfad83 --- /dev/null +++ b/scripts/Contents/Bullets/QKSwordHandler.gd @@ -0,0 +1,14 @@ +extends BulletBase +class_name QKSwordHandler + +func succeedToHit(_dmg: float, entity: EntityBase): + for bullet in BulletBase.generate( + ComponentManager.getBullet("QKSword"), + launcher, + entity.position, + 0 + ): + if bullet is QKSwordBullet: + bullet.position = entity.texture.global_position + MathTool.sampleInRing(50, 200) + bullet.tracer = entity + bullet.look_at(entity.position) diff --git a/scripts/Contents/Bullets/QKSwordHandler.gd.uid b/scripts/Contents/Bullets/QKSwordHandler.gd.uid new file mode 100644 index 0000000..b21dd02 --- /dev/null +++ b/scripts/Contents/Bullets/QKSwordHandler.gd.uid @@ -0,0 +1 @@ +uid://cmph2bim5oro5 diff --git a/scripts/Statemachine/BulletBase.gd b/scripts/Statemachine/BulletBase.gd index 23a5e4a..dc8a311 100644 --- a/scripts/Statemachine/BulletBase.gd +++ b/scripts/Statemachine/BulletBase.gd @@ -71,6 +71,7 @@ func _ready(): if autoSpawnAnimation: animator.play("spawn") await animator.animation_finished + afterSpawn() if freeAfterSpawn: tryDestroy() func _process(_delta: float) -> void: @@ -214,6 +215,8 @@ func destroy(_beacuseMap: bool): pass func spawn(): pass +func afterSpawn(): + pass func applyDot(): pass func succeedToHit(_dmg: float, _entity: EntityBase): diff --git a/scripts/Statemachine/CycleTimer.gd b/scripts/Statemachine/CycleTimer.gd index ff5eb0e..f73756a 100644 --- a/scripts/Statemachine/CycleTimer.gd +++ b/scripts/Statemachine/CycleTimer.gd @@ -19,7 +19,7 @@ func apply(): for index in len(bullets): var bullet = bullets[index] var offset = Vector2.from_angle(periodPercent(index)) - offset.y *= 0.5 + offset.y *= 0.25 bullet.position = bullet.launcher.position + offset * distance bullet.scale = Vector2.ONE * (1 + offset.y) func host(bullet: BulletBase):