1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-05-28 06:51:54 +08:00

feat(子弹): 为魔法飞弹添加命中动画和音效

refactor(角色): 将MTY角色从猫头鹰改为狗熊宝宝并调整攻击逻辑

fix(子弹): 修复ParryBall和Parrier子弹的实例有效性检查

style(场景): 清理场景文件中的冗余属性

feat(工具): 为findClosetBulletCanDamage添加最大距离参数

chore(配置): 更新测试用的波次配置
This commit is contained in:
2026-04-24 18:09:24 +08:00
parent f27d75befd
commit 3a8c48dae7
12 changed files with 161 additions and 60 deletions
+64 -4
View File
@@ -3,9 +3,61 @@
[ext_resource type="PackedScene" uid="uid://crtdkysmnkith" path="res://components/Abstracts/BulletBase.tscn" id="1_4sjx4"]
[ext_resource type="Script" uid="uid://b1y8r5xrhhso" path="res://scripts/Contents/Bullets/MagicMissle.gd" id="2_lrw10"]
[ext_resource type="Texture2D" uid="uid://bl1i26ovfpxwc" path="res://resources/bullets/magic-missle/tr.webp" id="2_ross6"]
[ext_resource type="AudioStream" uid="uid://cn876dtp1ypqx" path="res://resources/sounds/effect/Collect.wav" id="3_3jny5"]
[ext_resource type="Texture2D" uid="uid://bkiaaucejquik" path="res://resources/bullets/light-express/Everlasting_Rainbow.png" id="3_lrw10"]
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_rugh7"]
height = 76.0
[sub_resource type="Animation" id="Animation_3jny5"]
resource_name = "destroy"
length = 0.5
step = 0.1
[sub_resource type="Animation" id="Animation_grb5l"]
tracks/0/type = "audio"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("audio")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"clips": [{
"end_offset": 0.0,
"start_offset": 0.0,
"stream": ExtResource("3_3jny5")
}],
"times": PackedFloat32Array(0)
}
tracks/0/use_blend = true
[sub_resource type="Animation" id="Animation_jigkj"]
resource_name = "loop"
loop_mode = 1
step = 0.1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 6.28319]
}
[sub_resource type="Animation" id="Animation_mgg76"]
resource_name = "spawn"
[sub_resource type="AnimationLibrary" id="AnimationLibrary_s86wr"]
_data = {
&"destroy": SubResource("Animation_3jny5"),
&"hit": SubResource("Animation_grb5l"),
&"loop": SubResource("Animation_jigkj"),
&"spawn": SubResource("Animation_mgg76")
}
[sub_resource type="CircleShape2D" id="CircleShape2D_lrw10"]
radius = 18.027756
[sub_resource type="Curve" id="Curve_lrw10"]
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.2, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
@@ -48,12 +100,20 @@ script = ExtResource("2_lrw10")
penerate = 1.0
penerateDamageReduction = 0.1
[node name="animator" parent="texture" parent_id_path=PackedInt32Array(162977358) index="0" unique_id=1114087117]
libraries/ = SubResource("AnimationLibrary_s86wr")
[node name="audio" parent="texture" parent_id_path=PackedInt32Array(162977358) index="1" unique_id=1167114186]
stream = ExtResource("3_3jny5")
[node name="static" type="Sprite2D" parent="texture" parent_id_path=PackedInt32Array(162977358) index="2" unique_id=1008850333]
texture = ExtResource("2_ross6")
position = Vector2(-58.999996, 0)
scale = Vector2(0.79195106, 0.79195106)
texture = ExtResource("3_lrw10")
[node name="hitbox" parent="." index="1" unique_id=175349408]
rotation = 1.5707964
shape = SubResource("CapsuleShape2D_rugh7")
shape = SubResource("CircleShape2D_lrw10")
[node name="trail" type="GPUParticles2D" parent="." index="2" unique_id=1403224943]
z_index = -1
+8 -8
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://brt2q316hrswe"]
[gd_scene format=3 uid="uid://brt2q316hrswe"]
[ext_resource type="PackedScene" uid="uid://crtdkysmnkith" path="res://components/Abstracts/BulletBase.tscn" id="1_57y3f"]
[ext_resource type="Script" uid="uid://bi7nde2rs0w4m" path="res://scripts/Contents/Bullets/Parrier.gd" id="2_li4th"]
@@ -43,23 +43,23 @@ _data = {
radius = 60.0
height = 300.0
[node name="Parrier" instance=ExtResource("1_57y3f")]
[node name="Parrier" unique_id=2089111975 instance=ExtResource("1_57y3f")]
script = ExtResource("2_li4th")
parryRate = 0.0
displayName = "爪击"
baseDamage = 5.0
penerate = 1.0
autoSpawnAnimation = true
freeAfterSpawn = true
[node name="texture" parent="." index="0"]
[node name="texture" parent="." index="0" unique_id=162977358]
modulate = Color(1, 1, 1, 0)
scale = Vector2(0.8, 0.8)
[node name="animator" parent="texture" index="0"]
libraries = {
&"": SubResource("AnimationLibrary_k4ctn")
}
[node name="animator" parent="texture" index="0" unique_id=1114087117]
libraries/ = SubResource("AnimationLibrary_k4ctn")
[node name="hitbox" parent="." index="1"]
[node name="hitbox" parent="." index="1" unique_id=175349408]
position = Vector2(150, 0)
rotation = 1.5707964
shape = SubResource("CapsuleShape2D_li4th")
+7 -8
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=9 format=3 uid="uid://bhp1ru3rjwkbf"]
[gd_scene 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"]
@@ -119,8 +119,9 @@ _data = {
[sub_resource type="RectangleShape2D" id="RectangleShape2D_q2yg1"]
size = Vector2(146, 26)
[node name="QKSword" instance=ExtResource("1_1ke2b")]
[node name="QKSword" unique_id=1066937094 instance=ExtResource("1_1ke2b")]
script = ExtResource("2_x26jp")
displayName = "乾坤剑"
speed = 20.0
baseDamage = 25.0
penerate = 1.0
@@ -128,15 +129,13 @@ penerateDamageReduction = 0.2
lifeTime = 3000.0
autoSpawnAnimation = true
[node name="texture" parent="." index="0"]
[node name="texture" parent="." index="0" unique_id=162977358]
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="animator" parent="texture" index="0" unique_id=1114087117]
libraries/ = SubResource("AnimationLibrary_evtlt")
[node name="hitbox" parent="." index="1"]
[node name="hitbox" parent="." index="1" unique_id=175349408]
shape = SubResource("RectangleShape2D_q2yg1")
+38 -13
View File
@@ -1,9 +1,15 @@
[gd_scene load_steps=8 format=3 uid="uid://b5ysxff1ujv4l"]
[gd_scene format=3 uid="uid://b5ysxff1ujv4l"]
[ext_resource type="PackedScene" uid="uid://cvogxi7mktumf" path="res://components/Abstracts/EntityBase.tscn" id="1_8og84"]
[ext_resource type="Script" uid="uid://b80jr04qpitly" path="res://scripts/Contents/Characters/MTY.gd" id="2_hjlm2"]
[ext_resource type="Texture2D" uid="uid://b710rxx6yq2x1" path="res://resources/characters/bird/tera-a.svg" id="3_gxm04"]
[ext_resource type="Texture2D" uid="uid://cmmvtcahw5y5e" path="res://resources/characters/bird/tera-d.svg" id="4_ndje2"]
[ext_resource type="Texture2D" uid="uid://54vaedgxog4i" path="res://resources/characters/bear/bear-walk-h.svg" id="3_8xxno"]
[ext_resource type="Texture2D" uid="uid://cmapkmo8ck3g7" path="res://resources/characters/bear/bear-walk-a.svg" id="4_l1n2d"]
[ext_resource type="Texture2D" uid="uid://b8j8ee5jrcdlq" path="res://resources/characters/bear/bear-walk-b.svg" id="5_l3wi2"]
[ext_resource type="Texture2D" uid="uid://olok886lpghg" path="res://resources/characters/bear/bear-walk-c.svg" id="6_6o5k1"]
[ext_resource type="Texture2D" uid="uid://xka0dvhh8hhq" path="res://resources/characters/bear/bear-walk-d.svg" id="7_15shm"]
[ext_resource type="Texture2D" uid="uid://vm1drbho5j4t" path="res://resources/characters/bear/bear-walk-e.svg" id="8_vlc6w"]
[ext_resource type="Texture2D" uid="uid://c46b6dsltjglj" path="res://resources/characters/bear/bear-walk-f.svg" id="9_fbcw4"]
[ext_resource type="Texture2D" uid="uid://vaglbapaj4e0" path="res://resources/characters/bear/bear-walk-g.svg" id="10_0ftal"]
[sub_resource type="SpriteFrames" id="SpriteFrames_gxm04"]
animations = [{
@@ -22,7 +28,7 @@ animations = [{
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("3_gxm04")
"texture": ExtResource("3_8xxno")
}],
"loop": true,
"name": &"idle",
@@ -30,22 +36,40 @@ animations = [{
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("3_gxm04")
"texture": ExtResource("4_l1n2d")
}, {
"duration": 1.0,
"texture": ExtResource("4_ndje2")
"texture": ExtResource("5_l3wi2")
}, {
"duration": 1.0,
"texture": ExtResource("6_6o5k1")
}, {
"duration": 1.0,
"texture": ExtResource("7_15shm")
}, {
"duration": 1.0,
"texture": ExtResource("8_vlc6w")
}, {
"duration": 1.0,
"texture": ExtResource("9_fbcw4")
}, {
"duration": 1.0,
"texture": ExtResource("10_0ftal")
}, {
"duration": 1.0,
"texture": ExtResource("3_8xxno")
}],
"loop": true,
"name": &"walk",
"speed": 0.5
"speed": 10.0
}]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_fdkbo"]
size = Vector2(138, 181)
size = Vector2(232, 149)
[node name="MTY" instance=ExtResource("1_8og84")]
[node name="MTY" unique_id=1673957637 instance=ExtResource("1_8og84")]
script = ExtResource("2_hjlm2")
displayName = "猫头鹰"
displayName = "狗熊宝宝"
drops = Array[int]([0, 1, 3])
dropCounts = Array[Vector2]([Vector2(3, 6), Vector2(2, 4), Vector2(1, 3)])
useStatic = true
@@ -60,13 +84,14 @@ scale = Vector2(0.23718655, 0.23718655)
[node name="staticAnimation" parent="texture" index="1"]
position = Vector2(0, -27)
scale = Vector2(1.42485, 1.42485)
scale = Vector2(1.1823181, 1.1823181)
sprite_frames = SubResource("SpriteFrames_hjlm2")
animation = &"idle"
animation = &"walk"
frame_progress = 0.5124606
[node name="hitbox" parent="texture/hurtbox" index="0"]
position = Vector2(0, -26.5)
shape = SubResource("RectangleShape2D_fdkbo")
[node name="statebar" parent="." index="4"]
position = Vector2(0, -199)
position = Vector2(0, -195)
+2 -2
View File
@@ -2,7 +2,7 @@
[ext_resource type="PackedScene" uid="uid://cvogxi7mktumf" path="res://components/Abstracts/EntityBase.tscn" id="1_e5pl8"]
[ext_resource type="Script" uid="uid://cthtupc6dtbav" path="res://scripts/Contents/Characters/Rooster.gd" id="2_oqdqd"]
[ext_resource type="PackedScene" uid="uid://c6d23yqjhe0ju" path="res://components/Weapons/MagicMissle.tscn" id="3_da2ca"]
[ext_resource type="PackedScene" uid="uid://c0n3igy4hucrg" path="res://components/Weapons/PurpleCrystal.tscn" id="3_da2ca"]
[ext_resource type="AudioStream" uid="uid://cdrevrq7n6yqa" path="res://resources/sounds/effect/Boing.mp3" id="4_66s6c"]
[ext_resource type="AudioStream" uid="uid://benyec5bqni0b" path="res://resources/sounds/effect/Chomp.wav" id="4_k0yme"]
[ext_resource type="AudioStream" uid="uid://dmxh3bpk8vyy5" path="res://resources/sounds/effect/Coin.mp3" id="5_xnbhq"]
@@ -101,7 +101,7 @@ process_material = SubResource("ParticleProcessMaterial_joj4g")
[node name="weaponStore" parent="." index="2"]
process_mode = 4
[node name="MagicMissle" parent="weaponStore" index="0" unique_id=2085048785 instance=ExtResource("3_da2ca")]
[node name="PurpleCrystal" parent="weaponStore" index="0" unique_id=839753399 instance=ExtResource("3_da2ca")]
debugRebuild = false
[node name="sprint" parent="sounds" index="0"]
+1 -1
View File
@@ -17,7 +17,6 @@ func ai():
speedV2 *= 0.995
speed = speedV2.length()
PresetBulletAI.forward(self , speedV2.angle())
texture.rotation += deg_to_rad(sqrt(speed))
func succeedToHit(_dmg: float, entity: EntityBase):
if !accelerating:
@@ -38,3 +37,4 @@ func succeedToHit(_dmg: float, entity: EntityBase):
bullet.baseDamage = baseDamage
roundBullets.append(bullet)
powerScale = 0
animator.play("hit")
+3 -2
View File
@@ -6,8 +6,8 @@ class_name ParrierBullet
var parryiedTimes: int = 0
var maxParryTimes: int = 1
var maxBallCount: int = 3
var atk: float = 0
var reflectRate: float = 0.25
var atk: float = 1
var reflectRate: float = 0.3
func spawn():
var varians = randi_range(0, 1)
@@ -29,6 +29,7 @@ func succeedToHit(_dmg: float, entity: EntityBase):
eff.shot()
entity.impluse((effSpawn - position).normalized() * 450)
func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
if !is_instance_valid(bullet.launcher): return
if BulletTool.canDamage(bullet, launcher): # 其他子弹可以使当前子弹的发射者受伤吗?
if parryiedTimes < maxParryTimes && MathTool.rate(parryRate): # 一个刀光最多格挡多少个敌方子弹?
parryiedTimes += 1
+3 -2
View File
@@ -2,14 +2,15 @@ extends BulletBase
class_name ParryBallBullet
var cycler: CycleTimer
var atk: float = 0
var atk: float = 1
func spawn():
cycler = launcher.getOrCreateCycleTimer("parry")
cycler.host(self )
launcher.sprintMultiplier += 1
func destroy(_beacuseMap: bool):
launcher.sprintMultiplier -= 1
if is_instance_valid(launcher):
launcher.sprintMultiplier -= 1
func ai():
PresetBulletAI.selfRotate(self , 5)
hitbox.disabled = !launcher.sprinting # 玩家在冲刺时气的碰撞箱才生效
+9 -9
View File
@@ -27,7 +27,7 @@ func spawn():
if MathTool.rate(0.01):
setStage(2)
func ai():
PresetEntityAI.follow(self, currentFocusedBoss, 250)
PresetEntityAI.follow(self , currentFocusedBoss, 250)
for i in len(attackCooldownMap.keys()):
tryAttack(i)
func enterStage(stage):
@@ -50,7 +50,7 @@ func attack(type):
playSound("attack0")
for i in randi_range(20, 30):
if !is_instance_valid(currentFocusedBoss): return false
for bullet in BulletBase.generate(ComponentManager.getBullet("ArrowSeven"), self, findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))):
for bullet in BulletBase.generate(ComponentManager.getBullet("ArrowSeven"), self , findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))):
bullet.tracer = currentFocusedBoss
bullet.position += MathTool.sampleInCircle(50)
await TickTool.millseconds(50)
@@ -59,10 +59,10 @@ func attack(type):
await sprintTo(currentFocusedBoss.position - Vector2(MathTool.randomChoiceFrom([-300, 300]), 300), 0.25)
var count = randi_range(6, 8)
for i in range(count):
BulletBase.generate(ComponentManager.getBullet("SunDance"), self, weaponPos, deg_to_rad(360.0 / count * i))
BulletBase.generate(ComponentManager.getBullet("SunDance"), self , weaponPos, deg_to_rad(360.0 / count * i))
elif type == 2:
for i in range(13):
for bullet in BulletBase.generate(ComponentManager.getBullet("ForeverRainbow"), self, weaponPos, 0):
for bullet in BulletBase.generate(ComponentManager.getBullet("ForeverRainbow"), self , weaponPos, 0):
bullet.rotation = 360 / 13.0 * i
elif type == 3:
if !is_instance_valid(currentFocusedBoss): return false
@@ -72,7 +72,7 @@ func attack(type):
currentInvinsible = true
playSound("attack3")
await TickTool.millseconds(500)
BulletBase.generate(ComponentManager.getBullet("BearSprint"), self, weaponPos, 0)
BulletBase.generate(ComponentManager.getBullet("BearSprint"), self , weaponPos, 0)
await trySprint()
sprintParticle.emitting = false
canRunAi = true
@@ -84,7 +84,7 @@ func attack(type):
var count = randi_range(8, 12)
for i in range(count):
if !is_instance_valid(currentFocusedBoss): return false
for bullet in BulletBase.generate(ComponentManager.getBullet("ArrowSeven"), self, findWeaponAnchor("normal"), deg_to_rad(360.0 / count * i)):
for bullet in BulletBase.generate(ComponentManager.getBullet("ArrowSeven"), self , findWeaponAnchor("normal"), deg_to_rad(360.0 / count * i)):
bullet.tracer = currentFocusedBoss
await TickTool.millseconds(830.0 / count)
return false
@@ -93,7 +93,7 @@ func attack(type):
var count = randi_range(20, 30)
for i in range(count):
if !is_instance_valid(currentFocusedBoss): return false
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self, currentFocusedBoss.position, 0):
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self , currentFocusedBoss.position, 0):
bullet.rotation = deg_to_rad(360.0 / count * i)
await TickTool.millseconds(1670.0 / count)
return false
@@ -101,7 +101,7 @@ func attack(type):
playSound("attack6")
for i in 16:
if !is_instance_valid(currentFocusedBoss): return false
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self, currentFocusedBoss.position, 0):
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self , currentFocusedBoss.position, 0):
bullet.position += MathTool.sampleInCircle(600)
bullet.look_at(currentFocusedBoss.position + MathTool.sampleInCircle(50))
await TickTool.millseconds(100)
@@ -113,7 +113,7 @@ func attack(type):
if !is_instance_valid(currentFocusedBoss): return false
playSound("attack7")
for i in 16:
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self, currentFocusedBoss.position, 0):
for bullet in BulletBase.generate(ComponentManager.getBullet("LightGun"), self , currentFocusedBoss.position, 0):
bullet.rotation_degrees += initAngle
bullet.rotation -= angle / 2
bullet.rotation += angle / 16 * i
+18 -9
View File
@@ -5,23 +5,32 @@ func register():
fields[FieldStore.Entity.MAX_HEALTH] = 400
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.9
attackCooldownMap[0] = 2000
attackCooldownMap[1] = 250
attackCooldownMap[1] = 750
attackCooldownMap[2] = 450
sprintMultiplier = 5
func spawn():
texture.play("walk")
func ai():
PresetEntityAI.follow(self, currentFocusedBoss)
tryAttack(0)
tryAttack(1)
PresetEntityAI.follow(self , currentFocusedBoss)
for i in 3:
tryAttack(i)
func attack(type: int):
if type == 0:
trySprint()
elif type == 1:
BulletBase.generate(ComponentManager.getBullet("MTYSprint"), self, position, 0)
var track = getTrackingAnchor()
var bullet = BulletTool.findClosetBulletCanDamage(track, get_tree(), self , 400)
if is_instance_valid(bullet):
BulletBase.generate(ComponentManager.getBullet("Parrier"), self , track, track.angle_to_point(bullet.position))
elif type == 2:
if is_instance_valid(currentFocusedBoss):
var track = getTrackingAnchor()
if currentFocusedBoss.position.distance_to(track) <= 400:
BulletBase.generate(ComponentManager.getBullet("Parrier"), self , track, track.angle_to_point(currentFocusedBoss.position))
return true
func sprint():
var target = BulletTool.findClosetBulletCanDamage(position, get_tree(), self)
var track = getTrackingAnchor()
var target = BulletTool.findClosetBulletCanDamage(track, get_tree(), self , 300)
if is_instance_valid(target):
if position.distance_to(target.position) <= 200:
var dir = (target.position - position).rotated(MathTool.randomChoiceFrom([-1, 1]) * deg_to_rad(90))
move(dir.normalized() * sprintMultiplier, true)
var dir = (target.position - track).rotated(MathTool.randomChoiceFrom([-1, 1]) * deg_to_rad(90))
move(dir.normalized() * sprintMultiplier, true)
+4 -1
View File
@@ -59,9 +59,12 @@ static var WAVE_MOWING = [
Wave.create("Dog", 15, 30, false, 0, INF, 1),
Wave.create("MTY", 1, 5, false, 0, INF, 1),
]
static var WAVE_TESTMOB = [
Wave.create("MTY", 1, 1, false, 0, INF, 1)
]
static var WAVE_EMPTY = []
static var waveReleaseConfig = [WAVE_TESTBOSS_ALL, 1]
static var waveDebugConfig = [WAVE_TESTBOSS_ALL, 1]
static var waveDebugConfig = [WAVE_TESTMOB, 1]
static var current: int = startWith(waveReleaseConfig[1]) if WorldManager.isRelease() else startWith(waveDebugConfig[1])
static var data = waveReleaseConfig[0] if WorldManager.isRelease() else waveDebugConfig[0]
+4 -1
View File
@@ -28,7 +28,7 @@ static func findClosetBullet(to: Vector2, fromTree: SceneTree) -> BulletBase:
lastDistance = to.distance_to(bullet.position)
result = bullet
return result
static func findClosetBulletCanDamage(to: Vector2, fromTree: SceneTree, target: EntityBase) -> BulletBase:
static func findClosetBulletCanDamage(to: Vector2, fromTree: SceneTree, target: EntityBase, maxDistance: float = INF) -> BulletBase:
var result: BulletBase = null
var lastDistance = INF
for bullet in fromTree.get_nodes_in_group("bullets"):
@@ -36,4 +36,7 @@ static func findClosetBulletCanDamage(to: Vector2, fromTree: SceneTree, target:
if to.distance_to(bullet.position) < lastDistance:
lastDistance = to.distance_to(bullet.position)
result = bullet
if is_instance_valid(result):
if result.position.distance_to(to) > maxDistance:
return null
return result